From e907be8e54a3f59d03e0169b8cbc2e199d876794 Mon Sep 17 00:00:00 2001 From: ra Date: Sat, 23 Jan 2010 19:37:49 +0000 Subject: [PATCH] Move mezzo where it going to live --- LICENSE.txt | 30 + Mezzo.dox | 1078 +++++ SConstruct | 50 + lib/.sconsign | 5 + lib/SConscript | 16 + lib/expat/.sconsign | 8 + lib/expat/ascii.h | 86 + lib/expat/asciitab.h | 37 + lib/expat/iasciitab.h | 38 + lib/expat/latin1tab.h | 37 + lib/expat/nametab.h | 150 + lib/expat/utf8tab.h | 38 + lib/expat/xmldef.h | 52 + lib/expat/xmlparse.c | 3925 +++++++++++++++++ lib/expat/xmlparse.h | 527 +++ lib/expat/xmlrole.c | 1265 ++++++ lib/expat/xmlrole.h | 99 + lib/expat/xmltok.c | 1557 +++++++ lib/expat/xmltok.h | 301 ++ lib/expat/xmltok_impl.c | 1765 ++++++++ lib/expat/xmltok_impl.h | 46 + lib/expat/xmltok_ns.c | 96 + lib/portaudio/CVS/Entries | 4 + lib/portaudio/CVS/Entries.Log | 12 + lib/portaudio/CVS/Repository | 1 + lib/portaudio/CVS/Root | 1 + lib/portaudio/LICENSE.txt | 65 + lib/portaudio/README.txt | 81 + lib/portaudio/docs/CVS/Entries | 22 + lib/portaudio/docs/CVS/Repository | 1 + lib/portaudio/docs/CVS/Root | 1 + lib/portaudio/docs/index.html | 43 + lib/portaudio/docs/pa_impl_guide.html | 196 + lib/portaudio/docs/pa_impl_startstop.html | 190 + lib/portaudio/docs/pa_tut_asio.html | 55 + lib/portaudio/docs/pa_tut_callback.html | 91 + lib/portaudio/docs/pa_tut_devs.html | 65 + lib/portaudio/docs/pa_tut_explore.html | 42 + lib/portaudio/docs/pa_tut_init.html | 43 + lib/portaudio/docs/pa_tut_mac.html | 41 + lib/portaudio/docs/pa_tut_open.html | 52 + lib/portaudio/docs/pa_tut_oss.html | 45 + lib/portaudio/docs/pa_tut_over.html | 86 + lib/portaudio/docs/pa_tut_pc.html | 63 + lib/portaudio/docs/pa_tut_run.html | 56 + lib/portaudio/docs/pa_tut_rw.html | 77 + lib/portaudio/docs/pa_tut_term.html | 47 + lib/portaudio/docs/pa_tut_util.html | 55 + lib/portaudio/docs/pa_tutorial.html | 44 + lib/portaudio/docs/portaudio_h.txt | 427 ++ lib/portaudio/docs/portaudio_icmc2001.pdf | Bin 0 -> 50968 bytes lib/portaudio/docs/releases.html | 226 + lib/portaudio/index.html | 89 + lib/portaudio/macproj/CVS/Entries | 2 + lib/portaudio/macproj/CVS/Repository | 1 + lib/portaudio/macproj/CVS/Root | 1 + lib/portaudio/macproj/portaudio.mcp | Bin 0 -> 81930 bytes lib/portaudio/pa_common/CVS/Entries | 7 + lib/portaudio/pa_common/CVS/Repository | 1 + lib/portaudio/pa_common/CVS/Root | 1 + lib/portaudio/pa_common/pa_convert.c | 470 ++ lib/portaudio/pa_common/pa_host.h | 189 + lib/portaudio/pa_common/pa_lib.c | 806 ++++ lib/portaudio/pa_common/pa_trace.c | 83 + lib/portaudio/pa_common/pa_trace.h | 67 + lib/portaudio/pa_common/portaudio.h | 463 ++ lib/portaudio/pa_linux_oss/CVS/Entries | 2 + lib/portaudio/pa_linux_oss/CVS/Repository | 1 + lib/portaudio/pa_linux_oss/CVS/Root | 1 + lib/portaudio/pa_linux_oss/deprecated | 1 + lib/portaudio/pa_mac/CVS/Entries | 3 + lib/portaudio/pa_mac/CVS/Repository | 1 + lib/portaudio/pa_mac/CVS/Root | 1 + lib/portaudio/pa_mac/pa_mac.c | 1687 +++++++ lib/portaudio/pa_mac/patest_devinfo.c | 1 + lib/portaudio/pa_mac_core/CVS/Entries | 3 + lib/portaudio/pa_mac_core/CVS/Repository | 1 + lib/portaudio/pa_mac_core/CVS/Root | 1 + lib/portaudio/pa_mac_core/Makefile | 20 + lib/portaudio/pa_mac_core/pa_mac_core.c | 1915 ++++++++ lib/portaudio/pa_tests/CVS/Entries | 30 + lib/portaudio/pa_tests/CVS/Repository | 1 + lib/portaudio/pa_tests/CVS/Root | 1 + lib/portaudio/pa_tests/debug_dual.c | 183 + lib/portaudio/pa_tests/debug_multi_in.c | 179 + lib/portaudio/pa_tests/debug_multi_out.c | 144 + lib/portaudio/pa_tests/debug_record.c | 338 ++ lib/portaudio/pa_tests/debug_sine.c | 201 + lib/portaudio/pa_tests/debug_test1.c | 114 + lib/portaudio/pa_tests/pa_devs.c | 99 + lib/portaudio/pa_tests/pa_fuzz.c | 156 + lib/portaudio/pa_tests/pa_minlat.c | 172 + lib/portaudio/pa_tests/paqa_devs.c | 322 ++ lib/portaudio/pa_tests/paqa_errs.c | 330 ++ lib/portaudio/pa_tests/patest1.c | 114 + lib/portaudio/pa_tests/patest_clip.c | 156 + lib/portaudio/pa_tests/patest_dither.c | 152 + lib/portaudio/pa_tests/patest_latency.c | 176 + lib/portaudio/pa_tests/patest_leftright.c | 168 + lib/portaudio/pa_tests/patest_longsine.c | 137 + lib/portaudio/pa_tests/patest_many.c | 195 + lib/portaudio/pa_tests/patest_maxsines.c | 196 + lib/portaudio/pa_tests/patest_pink.c | 245 + lib/portaudio/pa_tests/patest_record.c | 323 ++ lib/portaudio/pa_tests/patest_ringmix.c | 41 + lib/portaudio/pa_tests/patest_saw.c | 118 + lib/portaudio/pa_tests/patest_sine.c | 141 + lib/portaudio/pa_tests/patest_sine8.c | 184 + lib/portaudio/pa_tests/patest_sine_time.c | 205 + lib/portaudio/pa_tests/patest_stop.c | 285 ++ lib/portaudio/pa_tests/patest_sync.c | 227 + lib/portaudio/pa_tests/patest_wire.c | 151 + lib/portaudio/pa_unix_oss/CVS/Entries | 6 + lib/portaudio/pa_unix_oss/CVS/Repository | 1 + lib/portaudio/pa_unix_oss/CVS/Root | 1 + lib/portaudio/pa_unix_oss/Makefile | 27 + lib/portaudio/pa_unix_oss/Makefile.in | 27 + lib/portaudio/pa_unix_oss/Makefile_freebsd | 36 + lib/portaudio/pa_unix_oss/pa_unix.c | 1113 +++++ lib/portaudio/pa_unix_oss/pa_unix.h | 141 + lib/portaudio/pa_unix_oss/pa_unix_oss.c | 393 ++ lib/portaudio/pa_win_ds/CVS/Entries | 5 + lib/portaudio/pa_win_ds/CVS/Repository | 1 + lib/portaudio/pa_win_ds/CVS/Root | 1 + lib/portaudio/pa_win_ds/dsound_wrapper.c | 466 ++ lib/portaudio/pa_win_ds/dsound_wrapper.h | 106 + lib/portaudio/pa_win_ds/pa_dsound.c | 1042 +++++ lib/portaudio/pa_win_ds/portaudio.def | 28 + lib/portaudio/pa_win_wmme/CVS/Entries | 3 + lib/portaudio/pa_win_wmme/CVS/Repository | 1 + lib/portaudio/pa_win_wmme/CVS/Root | 1 + lib/portaudio/pa_win_wmme/Makefile | 21 + lib/portaudio/pa_win_wmme/pa_win_wmme.c | 1709 +++++++ lib/portaudio/pablio/CVS/Entries | 9 + lib/portaudio/pablio/CVS/Repository | 1 + lib/portaudio/pablio/CVS/Root | 1 + lib/portaudio/pablio/pablio.c | 327 ++ lib/portaudio/pablio/pablio.h | 109 + lib/portaudio/pablio/ringbuffer.c | 199 + lib/portaudio/pablio/ringbuffer.h | 101 + lib/portaudio/pablio/test_rw.c | 99 + lib/portaudio/pablio/test_rw_echo.c | 123 + lib/portaudio/pablio/test_w_saw.c | 108 + lib/portaudio/pablio/test_w_saw8.c | 106 + lib/portaudio/winproj/CVS/Entries | 5 + lib/portaudio/winproj/CVS/Entries.Log | 2 + lib/portaudio/winproj/CVS/Repository | 1 + lib/portaudio/winproj/CVS/Root | 1 + lib/portaudio/winproj/PAStaticDS/CVS/Entries | 4 + .../winproj/PAStaticDS/CVS/Repository | 1 + lib/portaudio/winproj/PAStaticDS/CVS/Root | 1 + .../winproj/PAStaticDS/PAStaticDS.dep | 38 + .../winproj/PAStaticDS/PAStaticDS.dsp | 232 + .../winproj/PAStaticDS/PAStaticDS.mak | 330 ++ .../winproj/PAStaticWMME/CVS/Entries | 4 + .../winproj/PAStaticWMME/CVS/Repository | 1 + lib/portaudio/winproj/PAStaticWMME/CVS/Root | 1 + .../winproj/PAStaticWMME/PAStaticWMME.dep | 26 + .../winproj/PAStaticWMME/PAStaticWMME.dsp | 108 + .../winproj/PAStaticWMME/PAStaticWMME.mak | 310 ++ lib/portaudio/winproj/Readme.txt | 6 + lib/portaudio/winproj/WinVC.dsw | 82 + lib/portaudio/winproj/WinVC.ncb | Bin 0 -> 82944 bytes lib/portaudio/winproj/WinVC.opt | Bin 0 -> 58880 bytes lib/posh/.sconsign | 5 + lib/posh/posh.c | 918 ++++ lib/posh/posh.h | 998 +++++ scons/scons-LICENSE | 25 + scons/scons-README | 204 + scons/scons-local-0.95/SCons/Action.py | 447 ++ scons/scons-local-0.95/SCons/Builder.py | 606 +++ scons/scons-local-0.95/SCons/Conftest.py | 483 ++ scons/scons-local-0.95/SCons/Debug.py | 102 + scons/scons-local-0.95/SCons/Defaults.py | 271 ++ scons/scons-local-0.95/SCons/Environment.py | 1109 +++++ scons/scons-local-0.95/SCons/Errors.py | 70 + scons/scons-local-0.95/SCons/Executor.py | 171 + scons/scons-local-0.95/SCons/Job.py | 257 ++ scons/scons-local-0.95/SCons/Node/Alias.py | 101 + scons/scons-local-0.95/SCons/Node/FS.py | 1706 +++++++ scons/scons-local-0.95/SCons/Node/Python.py | 81 + scons/scons-local-0.95/SCons/Node/__init__.py | 877 ++++ .../scons-local-0.95/SCons/Optik/__init__.py | 32 + scons/scons-local-0.95/SCons/Optik/errors.py | 55 + scons/scons-local-0.95/SCons/Optik/option.py | 388 ++ .../SCons/Optik/option_parser.py | 730 +++ .../SCons/Options/BoolOption.py | 88 + .../SCons/Options/EnumOption.py | 101 + .../SCons/Options/ListOption.py | 131 + .../SCons/Options/PackageOption.py | 106 + .../SCons/Options/PathOption.py | 85 + .../SCons/Options/__init__.py | 240 + .../SCons/Platform/__init__.py | 123 + scons/scons-local-0.95/SCons/Platform/aix.py | 62 + .../scons-local-0.95/SCons/Platform/cygwin.py | 47 + scons/scons-local-0.95/SCons/Platform/hpux.py | 38 + scons/scons-local-0.95/SCons/Platform/irix.py | 38 + scons/scons-local-0.95/SCons/Platform/os2.py | 49 + .../scons-local-0.95/SCons/Platform/posix.py | 231 + .../scons-local-0.95/SCons/Platform/sunos.py | 38 + .../scons-local-0.95/SCons/Platform/win32.py | 327 ++ scons/scons-local-0.95/SCons/SConf.py | 727 +++ scons/scons-local-0.95/SCons/Scanner/C.py | 46 + scons/scons-local-0.95/SCons/Scanner/D.py | 54 + .../scons-local-0.95/SCons/Scanner/Fortran.py | 49 + scons/scons-local-0.95/SCons/Scanner/IDL.py | 43 + scons/scons-local-0.95/SCons/Scanner/Prog.py | 85 + .../SCons/Scanner/__init__.py | 298 ++ .../SCons/Script/SConscript.py | 716 +++ .../scons-local-0.95/SCons/Script/__init__.py | 1104 +++++ scons/scons-local-0.95/SCons/Sig/MD5.py | 93 + scons/scons-local-0.95/SCons/Sig/TimeStamp.py | 75 + scons/scons-local-0.95/SCons/Sig/__init__.py | 446 ++ scons/scons-local-0.95/SCons/Taskmaster.py | 439 ++ scons/scons-local-0.95/SCons/Tool/386asm.py | 54 + .../scons-local-0.95/SCons/Tool/BitKeeper.py | 58 + scons/scons-local-0.95/SCons/Tool/CVS.py | 66 + .../scons-local-0.95/SCons/Tool/JavaCommon.py | 225 + scons/scons-local-0.95/SCons/Tool/Perforce.py | 93 + .../SCons/Tool/PharLapCommon.py | 132 + scons/scons-local-0.95/SCons/Tool/RCS.py | 55 + scons/scons-local-0.95/SCons/Tool/SCCS.py | 55 + .../scons-local-0.95/SCons/Tool/Subversion.py | 62 + scons/scons-local-0.95/SCons/Tool/__init__.py | 341 ++ scons/scons-local-0.95/SCons/Tool/aixc++.py | 53 + scons/scons-local-0.95/SCons/Tool/aixcc.py | 68 + scons/scons-local-0.95/SCons/Tool/aixf77.py | 74 + scons/scons-local-0.95/SCons/Tool/aixlink.py | 70 + scons/scons-local-0.95/SCons/Tool/ar.py | 56 + scons/scons-local-0.95/SCons/Tool/as.py | 67 + scons/scons-local-0.95/SCons/Tool/bcc32.py | 76 + scons/scons-local-0.95/SCons/Tool/c++.py | 87 + scons/scons-local-0.95/SCons/Tool/cc.py | 72 + scons/scons-local-0.95/SCons/Tool/default.py | 44 + scons/scons-local-0.95/SCons/Tool/dmd.py | 196 + scons/scons-local-0.95/SCons/Tool/dvipdf.py | 56 + scons/scons-local-0.95/SCons/Tool/dvips.py | 55 + scons/scons-local-0.95/SCons/Tool/f77.py | 73 + scons/scons-local-0.95/SCons/Tool/g++.py | 87 + scons/scons-local-0.95/SCons/Tool/g77.py | 47 + scons/scons-local-0.95/SCons/Tool/gas.py | 47 + scons/scons-local-0.95/SCons/Tool/gcc.py | 53 + scons/scons-local-0.95/SCons/Tool/gnulink.py | 50 + scons/scons-local-0.95/SCons/Tool/gs.py | 70 + scons/scons-local-0.95/SCons/Tool/hpc++.py | 76 + scons/scons-local-0.95/SCons/Tool/hpcc.py | 44 + scons/scons-local-0.95/SCons/Tool/hplink.py | 69 + scons/scons-local-0.95/SCons/Tool/icc.py | 55 + scons/scons-local-0.95/SCons/Tool/icl.py | 112 + scons/scons-local-0.95/SCons/Tool/ifl.py | 54 + scons/scons-local-0.95/SCons/Tool/ilink.py | 53 + scons/scons-local-0.95/SCons/Tool/ilink32.py | 52 + scons/scons-local-0.95/SCons/Tool/jar.py | 105 + scons/scons-local-0.95/SCons/Tool/javac.py | 110 + scons/scons-local-0.95/SCons/Tool/javah.py | 127 + scons/scons-local-0.95/SCons/Tool/latex.py | 59 + scons/scons-local-0.95/SCons/Tool/lex.py | 52 + scons/scons-local-0.95/SCons/Tool/link.py | 75 + scons/scons-local-0.95/SCons/Tool/linkloc.py | 104 + scons/scons-local-0.95/SCons/Tool/m4.py | 55 + scons/scons-local-0.95/SCons/Tool/masm.py | 68 + scons/scons-local-0.95/SCons/Tool/midl.py | 72 + scons/scons-local-0.95/SCons/Tool/mingw.py | 151 + scons/scons-local-0.95/SCons/Tool/mslib.py | 74 + scons/scons-local-0.95/SCons/Tool/mslink.py | 192 + scons/scons-local-0.95/SCons/Tool/msvc.py | 491 +++ scons/scons-local-0.95/SCons/Tool/msvs.py | 1119 +++++ scons/scons-local-0.95/SCons/Tool/nasm.py | 65 + scons/scons-local-0.95/SCons/Tool/pdflatex.py | 58 + scons/scons-local-0.95/SCons/Tool/pdftex.py | 54 + scons/scons-local-0.95/SCons/Tool/qt.py | 218 + scons/scons-local-0.95/SCons/Tool/rmic.py | 110 + scons/scons-local-0.95/SCons/Tool/sgiar.py | 60 + scons/scons-local-0.95/SCons/Tool/sgic++.py | 54 + scons/scons-local-0.95/SCons/Tool/sgicc.py | 47 + scons/scons-local-0.95/SCons/Tool/sgilink.py | 50 + scons/scons-local-0.95/SCons/Tool/sunar.py | 59 + scons/scons-local-0.95/SCons/Tool/sunc++.py | 58 + scons/scons-local-0.95/SCons/Tool/suncc.py | 47 + scons/scons-local-0.95/SCons/Tool/sunlink.py | 64 + scons/scons-local-0.95/SCons/Tool/swig.py | 65 + scons/scons-local-0.95/SCons/Tool/tar.py | 62 + scons/scons-local-0.95/SCons/Tool/tex.py | 118 + scons/scons-local-0.95/SCons/Tool/tlib.py | 45 + scons/scons-local-0.95/SCons/Tool/yacc.py | 72 + scons/scons-local-0.95/SCons/Tool/zip.py | 92 + scons/scons-local-0.95/SCons/Util.py | 1252 ++++++ scons/scons-local-0.95/SCons/Warnings.py | 100 + scons/scons-local-0.95/SCons/__init__.py | 40 + scons/scons-local-0.95/SCons/exitfuncs.py | 71 + scons/scons.py | 145 + scons/sconsign.py | 308 ++ src/.sconsign | 9 + src/Buffer.cpp | 1357 ++++++ src/Buffer.h | 310 ++ src/BufferGroup.cpp | 46 + src/BufferGroup.h | 44 + src/Cubic.cpp | 74 + src/Cubic.h | 23 + src/DiskIO.cpp | 108 + src/DiskIO.h | 81 + src/DiskReaderNode.h | 40 + src/Exceptions.cpp | 64 + src/Exceptions.h | 91 + src/ManagedFile.cpp | 42 + src/ManagedFile.h | 108 + src/ManagedFileContext.cpp | 426 ++ src/ManagedFileContext.h | 123 + src/RTDiskIONode.cpp | 81 + src/RTDiskIONode.h | 51 + src/RTGraph.cpp | 240 + src/RTGraph.h | 65 + src/RTNode.h | 67 + src/RTSineWaveNode.cpp | 53 + src/RTSineWaveNode.h | 45 + src/SConscript | 27 + src/SeqBlock.cpp | 429 ++ src/SeqBlock.h | 181 + src/SeqBlockContext.h | 69 + src/SeqDataFileBlock.cpp | 324 ++ src/SeqDataFileBlock.h | 116 + src/SeqMemBlock.cpp | 188 + src/SeqMemBlock.h | 89 + src/Sequence.Paste | 151 + src/Sequence.cpp | 1160 +++++ src/Sequence.h | 210 + src/Storable.cpp | 55 + src/Storable.h | 137 + src/Types.h | 71 + src/Util.cpp | 43 + src/Util.h | 46 + src/XMLLoadStore.cpp | 161 + src/XMLLoadStore.h | 71 + src/platform/.sconsign | 3 + src/platform/DiskFunctions.h | 68 + src/platform/posix/.sconsign | 3 + src/platform/posix/DiskFunctions.cpp | 116 + src/srsw_queue.c | 71 + src/srsw_queue.h | 15 + tests/.sconsign | 4 + tests/MezzoCacheDisplay.cpp | 123 + tests/MezzoPlay.cpp | 240 + tests/MezzoRecord.cpp | 201 + tests/MezzoTest.cpp | 236 + tests/TestManagedFileContext.cpp | 118 + 345 files changed, 65119 insertions(+) create mode 100644 LICENSE.txt create mode 100644 Mezzo.dox create mode 100644 SConstruct create mode 100644 lib/.sconsign create mode 100644 lib/SConscript create mode 100644 lib/expat/.sconsign create mode 100644 lib/expat/ascii.h create mode 100644 lib/expat/asciitab.h create mode 100644 lib/expat/iasciitab.h create mode 100644 lib/expat/latin1tab.h create mode 100644 lib/expat/nametab.h create mode 100644 lib/expat/utf8tab.h create mode 100644 lib/expat/xmldef.h create mode 100644 lib/expat/xmlparse.c create mode 100644 lib/expat/xmlparse.h create mode 100644 lib/expat/xmlrole.c create mode 100644 lib/expat/xmlrole.h create mode 100644 lib/expat/xmltok.c create mode 100644 lib/expat/xmltok.h create mode 100644 lib/expat/xmltok_impl.c create mode 100644 lib/expat/xmltok_impl.h create mode 100644 lib/expat/xmltok_ns.c create mode 100644 lib/portaudio/CVS/Entries create mode 100644 lib/portaudio/CVS/Entries.Log create mode 100644 lib/portaudio/CVS/Repository create mode 100644 lib/portaudio/CVS/Root create mode 100644 lib/portaudio/LICENSE.txt create mode 100644 lib/portaudio/README.txt create mode 100644 lib/portaudio/docs/CVS/Entries create mode 100644 lib/portaudio/docs/CVS/Repository create mode 100644 lib/portaudio/docs/CVS/Root create mode 100644 lib/portaudio/docs/index.html create mode 100644 lib/portaudio/docs/pa_impl_guide.html create mode 100644 lib/portaudio/docs/pa_impl_startstop.html create mode 100644 lib/portaudio/docs/pa_tut_asio.html create mode 100644 lib/portaudio/docs/pa_tut_callback.html create mode 100644 lib/portaudio/docs/pa_tut_devs.html create mode 100644 lib/portaudio/docs/pa_tut_explore.html create mode 100644 lib/portaudio/docs/pa_tut_init.html create mode 100644 lib/portaudio/docs/pa_tut_mac.html create mode 100644 lib/portaudio/docs/pa_tut_open.html create mode 100644 lib/portaudio/docs/pa_tut_oss.html create mode 100644 lib/portaudio/docs/pa_tut_over.html create mode 100644 lib/portaudio/docs/pa_tut_pc.html create mode 100644 lib/portaudio/docs/pa_tut_run.html create mode 100644 lib/portaudio/docs/pa_tut_rw.html create mode 100644 lib/portaudio/docs/pa_tut_term.html create mode 100644 lib/portaudio/docs/pa_tut_util.html create mode 100644 lib/portaudio/docs/pa_tutorial.html create mode 100644 lib/portaudio/docs/portaudio_h.txt create mode 100644 lib/portaudio/docs/portaudio_icmc2001.pdf create mode 100644 lib/portaudio/docs/releases.html create mode 100644 lib/portaudio/index.html create mode 100644 lib/portaudio/macproj/CVS/Entries create mode 100644 lib/portaudio/macproj/CVS/Repository create mode 100644 lib/portaudio/macproj/CVS/Root create mode 100644 lib/portaudio/macproj/portaudio.mcp create mode 100644 lib/portaudio/pa_common/CVS/Entries create mode 100644 lib/portaudio/pa_common/CVS/Repository create mode 100644 lib/portaudio/pa_common/CVS/Root create mode 100644 lib/portaudio/pa_common/pa_convert.c create mode 100644 lib/portaudio/pa_common/pa_host.h create mode 100644 lib/portaudio/pa_common/pa_lib.c create mode 100644 lib/portaudio/pa_common/pa_trace.c create mode 100644 lib/portaudio/pa_common/pa_trace.h create mode 100644 lib/portaudio/pa_common/portaudio.h create mode 100644 lib/portaudio/pa_linux_oss/CVS/Entries create mode 100644 lib/portaudio/pa_linux_oss/CVS/Repository create mode 100644 lib/portaudio/pa_linux_oss/CVS/Root create mode 100644 lib/portaudio/pa_linux_oss/deprecated create mode 100644 lib/portaudio/pa_mac/CVS/Entries create mode 100644 lib/portaudio/pa_mac/CVS/Repository create mode 100644 lib/portaudio/pa_mac/CVS/Root create mode 100644 lib/portaudio/pa_mac/pa_mac.c create mode 100644 lib/portaudio/pa_mac/patest_devinfo.c create mode 100644 lib/portaudio/pa_mac_core/CVS/Entries create mode 100644 lib/portaudio/pa_mac_core/CVS/Repository create mode 100644 lib/portaudio/pa_mac_core/CVS/Root create mode 100644 lib/portaudio/pa_mac_core/Makefile create mode 100644 lib/portaudio/pa_mac_core/pa_mac_core.c create mode 100644 lib/portaudio/pa_tests/CVS/Entries create mode 100644 lib/portaudio/pa_tests/CVS/Repository create mode 100644 lib/portaudio/pa_tests/CVS/Root create mode 100644 lib/portaudio/pa_tests/debug_dual.c create mode 100644 lib/portaudio/pa_tests/debug_multi_in.c create mode 100644 lib/portaudio/pa_tests/debug_multi_out.c create mode 100644 lib/portaudio/pa_tests/debug_record.c create mode 100644 lib/portaudio/pa_tests/debug_sine.c create mode 100644 lib/portaudio/pa_tests/debug_test1.c create mode 100644 lib/portaudio/pa_tests/pa_devs.c create mode 100644 lib/portaudio/pa_tests/pa_fuzz.c create mode 100644 lib/portaudio/pa_tests/pa_minlat.c create mode 100644 lib/portaudio/pa_tests/paqa_devs.c create mode 100644 lib/portaudio/pa_tests/paqa_errs.c create mode 100644 lib/portaudio/pa_tests/patest1.c create mode 100644 lib/portaudio/pa_tests/patest_clip.c create mode 100644 lib/portaudio/pa_tests/patest_dither.c create mode 100644 lib/portaudio/pa_tests/patest_latency.c create mode 100644 lib/portaudio/pa_tests/patest_leftright.c create mode 100644 lib/portaudio/pa_tests/patest_longsine.c create mode 100644 lib/portaudio/pa_tests/patest_many.c create mode 100644 lib/portaudio/pa_tests/patest_maxsines.c create mode 100644 lib/portaudio/pa_tests/patest_pink.c create mode 100644 lib/portaudio/pa_tests/patest_record.c create mode 100644 lib/portaudio/pa_tests/patest_ringmix.c create mode 100644 lib/portaudio/pa_tests/patest_saw.c create mode 100644 lib/portaudio/pa_tests/patest_sine.c create mode 100644 lib/portaudio/pa_tests/patest_sine8.c create mode 100644 lib/portaudio/pa_tests/patest_sine_time.c create mode 100644 lib/portaudio/pa_tests/patest_stop.c create mode 100644 lib/portaudio/pa_tests/patest_sync.c create mode 100644 lib/portaudio/pa_tests/patest_wire.c create mode 100644 lib/portaudio/pa_unix_oss/CVS/Entries create mode 100644 lib/portaudio/pa_unix_oss/CVS/Repository create mode 100644 lib/portaudio/pa_unix_oss/CVS/Root create mode 100644 lib/portaudio/pa_unix_oss/Makefile create mode 100644 lib/portaudio/pa_unix_oss/Makefile.in create mode 100644 lib/portaudio/pa_unix_oss/Makefile_freebsd create mode 100644 lib/portaudio/pa_unix_oss/pa_unix.c create mode 100644 lib/portaudio/pa_unix_oss/pa_unix.h create mode 100644 lib/portaudio/pa_unix_oss/pa_unix_oss.c create mode 100644 lib/portaudio/pa_win_ds/CVS/Entries create mode 100644 lib/portaudio/pa_win_ds/CVS/Repository create mode 100644 lib/portaudio/pa_win_ds/CVS/Root create mode 100644 lib/portaudio/pa_win_ds/dsound_wrapper.c create mode 100644 lib/portaudio/pa_win_ds/dsound_wrapper.h create mode 100644 lib/portaudio/pa_win_ds/pa_dsound.c create mode 100644 lib/portaudio/pa_win_ds/portaudio.def create mode 100644 lib/portaudio/pa_win_wmme/CVS/Entries create mode 100644 lib/portaudio/pa_win_wmme/CVS/Repository create mode 100644 lib/portaudio/pa_win_wmme/CVS/Root create mode 100644 lib/portaudio/pa_win_wmme/Makefile create mode 100644 lib/portaudio/pa_win_wmme/pa_win_wmme.c create mode 100644 lib/portaudio/pablio/CVS/Entries create mode 100644 lib/portaudio/pablio/CVS/Repository create mode 100644 lib/portaudio/pablio/CVS/Root create mode 100644 lib/portaudio/pablio/pablio.c create mode 100644 lib/portaudio/pablio/pablio.h create mode 100644 lib/portaudio/pablio/ringbuffer.c create mode 100644 lib/portaudio/pablio/ringbuffer.h create mode 100644 lib/portaudio/pablio/test_rw.c create mode 100644 lib/portaudio/pablio/test_rw_echo.c create mode 100644 lib/portaudio/pablio/test_w_saw.c create mode 100644 lib/portaudio/pablio/test_w_saw8.c create mode 100644 lib/portaudio/winproj/CVS/Entries create mode 100644 lib/portaudio/winproj/CVS/Entries.Log create mode 100644 lib/portaudio/winproj/CVS/Repository create mode 100644 lib/portaudio/winproj/CVS/Root create mode 100644 lib/portaudio/winproj/PAStaticDS/CVS/Entries create mode 100644 lib/portaudio/winproj/PAStaticDS/CVS/Repository create mode 100644 lib/portaudio/winproj/PAStaticDS/CVS/Root create mode 100644 lib/portaudio/winproj/PAStaticDS/PAStaticDS.dep create mode 100644 lib/portaudio/winproj/PAStaticDS/PAStaticDS.dsp create mode 100644 lib/portaudio/winproj/PAStaticDS/PAStaticDS.mak create mode 100644 lib/portaudio/winproj/PAStaticWMME/CVS/Entries create mode 100644 lib/portaudio/winproj/PAStaticWMME/CVS/Repository create mode 100644 lib/portaudio/winproj/PAStaticWMME/CVS/Root create mode 100644 lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dep create mode 100644 lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dsp create mode 100644 lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.mak create mode 100644 lib/portaudio/winproj/Readme.txt create mode 100644 lib/portaudio/winproj/WinVC.dsw create mode 100644 lib/portaudio/winproj/WinVC.ncb create mode 100644 lib/portaudio/winproj/WinVC.opt create mode 100644 lib/posh/.sconsign create mode 100644 lib/posh/posh.c create mode 100644 lib/posh/posh.h create mode 100644 scons/scons-LICENSE create mode 100644 scons/scons-README create mode 100644 scons/scons-local-0.95/SCons/Action.py create mode 100644 scons/scons-local-0.95/SCons/Builder.py create mode 100644 scons/scons-local-0.95/SCons/Conftest.py create mode 100644 scons/scons-local-0.95/SCons/Debug.py create mode 100644 scons/scons-local-0.95/SCons/Defaults.py create mode 100644 scons/scons-local-0.95/SCons/Environment.py create mode 100644 scons/scons-local-0.95/SCons/Errors.py create mode 100644 scons/scons-local-0.95/SCons/Executor.py create mode 100644 scons/scons-local-0.95/SCons/Job.py create mode 100644 scons/scons-local-0.95/SCons/Node/Alias.py create mode 100644 scons/scons-local-0.95/SCons/Node/FS.py create mode 100644 scons/scons-local-0.95/SCons/Node/Python.py create mode 100644 scons/scons-local-0.95/SCons/Node/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Optik/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Optik/errors.py create mode 100644 scons/scons-local-0.95/SCons/Optik/option.py create mode 100644 scons/scons-local-0.95/SCons/Optik/option_parser.py create mode 100644 scons/scons-local-0.95/SCons/Options/BoolOption.py create mode 100644 scons/scons-local-0.95/SCons/Options/EnumOption.py create mode 100644 scons/scons-local-0.95/SCons/Options/ListOption.py create mode 100644 scons/scons-local-0.95/SCons/Options/PackageOption.py create mode 100644 scons/scons-local-0.95/SCons/Options/PathOption.py create mode 100644 scons/scons-local-0.95/SCons/Options/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Platform/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Platform/aix.py create mode 100644 scons/scons-local-0.95/SCons/Platform/cygwin.py create mode 100644 scons/scons-local-0.95/SCons/Platform/hpux.py create mode 100644 scons/scons-local-0.95/SCons/Platform/irix.py create mode 100644 scons/scons-local-0.95/SCons/Platform/os2.py create mode 100644 scons/scons-local-0.95/SCons/Platform/posix.py create mode 100644 scons/scons-local-0.95/SCons/Platform/sunos.py create mode 100644 scons/scons-local-0.95/SCons/Platform/win32.py create mode 100644 scons/scons-local-0.95/SCons/SConf.py create mode 100644 scons/scons-local-0.95/SCons/Scanner/C.py create mode 100644 scons/scons-local-0.95/SCons/Scanner/D.py create mode 100644 scons/scons-local-0.95/SCons/Scanner/Fortran.py create mode 100644 scons/scons-local-0.95/SCons/Scanner/IDL.py create mode 100644 scons/scons-local-0.95/SCons/Scanner/Prog.py create mode 100644 scons/scons-local-0.95/SCons/Scanner/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Script/SConscript.py create mode 100644 scons/scons-local-0.95/SCons/Script/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Sig/MD5.py create mode 100644 scons/scons-local-0.95/SCons/Sig/TimeStamp.py create mode 100644 scons/scons-local-0.95/SCons/Sig/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Taskmaster.py create mode 100644 scons/scons-local-0.95/SCons/Tool/386asm.py create mode 100644 scons/scons-local-0.95/SCons/Tool/BitKeeper.py create mode 100644 scons/scons-local-0.95/SCons/Tool/CVS.py create mode 100644 scons/scons-local-0.95/SCons/Tool/JavaCommon.py create mode 100644 scons/scons-local-0.95/SCons/Tool/Perforce.py create mode 100644 scons/scons-local-0.95/SCons/Tool/PharLapCommon.py create mode 100644 scons/scons-local-0.95/SCons/Tool/RCS.py create mode 100644 scons/scons-local-0.95/SCons/Tool/SCCS.py create mode 100644 scons/scons-local-0.95/SCons/Tool/Subversion.py create mode 100644 scons/scons-local-0.95/SCons/Tool/__init__.py create mode 100644 scons/scons-local-0.95/SCons/Tool/aixc++.py create mode 100644 scons/scons-local-0.95/SCons/Tool/aixcc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/aixf77.py create mode 100644 scons/scons-local-0.95/SCons/Tool/aixlink.py create mode 100644 scons/scons-local-0.95/SCons/Tool/ar.py create mode 100644 scons/scons-local-0.95/SCons/Tool/as.py create mode 100644 scons/scons-local-0.95/SCons/Tool/bcc32.py create mode 100644 scons/scons-local-0.95/SCons/Tool/c++.py create mode 100644 scons/scons-local-0.95/SCons/Tool/cc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/default.py create mode 100644 scons/scons-local-0.95/SCons/Tool/dmd.py create mode 100644 scons/scons-local-0.95/SCons/Tool/dvipdf.py create mode 100644 scons/scons-local-0.95/SCons/Tool/dvips.py create mode 100644 scons/scons-local-0.95/SCons/Tool/f77.py create mode 100644 scons/scons-local-0.95/SCons/Tool/g++.py create mode 100644 scons/scons-local-0.95/SCons/Tool/g77.py create mode 100644 scons/scons-local-0.95/SCons/Tool/gas.py create mode 100644 scons/scons-local-0.95/SCons/Tool/gcc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/gnulink.py create mode 100644 scons/scons-local-0.95/SCons/Tool/gs.py create mode 100644 scons/scons-local-0.95/SCons/Tool/hpc++.py create mode 100644 scons/scons-local-0.95/SCons/Tool/hpcc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/hplink.py create mode 100644 scons/scons-local-0.95/SCons/Tool/icc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/icl.py create mode 100644 scons/scons-local-0.95/SCons/Tool/ifl.py create mode 100644 scons/scons-local-0.95/SCons/Tool/ilink.py create mode 100644 scons/scons-local-0.95/SCons/Tool/ilink32.py create mode 100644 scons/scons-local-0.95/SCons/Tool/jar.py create mode 100644 scons/scons-local-0.95/SCons/Tool/javac.py create mode 100644 scons/scons-local-0.95/SCons/Tool/javah.py create mode 100644 scons/scons-local-0.95/SCons/Tool/latex.py create mode 100644 scons/scons-local-0.95/SCons/Tool/lex.py create mode 100644 scons/scons-local-0.95/SCons/Tool/link.py create mode 100644 scons/scons-local-0.95/SCons/Tool/linkloc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/m4.py create mode 100644 scons/scons-local-0.95/SCons/Tool/masm.py create mode 100644 scons/scons-local-0.95/SCons/Tool/midl.py create mode 100644 scons/scons-local-0.95/SCons/Tool/mingw.py create mode 100644 scons/scons-local-0.95/SCons/Tool/mslib.py create mode 100644 scons/scons-local-0.95/SCons/Tool/mslink.py create mode 100644 scons/scons-local-0.95/SCons/Tool/msvc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/msvs.py create mode 100644 scons/scons-local-0.95/SCons/Tool/nasm.py create mode 100644 scons/scons-local-0.95/SCons/Tool/pdflatex.py create mode 100644 scons/scons-local-0.95/SCons/Tool/pdftex.py create mode 100644 scons/scons-local-0.95/SCons/Tool/qt.py create mode 100644 scons/scons-local-0.95/SCons/Tool/rmic.py create mode 100644 scons/scons-local-0.95/SCons/Tool/sgiar.py create mode 100644 scons/scons-local-0.95/SCons/Tool/sgic++.py create mode 100644 scons/scons-local-0.95/SCons/Tool/sgicc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/sgilink.py create mode 100644 scons/scons-local-0.95/SCons/Tool/sunar.py create mode 100644 scons/scons-local-0.95/SCons/Tool/sunc++.py create mode 100644 scons/scons-local-0.95/SCons/Tool/suncc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/sunlink.py create mode 100644 scons/scons-local-0.95/SCons/Tool/swig.py create mode 100644 scons/scons-local-0.95/SCons/Tool/tar.py create mode 100644 scons/scons-local-0.95/SCons/Tool/tex.py create mode 100644 scons/scons-local-0.95/SCons/Tool/tlib.py create mode 100644 scons/scons-local-0.95/SCons/Tool/yacc.py create mode 100644 scons/scons-local-0.95/SCons/Tool/zip.py create mode 100644 scons/scons-local-0.95/SCons/Util.py create mode 100644 scons/scons-local-0.95/SCons/Warnings.py create mode 100644 scons/scons-local-0.95/SCons/__init__.py create mode 100644 scons/scons-local-0.95/SCons/exitfuncs.py create mode 100755 scons/scons.py create mode 100755 scons/sconsign.py create mode 100644 src/.sconsign create mode 100644 src/Buffer.cpp create mode 100644 src/Buffer.h create mode 100644 src/BufferGroup.cpp create mode 100644 src/BufferGroup.h create mode 100644 src/Cubic.cpp create mode 100644 src/Cubic.h create mode 100644 src/DiskIO.cpp create mode 100644 src/DiskIO.h create mode 100644 src/DiskReaderNode.h create mode 100644 src/Exceptions.cpp create mode 100644 src/Exceptions.h create mode 100644 src/ManagedFile.cpp create mode 100644 src/ManagedFile.h create mode 100644 src/ManagedFileContext.cpp create mode 100644 src/ManagedFileContext.h create mode 100644 src/RTDiskIONode.cpp create mode 100644 src/RTDiskIONode.h create mode 100644 src/RTGraph.cpp create mode 100644 src/RTGraph.h create mode 100644 src/RTNode.h create mode 100644 src/RTSineWaveNode.cpp create mode 100644 src/RTSineWaveNode.h create mode 100644 src/SConscript create mode 100644 src/SeqBlock.cpp create mode 100644 src/SeqBlock.h create mode 100644 src/SeqBlockContext.h create mode 100644 src/SeqDataFileBlock.cpp create mode 100644 src/SeqDataFileBlock.h create mode 100644 src/SeqMemBlock.cpp create mode 100644 src/SeqMemBlock.h create mode 100644 src/Sequence.Paste create mode 100644 src/Sequence.cpp create mode 100644 src/Sequence.h create mode 100644 src/Storable.cpp create mode 100644 src/Storable.h create mode 100644 src/Types.h create mode 100644 src/Util.cpp create mode 100644 src/Util.h create mode 100644 src/XMLLoadStore.cpp create mode 100644 src/XMLLoadStore.h create mode 100644 src/platform/.sconsign create mode 100644 src/platform/DiskFunctions.h create mode 100644 src/platform/posix/.sconsign create mode 100644 src/platform/posix/DiskFunctions.cpp create mode 100644 src/srsw_queue.c create mode 100644 src/srsw_queue.h create mode 100644 tests/.sconsign create mode 100644 tests/MezzoCacheDisplay.cpp create mode 100644 tests/MezzoPlay.cpp create mode 100644 tests/MezzoRecord.cpp create mode 100644 tests/MezzoTest.cpp create mode 100755 tests/TestManagedFileContext.cpp diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..22aa38d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,30 @@ + +Mezzo: A Cross-Platform Audio Editing Engine + +Copyright (c) 2004 Joshua Haberman, Dominic Mazzoni + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +In addition, the authors of Mezzo request (but do not require) that if +you modify Mezzo that you share your modifications with the Mezzo +development team so that everyone can benefit. Furthermore, we kindly +suggest that if you use Mezzo in a commercial product, that you make a +donation to the Mezzo development team. + diff --git a/Mezzo.dox b/Mezzo.dox new file mode 100644 index 0000000..704b61c --- /dev/null +++ b/Mezzo.dox @@ -0,0 +1,1078 @@ +# Doxyfile 1.3.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Mezzo + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = dox + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = NO + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output dir. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = NO + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similiar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..cee8a0e --- /dev/null +++ b/SConstruct @@ -0,0 +1,50 @@ +import string + +dbg = Environment(CCFLAGS = '-Wall -g', CPPPATH= ["#/lib", "#/src"]) + +release = Environment( CPPFLAGS = '-Wall') + +env = dbg +# env = release + +SConscript('src/SConscript', build_dir='build', duplicate=0, exports='env') +SConscript('lib/SConscript', exports='env') + +env.Program('tests/MezzoTest', + source = 'tests/MezzoTest.cpp', + LIBS=['audacity', 'expat', 'posh'], LIBPATH=['build', 'lib']) + +env.Program('tests/TestManagedFileContext', + source = 'tests/TestManagedFileContext.cpp', + LIBS=['audacity', 'expat', 'posh'], + LIBPATH=['build', 'lib']) + +#env.Program('tests/MezzoRecord', +# source = 'tests/MezzoRecord.cpp', +# LIBS=['audacity', 'expat', 'posh', 'portaudio'], +# LIBPATH=['build', 'lib'], +# LINKFLAGS=['-framework','CoreAudio', +# '-framework','AudioUnit', +# '-framework','AudioToolbox']) + +#env.Program('tests/MezzoPlay', +# source = 'tests/MezzoPlay.cpp', +# LIBS=['audacity', 'expat', 'posh', 'portaudio'], +# LIBPATH=['build', 'lib'], +# LINKFLAGS=['-framework','CoreAudio', +# '-framework','AudioUnit', +# '-framework','AudioToolbox']) + +#env.Program('tests/MezzoCacheDisplay', +# source = 'tests/MezzoCacheDisplay.cpp', +# LIBS=['audacity', 'expat', 'posh', 'portaudio'], +# LIBPATH=['build', 'lib'], +# LINKFLAGS=['-framework','CoreAudio', +# '-framework','AudioUnit', +# '-framework','AudioToolbox']) + +#for test in tests: +# testname = "tests/" + test +# env.Program(testname, source = testname + ".cpp", +# LIBS=['audacity', 'expat'], LIBPATH=['build', 'lib']) + diff --git a/lib/.sconsign b/lib/.sconsign new file mode 100644 index 0000000..7f89331 --- /dev/null +++ b/lib/.sconsign @@ -0,0 +1,5 @@ +}q(U libposh.aq(cSCons.Sig +SConsignEntry +qoq}qUbsigqNsbU +libexpat.aq(hoq}q Ubsigq +Nsbu. \ No newline at end of file diff --git a/lib/SConscript b/lib/SConscript new file mode 100644 index 0000000..64de984 --- /dev/null +++ b/lib/SConscript @@ -0,0 +1,16 @@ + +Import('env') + +expat_files = [ + "expat/xmlparse.c", + "expat/xmltok.c", + "expat/xmlrole.c" +] + +posh_files = [ + "posh/posh.c" +] + +env.StaticLibrary("expat", source = expat_files) + +env.StaticLibrary("posh", source = posh_files) diff --git a/lib/expat/.sconsign b/lib/expat/.sconsign new file mode 100644 index 0000000..e29c921 --- /dev/null +++ b/lib/expat/.sconsign @@ -0,0 +1,8 @@ +}q(Uascii.hq(cSCons.Sig +SConsignEntry +qoq}q(U timestampqJÎôÜAUcsigqU 7efed5c4a9dd1c07ba83398064948910qubU +xmlparse.cq (hoq +}q (hJÎôÜAhU 861876a740538dd5618fe47ab0e3249eq ubU +asciitab.hq (hoq}q(hJÎôÜAhU f0619a5f857583f35ef0f7d73d8cac50qubU +xmlparse.hq(hoq}q(hJÎôÜAhU 7bf212dbd89fda0f22a5e4df2ece85cfqubU latin1tab.hq(hoq}q(hJÎôÜAhU 9b4b21bf6e5a7a0a72ca244b3aa3e5eaqubUxmldef.hq(hoq}q(hJÎôÜAhU 9dfdb848e2c85065a37f7c17025f9cffqubU xmlrole.oq(hoq}qUbsigq NsbU nametab.hq!(hoq"}q#(hJÎôÜAhU 0675e6b65472addbf2beafb721063922q$ubU iasciitab.hq%(hoq&}q'(hJÎôÜAhU 07dd75cd360aa4841eb22eeb0e1b1ad5q(ubU +xmlparse.oq)(hoq*}q+h NsbU xmlrole.hq,(hoq-}q.(hJÎôÜAhU 8f8a23073a41ff885fdbb00cfa023527q/ubU utf8tab.hq0(hoq1}q2(hJÎôÜAhU 741d75eb3899f294e9b8baf106fc5fd8q3ubUxmltok.oq4(hoq5}q6h NsbU xmltok_impl.cq7(hoq8}q9(hJÎôÜAhU 4de96a301b55ccebd3a1a69d79c6455cq:ubUxmltok.hq;(hoq<}q=(hJÎôÜAhU 2f64d3aa98e042c4858cc3c7b6be6b06q>ubU xmltok_ns.cq?(hoq@}qA(hJÎôÜAhU a67b4b62a74b99c1c707a3dd24791bf8qBubU xmlrole.cqC(hoqD}qE(hJÎôÜAhU d2dbe6353b46d279ff96392a6e25e276qFubUxmltok.cqG(hoqH}qI(hJÎôÜAhU 3a0f7bfc9e214c1cbfa6a45f63b8ab39qJubU xmltok_impl.hqK(hoqL}qM(hJÎôÜAhU 8474d810ff77d7f7246f8b2a922176ceqNubu. \ No newline at end of file diff --git a/lib/expat/ascii.h b/lib/expat/ascii.h new file mode 100644 index 0000000..a8a621c --- /dev/null +++ b/lib/expat/ascii.h @@ -0,0 +1,86 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#define ASCII_A 0x41 +#define ASCII_B 0x42 +#define ASCII_C 0x43 +#define ASCII_D 0x44 +#define ASCII_E 0x45 +#define ASCII_F 0x46 +#define ASCII_G 0x47 +#define ASCII_H 0x48 +#define ASCII_I 0x49 +#define ASCII_J 0x4A +#define ASCII_K 0x4B +#define ASCII_L 0x4C +#define ASCII_M 0x4D +#define ASCII_N 0x4E +#define ASCII_O 0x4F +#define ASCII_P 0x50 +#define ASCII_Q 0x51 +#define ASCII_R 0x52 +#define ASCII_S 0x53 +#define ASCII_T 0x54 +#define ASCII_U 0x55 +#define ASCII_V 0x56 +#define ASCII_W 0x57 +#define ASCII_X 0x58 +#define ASCII_Y 0x59 +#define ASCII_Z 0x5A + +#define ASCII_a 0x61 +#define ASCII_b 0x62 +#define ASCII_c 0x63 +#define ASCII_d 0x64 +#define ASCII_e 0x65 +#define ASCII_f 0x66 +#define ASCII_g 0x67 +#define ASCII_h 0x68 +#define ASCII_i 0x69 +#define ASCII_j 0x6A +#define ASCII_k 0x6B +#define ASCII_l 0x6C +#define ASCII_m 0x6D +#define ASCII_n 0x6E +#define ASCII_o 0x6F +#define ASCII_p 0x70 +#define ASCII_q 0x71 +#define ASCII_r 0x72 +#define ASCII_s 0x73 +#define ASCII_t 0x74 +#define ASCII_u 0x75 +#define ASCII_v 0x76 +#define ASCII_w 0x77 +#define ASCII_x 0x78 +#define ASCII_y 0x79 +#define ASCII_z 0x7A + +#define ASCII_0 0x30 +#define ASCII_1 0x31 +#define ASCII_2 0x32 +#define ASCII_3 0x33 +#define ASCII_4 0x34 +#define ASCII_5 0x35 +#define ASCII_6 0x36 +#define ASCII_7 0x37 +#define ASCII_8 0x38 +#define ASCII_9 0x39 + +#define ASCII_TAB 0x09 +#define ASCII_SPACE 0x20 +#define ASCII_EXCL 0x21 +#define ASCII_QUOT 0x22 +#define ASCII_AMP 0x26 +#define ASCII_APOS 0x27 +#define ASCII_MINUS 0x2D +#define ASCII_PERIOD 0x2E +#define ASCII_COLON 0x3A +#define ASCII_SEMI 0x3B +#define ASCII_LT 0x3C +#define ASCII_EQUALS 0x3D +#define ASCII_GT 0x3E +#define ASCII_LSQB 0x5B +#define ASCII_RSQB 0x5D +#define ASCII_UNDERSCORE 0x5F diff --git a/lib/expat/asciitab.h b/lib/expat/asciitab.h new file mode 100644 index 0000000..e994576 --- /dev/null +++ b/lib/expat/asciitab.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/lib/expat/iasciitab.h b/lib/expat/iasciitab.h new file mode 100644 index 0000000..2694d9d --- /dev/null +++ b/lib/expat/iasciitab.h @@ -0,0 +1,38 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/lib/expat/latin1tab.h b/lib/expat/latin1tab.h new file mode 100644 index 0000000..6e01d50 --- /dev/null +++ b/lib/expat/latin1tab.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/lib/expat/nametab.h b/lib/expat/nametab.h new file mode 100644 index 0000000..b05e62c --- /dev/null +++ b/lib/expat/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/lib/expat/utf8tab.h b/lib/expat/utf8tab.h new file mode 100644 index 0000000..28d9b59 --- /dev/null +++ b/lib/expat/utf8tab.h @@ -0,0 +1,38 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/lib/expat/xmldef.h b/lib/expat/xmldef.h new file mode 100644 index 0000000..57b8333 --- /dev/null +++ b/lib/expat/xmldef.h @@ -0,0 +1,52 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#include + +#ifdef XML_WINLIB + +#define WIN32_LEAN_AND_MEAN +#define STRICT +#include + +#define malloc(x) HeapAlloc(GetProcessHeap(), 0, (x)) +#define calloc(x, y) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x)*(y)) +#define free(x) HeapFree(GetProcessHeap(), 0, (x)) +#define realloc(x, y) HeapReAlloc(GetProcessHeap(), 0, x, y) +#define abort() /* as nothing */ + +#else /* not XML_WINLIB */ + +#include + +#endif /* not XML_WINLIB */ + +/* This file can be used for any definitions needed in +particular environments. */ + +/* Mozilla specific defines */ + +#ifdef MOZILLA_CLIENT + +#include "nspr.h" +#define malloc(x) PR_Malloc((size_t)(x)) +#define realloc(x, y) PR_Realloc((x), (size_t)(y)) +#define calloc(x, y) PR_Calloc((x),(y)) +#define free(x) PR_Free(x) +#if PR_BYTES_PER_INT != 4 +#define int int32 +#endif + +/* Enable Unicode string processing in expat. */ +#ifndef XML_UNICODE +#define XML_UNICODE +#endif + +/* Enable external parameter entity parsing in expat */ +#ifndef XML_DTD +#define XML_DTD 1 +#endif + +#endif /* MOZILLA_CLIENT */ diff --git a/lib/expat/xmlparse.c b/lib/expat/xmlparse.c new file mode 100644 index 0000000..d11a146 --- /dev/null +++ b/lib/expat/xmlparse.c @@ -0,0 +1,3925 @@ +/* +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#include "xmldef.h" +#include "xmlparse.h" +#include + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) L ## x +#else +#define XML_T(x) x +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + size_t size; + size_t used; + size_t usedLim; +} HASH_TABLE; + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + int uriLen; +} TAG_NAME; + +typedef struct tag { + struct tag *parent; + const char *rawName; + int rawNameLength; + TAG_NAME name; + char *buf; + char *bufEnd; + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + char open; +} ENTITY; + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether +an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + char maybeTokenized; + char xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + char isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + int complete; + int standalone; +#ifdef XML_DTD + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *, const char *); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr); +static enum XML_Error +processInternalParamEntity(XML_Parser parser, ENTITY *entity); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, const char *end, const char **nextPtr); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, const char *end, const char **nextPtr); +#endif /* XML_DTD */ +static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static +int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, int isCdata, int isId, const XML_Char *dfltValue); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *, + STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *, + STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); +static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); + +static const XML_Char *getContext(XML_Parser parser); +static int setContext(XML_Parser parser, const XML_Char *context); +static void normalizePublicId(XML_Char *s); +static int dtdInit(DTD *); +static void dtdDestroy(DTD *); +static int dtdCopy(DTD *newDtd, const DTD *oldDtd); +static int copyEntityTable(HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); +#ifdef XML_DTD +static void dtdSwap(DTD *, DTD *); +#endif /* XML_DTD */ +static NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize); +static void hashTableInit(HASH_TABLE *); +static void hashTableDestroy(HASH_TABLE *); +static void hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED *hashTableIterNext(HASH_TABLE_ITER *); +static void poolInit(STRING_POOL *); +static void poolClear(STRING_POOL *); +static void poolDestroy(STRING_POOL *); +static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static int poolGrow(STRING_POOL *pool); +static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +typedef struct { + /* The first member must be userData so that the XML_GetUserData macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + long m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_ExternalParsedEntityDeclHandler m_externalParsedEntityDeclHandler; + XML_InternalParsedEntityDeclHandler m_internalParsedEntityDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + void *m_externalEntityRefHandlerArg; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + int m_ns; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (*m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + int m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + char m_declAttributeIsCdata; + char m_declAttributeIsId; + DTD m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned m_groupSize; + int m_hadExternalDoctype; + XML_Char m_namespaceSeparator; +#ifdef XML_DTD + enum XML_ParamEntityParsing m_paramEntityParsing; + XML_Parser m_parentParser; +#endif +} Parser; + +#define userData (((Parser *)parser)->m_userData) +#define handlerArg (((Parser *)parser)->m_handlerArg) +#define startElementHandler (((Parser *)parser)->m_startElementHandler) +#define endElementHandler (((Parser *)parser)->m_endElementHandler) +#define characterDataHandler (((Parser *)parser)->m_characterDataHandler) +#define processingInstructionHandler (((Parser *)parser)->m_processingInstructionHandler) +#define commentHandler (((Parser *)parser)->m_commentHandler) +#define startCdataSectionHandler (((Parser *)parser)->m_startCdataSectionHandler) +#define endCdataSectionHandler (((Parser *)parser)->m_endCdataSectionHandler) +#define defaultHandler (((Parser *)parser)->m_defaultHandler) +#define startDoctypeDeclHandler (((Parser *)parser)->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (((Parser *)parser)->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler (((Parser *)parser)->m_unparsedEntityDeclHandler) +#define notationDeclHandler (((Parser *)parser)->m_notationDeclHandler) +#define externalParsedEntityDeclHandler (((Parser *)parser)->m_externalParsedEntityDeclHandler) +#define internalParsedEntityDeclHandler (((Parser *)parser)->m_internalParsedEntityDeclHandler) +#define startNamespaceDeclHandler (((Parser *)parser)->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (((Parser *)parser)->m_endNamespaceDeclHandler) +#define notStandaloneHandler (((Parser *)parser)->m_notStandaloneHandler) +#define externalEntityRefHandler (((Parser *)parser)->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg (((Parser *)parser)->m_externalEntityRefHandlerArg) +#define unknownEncodingHandler (((Parser *)parser)->m_unknownEncodingHandler) +#define encoding (((Parser *)parser)->m_encoding) +#define initEncoding (((Parser *)parser)->m_initEncoding) +#define internalEncoding (((Parser *)parser)->m_internalEncoding) +#define unknownEncodingMem (((Parser *)parser)->m_unknownEncodingMem) +#define unknownEncodingData (((Parser *)parser)->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (((Parser *)parser)->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (((Parser *)parser)->m_unknownEncodingRelease) +#define protocolEncodingName (((Parser *)parser)->m_protocolEncodingName) +#define ns (((Parser *)parser)->m_ns) +#define prologState (((Parser *)parser)->m_prologState) +#define processor (((Parser *)parser)->m_processor) +#define errorCode (((Parser *)parser)->m_errorCode) +#define eventPtr (((Parser *)parser)->m_eventPtr) +#define eventEndPtr (((Parser *)parser)->m_eventEndPtr) +#define positionPtr (((Parser *)parser)->m_positionPtr) +#define position (((Parser *)parser)->m_position) +#define openInternalEntities (((Parser *)parser)->m_openInternalEntities) +#define defaultExpandInternalEntities (((Parser *)parser)->m_defaultExpandInternalEntities) +#define tagLevel (((Parser *)parser)->m_tagLevel) +#define buffer (((Parser *)parser)->m_buffer) +#define bufferPtr (((Parser *)parser)->m_bufferPtr) +#define bufferEnd (((Parser *)parser)->m_bufferEnd) +#define parseEndByteIndex (((Parser *)parser)->m_parseEndByteIndex) +#define parseEndPtr (((Parser *)parser)->m_parseEndPtr) +#define bufferLim (((Parser *)parser)->m_bufferLim) +#define dataBuf (((Parser *)parser)->m_dataBuf) +#define dataBufEnd (((Parser *)parser)->m_dataBufEnd) +#define dtd (((Parser *)parser)->m_dtd) +#define curBase (((Parser *)parser)->m_curBase) +#define declEntity (((Parser *)parser)->m_declEntity) +#define declNotationName (((Parser *)parser)->m_declNotationName) +#define declNotationPublicId (((Parser *)parser)->m_declNotationPublicId) +#define declElementType (((Parser *)parser)->m_declElementType) +#define declAttributeId (((Parser *)parser)->m_declAttributeId) +#define declAttributeIsCdata (((Parser *)parser)->m_declAttributeIsCdata) +#define declAttributeIsId (((Parser *)parser)->m_declAttributeIsId) +#define freeTagList (((Parser *)parser)->m_freeTagList) +#define freeBindingList (((Parser *)parser)->m_freeBindingList) +#define inheritedBindings (((Parser *)parser)->m_inheritedBindings) +#define tagStack (((Parser *)parser)->m_tagStack) +#define atts (((Parser *)parser)->m_atts) +#define attsSize (((Parser *)parser)->m_attsSize) +#define nSpecifiedAtts (((Parser *)parser)->m_nSpecifiedAtts) +#define idAttIndex (((Parser *)parser)->m_idAttIndex) +#define tempPool (((Parser *)parser)->m_tempPool) +#define temp2Pool (((Parser *)parser)->m_temp2Pool) +#define groupConnector (((Parser *)parser)->m_groupConnector) +#define groupSize (((Parser *)parser)->m_groupSize) +#define hadExternalDoctype (((Parser *)parser)->m_hadExternalDoctype) +#define namespaceSeparator (((Parser *)parser)->m_namespaceSeparator) +#ifdef XML_DTD +#define parentParser (((Parser *)parser)->m_parentParser) +#define paramEntityParsing (((Parser *)parser)->m_paramEntityParsing) +#endif /* XML_DTD */ + +#ifdef _MSC_VER +#ifdef _DEBUG +Parser *asParser(XML_Parser parser) +{ + return parser; +} +#endif +#endif + +XML_Parser XML_ParserCreate(const XML_Char *encodingName) +{ + XML_Parser parser = malloc(sizeof(Parser)); + if (!parser) + return parser; + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + userData = 0; + handlerArg = 0; + startElementHandler = 0; + endElementHandler = 0; + characterDataHandler = 0; + processingInstructionHandler = 0; + commentHandler = 0; + startCdataSectionHandler = 0; + endCdataSectionHandler = 0; + defaultHandler = 0; + startDoctypeDeclHandler = 0; + endDoctypeDeclHandler = 0; + unparsedEntityDeclHandler = 0; + notationDeclHandler = 0; + externalParsedEntityDeclHandler = 0; + internalParsedEntityDeclHandler = 0; + startNamespaceDeclHandler = 0; + endNamespaceDeclHandler = 0; + notStandaloneHandler = 0; + externalEntityRefHandler = 0; + externalEntityRefHandlerArg = parser; + unknownEncodingHandler = 0; + buffer = 0; + bufferPtr = 0; + bufferEnd = 0; + parseEndByteIndex = 0; + parseEndPtr = 0; + bufferLim = 0; + declElementType = 0; + declAttributeId = 0; + declEntity = 0; + declNotationName = 0; + declNotationPublicId = 0; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = 0; + eventEndPtr = 0; + positionPtr = 0; + openInternalEntities = 0; + tagLevel = 0; + tagStack = 0; + freeTagList = 0; + freeBindingList = 0; + inheritedBindings = 0; + attsSize = INIT_ATTS_SIZE; + atts = malloc(attsSize * sizeof(ATTRIBUTE)); + nSpecifiedAtts = 0; + dataBuf = malloc(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + groupSize = 0; + groupConnector = 0; + hadExternalDoctype = 0; + unknownEncodingMem = 0; + unknownEncodingRelease = 0; + unknownEncodingData = 0; + unknownEncodingHandlerData = 0; + namespaceSeparator = '!'; +#ifdef XML_DTD + parentParser = 0; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif + ns = 0; + poolInit(&tempPool); + poolInit(&temp2Pool); + protocolEncodingName = encodingName ? poolCopyString(&tempPool, encodingName) : 0; + curBase = 0; + if (!dtdInit(&dtd) || !atts || !dataBuf + || (encodingName && !protocolEncodingName)) { + XML_ParserFree(parser); + return 0; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + XmlInitEncoding(&initEncoding, &encoding, 0); + internalEncoding = XmlGetInternalEncoding(); + return parser; +} + +XML_Parser XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + static + const XML_Char implicitContext[] = { + XML_T('x'), XML_T('m'), XML_T('l'), XML_T('='), + XML_T('h'), XML_T('t'), XML_T('t'), XML_T('p'), XML_T(':'), + XML_T('/'), XML_T('/'), XML_T('w'), XML_T('w'), XML_T('w'), + XML_T('.'), XML_T('w'), XML_T('3'), + XML_T('.'), XML_T('o'), XML_T('r'), XML_T('g'), + XML_T('/'), XML_T('X'), XML_T('M'), XML_T('L'), + XML_T('/'), XML_T('1'), XML_T('9'), XML_T('9'), XML_T('8'), + XML_T('/'), XML_T('n'), XML_T('a'), XML_T('m'), XML_T('e'), + XML_T('s'), XML_T('p'), XML_T('a'), XML_T('c'), XML_T('e'), + XML_T('\0') + }; + + XML_Parser parser = XML_ParserCreate(encodingName); + if (parser) { + XmlInitEncodingNS(&initEncoding, &encoding, 0); + ns = 1; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = nsSep; + } + if (!setContext(parser, implicitContext)) { + XML_ParserFree(parser); + return 0; + } + return parser; +} + +int XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (!encodingName) + protocolEncodingName = 0; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return 0; + } + return 1; +} + +XML_Parser XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *oldDtd = &dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_ExternalParsedEntityDeclHandler oldExternalParsedEntityDeclHandler = externalParsedEntityDeclHandler; + XML_InternalParsedEntityDeclHandler oldInternalParsedEntityDeclHandler = internalParsedEntityDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler = externalEntityRefHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler = unknownEncodingHandler; + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + int oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + void *oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + int oldParamEntityParsing = paramEntityParsing; +#endif + parser = (ns + ? XML_ParserCreateNS(encodingName, namespaceSeparator) + : XML_ParserCreate(encodingName)); + if (!parser) + return 0; + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + externalParsedEntityDeclHandler = oldExternalParsedEntityDeclHandler; + internalParsedEntityDeclHandler = oldInternalParsedEntityDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(&dtd, oldDtd) || !setContext(parser, context)) { + XML_ParserFree(parser); + return 0; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + dtdSwap(&dtd, oldDtd); + parentParser = oldParser; + XmlPrologStateInitExternalEntity(&prologState); + dtd.complete = 1; + hadExternalDoctype = 1; + } +#endif /* XML_DTD */ + return parser; +} + +static +void destroyBindings(BINDING *bindings) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + free(b->uri); + free(b); + } +} + +void XML_ParserFree(XML_Parser parser) +{ + for (;;) { + TAG *p; + if (tagStack == 0) { + if (freeTagList == 0) + break; + tagStack = freeTagList; + freeTagList = 0; + } + p = tagStack; + tagStack = tagStack->parent; + free(p->buf); + destroyBindings(p->bindings); + free(p); + } + destroyBindings(freeBindingList); + destroyBindings(inheritedBindings); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + if (parentParser) { + if (hadExternalDoctype) + dtd.complete = 0; + dtdSwap(&dtd, &((Parser *)parentParser)->m_dtd); + } +#endif /* XML_DTD */ + dtdDestroy(&dtd); + free((void *)atts); + free(groupConnector); + free(buffer); + free(dataBuf); + free(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + free(parser); +} + +void XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +void XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +int XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&dtd.pool, p); + if (!p) + return 0; + curBase = p; + } + else + curBase = 0; + return 1; +} + +const XML_Char *XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +void XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = 0; +} + +void XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = 1; +} + +void XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XML_SetExternalParsedEntityDeclHandler(XML_Parser parser, + XML_ExternalParsedEntityDeclHandler handler) +{ + externalParsedEntityDeclHandler = handler; +} + +void XML_SetInternalParsedEntityDeclHandler(XML_Parser parser, + XML_InternalParsedEntityDeclHandler handler) +{ + internalParsedEntityDeclHandler = handler; +} + +void XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = arg; + else + externalEntityRefHandlerArg = parser; +} + +void XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +int XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing) +{ +#ifdef XML_DTD + paramEntityParsing = parsing; + return 1; +#else + return parsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +int XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + if (len == 0) { + if (!isFinal) + return 1; + positionPtr = bufferPtr; + errorCode = processor(parser, bufferPtr, parseEndPtr = bufferEnd, 0); + if (errorCode == XML_ERROR_NONE) + return 1; + eventEndPtr = eventPtr; + processor = errorProcessor; + return 0; + } + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + parseEndByteIndex += len; + positionPtr = s; + if (isFinal) { + errorCode = processor(parser, s, parseEndPtr = s + len, 0); + if (errorCode == XML_ERROR_NONE) + return 1; + eventEndPtr = eventPtr; + processor = errorProcessor; + return 0; + } + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return 0; + } + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == 0 || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + buffer = buffer == 0 ? malloc(len * 2) : realloc(buffer, len * 2); + /* FIXME storage leak if realloc fails */ + if (!buffer) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = 0; + processor = errorProcessor; + return 0; + } + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + } + return 1; + } + else { + memcpy(XML_GetBuffer(parser, len), s, len); + return XML_ParseBuffer(parser, len, isFinal); + } +} + +int XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndByteIndex += len; + errorCode = processor(parser, start, parseEndPtr = bufferEnd, + isFinal ? (const char **)0 : &bufferPtr); + if (errorCode == XML_ERROR_NONE) { + if (!isFinal) + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + return 1; + } + else { + eventEndPtr = eventPtr; + processor = errorProcessor; + return 0; + } +} + +void *XML_GetBuffer(XML_Parser parser, int len) +{ + if (len > bufferLim - bufferEnd) { + /* FIXME avoid integer overflow */ + int neededSize = len + (bufferEnd - bufferPtr); + if (neededSize <= bufferLim - buffer) { + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; + } + else { + char *newBuf; + int bufferSize = bufferLim - bufferPtr; + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + bufferSize *= 2; + } while (bufferSize < neededSize); + newBuf = malloc(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return 0; + } + bufferLim = newBuf + bufferSize; + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + free(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } + } + return bufferEnd; +} + +enum XML_Error XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +long XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return parseEndByteIndex - (parseEndPtr - eventPtr); + return -1; +} + +int XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return eventEndPtr - eventPtr; + return 0; +} + +int XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +int XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar *XML_ErrorString(int code) +{ + static const XML_LChar *message[] = { + 0, + XML_T("out of memory"), + XML_T("syntax error"), + XML_T("no element found"), + XML_T("not well-formed"), + XML_T("unclosed token"), + XML_T("unclosed token"), + XML_T("mismatched tag"), + XML_T("duplicate attribute"), + XML_T("junk after document element"), + XML_T("illegal parameter entity reference"), + XML_T("undefined entity"), + XML_T("recursive entity reference"), + XML_T("asynchronous entity"), + XML_T("reference to invalid character number"), + XML_T("reference to binary entity"), + XML_T("reference to external entity in attribute"), + XML_T("xml processing instruction not at start of external entity"), + XML_T("unknown encoding"), + XML_T("encoding specified in XML declaration is incorrect"), + XML_T("unclosed CDATA section"), + XML_T("error in processing external entity reference"), + XML_T("document is not standalone") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return 0; +} + +static +enum XML_Error contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + return doContent(parser, 0, encoding, start, end, endPtr); +} + +static +enum XML_Error externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static +enum XML_Error externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next; + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + start = next; + break; + case XML_TOK_PARTIAL: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static +enum XML_Error externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next; + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + start = next; + } + break; + case XML_TOK_PARTIAL: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return doContent(parser, 1, encoding, start, end, endPtr); +} + +static +enum XML_Error externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + return doContent(parser, 1, encoding, start, end, endPtr); +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr) +{ + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd.pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0); + poolDiscard(&dtd.pool); + if (!entity) { + if (dtd.complete || dtd.standalone) + return XML_ERROR_UNDEFINED_ENTITY; + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity) { + if (entity->textPtr) { + enum XML_Error result; + OPEN_INTERNAL_ENTITY openEntity; + if (defaultHandler && !defaultExpandInternalEntities) { + reportDefault(parser, enc, s, next); + break; + } + entity->open = 1; + openEntity.next = openInternalEntities; + openInternalEntities = &openEntity; + openEntity.entity = entity; + openEntity.internalEventPtr = 0; + openEntity.internalEventEndPtr = 0; + result = doContent(parser, + tagLevel, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + entity->textLen), + 0); + entity->open = 0; + openInternalEntities = openEntity.next; + if (result) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = 1; + context = getContext(parser); + entity->open = 0; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + } + case XML_TOK_START_TAG_WITH_ATTS: + if (!startElementHandler) { + enum XML_Error result = storeAtts(parser, enc, s, 0, 0); + if (result) + return result; + } + /* fall through */ + case XML_TOK_START_TAG_NO_ATTS: + { + TAG *tag; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = malloc(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = malloc(INIT_TAG_BUF_SIZE); + if (!tag->buf) + return XML_ERROR_NO_MEMORY; + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = 0; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = 0; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + if (nextPtr) { + /* Need to guarantee that: + tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)) <= tag->bufEnd - sizeof(XML_Char) */ + if (tag->rawNameLength + (int)(sizeof(XML_Char) - 1) + (int)sizeof(XML_Char) > tag->bufEnd - tag->buf) { + int bufSize = tag->rawNameLength * 4; + bufSize = ROUND_UP(bufSize, sizeof(XML_Char)); + tag->buf = realloc(tag->buf, bufSize); + if (!tag->buf) + return XML_ERROR_NO_MEMORY; + tag->bufEnd = tag->buf + bufSize; + } + memcpy(tag->buf, tag->rawName, tag->rawNameLength); + tag->rawName = tag->buf; + } + ++tagLevel; + if (startElementHandler) { + enum XML_Error result; + XML_Char *toPtr; + for (;;) { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + int bufSize; + if (nextPtr) + toPtr = (XML_Char *)(tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char))); + else + toPtr = (XML_Char *)tag->buf; + tag->name.str = toPtr; + XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + if (fromPtr == rawNameEnd) + break; + bufSize = (tag->bufEnd - tag->buf) << 1; + tag->buf = realloc(tag->buf, bufSize); + if (!tag->buf) + return XML_ERROR_NO_MEMORY; + tag->bufEnd = tag->buf + bufSize; + if (nextPtr) + tag->rawName = tag->buf; + } + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + startElementHandler(handlerArg, tag->name.str, (const XML_Char **)atts); + poolClear(&tempPool); + } + else { + tag->name.str = 0; + if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + } + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + if (!startElementHandler) { + enum XML_Error result = storeAtts(parser, enc, s, 0, 0); + if (result) + return result; + } + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + if (startElementHandler || endElementHandler) { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = 0; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + } + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler && tag->name.str) { + if (tag->name.localPart) { + XML_Char *to = (XML_Char *)tag->name.str + tag->name.uriLen; + const XML_Char *from = tag->name.localPart; + while ((*to++ = *from++) != 0) + ; + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the transformation + and writes the characters out escaping them as necessary. This case + will fail to work if we leave out the following two lines (because & + and < inside CDATA sections will be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. */ + + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr); + if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)end - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)next - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + } + /* not reached */ +} + +/* If tagNamePtr is non-null, build a real list of attributes, +otherwise just check the attributes for well-formedness. */ + +static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + ELEMENT_TYPE *elementType = 0; + int nDefaultAtts = 0; + const XML_Char **appAtts; /* the attribute list to pass to the application */ + int attIndex = 0; + int i; + int n; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + if (tagNamePtr) { + elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, 0); + if (!elementType) { + tagNamePtr->str = poolCopyString(&dtd.pool, tagNamePtr->str); + if (!tagNamePtr->str) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + } + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + atts = realloc((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (!atts) + return XML_ERROR_NO_MEMORY; + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name, + atts[i].name + + XmlNameLength(enc, atts[i].name)); + if (!attId) + return XML_ERROR_NO_MEMORY; + /* detect duplicate attributes */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + int isCdata = 1; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + if (tagNamePtr) { + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else + poolDiscard(&tempPool); + } + else if (tagNamePtr) { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix && tagNamePtr) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + if (!addBinding(parser, attId->prefix, attId, appAtts[attIndex], bindingsPtr)) + return XML_ERROR_NO_MEMORY; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + if (tagNamePtr) { + int j; + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + /* do attribute defaulting */ + for (j = 0; j < nDefaultAtts; j++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + j; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + if (!addBinding(parser, da->id->prefix, da->id, da->value, bindingsPtr)) + return XML_ERROR_NO_MEMORY; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + } + i = 0; + if (nPrefixes) { + /* expand prefixed attribute names */ + for (; i < attIndex; i += 2) { + if (appAtts[i][-1] == 2) { + ATTRIBUTE_ID *id; + ((XML_Char *)(appAtts[i]))[-1] = 0; + id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, appAtts[i], 0); + if (id->prefix->binding) { + int j; + const BINDING *b = id->prefix->binding; + const XML_Char *s = appAtts[i]; + for (j = 0; j < b->uriLen; j++) { + if (!poolAppendChar(&tempPool, b->uri[j])) + return XML_ERROR_NO_MEMORY; + } + while (*s++ != ':') + ; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + appAtts[i] = poolStart(&tempPool); + poolFinish(&tempPool); + } + if (!--nPrefixes) + break; + } + else + ((XML_Char *)(appAtts[i]))[-1] = 0; + } + } + /* clear the flags that say whether attributes were specified */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + if (!tagNamePtr) + return XML_ERROR_NONE; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_NONE; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(':')) + ; + } + else if (dtd.defaultPrefix.binding) { + binding = dtd.defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + for (i = 0; localPart[i++];) + ; + n = i + binding->uriLen; + if (n > binding->uriAlloc) { + TAG *p; + XML_Char *uri = malloc((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + free(binding->uri); + binding->uri = uri; + } + memcpy(binding->uri + binding->uriLen, localPart, i * sizeof(XML_Char)); + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +static +int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr) +{ + BINDING *b; + int len; + for (len = 0; uri[len]; len++) + ; + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + b->uri = realloc(b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) + return 0; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = malloc(sizeof(BINDING)); + if (!b) + return 0; + b->uri = malloc(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + free(b); + return 0; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + if (*uri == XML_T('\0') && prefix == &dtd.defaultPrefix) + prefix->binding = 0; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + if (startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return 1; +} + +/* The idea here is to avoid using stack for each CDATA section when +the whole file is parsed with one call. */ + +static +enum XML_Error cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, endPtr); + if (start) { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null if +the section is not yet closed. */ + +static +enum XML_Error doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = 0; + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)next - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + abort(); + } + *eventPP = s = next; + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when +the whole file is parsed with one call. */ + +static +enum XML_Error ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, endPtr); + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null if +the section is not yet closed. */ + +static +enum XML_Error doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = 0; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + abort(); + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = 0; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = 0; + const ENCODING *newEncoding = 0; + const char *version; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &encodingName, + &newEncoding, + &standalone)) + return XML_ERROR_SYNTAX; + if (!isGeneralTextEntity && standalone == 1) { + dtd.standalone = 1; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (!protocolEncodingName) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + const XML_Char *s = poolStoreString(&tempPool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!s) + return XML_ERROR_NO_MEMORY; + result = handleUnknownEncoding(parser, s); + poolDiscard(&tempPool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = 0; + info.data = 0; + info.release = 0; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, &info)) { + ENCODING *enc; + unknownEncodingMem = malloc(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +static enum XML_Error +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { '#' , '\0' }; +#endif /* XML_DTD */ + + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + for (;;) { + int role; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (nextPtr != 0 && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: +#ifdef XML_DTD + if (enc != encoding) + return XML_ERROR_NONE; + if (parentParser) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_SYNTAX; + hadExternalDoctype = 0; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + const XML_Char *name = poolStoreString(&tempPool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + startDoctypeDeclHandler(handlerArg, name); + poolClear(&tempPool); + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + declEntity = (ENTITY *)lookup(&dtd.paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_SYNTAX; + if (declEntity) { + XML_Char *tem = poolStoreString(&dtd.pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd.pool); + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (dtd.complete && hadExternalDoctype) { + dtd.complete = 0; +#ifdef XML_DTD + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(&dtd.paramEntities, + externalSubsetName, + 0); + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } +#endif /* XML_DTD */ + if (!dtd.complete + && !dtd.standalone + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + if (endDoctypeDeclHandler) + endDoctypeDeclHandler(handlerArg); + break; + case XML_ROLE_INSTANCE_START: + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + { + const XML_Char *name = poolStoreString(&dtd.pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declElementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + if (declElementType->name != name) + poolDiscard(&dtd.pool); + else { + poolFinish(&dtd.pool); + if (!setElementTypePrefix(parser, declElementType)) + return XML_ERROR_NO_MEMORY; + } + break; + } + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = 0; + declAttributeIsId = 0; + break; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = 1; + break; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = 1; + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd.complete + && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, + declAttributeIsId, 0)) + return XML_ERROR_NO_MEMORY; + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + { + const XML_Char *attVal; + enum XML_Error result + = storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd.pool); + if (result) + return result; + attVal = poolStart(&dtd.pool); + poolFinish(&dtd.pool); + if (dtd.complete + // ID attributes aren't allowed to have a default + && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, 0, attVal)) + return XML_ERROR_NO_MEMORY; + break; + } + case XML_ROLE_ENTITY_VALUE: + { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd.pool); + declEntity->textLen = poolLength(&dtd.pool); + poolFinish(&dtd.pool); + if (internalParsedEntityDeclHandler + // Check it's not a parameter entity + && ((ENTITY *)lookup(&dtd.generalEntities, declEntity->name, 0) + == declEntity)) { + *eventEndPP = s; + internalParsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->textPtr, + declEntity->textLen); + } + } + else + poolDiscard(&dtd.pool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: + if (!dtd.standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + hadExternalDoctype = 1; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(&dtd.paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (declEntity) { + declEntity->systemId = poolStoreString(&dtd.pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd.pool); + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (declEntity) { + declEntity->notation = poolStoreString(&dtd.pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd.pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + } + + } + break; + case XML_ROLE_EXTERNAL_GENERAL_ENTITY_NO_NOTATION: + if (declEntity && externalParsedEntityDeclHandler) { + *eventEndPP = s; + externalParsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId); + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + const XML_Char *name; + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = 0; + break; + } + name = poolStoreString(&dtd.pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + if (dtd.complete) { + declEntity = (ENTITY *)lookup(&dtd.generalEntities, name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd.pool); + declEntity = 0; + } + else + poolFinish(&dtd.pool); + } + else { + poolDiscard(&dtd.pool); + declEntity = 0; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd.complete) { + const XML_Char *name = poolStoreString(&dtd.pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(&dtd.paramEntities, name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd.pool); + declEntity = 0; + } + else + poolFinish(&dtd.pool); + } +#else /* not XML_DTD */ + declEntity = 0; +#endif /* not XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = 0; + declNotationName = 0; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_SYNTAX; + if (declNotationName) { + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doIgnoreSection(parser, enc, &next, end, nextPtr); + if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) + groupConnector = realloc(groupConnector, groupSize *= 2); + else + groupConnector = malloc(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + groupConnector[prologState.level] = 0; + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == '|') + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ','; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ',') + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = '|'; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + if (paramEntityParsing + && (dtd.complete || role == XML_ROLE_INNER_PARAM_ENTITY_REF)) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd.pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd.paramEntities, name, 0); + poolDiscard(&dtd.pool); + if (!entity) { + /* FIXME what to do if !dtd.complete? */ + return XML_ERROR_UNDEFINED_ENTITY; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + result = processInternalParamEntity(parser, entity); + if (result != XML_ERROR_NONE) + return result; + break; + } + if (role == XML_ROLE_INNER_PARAM_ENTITY_REF) + return XML_ERROR_PARAM_ENTITY_REF; + if (externalEntityRefHandler) { + dtd.complete = 0; + entity->open = 1; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = 0; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = 0; + if (dtd.complete) + break; + } + } +#endif /* XML_DTD */ + if (!dtd.standalone + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + dtd.complete = 0; + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + } + break; + } + if (defaultHandler) { + switch (tok) { + case XML_TOK_PI: + case XML_TOK_COMMENT: + case XML_TOK_BOM: + case XML_TOK_XML_DECL: +#ifdef XML_DTD + case XML_TOK_IGNORE_SECT: +#endif /* XML_DTD */ + case XML_TOK_PARAM_ENTITY_REF: + break; + default: +#ifdef XML_DTD + if (role != XML_ROLE_IGNORE_SECT) +#endif /* XML_DTD */ + reportDefault(parser, enc, s, next); + } + } + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + /* not reached */ +} + +static +enum XML_Error epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + eventEndPtr = end; + reportDefault(parser, encoding, s, end); + } + /* fall through */ + case XML_TOK_NONE: + if (nextPtr) + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + } +} + +#ifdef XML_DTD + +static enum XML_Error +processInternalParamEntity(XML_Parser parser, ENTITY *entity) +{ + const char *s, *end, *next; + int tok; + enum XML_Error result; + OPEN_INTERNAL_ENTITY openEntity; + entity->open = 1; + openEntity.next = openInternalEntities; + openInternalEntities = &openEntity; + openEntity.entity = entity; + openEntity.internalEventPtr = 0; + openEntity.internalEventEndPtr = 0; + s = (char *)entity->textPtr; + end = (char *)(entity->textPtr + entity->textLen); + tok = XmlPrologTok(internalEncoding, s, end, &next); + result = doProlog(parser, internalEncoding, s, end, tok, next, 0); + entity->open = 0; + openInternalEntities = openEntity.next; + return result; +} + +#endif /* XML_DTD */ + +static +enum XML_Error errorProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0); + poolDiscard(&temp2Pool); + if (!entity) { + if (dtd.complete) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNDEFINED_ENTITY; + } + } + else if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + else if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + else if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = 1; + result = appendAttributeValue(parser, internalEncoding, isCdata, (char *)entity->textPtr, (char *)textEnd, pool); + entity->open = 0; + if (result) + return result; + } + } + break; + default: + abort(); + } + ptr = next; + } + /* not reached */ +} + +static +enum XML_Error storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + STRING_POOL *pool = &(dtd.pool); + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (parentParser || enc != encoding) { + enum XML_Error result; + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd.paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + if (enc == encoding) + eventPtr = entityTextPtr; + return XML_ERROR_UNDEFINED_ENTITY; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->systemId) { + if (enc == encoding) + eventPtr = entityTextPtr; + return XML_ERROR_PARAM_ENTITY_REF; + } + entity->open = 1; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + entity->textLen)); + entity->open = 0; + if (result) + return result; + break; + } +#endif /* XML_DTD */ + eventPtr = entityTextPtr; + return XML_ERROR_SYNTAX; + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + return XML_ERROR_BAD_CHAR_REF; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + default: + abort(); + } + entityTextPtr = next; + } + /* not reached */ +} + +static void +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + *eventPP = s; + } while (s != end); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (XML_Char *)end - (XML_Char *)s); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, int isCdata, int isId, const XML_Char *value) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = malloc(type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE)); + } + else { + type->allocDefaultAtts *= 2; + type->defaultAtts = realloc(type->defaultAtts, + type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE)); + } + if (!type->defaultAtts) + return 0; + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = 1; + type->nDefaultAtts += 1; + return 1; +} + +static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(':')) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd.pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd.pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd.pool)) + poolFinish(&dtd.pool); + else + poolDiscard(&dtd.pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end) +{ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd.pool, XML_T('\0'))) + return 0; + name = poolStoreString(&dtd.pool, enc, start, end); + if (!name) + return 0; + ++name; + id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return 0; + if (id->name != name) + poolDiscard(&dtd.pool); + else { + poolFinish(&dtd.pool); + if (!ns) + ; + else if (name[0] == 'x' + && name[1] == 'm' + && name[2] == 'l' + && name[3] == 'n' + && name[4] == 's' + && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) { + if (name[5] == '\0') + id->prefix = &dtd.defaultPrefix; + else + id->prefix = (PREFIX *)lookup(&dtd.prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = 1; + } + else { + int i; + for (i = 0; name[i]; i++) { + if (name[i] == XML_T(':')) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd.pool, name[j])) + return 0; + } + if (!poolAppendChar(&dtd.pool, XML_T('\0'))) + return 0; + id->prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX)); + if (id->prefix->name == poolStart(&dtd.pool)) + poolFinish(&dtd.pool); + else + poolDiscard(&dtd.pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T('\f') + +static +const XML_Char *getContext(XML_Parser parser) +{ + HASH_TABLE_ITER iter; + int needSep = 0; + + if (dtd.defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T('='))) + return 0; + len = dtd.defaultPrefix.binding->uriLen; + if (namespaceSeparator != XML_T('\0')) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd.defaultPrefix.binding->uri[i])) + return 0; + needSep = 1; + } + + hashTableIterInit(&iter, &(dtd.prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return 0; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + if (!poolAppendChar(&tempPool, XML_T('='))) + return 0; + len = prefix->binding->uriLen; + if (namespaceSeparator != XML_T('\0')) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return 0; + needSep = 1; + } + + + hashTableIterInit(&iter, &(dtd.generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return 0; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = 1; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return 0; + return tempPool.start; +} + +static +int setContext(XML_Parser parser, const XML_Char *context) +{ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return 0; + e = (ENTITY *)lookup(&dtd.generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = 1; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == '=') { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd.defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&tempPool), sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd.pool, prefix->name); + if (!prefix->name) + return 0; + } + poolDiscard(&tempPool); + } + for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0'); context++) + if (!poolAppendChar(&tempPool, *context)) + return 0; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return 0; + if (!addBinding(parser, prefix, 0, poolStart(&tempPool), &inheritedBindings)) + return 0; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return 0; + s++; + } + } + return 1; +} + + +static +void normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static int dtdInit(DTD *p) +{ + poolInit(&(p->pool)); + hashTableInit(&(p->generalEntities)); + hashTableInit(&(p->elementTypes)); + hashTableInit(&(p->attributeIds)); + hashTableInit(&(p->prefixes)); + p->complete = 1; + p->standalone = 0; +#ifdef XML_DTD + hashTableInit(&(p->paramEntities)); +#endif /* XML_DTD */ + p->defaultPrefix.name = 0; + p->defaultPrefix.binding = 0; + return 1; +} + +#ifdef XML_DTD + +static void dtdSwap(DTD *p1, DTD *p2) +{ + DTD tem; + memcpy(&tem, p1, sizeof(DTD)); + memcpy(p1, p2, sizeof(DTD)); + memcpy(p2, &tem, sizeof(DTD)); +} + +#endif /* XML_DTD */ + +static void dtdDestroy(DTD *p) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + free(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory; non-zero otherwise. +The new DTD has already been initialized. */ + +static int dtdCopy(DTD *newDtd, const DTD *oldDtd) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(&(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), name, sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(&(newDtd->prefixes), oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(&(newDtd->elementTypes), name, sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *)malloc(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) + return 0; + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(&(newDtd->prefixes), oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = 0; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(&(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(&(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; +#endif /* XML_DTD */ + + newDtd->complete = oldDtd->complete; + newDtd->standalone = oldDtd->standalone; + return 1; +} + +static int copyEntityTable(HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = 0; + const XML_Char *cachedNewBase = 0; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + } + return 1; +} + +#define INIT_SIZE 64 + +static +int keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return 1; + return 0; +} + +static +unsigned long hash(KEY s) +{ + unsigned long h = 0; + while (*s) + h = (h << 5) + h + (unsigned char)*s++; + return h; +} + +static +NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + if (!createSize) + return 0; + table->v = calloc(INIT_SIZE, sizeof(NAMED *)); + if (!table->v) + return 0; + table->size = INIT_SIZE; + table->usedLim = INIT_SIZE / 2; + i = hash(name) & (table->size - 1); + } + else { + unsigned long h = hash(name); + for (i = h & (table->size - 1); + table->v[i]; + i == 0 ? i = table->size - 1 : --i) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + } + if (!createSize) + return 0; + if (table->used == table->usedLim) { + /* check for overflow */ + size_t newSize = table->size * 2; + NAMED **newV = calloc(newSize, sizeof(NAMED *)); + if (!newV) + return 0; + for (i = 0; i < table->size; i++) + if (table->v[i]) { + size_t j; + for (j = hash(table->v[i]->name) & (newSize - 1); + newV[j]; + j == 0 ? j = newSize - 1 : --j) + ; + newV[j] = table->v[i]; + } + free(table->v); + table->v = newV; + table->size = newSize; + table->usedLim = newSize/2; + for (i = h & (table->size - 1); + table->v[i]; + i == 0 ? i = table->size - 1 : --i) + ; + } + } + table->v[i] = calloc(1, createSize); + if (!table->v[i]) + return 0; + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static +void hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + NAMED *p = table->v[i]; + if (p) + free(p); + } + if (table->v) + free(table->v); +} + +static +void hashTableInit(HASH_TABLE *p) +{ + p->size = 0; + p->usedLim = 0; + p->used = 0; + p->v = 0; +} + +static +void hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static +NAMED *hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return 0; +} + + +static +void poolInit(STRING_POOL *pool) +{ + pool->blocks = 0; + pool->freeBlocks = 0; + pool->start = 0; + pool->ptr = 0; + pool->end = 0; +} + +static +void poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = 0; + pool->start = 0; + pool->ptr = 0; + pool->end = 0; +} + +static +void poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + free(p); + p = tem; + } + pool->blocks = 0; + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + free(p); + p = tem; + } + pool->freeBlocks = 0; + pool->ptr = 0; + pool->start = 0; + pool->end = 0; +} + +static +XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return 0; + for (;;) { + XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if (ptr == end) + break; + if (!poolGrow(pool)) + return 0; + } + return pool->start; +} + +static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return 0; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return 0; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return 0; + + } + s = pool->start; + poolFinish(pool); + return s; +} + +static +XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return 0; + if (pool->ptr == pool->end && !poolGrow(pool)) + return 0; + *(pool->ptr)++ = 0; + return pool->start; +} + +static +int poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = 0; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return 1; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return 1; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + int blockSize = (pool->end - pool->start)*2; + pool->blocks = realloc(pool->blocks, offsetof(BLOCK, s) + blockSize * sizeof(XML_Char)); + if (!pool->blocks) + return 0; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = pool->end - pool->start; + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = malloc(offsetof(BLOCK, s) + blockSize * sizeof(XML_Char)); + if (!tem) + return 0; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return 1; +} diff --git a/lib/expat/xmlparse.h b/lib/expat/xmlparse.h new file mode 100644 index 0000000..6dffe74 --- /dev/null +++ b/lib/expat/xmlparse.h @@ -0,0 +1,527 @@ +/* +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#ifndef XmlParse_INCLUDED +#define XmlParse_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XMLPARSEAPI +#define XMLPARSEAPI /* as nothing */ +#endif + +typedef void *XML_Parser; + +#ifdef XML_UNICODE_WCHAR_T + +/* XML_UNICODE_WCHAR_T will work only if sizeof(wchar_t) == 2 and wchar_t +uses Unicode. */ +/* Information is UTF-16 encoded as wchar_ts */ + +#ifndef XML_UNICODE +#define XML_UNICODE +#endif + +#include +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; + +#else /* not XML_UNICODE_WCHAR_T */ + +#ifdef XML_UNICODE + +/* Information is UTF-16 encoded as unsigned shorts */ +typedef unsigned short XML_Char; +typedef char XML_LChar; + +#else /* not XML_UNICODE */ + +/* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; + +#endif /* not XML_UNICODE */ + +#endif /* not XML_UNICODE_WCHAR_T */ + + +/* Constructs a new parser; encoding is the encoding specified by the external +protocol or null if there is none specified. */ + +XML_Parser XMLPARSEAPI +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type names +and attribute names that belong to a namespace will be expanded; +unprefixed attribute names are never expanded; unprefixed element type +names are expanded only if there is a default namespace. The expanded +name is the concatenation of the namespace URI, the namespace separator character, +and the local part of the name. If the namespace separator is '\0' then +the namespace URI and the local part will be concatenated without any +separator. When a namespace is not declared, the name and prefix will be +passed through without expansion. */ + +XML_Parser XMLPARSEAPI +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. */ + +typedef void (*XML_StartElementHandler)(void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (*XML_EndElementHandler)(void *userData, + const XML_Char *name); + +/* s is not 0 terminated. */ +typedef void (*XML_CharacterDataHandler)(void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (*XML_ProcessingInstructionHandler)(void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (*XML_CommentHandler)(void *userData, const XML_Char *data); + +typedef void (*XML_StartCdataSectionHandler)(void *userData); +typedef void (*XML_EndCdataSectionHandler)(void *userData); + +/* This is called for any characters in the XML document for +which there is no applicable handler. This includes both +characters that are part of markup which is of a kind that is +not reported (comments, markup declarations), or characters +that are part of a construct which could be reported but +for which no handler has been supplied. The characters are passed +exactly as they were in the XML document except that +they will be encoded in UTF-8. Line boundaries are not normalized. +Note that a byte order mark character is not passed to the default handler. +There are no guarantees about how characters are divided between calls +to the default handler: for example, a comment might be split between +multiple calls. */ + +typedef void (*XML_DefaultHandler)(void *userData, + const XML_Char *s, + int len); + +/* This is called for the start of the DOCTYPE declaration when the +name of the DOCTYPE is encountered. */ +typedef void (*XML_StartDoctypeDeclHandler)(void *userData, + const XML_Char *doctypeName); + +/* This is called for the start of the DOCTYPE declaration when the +closing > is encountered, but after processing any external subset. */ +typedef void (*XML_EndDoctypeDeclHandler)(void *userData); + +/* This is called for a declaration of an unparsed (NDATA) +entity. The base argument is whatever was set by XML_SetBase. +The entityName, systemId and notationName arguments will never be null. +The other arguments may be. */ + +typedef void (*XML_UnparsedEntityDeclHandler)(void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. +The base argument is whatever was set by XML_SetBase. +The notationName will never be null. The other arguments can be. */ + +typedef void (*XML_NotationDeclHandler)(void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +typedef void (*XML_ExternalParsedEntityDeclHandler)(void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +typedef void (*XML_InternalParsedEntityDeclHandler)(void *userData, + const XML_Char *entityName, + const XML_Char *replacementText, + int replacementTextLength); + +/* When namespace processing is enabled, these are called once for +each namespace declaration. The call to the start and end element +handlers occur between the calls to the start and end namespace +declaration handlers. For an xmlns attribute, prefix will be null. +For an xmlns="" attribute, uri will be null. */ + +typedef void (*XML_StartNamespaceDeclHandler)(void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (*XML_EndNamespaceDeclHandler)(void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone (it has an +external subset or a reference to a parameter entity, but does not +have standalone="yes"). If this handler returns 0, then processing +will not continue, and the parser will return a +XML_ERROR_NOT_STANDALONE error. */ + +typedef int (*XML_NotStandaloneHandler)(void *userData); + +/* This is called for a reference to an external parsed general entity. +The referenced entity is not automatically parsed. +The application can parse it immediately or later using +XML_ExternalEntityParserCreate. +The parser argument is the parser parsing the entity containing the reference; +it can be passed as the parser argument to XML_ExternalEntityParserCreate. +The systemId argument is the system identifier as specified in the entity declaration; +it will not be null. +The base argument is the system identifier that should be used as the base for +resolving systemId if systemId was relative; this is set by XML_SetBase; +it may be null. +The publicId argument is the public identifier as specified in the entity declaration, +or null if none was specified; the whitespace in the public identifier +will have been normalized as required by the XML spec. +The context argument specifies the parsing context in the format +expected by the context argument to +XML_ExternalEntityParserCreate; context is valid only until the handler +returns, so if the referenced entity is to be parsed later, it must be copied. +The handler should return 0 if processing should not continue because of +a fatal error in the handling of the external entity. +In this case the calling parser will return an XML_ERROR_EXTERNAL_ENTITY_HANDLING +error. +Note that unlike other handlers the first argument is the parser, not userData. */ + +typedef int (*XML_ExternalEntityRefHandler)(XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This structure is filled in by the XML_UnknownEncodingHandler +to provide information to the parser about encodings that are unknown +to the parser. +The map[b] member gives information about byte sequences +whose first byte is b. +If map[b] is c where c is >= 0, then b by itself encodes the Unicode scalar value c. +If map[b] is -1, then the byte sequence is malformed. +If map[b] is -n, where n >= 2, then b is the first byte of an n-byte +sequence that encodes a single Unicode scalar value. +The data member will be passed as the first argument to the convert function. +The convert function is used to convert multibyte sequences; +s will point to a n-byte sequence where map[(unsigned char)*s] == -n. +The convert function must return the Unicode scalar value +represented by this byte sequence or -1 if the byte sequence is malformed. +The convert function may be null if the encoding is a single-byte encoding, +that is if map[b] >= -1 for all bytes b. +When the parser is finished with the encoding, then if release is not null, +it will call release passing it the data member; +once release has been called, the convert function will not be called again. + +Expat places certain restrictions on the encodings that are supported +using this mechanism. + +1. Every ASCII character that can appear in a well-formed XML document, +other than the characters + + $@\^`{}~ + +must be represented by a single byte, and that byte must be the +same byte that represents that character in ASCII. + +2. No character may require more than 4 bytes to encode. + +3. All characters encoded must have Unicode scalar values <= 0xFFFF, +(ie characters that would be encoded by surrogates in UTF-16 +are not allowed). Note that this restriction doesn't apply to +the built-in support for UTF-8 and UTF-16. + +4. No Unicode character may be encoded by more than one distinct sequence +of bytes. */ + +typedef struct { + int map[256]; + void *data; + int (*convert)(void *data, const char *s); + void (*release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. +The encodingHandlerData argument is that which was passed as the +second argument to XML_SetUnknownEncodingHandler. +The name argument gives the name of the encoding as specified in +the encoding declaration. +If the callback can provide information about the encoding, +it must fill in the XML_Encoding structure, and return 1. +Otherwise it must return 0. +If info does not describe a suitable encoding, +then the parser will return an XML_UNKNOWN_ENCODING error. */ + +typedef int (*XML_UnknownEncodingHandler)(void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +void XMLPARSEAPI +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +void XMLPARSEAPI +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +void XMLPARSEAPI +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +void XMLPARSEAPI +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +void XMLPARSEAPI +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of internal entities. +The entity reference will be passed to the default handler. */ + +void XMLPARSEAPI +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of internal entities. +The entity reference will not be passed to the default handler. */ + +void XMLPARSEAPI +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +void XMLPARSEAPI +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end); + +void XMLPARSEAPI +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +void XMLPARSEAPI +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +void XMLPARSEAPI +XML_SetExternalParsedEntityDeclHandler(XML_Parser parser, + XML_ExternalParsedEntityDeclHandler handler); + +void XMLPARSEAPI +XML_SetInternalParsedEntityDeclHandler(XML_Parser parser, + XML_InternalParsedEntityDeclHandler handler); + +void XMLPARSEAPI +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +void XMLPARSEAPI +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +void XMLPARSEAPI +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-null value for arg is specified here, then it will be passed +as the first argument to the external entity ref handler instead +of the parser object. */ +void XMLPARSEAPI +XML_SetExternalEntityRefHandlerArg(XML_Parser, void *arg); + +void XMLPARSEAPI +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end element, +processing instruction or character data. It causes the corresponding +markup to be passed to the default handler. */ +void XMLPARSEAPI XML_DefaultCurrent(XML_Parser parser); + +/* This value is passed as the userData argument to callbacks. */ +void XMLPARSEAPI +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or null. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument +to XML_ParserCreate. It must not be called after XML_Parse +or XML_ParseBuffer. */ + +int XMLPARSEAPI +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed +as the first argument to callbacks instead of userData. +The userData will still be accessible using XML_GetUserData. */ + +void XMLPARSEAPI +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* Sets the base to be used for resolving relative URIs in system identifiers in +declarations. Resolving relative identifiers is left to the application: +this value will be passed through as the base argument to the +XML_ExternalEntityRefHandler, XML_NotationDeclHandler +and XML_UnparsedEntityDeclHandler. The base argument will be copied. +Returns zero if out of memory, non-zero otherwise. */ + +int XMLPARSEAPI +XML_SetBase(XML_Parser parser, const XML_Char *base); + +const XML_Char XMLPARSEAPI * +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attribute/value pairs passed in last call +to the XML_StartElementHandler that were specified in the start-tag +rather than defaulted. Each attribute/value pair counts as 2; thus +this correspondds to an index into the atts array passed to the +XML_StartElementHandler. */ + +int XMLPARSEAPI XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Returns the index of the ID attribute passed in the last call to +XML_StartElementHandler, or -1 if there is no ID attribute. Each +attribute/value pair counts as 2; thus this correspondds to an index +into the atts array passed to the XML_StartElementHandler. */ +int XMLPARSEAPI XML_GetIdAttributeIndex(XML_Parser parser); + +/* Parses some input. Returns 0 if a fatal error is detected. +The last call to XML_Parse must have isFinal true; +len may be zero for this call (or any other). */ +int XMLPARSEAPI +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +void XMLPARSEAPI * +XML_GetBuffer(XML_Parser parser, int len); + +int XMLPARSEAPI +XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +/* Creates an XML_Parser object that can parse an external general entity; +context is a '\0'-terminated string specifying the parse context; +encoding is a '\0'-terminated string giving the name of the externally specified encoding, +or null if there is no externally specified encoding. +The context string consists of a sequence of tokens separated by formfeeds (\f); +a token consisting of a name specifies that the general entity of the name +is open; a token of the form prefix=uri specifies the namespace for a particular +prefix; a token of the form =uri specifies the default namespace. +This can be called at any point after the first call to an ExternalEntityRefHandler +so longer as the parser has not yet been freed. +The new parser is completely independent and may safely be used in a separate thread. +The handlers and userData are initialized from the parser argument. +Returns 0 if out of memory. Otherwise returns a new XML_Parser object. */ +XML_Parser XMLPARSEAPI +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_ParamEntityParsing { + XML_PARAM_ENTITY_PARSING_NEVER, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, + XML_PARAM_ENTITY_PARSING_ALWAYS +}; + +/* Controls parsing of parameter entities (including the external DTD +subset). If parsing of parameter entities is enabled, then references +to external parameter entities (including the external DTD subset) +will be passed to the handler set with +XML_SetExternalEntityRefHandler. The context passed will be 0. +Unlike external general entities, external parameter entities can only +be parsed synchronously. If the external parameter entity is to be +parsed, it must be parsed during the call to the external entity ref +handler: the complete sequence of XML_ExternalEntityParserCreate, +XML_Parse/XML_ParseBuffer and XML_ParserFree calls must be made during +this call. After XML_ExternalEntityParserCreate has been called to +create the parser for the external parameter entity (context must be 0 +for this call), it is illegal to make any calls on the old parser +until XML_ParserFree has been called on the newly created parser. If +the library has been compiled without support for parameter entity +parsing (ie without XML_DTD being defined), then +XML_SetParamEntityParsing will return 0 if parsing of parameter +entities is requested; otherwise it will return non-zero. */ + +int XMLPARSEAPI +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing); + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE +}; + +/* If XML_Parse or XML_ParseBuffer have returned 0, then XML_GetErrorCode +returns information about the error. */ + +enum XML_Error XMLPARSEAPI XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse location. +They may be called when XML_Parse or XML_ParseBuffer return 0; +in this case the location is the location of the character at which +the error was detected. +They may also be called from any other callback called to report +some parse event; in this the location is the location of the first +of the sequence of characters that generated the event. */ + +int XMLPARSEAPI XML_GetCurrentLineNumber(XML_Parser parser); +int XMLPARSEAPI XML_GetCurrentColumnNumber(XML_Parser parser); +long XMLPARSEAPI XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. +Returns 0 if the event is in an internal entity. */ + +int XMLPARSEAPI XML_GetCurrentByteCount(XML_Parser parser); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees memory used by the parser. */ +void XMLPARSEAPI +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +const XML_LChar XMLPARSEAPI *XML_ErrorString(int code); + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlParse_INCLUDED */ diff --git a/lib/expat/xmlrole.c b/lib/expat/xmlrole.c new file mode 100644 index 0000000..6edadc9 --- /dev/null +++ b/lib/expat/xmlrole.c @@ -0,0 +1,1265 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#include "xmldef.h" +#include "xmlrole.h" +#include "ascii.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +static const char KW_ANY[] = { ASCII_A, ASCII_N, ASCII_Y, '\0' }; +static const char KW_ATTLIST[] = { ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; +static const char KW_CDATA[] = { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_DOCTYPE[] = { ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; +static const char KW_ELEMENT[] = { ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; +static const char KW_EMPTY[] = { ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; +static const char KW_ENTITIES[] = { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; +static const char KW_ENTITY[] = { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; +static const char KW_FIXED[] = { ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; +static const char KW_ID[] = { ASCII_I, ASCII_D, '\0' }; +static const char KW_IDREF[] = { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; +static const char KW_IDREFS[] = { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +//static const char KW_IGNORE[] = { ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +static const char KW_IMPLIED[] = { ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +//static const char KW_INCLUDE[] = { ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +static const char KW_NDATA[] = { ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_NMTOKEN[] = { ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; +static const char KW_NMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; +static const char KW_NOTATION[] = { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, '\0' }; +static const char KW_PCDATA[] = { ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_PUBLIC[] = { ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; +static const char KW_REQUIRED[] = { ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, '\0' }; +static const char KW_SYSTEM[] = { ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +#ifdef XML_DTD +#define setTopLevel(state) \ + ((state)->handler = ((state)->documentEntity \ + ? internalSubset \ + : externalSubset1)) +#else /* not XML_DTD */ +#define setTopLevel(state) ((state)->handler = internalSubset) +#endif /* not XML_DTD */ + +typedef int PROLOG_HANDLER(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, +#ifdef XML_DTD + externalSubset0, externalSubset1, + condSect0, condSect1, condSect2, +#endif /* XML_DTD */ + declClose, + error; + +static +int common(PROLOG_STATE *state, int tok); + +static +int prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_COMMENT: + state->handler = prolog1; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static +int prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + case XML_TOK_COMMENT: + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static +int prolog2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + case XML_TOK_COMMENT: + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static +int doctype0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return common(state, tok); +} + +static +int doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = doctype3; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = doctype2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static +int doctype2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return common(state, tok); +} + +static +int doctype3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return common(state, tok); +} + +static +int doctype4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static +int doctype5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static +int internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ENTITY)) { + state->handler = entity0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ATTLIST)) { + state->handler = attlist0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ELEMENT)) { + state->handler = element0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_NOTATION)) { + state->handler = notation0; + return XML_ROLE_NONE; + } + break; + case XML_TOK_PI: + case XML_TOK_COMMENT: + return XML_ROLE_NONE; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static +int externalSubset0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + state->handler = externalSubset1; + if (tok == XML_TOK_XML_DECL) + return XML_ROLE_TEXT_DECL; + return externalSubset1(state, tok, ptr, end, enc); +} + +static +int externalSubset1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_COND_SECT_OPEN: + state->handler = condSect0; + return XML_ROLE_NONE; + case XML_TOK_COND_SECT_CLOSE: + if (state->includeLevel == 0) + break; + state->includeLevel -= 1; + return XML_ROLE_NONE; + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_BRACKET: + break; + case XML_TOK_NONE: + if (state->includeLevel) + break; + return XML_ROLE_NONE; + default: + return internalSubset(state, tok, ptr, end, enc); + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static +int entity0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return common(state, tok); +} + +static +int entity1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return common(state, tok); +} + +static +int entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity4; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity3; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static +int entity3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + + +static +int entity4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static +int entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_EXTERNAL_GENERAL_ENTITY_NO_NOTATION; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { + state->handler = entity6; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static +int entity6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = declClose; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return common(state, tok); +} + +static +int entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity9; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity8; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static +int entity8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static +int entity9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static +int notation0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return common(state, tok); +} + +static +int notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = notation3; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = notation2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static +int notation2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return common(state, tok); +} + +static +int notation3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return common(state, tok); +} + +static +int notation4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return common(state, tok); +} + +static +int attlist0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return common(state, tok); +} + +static +int attlist1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return common(state, tok); +} + +static +int attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + { + static const char *types[] = { + KW_CDATA, + KW_ID, + KW_IDREF, + KW_IDREFS, + KW_ENTITY, + KW_ENTITIES, + KW_NMTOKEN, + KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { + state->handler = attlist5; + return XML_ROLE_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static +int attlist3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return common(state, tok); +} + +static +int attlist4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static +int attlist5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_NONE; + } + return common(state, tok); +} + + +static +int attlist6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return common(state, tok); +} + +static +int attlist7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +/* default value */ +static +int attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_IMPLIED)) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_REQUIRED)) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_FIXED)) { + state->handler = attlist9; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static +int attlist9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static +int element0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return common(state, tok); +} + +static +int element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { + state->handler = declClose; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { + state->handler = declClose; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return common(state, tok); +} + +static +int element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_PCDATA)) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static +int element3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static +int element4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return common(state, tok); +} + +static +int element5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static +int element6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static +int element7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static +int condSect0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { + state->handler = condSect1; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { + state->handler = condSect2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static +int condSect1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + state->includeLevel += 1; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static +int condSect2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + return XML_ROLE_IGNORE_SECT; + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static +int declClose(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NONE; + } + return common(state, tok); +} + +#if 0 + +static +int ignore(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return 0; + default: + return XML_ROLE_NONE; + } + return common(state, tok); +} +#endif + +static +int error(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + return XML_ROLE_NONE; +} + +static +int common(PROLOG_STATE *state, int tok) +{ +#ifdef XML_DTD + if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + return XML_ROLE_INNER_PARAM_ENTITY_REF; +#endif + state->handler = error; + return XML_ROLE_ERROR; +} + +void XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +#ifdef XML_DTD + state->documentEntity = 1; + state->includeLevel = 0; +#endif /* XML_DTD */ +} + +#ifdef XML_DTD + +void XmlPrologStateInitExternalEntity(PROLOG_STATE *state) +{ + state->handler = externalSubset0; + state->documentEntity = 0; + state->includeLevel = 0; +} + +#endif /* XML_DTD */ diff --git a/lib/expat/xmlrole.h b/lib/expat/xmlrole.h new file mode 100644 index 0000000..22958df --- /dev/null +++ b/lib/expat/xmlrole.h @@ -0,0 +1,99 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, +#ifdef XML_DTD + XML_ROLE_TEXT_DECL, + XML_ROLE_IGNORE_SECT, + XML_ROLE_INNER_PARAM_ENTITY_REF, +#endif /* XML_DTD */ + XML_ROLE_PARAM_ENTITY_REF, + XML_ROLE_EXTERNAL_GENERAL_ENTITY_NO_NOTATION +}; + +typedef struct prolog_state { + int (*handler)(struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; +#ifdef XML_DTD + unsigned includeLevel; + int documentEntity; +#endif /* XML_DTD */ +} PROLOG_STATE; + +void XMLTOKAPI XmlPrologStateInit(PROLOG_STATE *); +#ifdef XML_DTD +void XMLTOKAPI XmlPrologStateInitExternalEntity(PROLOG_STATE *); +#endif /* XML_DTD */ + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/lib/expat/xmltok.c b/lib/expat/xmltok.c new file mode 100644 index 0000000..f7a5b2b --- /dev/null +++ b/lib/expat/xmltok.c @@ -0,0 +1,1557 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#include "xmldef.h" +#include "xmltok.h" +#include "nametab.h" + +#ifdef XML_DTD +#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +#else +#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +#endif + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits +between the bottom 5 and 6 bits of the bytes. +We need 8 bits to index into pages, 3 bits to add to that index and +5 bits to generate the mask. */ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1 << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits +between the bottom 4, 6 and 6 bits of the bytes. +We need 8 bits to index into pages, 3 bits to add to that index and +5 bits to generate the mask. */ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1 << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +#define UTF8_INVALID3(p) \ + ((*p) == 0xED \ + ? (((p)[1] & 0x20) != 0) \ + : ((*p) == 0xEF \ + ? ((p)[1] == 0xBF && ((p)[2] == 0xBF || (p)[2] == 0xBE)) \ + : 0)) + +#define UTF8_INVALID4(p) ((*p) == 0xF4 && ((p)[1] & 0x30) != 0) + +static +int isNever(const ENCODING *enc, const char *p) +{ + return 0; +} + +static +int utf8_isName2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static +int utf8_isName3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static +int utf8_isNmstrt2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static +int utf8_isNmstrt3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +#define utf8_isInvalid2 isNever + +static +int utf8_isInvalid3(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static +int utf8_isInvalid4(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (*byteType)(const ENCODING *, const char *); + int (*isNameMin)(const ENCODING *, const char *); + int (*isNmstrtMin)(const ENCODING *, const char *); + int (*byteToAscii)(const ENCODING *, const char *); + int (*charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (*isName2)(const ENCODING *, const char *); + int (*isName3)(const ENCODING *, const char *); + int (*isName4)(const ENCODING *, const char *); + int (*isNmstrt2)(const ENCODING *, const char *); + int (*isNmstrt3)(const ENCODING *, const char *); + int (*isNmstrt4)(const ENCODING *, const char *); + int (*isInvalid2)(const ENCODING *, const char *); + int (*isInvalid3)(const ENCODING *, const char *); + int (*isInvalid4)(const ENCODING *, const char *); +}; + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +static int checkCharRefNumber(int); + +#include "xmltok_impl.h" +#include "ascii.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static +int sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (((const struct normal_encoding *)(enc))->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (((const struct normal_encoding *)(enc))->byteToAscii(enc, p)) +static +int sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*(p)) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (((const struct normal_encoding *)(enc))->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (((const struct normal_encoding *)(enc))->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (((const struct normal_encoding *)(enc))->charMatches(enc, p, c)) +static +int sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +static +void utf8_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) + if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) + break; + } + for (to = *toP, from = *fromP; from != fromLim; from++, to++) + *to = *from; + *fromP = from; + *toP = to; +} + +static +void utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + unsigned short *to = *toP; + const char *from = *fromP; + while (from != fromLim && to != toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + *to++ = ((from[0] & 0x1f) << 6) | (from[1] & 0x3f); + from += 2; + break; + case BT_LEAD3: + *to++ = ((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (to + 1 == toLim) + break; + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } + *fromP = from; + *toP = to; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +static +void latin1_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + break; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + break; + *(*toP)++ = ((c >> 6) | UTF8_cval2); + *(*toP)++ = ((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + break; + *(*toP)++ = *(*fromP)++; + } + } +} + +static +void latin1_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +static +void ascii_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = *(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +static int unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static \ +void E ## toUtf8(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from; \ + for (from = *fromP; from != fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static \ +void E ## toUtf16(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ + fromLim -= 2; \ + for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static +int little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static +int little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static +int little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static +int little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static +int little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 12 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 12 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#if XML_BYTE_ORDER != 21 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static +int big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static +int big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static +int big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static +int big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static +int big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 21 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 21 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#if XML_BYTE_ORDER != 12 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +#undef PREFIX + +static +int streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (ASCII_a <= c1 && c1 <= ASCII_z) + c1 += ASCII_A - ASCII_a; + if (ASCII_a <= c2 && c2 <= ASCII_z) + c2 += ASCII_A - ASCII_a; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static +void initUpdatePosition(const ENCODING *enc, const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static +int toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static +int isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space +or there's an S followed by name=val. */ +static +int parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **nameEndPtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = 0; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = 0; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == ASCII_EQUALS) { + *nameEndPtr = ptr; + break; + } + if (isSpace(c)) { + *nameEndPtr = ptr; + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != ASCII_EQUALS) { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != ASCII_QUOT && c != ASCII_APOS) { + *nextTokPtr = ptr; + return 0; + } + open = c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!(ASCII_a <= c && c <= ASCII_z) + && !(ASCII_A <= c && c <= ASCII_Z) + && !(ASCII_0 <= c && c <= ASCII_9) + && c != ASCII_PERIOD + && c != ASCII_MINUS + && c != ASCII_UNDERSCORE) { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static const char KW_version[] = { + ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' +}; + +static const char KW_encoding[] = { + ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' +}; + +static const char KW_standalone[] = { + ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, ASCII_n, ASCII_e, '\0' +}; + +static const char KW_yes[] = { + ASCII_y, ASCII_e, ASCII_s, '\0' +}; + +static const char KW_no[] = { + ASCII_n, ASCII_o, '\0' +}; + +static +int doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = 0; + const char *name = 0; + const char *nameEnd = 0; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { + int c = toAscii(enc, val, end); + if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static +int checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = ((c >> 6) | UTF8_cval2); + buf[1] = ((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = ((c >> 12) | UTF8_cval3); + buf[1] = (((c >> 6) & 0x3f) | 0x80); + buf[2] = ((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = ((c >> 18) | UTF8_cval4); + buf[1] = (((c >> 12) & 0x3f) | 0x80); + buf[2] = (((c >> 6) & 0x3f) | 0x80); + buf[3] = ((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (charNum >> 10) + 0xD800; + buf[1] = (charNum & 0x3FF) + 0xDC00; + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + int (*convert)(void *userData, const char *p); + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +int XmlSizeOfUnknownEncoding(void) +{ + return sizeof(struct unknown_encoding); +} + +static +int unknown_isName(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static +int unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static +int unknown_isInvalid(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static +void unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + break; + utf8 = ((const struct unknown_encoding *)enc)->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + break; + utf8 = buf; + *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2); + } + else { + if (n > toLim - *toP) + break; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static +void unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) { + unsigned short c + = ((const struct unknown_encoding *)enc)->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short)((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, *fromP); + *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2); + } + else + (*fromP)++; + *(*toP)++ = c; + } +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + int (*convert)(void *userData, const char *p), + void *userData) +{ + int i; + struct unknown_encoding *e = mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = BT_LEAD2 - (c + 2); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = c == 0 ? 0xFFFF : c; + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static const char KW_ISO_8859_1[] = { + ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, ASCII_MINUS, ASCII_1, '\0' +}; +static const char KW_US_ASCII[] = { + ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, '\0' +}; +static const char KW_UTF_8[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' +}; +static const char KW_UTF_16[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' +}; +static const char KW_UTF_16BE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, '\0' +}; +static const char KW_UTF_16LE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, '\0' +}; + +static +int getEncodingIndex(const char *name) +{ + static const char *encodingNames[] = { + KW_ISO_8859_1, + KW_US_ASCII, + KW_UTF_8, + KW_UTF_16, + KW_UTF_16BE, + KW_UTF_16LE, + }; + int i; + if (name == 0) + return NO_ENC; + for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding specified +at initialization in the isUtf16 member. */ + +#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) +#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) + +/* This is what detects the encoding. +encodingTable maps from encoding indices to encodings; +INIT_ENC_INDEX(enc) is the index of the external (protocol) specified encoding; +state is XML_CONTENT_STATE if we're parsing an external text entity, +and XML_PROLOG_STATE otherwise. +*/ + + +static +int initScan(const ENCODING **encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr == end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ +#ifndef XML_DTD /* FIXME */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; +#endif + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document entity can only + start with ASCII characters. So the only way this can fail to be big-endian + UTF-16 if it it's an external parsed general entity that's labelled as + UTF-16LE. */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#include "xmltok_ns.c" +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#include "xmltok_ns.c" + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + int (*convert)(void *userData, const char *p), + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/lib/expat/xmltok.h b/lib/expat/xmltok.h new file mode 100644 index 0000000..f7703ce --- /dev/null +++ b/lib/expat/xmltok.h @@ -0,0 +1,301 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XMLTOKAPI +#define XMLTOKAPI /* as nothing */ +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be start of + illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok */ + +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok + for a name with a colon. */ +#define XML_TOK_PREFIXED_NAME 41 + +#ifdef XML_DTD +#define XML_TOK_IGNORE_SECT 42 +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define XML_N_STATES 4 +#else /* not XML_DTD */ +#define XML_N_STATES 3 +#endif /* not XML_DTD */ + +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 +#ifdef XML_DTD +#define XML_IGNORE_SECTION_STATE 3 +#endif /* XML_DTD */ + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + unsigned long lineNumber; + unsigned long columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +struct encoding { + int (*scanners[XML_N_STATES])(const ENCODING *, + const char *, + const char *, + const char **); + int (*literalScanners[XML_N_LITERAL_TYPES])(const ENCODING *, + const char *, + const char *, + const char **); + int (*sameName)(const ENCODING *, + const char *, const char *); + int (*nameMatchesAscii)(const ENCODING *, + const char *, const char *, const char *); + int (*nameLength)(const ENCODING *, const char *); + const char *(*skipS)(const ENCODING *, const char *); + int (*getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts); + int (*charRefNumber)(const ENCODING *enc, const char *ptr); + int (*predefinedEntityName)(const ENCODING *, const char *, const char *); + void (*updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (*isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr); + void (*utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + void (*utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* +Scan the string starting at ptr until the end of the next complete token, +but do not scan past eptr. Return an integer giving the type of token. + +Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + +Return XML_TOK_PARTIAL when the string does not contain a complete token; +nextTokPtr will not be set. + +Return XML_TOK_INVALID when the string does not start a valid token; nextTokPtr +will be set to point to the character which made the token invalid. + +Otherwise the string starts with a valid token; nextTokPtr will be set to point +to the character following the end of that token. + +Each data character counts as a single token, but adjacent data characters +may be returned together. Similarly for characters in the prolog outside +literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +#ifdef XML_DTD + +#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) + +#endif /* XML_DTD */ + +/* This is used for performing a 2nd-level tokenization on +the content of a literal that has already been returned by XmlTok. */ + +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XMLTOKAPI XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XMLTOKAPI XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncoding(void); +const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncoding(void); +int XMLTOKAPI XmlUtf8Encode(int charNumber, char *buf); +int XMLTOKAPI XmlUtf16Encode(int charNumber, unsigned short *buf); + +int XMLTOKAPI XmlSizeOfUnknownEncoding(void); +ENCODING XMLTOKAPI * +XmlInitUnknownEncoding(void *mem, + int *table, + int (*conv)(void *userData, const char *p), + void *userData); + +int XMLTOKAPI XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); +int XMLTOKAPI XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncodingNS(void); +const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncodingNS(void); +ENCODING XMLTOKAPI * +XmlInitUnknownEncodingNS(void *mem, + int *table, + int (*conv)(void *userData, const char *p), + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/lib/expat/xmltok_impl.c b/lib/expat/xmltok_impl.c new file mode 100644 index 0000000..b46e441 --- /dev/null +++ b/lib/expat/xmltok_impl.c @@ -0,0 +1,1765 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + +/* ptr points to character following " */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_x: + break; + case ASCII_X: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_m: + break; + case ASCII_M: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + break; + case ASCII_L: + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (ptr + MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (ptr + 2*MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static +int PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -XML_TOK_POUND_NAME; +} + +static +int PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (ptr != end) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (ptr == end) + return -XML_TOK_LITERAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) + return -XML_TOK_PROLOG_S; + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_BRACKET; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_PAREN; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + if (ptr == end) + return XML_TOK_PARTIAL; + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -tok; +} + +static +int PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static +int PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +#ifdef XML_DTD + +static +int PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int level = 0; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + end = ptr + n; + } + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_LT: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { + ++level; + ptr += MINBPC(enc); + } + } + break; + case BT_RSQB: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr += MINBPC(enc); + if (level == 0) { + *nextTokPtr = ptr; + return XML_TOK_IGNORE_SECT; + } + --level; + } + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +#endif /* XML_DTD */ + +static +int PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty element tag. +Returns the number of attributes. Pointers to the first attsMax attributes +are stored in atts. */ + +static +int PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; /* defined when state == inValue; + initialization just to shut up compilers */ + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static +int PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_x)) { + for (ptr += MINBPC(enc); !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: + case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + result <<= 4; + result |= (c - ASCII_0); + break; + case ASCII_A: case ASCII_B: case ASCII_C: case ASCII_D: case ASCII_E: case ASCII_F: + result <<= 4; + result += 10 + (c - ASCII_A); + break; + case ASCII_a: case ASCII_b: case ASCII_c: case ASCII_d: case ASCII_e: case ASCII_f: + result <<= 4; + result += 10 + (c - ASCII_a); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - ASCII_0); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static +int PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + return ASCII_LT; + case ASCII_g: + return ASCII_GT; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, ASCII_a)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_m)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) + return ASCII_AMP; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_q: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_u)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_t)) + return ASCII_QUOT; + } + } + break; + case ASCII_a: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_s)) + return ASCII_APOS; + } + } + break; + } + } + return 0; +} + +static +int PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static +int PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, + const char *end1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (ptr1 == end1) + return 0; + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + return ptr1 == end1; +} + +static +int PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return ptr - start; + } + } +} + +static +const char *PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static +void PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (unsigned)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (unsigned)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES diff --git a/lib/expat/xmltok_impl.h b/lib/expat/xmltok_impl.h new file mode 100644 index 0000000..eb92802 --- /dev/null +++ b/lib/expat/xmltok_impl.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file copying.txt for copying permission. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include diff --git a/lib/expat/xmltok_ns.c b/lib/expat/xmltok_ns.c new file mode 100644 index 0000000..2427898 --- /dev/null +++ b/lib/expat/xmltok_ns.c @@ -0,0 +1,96 @@ +const ENCODING *NS(XmlGetUtf8InternalEncoding)(void) +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING *NS(XmlGetUtf16InternalEncoding)(void) +{ +#if XML_BYTE_ORDER == 12 + return &ns(internal_little2_encoding).enc; +#elif XML_BYTE_ORDER == 21 + return &ns(internal_big2_encoding).enc; +#else + const short n = 1; + return *(const char *)&n ? &ns(internal_little2_encoding).enc : &ns(internal_big2_encoding).enc; +#endif +} + +static +const ENCODING *NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static +int NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static +int NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + SET_INIT_ENC_INDEX(p, i); + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static +const ENCODING *NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + encodingName, + encoding, + standalone); +} diff --git a/lib/portaudio/CVS/Entries b/lib/portaudio/CVS/Entries new file mode 100644 index 0000000..cd8ac0a --- /dev/null +++ b/lib/portaudio/CVS/Entries @@ -0,0 +1,4 @@ +/LICENSE.txt/1.3/Sun Mar 2 08:01:30 2003// +/README.txt/1.3/Sun Mar 2 08:01:31 2003// +/index.html/1.3/Sun Mar 2 08:01:32 2003// +D diff --git a/lib/portaudio/CVS/Entries.Log b/lib/portaudio/CVS/Entries.Log new file mode 100644 index 0000000..58db945 --- /dev/null +++ b/lib/portaudio/CVS/Entries.Log @@ -0,0 +1,12 @@ +A D/docs//// +A D/macproj//// +A D/pa_common//// +A D/pa_linux_oss//// +A D/pa_mac//// +A D/pa_mac_core//// +A D/pa_tests//// +A D/pa_unix_oss//// +A D/pa_win_ds//// +A D/pa_win_wmme//// +A D/pablio//// +A D/winproj//// diff --git a/lib/portaudio/CVS/Repository b/lib/portaudio/CVS/Repository new file mode 100644 index 0000000..796c77e --- /dev/null +++ b/lib/portaudio/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio diff --git a/lib/portaudio/CVS/Root b/lib/portaudio/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/LICENSE.txt b/lib/portaudio/LICENSE.txt new file mode 100644 index 0000000..105da3f --- /dev/null +++ b/lib/portaudio/LICENSE.txt @@ -0,0 +1,65 @@ +Portable header file to contain: +/* + * PortAudio Portable Real-Time Audio Library + * PortAudio API Header File + * Latest version available at: http://www.audiomulch.com/portaudio/ + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + + +Implementation files to contain: +/* + * PortAudio Portable Real-Time Audio Library + * Latest version at: http://www.audiomulch.com/portaudio/ + * Implementation + * Copyright (c) 1999-2000 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ \ No newline at end of file diff --git a/lib/portaudio/README.txt b/lib/portaudio/README.txt new file mode 100644 index 0000000..d1e5d7d --- /dev/null +++ b/lib/portaudio/README.txt @@ -0,0 +1,81 @@ +README for PortAudio +Implementations for PC DirectSound and Mac SoundManager + +/* + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com// + * + * Copyright (c) 1999-2000 Phil Burk and Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +PortAudio is a portable audio I/O library designed for cross-platform +support of audio. It uses a callback mechanism to request audio processing. +Audio can be generated in various formats, including 32 bit floating point, +and will be converted to the native format internally. + +Documentation: + See "pa_common/portaudio.h" for API spec. + See docs folder for a tutorial. + Also see http://www.portaudio.com/docs/ + And see "pa_tests/patest_saw.c" for an example. + +For information on compiling programs with PortAudio, please see the +tutorial at: + + http://www.portaudio.com/docs/pa_tutorial.html + +Important Files and Folders: + pa_common/ = platform independant code + pa_common/portaudio.h = header file for PortAudio API. Specifies API. + pa_common/pa_lib.c = host independant code for all implementations. + + pablio = simple blocking read/write interface + +Platform Implementations + pa_asio = ASIO for Windows and Macintosh + pa_beos = BeOS + pa_mac = Macintosh Sound Manager for OS 8,9 and Carbon + pa_mac_core = Macintosh Core Audio for OS X + pa_sgi = Silicon Graphics AL + pa_unix_oss = OSS implementation for various Unixes + pa_win_ds = Windows Direct Sound + pa_win_wmme = Windows MME (most widely supported) + +Test Programs + pa_tests/pa_fuzz.c = guitar fuzz box + pa_tests/pa_devs.c = print a list of available devices + pa_tests/pa_minlat.c = determine minimum latency for your machine + pa_tests/paqa_devs.c = self test that opens all devices + pa_tests/paqa_errs.c = test error detection and reporting + pa_tests/patest_clip.c = hear a sine wave clipped and unclipped + pa_tests/patest_dither.c = hear effects of dithering (extremely subtle) + pa_tests/patest_pink.c = fun with pink noise + pa_tests/patest_record.c = record and playback some audio + pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad(). + pa_tests/patest_sine.c = output a sine wave in a simple PA app + pa_tests/patest_sync.c = test syncronization of audio and video + pa_tests/patest_wire.c = pass input to output, wire simulator diff --git a/lib/portaudio/docs/CVS/Entries b/lib/portaudio/docs/CVS/Entries new file mode 100644 index 0000000..2fea09f --- /dev/null +++ b/lib/portaudio/docs/CVS/Entries @@ -0,0 +1,22 @@ +/index.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_impl_guide.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_impl_startstop.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_asio.html/1.1.1.1/Mon Nov 26 05:47:25 2001// +/pa_tut_callback.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_devs.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_explore.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_init.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_mac.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_open.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_oss.html/1.1.1.1/Mon Nov 26 05:47:25 2001// +/pa_tut_over.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_pc.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_run.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_rw.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_term.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tut_util.html/1.2/Mon Nov 26 05:56:05 2001// +/pa_tutorial.html/1.2/Mon Nov 26 05:56:05 2001// +/portaudio_h.txt/1.2/Mon Nov 26 05:56:05 2001// +/portaudio_icmc2001.pdf/1.1.1.1/Mon Nov 26 05:47:28 2001// +/releases.html/1.2/Mon Nov 26 05:56:05 2001// +D diff --git a/lib/portaudio/docs/CVS/Repository b/lib/portaudio/docs/CVS/Repository new file mode 100644 index 0000000..89bf592 --- /dev/null +++ b/lib/portaudio/docs/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/docs diff --git a/lib/portaudio/docs/CVS/Root b/lib/portaudio/docs/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/docs/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/docs/index.html b/lib/portaudio/docs/index.html new file mode 100644 index 0000000..b9a83c2 --- /dev/null +++ b/lib/portaudio/docs/index.html @@ -0,0 +1,43 @@ + + + + + + + + + PortAudio Docs + + +  +
+ + + +
+
+

+PortAudio Documentation

+
+ +

Copyright 2000 Phil Burk and Ross Bencina +

+Tutorial

+ +
Describes how to write audio programs using the PortAudio API.
+ +

+Implementation Guide

+ +
Describes how to write an implementation of PortAudio for a +new computer platform.
+ +

+Paper Presented at ICMC2001 (PDF)

+ +
Describes the PortAudio API and discusses implementation issues. +Written July 2001.
+ +


Return to PortAudio Home Page + + diff --git a/lib/portaudio/docs/pa_impl_guide.html b/lib/portaudio/docs/pa_impl_guide.html new file mode 100644 index 0000000..042a717 --- /dev/null +++ b/lib/portaudio/docs/pa_impl_guide.html @@ -0,0 +1,196 @@ + + + + + + + + + PortAudio Implementation - Start/Stop + + +  +

+ + + +
+
+

+PortAudio Implementation Guide

+
+ +

This document describes how to implement the PortAudio API on a new +computer platform. Implementing PortAudio on a new platform, makes it possible +to port many existing audio applications to that platform. +

By Phil Burk +
Copyright 2000 Phil Burk and Ross Bencina +

Note that the license says: "Any person wishing to distribute modifications +to the Software is requested to send the modifications to the original +developer so that they can be incorporated into the canonical version.". +So when you have finished a new implementation, please send it back to +us at  "http://www.portaudio.com" +so that we can make it available for other users. Thank you! +

+Download the Latest PortAudio Implementation

+Always start with the latest implementation available at "http://www.portaudio.com". +

+Select an Existing Implementation as a Basis

+The fastest way to get started is to take an existing implementation and +translate it for your new platform. Choose an implementation whose architecture +is as close as possible to your target. +
    +
  • +DirectSound Implementation - pa_win_ds - Uses a timer callback for the +background "thread". Polls a circular buffer and writes blocks of data +to keep it full.
  • + +
  • +Windows MME - pa_win_wmme - Spawns an actual Win32 thread. Writes blocks +of data to the HW device and waits for events that signal buffer completion.
  • + +
  • +Linux OSS - pa_linux - Spawns a real thread that writes to the "/dev/dsp" +stream using blocking I/O calls.
  • +
+When you write a new implementation, you will be using some code that is +in common with all implementations. This code is in the folder "pa_common". +It provides various functions such as parameter checking, error code to +text conversion, sample format conversion, clipping and dithering, etc. +

The code that you write will go into a separate folder called "pa_{os}_{api}". +For example, code specific to the DirectSound interface for Windows goes +in "pa_win_ds". +

+Read Docs and Code

+Famialiarize yourself with the system by reading the documentation provided. +here is a suggested order: +
    +
  1. +User Programming Tutorial
  2. + +
  3. +Header file "pa_common/portaudio.h" which defines API.
  4. + +
  5. +Header file "pa_common/pa_host.h" for host dependant code. This definces +the routine you will need to provide.
  6. + +
  7. +Shared code in "pa_common/pa_lib.c".
  8. + +
  9. +Docs on Implementation of Start/Stop +code.
  10. +
+ +

+Implement  Output to Default Device

+Now we are ready to crank some code. For instant gratification, let's try +to play a sine wave. +
    +
  1. +Link the test program "pa_tests/patest_sine.c" with the file "pa_lib.c" +and the implementation specific file you are creating.
  2. + +
  3. +For now, just stub out the device query code and the audio input code.
  4. + +
  5. +Modify PaHost_OpenStream() to open your default target device and get everything +setup.
  6. + +
  7. +Modify PaHost_StartOutput() to start playing audio.
  8. + +
  9. +Modify PaHost_StopOutput() to stop audio.
  10. + +
  11. +Modify PaHost_CloseStream() to clean up. Free all memory that you allocated +in PaHost_OpenStream().
  12. + +
  13. +Keep cranking until you can play a sine wave using "patest_sine.c".
  14. + +
  15. +Once that works, try "patest_pink.c", "patest_clip.c", "patest_sine8.c".
  16. + +
  17. +To test your Open and Close code, try "patest_many.c".
  18. + +
  19. +Now test to make sure that the three modes of stopping are properly supported +by running "patest_stop.c".
  20. + +
  21. +Test your implementation of time stamping with "patest_sync.c".
  22. +
+ +

+Implement Device Queries

+Now that output is working, lets implement the code for querying what devices +are available to the user. Run "pa_tests/pa_devs.c". It should print all +of the devices available and their characteristics. +

+Implement Input

+Implement audio input and test it with: +
    +
  1. +patest_record.c - record in half duplex, play back as recorded.
  2. + +
  3. +patest_wire.c - full duplex, copies input to output. Note that some HW +may not support full duplex.
  4. + +
  5. +patest_fuzz.c - plug in your guitar and get a feel for why latency is an +important issue in computer music.
  6. + +
  7. +paqa_devs.c - try to open every device and use it with every possible format
  8. +
+ +

+Debugging Tools

+You generally cannot use printf() calls to debug real-time processes because +they disturb the timing. Also calling printf() from your background thread +or interrupt could crash the machine. So PA includes a tool for capturing +events and storing the information while it is running. It then prints +the events when Pa_Terminate() is called. +
    +
  1. +To enable trace mode, change TRACE_REALTIME_EVENTS in "pa_common/pa_trace.h" +from a (0) to a (1).
  2. + +
  3. +Link with "pa_common/pa_trace.c".
  4. + +
  5. +Add trace messages to your code by calling:
    +   void AddTraceMessage( char *msg, int data );
    +for example
    +   AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks +);
  6. + +
  7. +Run your program. You will get a dump of events at the end.
  8. + +
  9. +You can leave the trace messages in your code. They will turn to NOOPs +when you change TRACE_REALTIME_EVENTS back to (0).
  10. +
+ +

+Delivery

+Please send your new code along with notes on the implementation back to +us at "http://www.portaudio.com". +We will review the implementation and post it with your name. If you had +to make any modifications to the code in "pa_common" or "pa_tests" please +send us those modifications and your notes. We will try to merge your changes +so that the "pa_common" code works with all implementations. +

If you have suggestions for how to make future implementations easier, +please let us know. +
THANKS! +
  + + diff --git a/lib/portaudio/docs/pa_impl_startstop.html b/lib/portaudio/docs/pa_impl_startstop.html new file mode 100644 index 0000000..a1ee93a --- /dev/null +++ b/lib/portaudio/docs/pa_impl_startstop.html @@ -0,0 +1,190 @@ + + + + + + + + + PortAudio Implementation - Start/Stop + + +  +

+ + + +
+
+

+PortAudio Implementation

+
+ +

+Starting and Stopping Streams

+PortAudio is generally executed in two "threads". The foreground thread +is the application thread. The background "thread" may be implemented as +an actual thread, an interrupt handler, or a callback from a timer thread. +

There are three ways that PortAudio can stop a stream. In each case +we look at the sequence of events and the messages sent between the two +threads. The following variables are contained in the internalPortAudioStream. +

int   past_IsActive;     +/* Background is still playing. */ +
int   past_StopSoon;     /* Stop +when last buffer done. */ +
int   past_StopNow;      /* +Stop IMMEDIATELY. */
+ +

+Pa_AbortStream()

+This function causes the background thread to terminate as soon as possible +and audio I/O to stop abruptly. +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Foreground ThreadBackground Thread
sets StopNow
sees StopNow
clears IsActive, stops thread
waits for thread to exit
turns off audio I/O
+ +

+Pa_StopStream()

+This function stops the user callback function from being called and then +waits for all audio data written to the output buffer to be played. In +a system with very low latency, you may not hear any difference between +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Foreground ThreadBackground Thread
sets StopSoon
stops calling user callback
continues until output buffer empty
clears IsActive, stops thread
waits for thread to exit
turns off audio I/O
+ +

+User callback returns one.

+If the user callback returns one then the user callback function will no +longer be called. Audio output will continue until all audio data written +to the output buffer has been played. Then the audio I/O is stopped, the +background thread terminates, and the stream becomes inactive. +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Foreground ThreadBackground Thread
callback returns 1
sets StopSoon
stops calling user callback
continues until output buffer empty
clears IsActive, stops thread
waits for thread to exit
turns off audio I/O
+ +
  + + diff --git a/lib/portaudio/docs/pa_tut_asio.html b/lib/portaudio/docs/pa_tut_asio.html new file mode 100644 index 0000000..0eb3c5d --- /dev/null +++ b/lib/portaudio/docs/pa_tut_asio.html @@ -0,0 +1,55 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for ASIO (Windows or Macintosh)

+ +
ASIO is a low latency audio API from Steinberg. To compile +an ASIO application, you must first download +the ASIO SDK from Steinberg. You also need to obtain ASIO drivers for +your audio device. +

Note: I am using '/' as a file separator below. On Macintosh replace +'/' with ':'. On Windows, replace '/' with '\'. +

 To use ASIO with the PortAudio library add the following source +files to your project: +

+
pa_asio/pa_asio.cpp
+
+and also these files from the ASIO SDK: +
+
common/asio.cpp
+host/asiodrivers.cpp
+host/asiolist.cpp
+
+and add these directories to the path for include files +
+
asiosdk2/host/pc   (for Windows)
+asiosdk2/common
+asiosdk2/host
+
+You may try compiling the "pa_tests/patest_saw.c" file first because it +is the simplest.
+home | +contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_callback.html b/lib/portaudio/docs/pa_tut_callback.html new file mode 100644 index 0000000..fb7b6d7 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_callback.html @@ -0,0 +1,91 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Writing a Callback Function

+ +
To write a program using PortAudio, you must include the "portaudio.h" +include file. You may wish to read "portaudio.h" +because it contains a complete description of the PortAudio functions and +constants. +
+
#include "portaudio.h"
+
+The next task is to write your custom callback function. It is a function +that is called by the PortAudio engine whenever it has captured audio data, +or when it needs more audio data for output. +

Your callback function is often called by an interrupt, or low level +process so you should not do any complex system activities like allocating +memory, or reading or writing files, or printf(). Just crunch numbers and +generate audio signals. What is safe or not safe will vary from platform +to platform. On the Macintosh, for example, you can only call "interrupt +safe" routines. Also do not call any PortAudio functions in the callback +except for Pa_StreamTime() and Pa_GetCPULoad(). +

Your callback function must return an int and accept the exact parameters +specified in this typedef: +

+
typedef int (PortAudioCallback)(
+               void *inputBuffer, void *outputBuffer,
+               unsigned long framesPerBuffer,
+               PaTimestamp outTime, void *userData );
+
+Here is an example callback function from the test file "patests/patest_saw.c". +It calculates a simple left and right sawtooth signal and writes it to +the output buffer. Notice that in this example, the signals are of float +data type. The signals must be between -1.0 and +1.0. You can also use +16 bit integers or other formats which are specified during setup. You +can pass a pointer to your data structure through PortAudio which will +appear as userData. +
+
int patestCallback(  void *inputBuffer, void *outputBuffer,
+                     unsigned long framesPerBuffer,
+                     PaTimestamp outTime, void *userData )
+{
+    unsigned int i;
+/* Cast data passed through stream to our structure type. */
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+        
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+    /* Stereo channels are interleaved. */
+        *out++ = data->left_phase;              /* left */
+        *out++ = data->right_phase;             /* right */
+
+    /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
+        data->left_phase += 0.01f;
+    /* When signal reaches top, drop back down. */
+        if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
+
+    /* higher pitch so we can distinguish left and right. */
+        data->right_phase += 0.03f; 
+        if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
+    }
+    return 0;
+}
+
+
+home | +contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_devs.html b/lib/portaudio/docs/pa_tut_devs.html new file mode 100644 index 0000000..6a8c08a --- /dev/null +++ b/lib/portaudio/docs/pa_tut_devs.html @@ -0,0 +1,65 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Querying for Available Devices

+ +
There are often several different audio devices available in +a computer with different capabilities. They can differ in the sample rates +supported, bit widths, etc. PortAudio provides a simple way to query for +the available devices, and then pass the selected device to Pa_OpenStream(). +For an example, see the file "pa_tests/pa_devs.c". +

To determine the number of devices: +

+
numDevices = Pa_CountDevices();
+
+You can then query each device in turn by calling Pa_GetDeviceInfo() with +an index. +
+
for( i=0; i<numDevices; i++ ) {
+     pdi = Pa_GetDeviceInfo( i );
+
+It will return a pointer to a PaDeviceInfo structure which is +defined as: +
+
typedef struct{
+    int structVersion; 
+    const char *name;
+    int maxInputChannels;
+    int maxOutputChannels;
+/* Number of discrete rates, or -1 if range supported. */
+    int numSampleRates;
+/* Array of supported sample rates, or {min,max} if range supported. */
+    const double *sampleRates;
+    PaSampleFormat nativeSampleFormat;
+}PaDeviceInfo;
+
+If the device supports a continuous range of sample rates, then numSampleRates +will equal -1, and the sampleRates array will have two values, the minimum  +and maximum rate. +

The device information is allocated by Pa_Initialize() and freed by +Pa_Terminate() so you do not have to free() the structure returned by Pa_GetDeviceInfo().

+home | +contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_explore.html b/lib/portaudio/docs/pa_tut_explore.html new file mode 100644 index 0000000..ea616c1 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_explore.html @@ -0,0 +1,42 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Exploring PortAudio

+ +
Now that you have a good idea of how PortAudio works, you can +try out the test programs. +
    +
  • +For an example of playing a sine wave, see "pa_tests/patest_sine.c".
  • + +
  • +For an example of recording and playing back a sound, see  "pa_tests/patest_record.c".
  • +
+I also encourage you to examine the source for the PortAudio libraries. +If you have suggestions on ways to improve them, please let us know. if +you want to implement PortAudio on a new platform, please let us know as +well so we can coordinate people's efforts.
+home | contents +| previous |  next + + diff --git a/lib/portaudio/docs/pa_tut_init.html b/lib/portaudio/docs/pa_tut_init.html new file mode 100644 index 0000000..876130b --- /dev/null +++ b/lib/portaudio/docs/pa_tut_init.html @@ -0,0 +1,43 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Initializing PortAudio

+ +
Before making any other calls to PortAudio, you must call Pa_Initialize(). +This will trigger a scan of available devices which can be queried later. +Like most PA functions, it will return a result of type paError. +If the result is not paNoError, then an error has occurred. +
+
err = Pa_Initialize();
+if( err != paNoError ) goto error;
+
+You can get a text message that explains the error message by passing it +to +
+
printf(  "PortAudio error: %s\n", Pa_GetErrorText( err ) );
+
+
+home | contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_mac.html b/lib/portaudio/docs/pa_tut_mac.html new file mode 100644 index 0000000..57b6e1d --- /dev/null +++ b/lib/portaudio/docs/pa_tut_mac.html @@ -0,0 +1,41 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for Macintosh

+ +
To compile a Macintosh application with the PortAudio library, +add the following source files to your project: +
+
pa_mac:pa_mac.c
+pa_common:pa_lib.c
+pa_common:portaudio.h
+pa_common:pa_host.h
+
+Also add the Apple SoundLib to your project. +

You may try compiling the "pa_tests:patest_saw.c" file first because +it is the simplest.

+home | +contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_open.html b/lib/portaudio/docs/pa_tut_open.html new file mode 100644 index 0000000..977d59f --- /dev/null +++ b/lib/portaudio/docs/pa_tut_open.html @@ -0,0 +1,52 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Opening a Stream using Defaults

+ +
The next step is to open a stream which is similar to opening +a file. You can specify whether you want audio input and/or output, how +many channels, the data format, sample rate, etc. There are two calls for +opening streams, Pa_OpenStream() and Pa_OpenDefaultStream(). +

Pa_OpenStream() takes extra  parameters which give you +more control. You can normally just use Pa_OpenDefaultStream() +which just calls Pa_OpenStream() with some reasonable +default values.  Let's open a stream for stereo output, using floating +point data, at 44100 Hz. +

+
err = Pa_OpenDefaultStream(
+    &stream,        /* passes back stream pointer */
+    0,              /* no input channels */
+    2,              /* stereo output */
+    paFloat32,      /* 32 bit floating point output */
+    44100,          /* sample rate */
+    256,            /* frames per buffer */
+    0,              /* number of buffers, if zero then use default minimum */
+    patestCallback, /* specify our custom callback */
+    &data );        /* pass our data through to callback */
+
+If you want to use 16 bit integer data, pass paInt16 instead of +paFloat32.
+home | contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_oss.html b/lib/portaudio/docs/pa_tut_oss.html new file mode 100644 index 0000000..14c55b2 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_oss.html @@ -0,0 +1,45 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for Linux

+ +
[Skip this page if you are not using Linux] +

We currently only support the OSS audio drivers for Linux. We hope to +someday support the newer ALSA drivers. +

    +
  1. +cd to pa_linux_oss directory
  2. + +
  3. +Edit the Makefile and uncomment one of the tests. You may try compiling +the "patest_sine.c" file first because it is very simple.
  4. + +
  5. +gmake run
  6. +
+
+home | +contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_over.html b/lib/portaudio/docs/pa_tut_over.html new file mode 100644 index 0000000..1de6ec4 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_over.html @@ -0,0 +1,86 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Overview of PortAudio

+ +
PortAudio is a library that provides streaming audio input +and output. It is a cross-platform API (Application Programming Interface) +that works on Windows and Macintosh, and perhaps other platforms by the +time you read this. This means that you can write a simple 'C' program +to process or generate an audio signal, and that program can run on several +different computers just by recompiling the source code. +

Here are the steps to writing a PortAudio application: +

    +
  1. +Write a callback function that will be called by PortAudio when audio processing +is needed.
  2. + +
  3. +Initialize the PA library and open a stream for audio I/O.
  4. + +
  5. +Start the stream. Your callback function will be now be called repeatedly +by PA in the background.
  6. + +
  7. +In your callback you can read audio data from the inputBuffer and/or write +data to the outputBuffer.
  8. + +
  9. +Stop the stream by returning 1 from your callback, or by calling a stop +function.
  10. + +
  11. +Close the stream and terminate the library.
  12. +
+
+ +
There is also another interface +provided that allows you to generate audio in the foreground. You then +simply write data to the stream and the tool will not return until it is +ready to accept more data. This interface is simpler to use but is usually +not preferred for large applications because it requires that you launch +a thread to perform the synthesis. Launching a thread may be difficult +on non-multi-tasking systems such as the Macintosh prior to MacOS X. +

Let's continue by building a simple application that will play a sawtooth +wave. +

Please select the page for the implementation you would like to use: +

+
+home | +contents +| previous + + diff --git a/lib/portaudio/docs/pa_tut_pc.html b/lib/portaudio/docs/pa_tut_pc.html new file mode 100644 index 0000000..8a73a3a --- /dev/null +++ b/lib/portaudio/docs/pa_tut_pc.html @@ -0,0 +1,63 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for Windows (WMME or DirectSound)

+ +
To compile PortAudio for Windows, you can choose between two +options. One implementation uses the DirectSound API. The other uses the +Windows MultiMedia Extensions API (aka WMME or WAVE). +

Some advantages of using DirectSound are that DirectSound may have lower +latency than WMME, and supports effects processing plugins. But one disadvantage +is that DirectSound is not installed on all PCs, and is not well supported +under Windows NT. +

For either implementation add the following source files to your project: +

+
pa_common\pa_lib.c
+pa_common\portaudio.h
+pa_common\pa_host.h
+ +
Link with the system library: winmm.lib
+
+WMME - To use the WMME implementation, add the following source +files to your project: +
pa_win_wmme/pa_win_wmme.c
+DirectSound - If you want to use the DirectSound implementation +of PortAudio then you must have a recent copy of the free +DirectX +SDK for Developers from Microsoft installed on your computer. To compile +an application add the following source files to your project: +
+
pa_win_ds\dsound_wrapper.c
+pa_win_ds\pa_dsound.c
+
+ +
+
Link with the system library: dsound.lib
+
+You may try compiling the "pa_tests\patest_saw.c" file first because it +is the simplest.
+home | +contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_run.html b/lib/portaudio/docs/pa_tut_run.html new file mode 100644 index 0000000..476df68 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_run.html @@ -0,0 +1,56 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Starting and Stopping a Stream

+ +
The stream will not start running until you call Pa_StartStream(). +Then it will start calling your callback function to perform the audio +processing. +
+
err = Pa_StartStream( stream );
+if( err != paNoError ) goto error;
+
+At this point, audio is being generated. You can communicate to your callback +routine through the data structure you passed in on the open call, or through +global variables, or using other interprocess communication techniques. +Please be aware that your callback function may be called at interrupt +time when your foreground process is least expecting it. So avoid sharing +complex data structures that are easily corrupted like double linked lists. +

In many of the tests we simply sleep for a few seconds so we can hear +the sound. This is easy to do with Pa_Sleep() which will sleep for some +number of milliseconds. Do not rely on this function for accurate scheduling. +it is mostly for writing examples. +

+
/* Sleep for several seconds. */
+Pa_Sleep(NUM_SECONDS*1000);
+
+When you are through, you can stop the stream from the foreground. +
+
err = Pa_StopStream( stream );
+if( err != paNoError ) goto error;
+
+You can also stop the stream by returning 1 from your custom callback function.
+home | contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_rw.html b/lib/portaudio/docs/pa_tut_rw.html new file mode 100644 index 0000000..89441d7 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_rw.html @@ -0,0 +1,77 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Blocking Read/Write Functions

+ +
[Note: These functions are not part of the official PortAudio +API. They are simply built on top of PortAudio as an extra utility. Also +note that they are under evaluation and their definition may change.] +

There are two fundamentally different ways to design an audio API. One +is to use callback functions the way we have already shown. The callback +function operates under an interrupt or background thread This leaves the +foreground application free to do other things while the audio just runs +in the background. But this can sometimes be awkward. +

So we have provided an alternative technique that lets a program generate +audio in the foreground and then just write it to the audio stream as if +it was a file. If there is not enough room in the audio buffer for more +data, then the write function will just block until more room is available. +This can make it very easy to write an audio example. To use this tool, +you must add the files "pablio/pablio.c" and "pablio/ringbuffer.c" to your +project. You must also: +

+
#include "pablio.h"
+
+Here is a short excerpt of a program that opens a stream for input and +output. It then read a pair of samples at a time from input, and writes +them to output, in a loop. The complete example can be found in "pablio/test_rw.c". +
+
    SAMPLE          samples[2];
+    PaError         err;
+    PABLIO_Stream  *aStream;
+
+/* Open simplified blocking I/O layer on top of PortAudio. */
+    err = OpenAudioStream( &rwbl, SAMPLE_RATE, paFloat32,
+                         (PABLIO_READ_WRITE | PABLIO_STEREO) );
+    if( err != paNoError ) goto error;
+
+/* Process samples in the foreground. */
+    for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
+    {
+    /* Read one frame of data into sample array from audio input. */
+        ReadAudioStream( aStream, samples, 1 );
+    /*
+    ** At this point you could process the data in samples[0] and samples[1],
+    ** and write the result back to the same samples array.
+    */
+    /* Write that same frame of data to output. */
+        WriteAudioStream( aStream, samples, 1 );
+    }
+
+    CloseAudioStream( aStream );
+
+
+home | +contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_term.html b/lib/portaudio/docs/pa_tut_term.html new file mode 100644 index 0000000..f96e628 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_term.html @@ -0,0 +1,47 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Terminating PortAudio

+ +
You can start and stop a stream as many times as you like. +But when you are done using it, you should close it by calling:
+ +
+
+
err = Pa_CloseStream( stream );
+if( err != paNoError ) goto error;
+
+Then when you are done using PortAudio, you should terminate the whole +system by calling: +
+
Pa_Terminate();
+
+That's basically it. You can now write an audio program in 'C' that will +run on multiple platforms, for example PCs and Macintosh. +

In the rest of the tutorial we will look at some additional utility +functions, and a different way of using PortAudio that does not require +the use of a callback function.

+home | contents +| previousnext + + diff --git a/lib/portaudio/docs/pa_tut_util.html b/lib/portaudio/docs/pa_tut_util.html new file mode 100644 index 0000000..ddf7de8 --- /dev/null +++ b/lib/portaudio/docs/pa_tut_util.html @@ -0,0 +1,55 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Utility Functions

+ +
Here are several more functions that are not critical, but +may be handy when using PortAudio. +

Pa_StreamActive() returns one when the stream in playing audio, zero +when not playing, or a negative error number if the stream is invalid. +The stream is active between calls to Pa_StartStream() and Pa_StopStream(), +but may also become inactive if the callback returns a non-zero value. +In the latter case, the stream is considered inactive after the last buffer +has finished playing. +

+
PaError Pa_StreamActive( PortAudioStream *stream );
+
+Pa_StreamTime() returns the number of samples that have been generated. +PaTimeStamp is a double precision number which is a convenient way to pass +big numbers around even though we only need integers. +
+
PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+The "CPU Load" is a fraction of total CPU time consumed by the stream's +audio processing. A value of 0.5 would imply that PortAudio and the sound +generating callback was consuming roughly 50% of the available CPU time. +This function may be called from the callback function or the application. +
+
double Pa_GetCPULoad( PortAudioStream* stream );
+
+
+home | +contents | previous +|  next + + diff --git a/lib/portaudio/docs/pa_tutorial.html b/lib/portaudio/docs/pa_tutorial.html new file mode 100644 index 0000000..b3b7d0e --- /dev/null +++ b/lib/portaudio/docs/pa_tutorial.html @@ -0,0 +1,44 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

Copyright 2000 Phil Burk and Ross Bencina +

+Table of Contents

+ +
Overview of PortAudio +
Compiling for Macintosh +
Compiling for Windows (DirectSound and WMME) +
Compiling for ASIO on Windows or Macintosh +
Compiling for Unix OSS +
Writing a Callback Function +
Initializing PortAudio +
Opening a Stream using Defaults +
Starting and Stopping a Stream +
Cleaning Up +
Utilities +
Querying for Devices +
Blocking Read/Write Functions +
Exploring the PortAudio Package
+home | contents | +previous |  next + + diff --git a/lib/portaudio/docs/portaudio_h.txt b/lib/portaudio/docs/portaudio_h.txt new file mode 100644 index 0000000..7f70df7 --- /dev/null +++ b/lib/portaudio/docs/portaudio_h.txt @@ -0,0 +1,427 @@ +#ifndef PORT_AUDIO_H +#define PORT_AUDIO_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * PortAudio Portable Real-Time Audio Library + * PortAudio API Header File + * Latest version available at: http://www.audiomulch.com/portaudio/ + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +typedef int PaError; +typedef enum { + paNoError = 0, + + paHostError = -10000, + paInvalidChannelCount, + paInvalidSampleRate, + paInvalidDeviceId, + paInvalidFlag, + paSampleFormatNotSupported, + paBadIODeviceCombination, + paInsufficientMemory, + paBufferTooBig, + paBufferTooSmall, + paNullCallback, + paBadStreamPtr, + paTimedOut, + paInternalError +} PaErrorNum; + +/* + Pa_Initialize() is the library initialisation function - call this before + using the library. +*/ + +PaError Pa_Initialize( void ); + +/* + Pa_Terminate() is the library termination function - call this after + using the library. +*/ + +PaError Pa_Terminate( void ); + +/* + Return host specific error. + This can be called after receiving a paHostError. +*/ +long Pa_GetHostError( void ); + +/* + Translate the error number into a human readable message. +*/ +const char *Pa_GetErrorText( PaError errnum ); + +/* + Sample formats + + These are formats used to pass sound data between the callback and the + stream. Each device has a "native" format which may be used when optimum + efficiency or control over conversion is required. + + Formats marked "always available" are supported (emulated) by all devices. + + The floating point representation uses +1.0 and -1.0 as the respective + maximum and minimum. + +*/ + +typedef unsigned long PaSampleFormat; +#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/ +#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/ +#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/ +#define paInt24 ((PaSampleFormat) (1<<3)) +#define paPackedInt24 ((PaSampleFormat) (1<<4)) +#define paInt8 ((PaSampleFormat) (1<<5)) +#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */ +#define paCustomFormat ((PaSampleFormat) (1<<16)) + +/* + Device enumeration mechanism. + + Device ids range from 0 to Pa_CountDevices()-1. + + Devices may support input, output or both. Device 0 is always the "default" + device and should support at least stereo in and out if that is available + on the taget platform _even_ if this involves kludging an input/output + device on platforms that usually separate input from output. Other platform + specific devices are specified by positive device ids. +*/ + +typedef int PaDeviceID; +#define paNoDevice -1 + +typedef struct{ + int structVersion; + const char *name; + int maxInputChannels; + int maxOutputChannels; +/* Number of discrete rates, or -1 if range supported. */ + int numSampleRates; +/* Array of supported sample rates, or {min,max} if range supported. */ + const double *sampleRates; + PaSampleFormat nativeSampleFormats; +} PaDeviceInfo; + + +int Pa_CountDevices(); +/* + Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() + + Return the default device ID or paNoDevice if there is no devices. + The result can be passed to Pa_OpenStream(). + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PA_RECOMMENDED_OUTPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "pa_devs". +*/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ); +PaDeviceID Pa_GetDefaultOutputDeviceID( void ); + +/* + PaTimestamp is used to represent a continuous sample clock with arbitrary + start time useful for syncronisation. The type is used in the outTime + argument to the callback function and the result of Pa_StreamTime() +*/ + +typedef double PaTimestamp; + +/* + Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure + referring to the device specified by id. + If id is out of range the function returns NULL. + + The returned structure is owned by the PortAudio implementation and must + not be manipulated or freed. The pointer is guaranteed to be valid until + between calls to Pa_Initialize() and Pa_Terminate(). +*/ + +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID devID ); + +/* + PortAudioCallback is implemented by clients of the portable audio api. + + inputBuffer and outputBuffer are arrays of interleaved samples, + the format, packing and number of channels used by the buffers are + determined by parameters to Pa_OpenStream() (see below). + + framesPerBuffer is the number of sample frames to be processed by the callback. + + outTime is the time in samples when the buffer(s) processed by + this callback will begin being played at the audio output. + See also Pa_StreamTime() + + userData is the value of a user supplied pointer passed to Pa_OpenStream() + intended for storing synthesis data etc. + + return value: + The callback can return a nonzero value to stop the stream. This may be + useful in applications such as soundfile players where a specific duration + of output is required. However, it is not necessary to utilise this mechanism + as StopStream() will also terminate the stream. A callback returning a + nonzero value must fill the entire outputBuffer. + + NOTE: None of the other stream functions may be called from within the + callback function except for Pa_GetCPULoad(). + +*/ + +typedef int (PortAudioCallback)( + void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + + +/* + Stream flags + + These flags may be supplied (ored together) in the streamFlags argument to + the Pa_OpenStream() function. + + [ suggestions? ] +*/ + +#define paNoFlag (0) +#define paClipOff (1<<0) /* disable defult clipping of out of range samples */ +#define paDitherOff (1<<1) /* disable default dithering */ +#define paPlatformSpecificFlags (0x00010000) +typedef unsigned long PaStreamFlags; + +/* + A single PortAudioStream provides multiple channels of real-time + input and output audio streaming to a client application. + Pointers to PortAudioStream objects are passed between PortAudio functions. +*/ + +typedef void PortAudioStream; +#define PaStream PortAudioStream + +/* + Pa_OpenStream() opens a stream for either input, output or both. + + stream is the address of a PortAudioStream pointer which will receive + a pointer to the newly opened stream. + + inputDevice is the id of the device used for input (see PaDeviceID above.) + inputDevice may be paNoDevice to indicate that an input device is not required. + + numInputChannels is the number of channels of sound to be delivered to the + callback. It can range from 1 to the value of maxInputChannels in the + device input record for the device specified in the inputDevice parameter. + If inputDevice is paNoDevice numInputChannels is ignored. + + inputSampleFormat is the format of inputBuffer provided to the callback + function. inputSampleFormat may be any of the formats described by the + PaSampleFormat enumeration (see above). PortAudio guarantees support for + the sound devices native formats (nativeSampleFormats in the device info + record) and additionally 16 and 32 bit integer and 32 bit floating point + formats. Support for other formats is implementation defined. + + inputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or stream processing. + inputDriverInfo is never required for correct operation. If not used + inputDriverInfo should be NULL. + + outputDevice is the id of the device used for output (see PaDeviceID above.) + outputDevice may be paNoDevice to indicate that an output device is not required. + + numOutputChannels is the number of channels of sound to be supplied by the + callback. See the definition of numInputChannels above for more details. + + outputSampleFormat is the sample format of the outputBuffer filled by the + callback function. See the definition of inputSampleFormat above for more + details. + + outputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or stream processing. + outputDriverInfo is never required for correct operation. If not used + outputDriverInfo should be NULL. + + sampleRate is the desired sampleRate for input and output + + framesPerBuffer is the length in sample frames of all internal sample buffers + used for communication with platform specific audio routines. Wherever + possible this corresponds to the framesPerBuffer parameter passed to the + callback function. + + numberOfBuffers is the number of buffers used for + multibuffered communication with the platform specific audio + routines. If you pass zero, then an optimum value will be + chosen for you internally. This parameter is provided only + as a guide - and does not imply that an implementation must + use multibuffered i/o when reliable double buffering is + available (such as SndPlayDoubleBuffer() on the Macintosh.) + + streamFlags may contain a combination of flags ORed together. + These flags modify the behavior of the + streaming process. Some flags may only be relevant to certain buffer formats. + + callback is a pointer to a client supplied function that is responsible + for processing and filling input and output buffers (see above for details.) + + userData is a client supplied pointer which is passed to the callback + function. It could for example, contain a pointer to instance data necessary + for processing the audio buffers. + + return value: + Apon success Pa_OpenStream() returns PaNoError and places a pointer to a + valid PortAudioStream in the stream argument. The stream is inactive (stopped). + If a call to Pa_OpenStream() fails a nonzero error code is returned (see + PAError above) and the value of stream is invalid. + +*/ + +PaError Pa_OpenStream( PortAudioStream** stream, + PaDeviceID inputDevice, + int numInputChannels, + PaSampleFormat inputSampleFormat, + void *inputDriverInfo, + PaDeviceID outputDevice, + int numOutputChannels, + PaSampleFormat outputSampleFormat, + void *outputDriverInfo, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PaStreamFlags streamFlags, + PortAudioCallback *callback, + void *userData ); + + +/* + Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that + opens the default input and/or ouput devices. Most parameters have + identical meaning to their Pa_OpenStream() counterparts, with the following + exceptions: + + If either numInputChannels or numOutputChannels is 0 the respective device + is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() ) + + sampleFormat applies to both the input and output buffers. +*/ + +PaError Pa_OpenDefaultStream( PortAudioStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PortAudioCallback *callback, + void *userData ); + +/* + Pa_CloseStream() closes an audio stream, flushing any pending buffers. +*/ + +PaError Pa_CloseStream( PortAudioStream* ); + +/* + Pa_StartStream() and Pa_StopStream() begin and terminate audio processing. + Pa_StopStream() waits until all pending audio buffers have been played. + Pa_AbortStream() stops playing immediately without waiting for pending + buffers to complete. +*/ + +PaError Pa_StartStream( PortAudioStream *stream ); + +PaError Pa_StopStream( PortAudioStream *stream ); + +PaError Pa_AbortStream( PortAudioStream *stream ); + +/* + Pa_StreamActive() returns one when the stream is playing audio, + zero when not playing, or a negative error number if the + stream is invalid. + The stream is active between calls to Pa_StartStream() and Pa_StopStream(), + but may also become inactive if the callback returns a non-zero value. + In the latter case, the stream is considered inactive after the last + buffer has finished playing. +*/ + +PaError Pa_StreamActive( PortAudioStream *stream ); + +/* + Pa_StreamTime() returns the current output time for the stream in samples. + This time may be used as a time reference (for example syncronising audio to + MIDI). +*/ + +PaTimestamp Pa_StreamTime( PortAudioStream *stream ); + +/* + The "CPU Load" is a fraction of total CPU time consumed by the + stream's audio processing. + A value of 0.5 would imply that PortAudio and the sound generating + callback was consuming roughly 50% of the available CPU time. + This function may be called from the callback function or the application. +*/ +double Pa_GetCPULoad( PortAudioStream* stream ); + +/* + Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for + the current host based on minimum latency. + On the PC, for the DirectSound implementation, latency can be optionally set + by user by setting an environment variable. + For example, to set latency to 200 msec, put: + + set PA_MIN_LATENCY_MSEC=200 + + in the AUTOEXEC.BAT file and reboot. + If the environment variable is not set, then the latency will be determined + based on the OS. Windows NT has higher latency than Win95. +*/ + +int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ); + +/* + Sleep for at least 'msec' milliseconds. + You may sleep longer than the requested time so don't rely + on this for accurate musical timing. +*/ +void Pa_Sleep( long msec ); + +/* + Return size in bytes of a single sample in a given PaSampleFormat + or paSampleFormatNotSupported. +*/ +PaError Pa_GetSampleSize( PaSampleFormat format ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORT_AUDIO_H */ diff --git a/lib/portaudio/docs/portaudio_icmc2001.pdf b/lib/portaudio/docs/portaudio_icmc2001.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dd074b7d334d9b0d4415330c2a123131aa130115 GIT binary patch literal 50968 zcma&NcUV(h8|F(dAwf#$sG)aABM?ApfY2cnktV&DP((yTdha08M0y7S0RaK&9i%D( zBE5G3yN^)v_ohd?17 z*3KY+j0^}MhJl#Eet&?2h#B89fwA=AL z%=7=}#3RHJmuCE{W8mmZON9N^GjJIC(u{vK4GeL4jem6w41VeC!~Sg>B`-TGA14oY zjFpcagdZaXL&Kmjs017VLn6^|ae?0pN7u{4_KuC67lhvoA%Xh+k}!ipp$dP3&7a`) zCwTk`)_;Q4pWyQ+K>h^GpWyT-c>f9ieDTkr>z~7ag596iKUeujf%tPM`X@mD1lXVO z&lmQ8TK^>ae@?#n=d*vV=l-W<`@c`Q`={mor~m(y=<&PzDqdD@c3M_0ze^Dp6&FQl z!o;n9pYVUKeb`?Y$3HiYKjHtnYry`xJpNym|6U+!kbiDh22MV%zh^|n)#EN&5&?t$ zr=wyj5cr>Uf%|vA;s3V0rk%Tkk0V3^4VU~q04h$dKEG=g(=@q>v9s~8wfmo1Z=c_D z?*;+{(ZmEz=(fcnXn+_}B_xeCoCe4V%?Rpsm0$v?bQ6}XTZ^*w4@b;#t7}5gEX!e> zok13=0^M<2FhX!031b{-4`I+0qfH279|=88k%=UjeLT%7s#n(wnz3Uyl`lw1#@cO# zpU_=5lYFYS_l9D5>aHwn`~cBPj-o2hlb)?lgh7kmRMHd5qzc$YT}N;nDOoah&`zxc zL`nn{oa29g}WgVwh?J-%|RcM zCjp@D#fyfeJ0qZ46ZYjo!$2CxI|1ypukLHVrc#m2cPzac4`$<^_Xddp`4McV^V9B3 zjAlJRte+WEAyA;gFj!CY(Q^TSAgyR9vjQ&YOR2~1DJj49gza=gm1OS-nN_VWw9QrW zv{0doxQC*#r);oWenozX2E>J5ma5G!)0HA2rdWiddMewddu{T}h9ZTHQ^6pSJ?hct zcpsmx+G@%1xq4@eaUq$e!R;$>SJQ%@Seos5Nl?<7HbT)OhC+~3BhRubCW$qirxxJj z-}jO@w0Ed>t*Gif@Ss8cSPkT}1p;veY=IMq!?pxl)RkUJ#IN}T((vb}X(5JeUN)^M zrUh9Sfw~9|IJ-_=VI)11Jm6j_Eom0+aJsx6bV4FpBD>y@IghV2MfVUi5XU#IsD_ya82moR^C4(P*dSAIG4& zYroq>y`pY&V4i9?6RP8@(|@EE_!0G-M}!c!-j=Lsc-L#nH=f)YELEjD)K5)FFc0mC zla(g%=ki$-{3=VLvwcuo7TO83!SA9PMaGPG>h7u*&`3~=DdA4JY!a+{{c2Z{MlK2^s~6hN z?H(|m(S}=`Pc5*Sjxc@3leA9A0|#uEB;WRm9Z;~(GQAR$0+F>d z7BiI0Tb*=7p+wteG3?3H4R#=1XptfdntNG7cSeWHB17)vGAc5B)ekKw)!%7l$u6e; zk+coL%P+qYzRRt&s#f~IW0DTMosq;*e_h+r{5}yu4M3ru`gU-VMC2XnoNgCuQaanh zkf0dbB|ul0+w6Kj-KuHg7b%UM;LR}A*7e|Q8gcg}?hJg1$D~QUY1QO*1~JJ-oLMI4 zuZ7CL6r;K&cNL%;|2w56ePvy8AAXyjoXotB@{${ z`qK2}>rkS(8e@)cp~{`ZGTr9{`Z>f36X6&>2y`}Iz)3iorO;OxdJ(2#4*Y$)=y5KK90HrpF}XOOv97;^Jcn~ z&m9A1pA%X-tT!_|8C!&8?HQDp13AO-Pe>&_JNs2oLpN_s@ zj^_E&f|%_9)@B+!TR8nriktxJeg2{5_?^Sf-l}1$)GU9S8411EjH?kOWEbJQ8h@nb zF=cRaQ$JqQI?CuJI6VCcj_tFyPXlsFKc_vu*-_Ef!hIME?TTKOZ0y}j5Rx!5JdD;TcC#@cZJoX&HE^qFa$APK9s3k`)+s7|tj!&z9DCv>Bs zig{h5p@c<|{>(illy;v5GH-k7-l^7L5bFvrzm2~g8LXIKT_RuvpB@WXGf513yHUR^ zLmBZ*piD0{lk})8@E!QU+3`VeSi!HBBI38oz3ezQ)g_0gQu_%*FBbNJz$+0j^{ zRQAEU)Y>iEqW~7ip0v){@GnR0suq^71=~#rxXf4XL#-yhu{u&K%LytIxcdtGNV9Tg zKQ->J5f~ID)4F5Mq%q5qeNknNeQrR06KG{Tnw3cg9#!TpWCtL+q(^#7m}}SfK=qGg z%4SQ!2j0Bpx3t~u(D4pEP}UdrOiLvfUp$!hjFV~(y`p@kGRnJKufnN6R5XJN%`&x) zWGZm}4{+}oJHA(EMyh!2ckoaKywkXNQhZ7q6ksK2XTMyeIOI{=HdDA!gyR_gc@x71zf&gCEzQ{rnkl zq_g@)&7y7)kg>(NzLpMr9q8}yv(ZCcG-Q4~ay7$pJLV|Pw2%L%a?vce6t(Ix{PjL? zHJ@lRAOnwFF7#O{!x|_Vycdu{W7wA+cFWe1maKoyRN)H3Wp~|~G20DunD0``+zLHW z0wj_G6OK^4CCvSE3iU7Ys%H10_346{_2_FiOZ_*?saq_y)f{uwxwJcM7z24aP$Wx| z(HD^-i)xNPs19kA$^qhG^R{@B*kaBP0-xWH)Tpba>vZQ771!Hf*vm9{UzC+-9?Po~ z=V8W-1!yL4RL1K{$8viUD0gfHmN`PWjyM^vMtqfo;v0!p?^~t9Mkjo!3i}* z;h?POny6wmejWT0@_1GCxVJ&z*tE6}00DCy)bRDWm)iNRYwkW$`N9g(#bdwL#Xn>R zB`EdE*HVZ4B4^&CZkTgl?mjxkkWMLjSS*f*90*Z^UY{^0?fP>oW|N1!A5IXUeoWj4 z@H?o4jDFxCl3lR$S}48;sE4R0(38@*DxUc25lhfch>?=Y7!hTpV+8Tq$fde=&c1@x zdKkrq{DiIp*;F*s$Ko zAWY5i=T?Do%r~eIXTZQu6LO3+?ohO1TTH#EKk_)FItIAB1l+MmkB=I)FIb-y%A)R)&9S(SZ1uRA-8@>;J$CwThFR8*GN}a60l`R*qyu3HKz0Sj8qxx5SvC?cM|7gMlw|8B6l4=}B%tbc@k398nvZe%$GqKQIX_{2a<*j; z&tAZ&_d|+=kHsm8$6`=aT)2XmMy~Wjhj3+aHU6HCZIOrAgnm(xS9H+^YA{i_1BA?m zM8*Nv9cqjwiZ$xTC>FB|FyJlLttflHSH`K{?AuYU&9Tv1k+m%lAQijW!<`7}#>9XN zxJc}JGZYK&>rDn5#k!d4rkU`jN$|LtVdW@iD+QH|fR!l%WC1FT2?yTWa{?E)8v_z} z3JnuAJp}Z{=+4z%bL-Y4H?I7U*uoU0`ucmB{1l179ai4hD(E1NMIQLqh8Z1z$nlK}TIYF)(G4;FW%|FFbyo5Lp1f2M^d(S#8&q=6wNv~__JkePF0mq(w z!+Ge=l+1d^N`Ewz9r&3ZADBT7(-NJj1!P(wizbG$kq%rqogQxb)`JA7!AFY;6?(e* zGxGmouwLGKn67`UUHzFUv9gcR4!7_Ec++8JHem zUu>E>_`*-JlHkLdk@;Ewp2u>Bmit}C7!1mI4S=h@wtnmo<(C zG$*PdA&qOt_nx6X`JA0-G@Wtl5Ub~jyIfT^+z_;%ixf3M9{J_0Zy_+5h~!stQ!t$^ zKt32HO)6&U?vYf;2plk$X3TUjqKbhZLf_zJhevqW%YD!+wQGfX z!d`e2-l4n~Qq&AT?m(=fOS8(-v;1w1(^Cn>Dtg~$v?uNH#Joi{9H^Y1wCj_P&NT`p zm;NxPy`4_0CJZXj`}{7x(~K-*g+p>nOv1(5s~ zK~=v`W`2Xn&p*+u*77Vx>ye{xzmRO%#KzxY#LNoL)kW*Iq|`&+N%VzLH|j#Se&!6o zweI9I=x6vNjA538_2Rq|0;2}$LE4VK2btcEIDxpkOqLB7eDe0>l5}G7(>RkX@jHSK zFIu7J29J(V;rDDmAy2auuZ%x^T$bbnXM#^0>Z$oSfrFU^1li-KlEykp*M*t!dA(gGQbH?J%RnjVl%(<%we2>VRXtn3 zfQkCl^fRvctKQiMwC|EZ>OI-l$3X3nQ*r@UiJL}5zVe4y@1}9-wKf+e|4-~q;wd$l z(IRM-k@P61onw~X+jrEiezt4ow(=$}i@jbL7n7G~bNq$g0TcF{5a~@`7mVjf(K@C- z1jqil$*#>#2{7WrRPnXG)Z|4P=h_|cXOV#{3dEYVGD)glsLIp;*<{oj$y3*z$U?2G z&N@%o-_PB?ienD1=06d4f1ooXu`cjl-Jq~fSWO(+S72fTQ%sOR)EXrNbknk#7bKG_ zL)I!6c)3Nf7^y=?w|I8-HqQAWZ~vJbD5oB;t}39_Y*48tnYPx(NBK#;)^1$hhFlFM zT|cHx%e+XBbNwP57^9Mp;(IcL< zhyM2kK zcA-IZ3kl}k#!aOg<5Y(TTj##oet(`DU7!h>Yt{l^PN|hfre&6*CH=elkt&tKA(ke# zBd^A7c%!S03kM2!cW4}qe(jX~9#tvpqE-hbrmxau1=3@JbDl950r)a7o&6czZx`ZB)>CSqfwdd;N1(A zi#O5L=^^bXlxm>+mcm!~RQLM5bF=j@iGD8y^%L*Dr!WfguRARS4Yc}NKe-;gIaTP~ z52}A;W@w=I$q(`=fa7fhr#1KJrzk1ce$L{6QLoc!&)o6m%#Dz+v8v;nL=i^?NyjuJ zc04AgIh!)hHv%S2Fnw18-yRFyJrfS;d+7Rx%$R`7(u+^tmG5n4v=Pzt6bHQh)BYa3 zfiZxTh`1WWu0p~Bz-F>|~!U0HM9J7Sg?o)ea;PF>$@Rm)PYx)L>Za6FfTCt=$d z1-l;PI9Z`U374p+Od`!u|3H{AMx$6!7YP<&j*SQnZeCXKVgg2RC6@)2VVZ%YJ+wj7 z1b3Sm`^dbjOW8CF6!{`-%3De0q?CC3caQ~!*V1Evsa9lxDkAwg2`w4I*A4ik#u5yr z!PeqtWpmsnS07*$&^QX{dUxv1Y)6#H4O-2_^^i>Kza}mz`x~v7a z<@~5YdFWY`%qnHjG}Sg6NTpyUXx%0VwfwW^%3v9+hR}uLm)yQCxF4XP-jr47IJ}iI7*Q zHDjxzns8)`$bU}IB?{&G-YE}Nl+3Boqv$T| zH-bm+!Xvse+?mL{n9F_y>vH|#xuIA@ogKfHu?eFfrfQ1-6_(Ax;tBEn67l(#@{*Vy z%U-DjCF3A;C2`)SOngr<2*byjH_J-Vl|9#~Du&pDkLuXz_HwWEtkRH;w9T`>(pk3R zmSnd z&sYVrqia}K?r|EAPD4%B5<=fy8h4kXo{_=(bLJeUhNz{l5u>W&k57VMT^&E=68+Wf z(=K;qq@DfYjj8kZLEs89GbsM+#z(L1Hs2p_`CeeJl54Zhe!m&VqLq3#akA*=ZNz5y z;?0QG_8T6`FZ|z2)QLfVe&X2PxmK{u;l^^PpXbRhB)+S8_j?15Ti%y!+ivAqU3e$eoh!)OQ)_bF^R(Q&0zdY8 zoNp!-K{|W0B`h3deeW>*S@omPu))H(Ks`n zqN?G#c#o^L@&um36Z>Ew?^MR^&vG#04-sR830^Zh(BIg ztP6dg)X+tti#w)Jk4=|)OeLq*!fQZJg6WF^VKFD2}5 z`E~7i1FBmHs>)Y}``S)Vr;bOv>#sozxsi=^X=KW^h%q?TXE4L$+KH9=(sLfWK_mUvbv zh9qYzEGP#+5T=zUPe*a}Zi)*OO{OAEq74xdvc&~<3jCxX#zKljmV2)&?V0PxvfLd? zAHPvJ!FElf!{1@zGgGXV{M5(y!8`u+^YdKx`9#ke)533#I5@fP0Ll_+mKG-@LRMcF z7m+YduFPjAn!DqqvAzl1W|g<`*664fqr>fmnMhxI0`b;QJnN4o27GF+<~=Z~KrE>4 zR*C7yw5BnBdcS}OsBtSUln~wi;Qd~4WVG!SYJRfAMee?iJMl}VH@Ztyee#pK!)k{`5ZrLQFdijykw8fB0h8NWvu_e3*Mf^JS#X2`sRz^R_no- z2MXD~gOXo*EplR$KVB2}$av0@p&6~}R)P%8c>lsWE;=XEf%Hk8$s&Gp6F8c%s$!COPutQ_hAoc=B|Yg+kIv9^N%2NWg+_Lr>ScpUYfAf_O!{I#8}pUlRd9TR^Od3RF(pMO@yyoO*4-ezh^NUId}IKb=%p%#OY9& zKiTTK(yzL(^!bNAUcOjqZ+(t_ zxx3d~Z11rYSukNE$iI#n8=POL-f(L(eV#KiyqNLA_V~ec_GgP;*J~*L~lwqAdiH7*7`m)=7+?geqkCC+g{_|DyUbTvih5kgIN1xA>Hxp>1hYOUA#Q=x%J04;vWb0 zp4@x%$>_Yn$w%@C$mH?(p?xgl$lD#C=}>*vT!isxI`+rl;4jtsQE% zvRbs)OLWIFS^#rX={B=foIO!dkc8Tg!3ts2ER?|PA5k#JP>RuXYixT^tn63!V1;C+ zA+6HRgp@kD(q2MtFJDGx8Y5Rc2w#fj9{*BU z^~)>IOs$ilxK|$=^%)Yy&cGr@@A>joGCE_D2WTnLc{)I`oeL@#mczFs2a7A-%W4M- zP;S4A;-4xRRs|p~Vn;p_K|%lUKL2YA{C}Z8@P7j!zsUz1 zeLEkBsn~C{2qI=+=jQ|Yz3nfi;^FS|KX>U>5D@qu_T%?6{HmJtU60)CuB$_Ye|&dCtFk z2#LDX@xS4|zc~T&)W5CpAJPnsMx*}!lV%eCA{8v@{@-ph3(kxkj`ZVj)m7jJsC0*5n{hC<7!ndC{vt;x z_-aKV>#f=)0Ty5%IiRco&yQx`IS(c*>4bsV!Z{a$r(S5F&HETD^Ja8?Uc?ADdGm}f zLux^!WU0OoyBO3;xjpLx;kR*!IblWO>M|SSu9-T{(0dsQK`dq<#saYzxaJY(Mo zH|7YQt0?}Bw`oFJvA>_oR0&6;nXw$+%etLrFxH8(pbC_(O_;Q4l|&nA_i5O4b}yOq z6VVR%E4Jr7911h<%ElH?K7F4kyos-$>Ln+>H?j;y4IA%vw*!rrN+mt&bIU zfkGz~)C%g~dr6;VjMkD1o9?27TqKQKy4RW~kZlPs_fOiYxfxsJ^Br+Z5}mqb3~0bw z`Z^g_F`}R&IF+}hN_#531*Ul>`UR#tXMaImQLBpr#S)*vL2K*b=uQ&ouoCVd0>Ib0t)8vt~^(k0&NQWGQI@qtOPRJ zb6jB<-D!)KC^3F&u&28IwF>ko<$F=vn61#Yj$juC^S7g*VBoW!&^gY7NaGt_RUe zv~ewKykh;{eoUn38e@gHEtgj{{=!e@mPUfiWJ5l2RaqGDbnj+kE3v1Pz|UD zwx0esh@_ioAA(qSfetNRO(<6c|(0*6^ zh2;CToqF4lS@GWwsi+c|1GyKv^IOwPox2E9W-Op%{u0kLPu~rIUQ6{#kLRR~`G%Qh)3zQY8r^mj4i~tb};4;;Vplw|<&^Y=Koi z5jO(skAzg-u=^3lL7H)&mU7X*Bsz$juJ^&B+Or_M$dlY2&fM!tF4Fir=lKLvSBXbS zZ8koCe7uhOxX4=MUv|)q;0{~`?o;Ti)~zB|Q_NW>KizF_bai&)26eSnT5u`JA)}gi(iNKtArZD?7Qe~jYD15jZaOIHy@21=Xyq$Hw%caVny)
_indHVG)3^ZrAZg!fH65_!ltCUc#av(>q zqIL4E1T4iIn%nYCtx-Cd3gPrq)Osv{=t_kE&hSyhsWXS2 znn+n^S+{qiB-ko2b>l{rA-#5}2BB*ezh9NkiysE1!Fu7MPL8-uJ;*K3K1uP7j2kR8 z=Y(w&5gW`xZz91i3|>~^9^4bpcd;1`3s)?I^511i^*aow)+xW{4G`Ok`lzO6`ADT1 zOir`A9y>zsAwD0h&wQj9RSs^VJa;BG)NxBMrY$|w!_9z$Rh;xDH0%g5!C%bF9vC#_*to0WPUkf zFAD%DTq*1qKDDYHzAd07RQzJo@(+jl^(NQOVt)HO;gV^DT@-8v67 zOYy2Fsv&#Cp)6M2b}M=wN<5fz!s-<>?+E|gp5BjuI{c&RgO(;FE!nfNZzb}AbGq5z z71cM1xp?J>CxyY~2xTVzi5~$hinUCK6sj(6h5_V?<#_F}u19Z4xQFf>_qm6v+$K!f zUezd%KI|9ZR6C0wZD&+D2|;K1T`QPRGKti}SjZ~po!c?<2W!`Bgp{5<$xSbNew{^U z;CvopecqDgvgtaxzGt3;uC^Tca$8B@YrMP&apjpP!Nk!|1^pM5g>FkpU9CFgD>1qT zcc|HB{5)`rSM@5(cxzi{=J)A{u7N(`;eB7FUb!t0>zN~HB&b&S+j693QYr4d?Qf?? zi;9LMEVPDIJ~3=13%s?M-uIk6HZ5y#u5ZrY;V$V2982TgY*Tz+(#I5)A}ErxFlX&P z7V&&+3b)u3T^ReZo|5TS>ymdT^0gKp^}KlQ)|Kg}ZV@o>#?WTa*tG|d=8TB*Yo%}f zEg_)k2nh+KDiHNf}54vtVa%*ZseIiukA}!r% zl)aj~YsG2U<5q6n=Hb)9P9*Nn(#mQfP13w@-8Fk7QLCTjwH!;2!M@tcm#)%yNjvF> zaeM52Lf!ojuY8&^dZ36e!H%yL+MWzO6+3+`^TRy7P|#FJ&n@}uw~)PWQGj&O!l_8@ z#Y3X^LbelYvSh0e5;6nyAObsw#RZ5D25q{6a7$yI*mh3kHqvS zA1hPE6c!&-#2Cz$t+%=p^-)uB5l?LP=@1G7UatBqtYxeyC-wIRId!j}7g7-bwFu

&gNSFrDZ*$W4OAjDCKcIb)x)gQpF4a^Jqd-5UNW` zmDh_m>a8Xx?dH!|!=ZZ$32D;$b}0HeeGc*KQoAt^Oz{kt~N969_QJgN>80Nx1EBt zMHxO5bdG)t-iizCi-QiQI|pgI+FUg9JY_h{c-lSTZhzXMwCS>C{EeL1m8{HmfatTe zPn!T;gjR={2fuu&@oN%;i{aimhq<6Z?i*37kC)>T5{MEx1ruEwDJgkh0_o7zzG#7( zZsz zlU=5*tM^rqThQ=XBZU?@MN<4#OL;O&JU7If(DMW3#8m)jmufk;nNRdK!6LvVl)knK z$lcZpHDphk%W$(HUF9wXE%2{$qK^;X;-K8BfU?4`uV{=WG%O|#fO)HQQV_FD_@n?X zqrh!L;JBdB@+sAh!OX-MU_!K+>%9=Xj(9KO5w3X_-cfvfi=%!3v{$QIV#0|_;_?!@ zLY(r8D~F%Bn7sD8A8n3Gbw5|kHEwsGF?WCbb8Sy*O$W!64aM3(`@C%x)+cn1m^Grc z&n~DJs8)g6rl(s{cRgou~^pV z8qjI&8tsMRWT#Z`OD6I(p!`e+#*}S&huvF5H1?Yq-C-6T7oyXNV0?ox_C6F>D2SH^ zOg$4437D#H6@==8aGkTK>?O;N99uwB8sJK7=UbcWthM)Vyf(WL`B}LaK)uZvP{td^ z&)HLu4&p|UVWgxL`;>dHDPyncs{mkSrht!ghTNtKOLV^k#?$7yltSC7oNBhMQvUc!ei#o~u2{31`6p*z$*V znUM{9mHgr?MY!hKut$`yLtbwjcfE(@8xo3lxHTrMjVcT8McQ_KIZ++e>o)4N<)DVa zFB&izJ`#JVpC`fcm)}4AuruFPs*OrsYimH;qrW=zFE!e1X-1@6QMTXi&vN3gaEMA8 zIZT`Ou@*iF%C3LN;M48!h`<%=4rKyDHS{fEdqOf&-fhIfY3&dFJ}$oSugnUNGLE3W7rX5x$poZ$BI38&73nYghG z=Qt7bHwZ~_LFefhCzzjCvAwJP0CT8{j1Zj>trl*4!VTN~NwL?saLbkfTQiWyKX8uK zmP4&>Ng$3Pm1pni_TS@94{)T2t{lx%vAzbD+fD}hI5fD z<;}P3OTe3Eg@r0go+JC@@f?in+=@J$-9BBF_b3HpJUf>yqv$S%TVDeTjMu1fcQy4D zzncw=H>k;|buU+LR@{7Hwh_}TwihSCxpGr+n;V*@)9@69@%kk+pk7L=f}&}N4619? z;iWHS?jNorzbehgzACD@=Hni8@?B7EScoZ)lsx9l1$=h|d#B(WW;cFgTyq^majQH1 zqBqucTOGA}R;mh8z{pdovTJqQFmRsr>BlI6o<_SA)Ni+ z_?eWxg(1C%`EJHXJjYqz_0uW9lq%AqV4R0xVOi&AxsKp{ryetb@ozJOI~OXri$d*? z0^s%&9rbEw@l_B0L9O0m1N*A%rkqH_3ny%?qmGza?+JHZgoiJk4Qi^fme{S?H{UBx zTo6Jo)B~kqbN@)zO%T#78rCe<-Q)QY-#OyR#1Q6Js+fuhhYt&`D!X$k#2Q;&F&@E? z)2!4LHMW>`*G^_Obd2xgfE5hv=6s8UmR*HQQX@IDFDJM?C!`r>2-4 zg&n7iJgH$uvPQm2ws$OI?~K?#y1}fnl~R-*Wf;kOQStQIOx2odV*fTN!C?;E)d72- zVQLlWo#$b>$Lr~?L#ON5ypQ6~-*V|D;)C-VxdT-cyQ2+j$FyO`XLtZ0O zt(pZD-3Y^DPbw;2lxn7LIgSP5Y=4jGZHn{Y;zmTd&M7w6Z&lMh_d>_f$fND%Uoi;D-%{(faytaU_kLVk4-f(ac*m}bARL>V9lF@zZLU+7} zt+kz)3-MMzl`NT&vs`j1IH z>%1Q>uw?pW5;`iw#;z{y3=iIcdPV{_Rk)z=uV*e%WhV}ucc@UU!kP&D57&f-?C*{> zn&yl6FR_wNHJZOtt8G+xSD!I4^(IiKR)h6dTZ@xFS9BWV0|Pn3Z9YEmP0${ z3cXlg73qbZZOfU^nVs*wSa#l852l|#$mE<`D=F8z531rV6Y8!~$~%-*ddHO52)3UY zFF)MAM{Y#)>!qI4t!j_3(!~q?6KZ zE<9`Pbx3b}!}`uuPq|VqU%vLgmx4U2D}po5&QWsHQe`_Tsjd zEnv1m-V9F55N*bldN>(5N(wqKOXUvfvU@_HT;&3!HZs?P!=x&Uz@ zj*hE4`c7V7`}NK4#NeG_m*}^rJjc|Q`Co3wb8zaD)_41e)sVeQx5GoqBKYE*NQ z>YW}=G9m(gc#MjYr@|=i_k&DGk`0hs*DWH3nwF46bLmd>6F@RTQ=N*45wp56mCw%# z5Cjx+P6jnwLRq5#5opaeE7Gj18$5OLA;AhXSL0WcUBjkqX#j-=ez0+JZ7_ROniFLN z3l17Z6_PwxqQy^2HQ3`x(C=uUmZ01doEKz;r!f`jCd?R~6KA6!>!B@cww26cv;RmK zHSB~U)#z8EQo#8Eo~kMr(njS?(gep@b`vv~O7cg>TK4zY(%ACDl+MsUAbD>Y5eno) zq2({l$s&M_yjjpuT@MKbveeJ%1}+5?3W=P==^j3J(D;5cJ+egUnbqU)(i$^BJQ(R66BhbS7t53y|#ZcLN_~NxSfz-5ZoddPZC}+ zrp0svO?zx|CNV6nuc*VtFE9O?0)bG>)1%VY)mLxie8qPLG-U1W()7O32e#~yZ5EZ>rUZS@R(W(jB&v5SF<&kr9s22ttE!W*G{M^8x(2{Ll`8UNHv7NXZ?F zP>UA^^@`F~gz_!X;SvB`z3^MXH%--_Otuoj2=i>e$uT|J?u6bPCF|*YJMRhTLM_W9 zP^T#N8A&dma_2=Csj0O0FK`?Aq9dJV?G_5Pu1sV?FUbjCWcm_B7D;lV(401+6``QA z-K6LOERHN`H7ev~Y?X^>prF}EiA;;z?|32dW0>? zIV=azLhhQ5E1w{e191vax(1A&e?Za#Z8gJUL2oV3g(h z==wNK?}HwRV9--0B}~%0lRU?%2!=E-pDP;XPdoGIla$276Y8oU6Ez{Zm{GQOt^5LK za9S+h6rtbB9Z?)ZIh^O*RG6QJ*-clfSB-m5`Po6Db7DQx)*=Q}!CNqsLd|0GgfKx% zq-1-2G8@P3Pj^a>@LIW9Q7E4`-6fagKp;s@{>FW@TE zK;0w#Wnbs1v2Ee&+V@%6p(uJ7aolo2iOxqw{Gj19-h(_pU)KkUjh~kdhk+SrW8kRBZm3ek z{DjrQb;rC>){K4(NaHh_?1TaJY=*!(E8ob{>I#@Bb|q#wLdde(wp_f;h|Ls+bE6H8 zSIQV%yhVYG4{AkOy~zODxT-+sV6Inny_R)?RUeMN;~$sGMyo&nSSG-0d2L#h4PZ#r zyeGoR8J?7)JNYrS|2l_?m;GVlS+QN}Yuc{3CuFb9XG(l~_w|Y%tBRoWUwaKlGdRWC z42mz^X9}S&P^Yp!NM7$NOOhbF?;Yl24k(_;mo zH7~x>3^?OOi%V5^+MnsDGN?fKm!B^0GQXo4AETJl^G2G%t^_wDp}?pC zX16-pkB>QBf(m$hPx*!hGuL(w9?V(Xn0OaWWhMOUfP zb~}Ays1M;)mJ%EijZ^XX${tQ_|5)6>YTnjOZ-MFq+c=9z#~wW3TT68r=bJ|x*(KiY zN#=9#PxTqvmAwJp$9FmuYH3V!^P(kXbz8)W&$k6-3w11=B-Qjp?ff3e;*^HF(ckX8 z9VsuE?aEI=X-C+{+>y~l2{j4NQuWVV7O9PpIN1y)`}$)RmXR;QZ(5>d zTX&T@6(Y=EYQYFb*+K(nE7e}B_8yq<-SNu<5Me?Ee!VEAyDt64dfj4+`3TT|qcAbH zjFoD;DfanEE7eC}y(DlIn3;Vy1x7>TH&QBaF6eZ@-aopH>T$Oe5rve9r$;F+ zFd41&B=di;_TEuVZR`3lEf^r72BaH$LJx!v8hVG&k)retQWZO$&|9bin-Y4JCRORu zI|ztKmo803Mg7?Oo_l}i9N8b|j&c86gz>ERopU{F&N1eC-shzyY)R#%Un!#Brc4tJ zw4*I-6^hR}5on=PLQ%3mOA$NV0kzC!mu8@X>S?p@!1+!~d z#pF1=`52i)d_~=Aj_9~mB3V+hd@MXph6j|NP79mI&dx;_uS7a?V26Cmq$qRgkEu}u zG3C|SBEM*Ntdcq_C1zJUrs^cAK!cpMiA%yb(mdHo1!`-O!P&ts|NQ9IO6s##a2bP^ zY?G$x2o|_Dq(94TS_W$E1(r8!fyBVyBQ*_MA~Azl#V8Fo$^y>YJXm=3F%J#%O(DxFSXuHJ{^%Nv zXt@xVRUJ)NA^E0?P_BxgpJD7Yf^j|KVRA1OfG}9DV{oF4J-Uiup<`LM-|cFg|7@Mr zdz@Bu^3s>Kf!rP|Nf@8{Xl<)po?Gnq*E(2DQBX`Ara<%L114ZOPBycmI|}-(r^IYs zedj$&lSh#c&7NKWd%w|Hr_!Q?!^N=1cQ8?QXo&G;l@uo*q!OF% z7^tK<*jkQ(^kE=f_OVO`>Vw{s$f1{}fmCIilaGZV zbKyf}k0-D=1AmdE-4O$^t8I2IrCQyZ6=H)qb1!;?`8tc@{KIq!k6w~?i+K0o%s!Vl ze(Z<>Rx*BJDDBNz*c;7RR=|e^;z3`03u-ADVHk)W)rtUyD$7Jo@jFoxN=g^=g$W%k zvXO@26QsmN?u|}-ty_2CIC#^QMOK?G(V52Fj{XC(Igk}-6N6Bt)w7IkvrS2mld?u9 zHKB*}u7gikVx=@@4z*G;e6!G>>1;i5_Fiu^6l3v?x)jsotXHD**K$_EBjxy$wQr<& zF0l$c(a@?NOqYE_Vma_tBbh#s2Z4@B?=?PDqkX4koJcAyB_Ze(NkK1)8~fPCp(=!p zl?{LhF^p0?oX}fKHCDpr?4U|W2Po&iiXH{m;eDyd2&jmh7E>szJlFP?C zT!xt~YiO3Wk$=>n`>XvApx<|{QgSFT{fMixl!v!*iE<7r#z&ku4(NsFn9gIBSU`<3 zwakxS=6oJNy~CFGQf2kUn^weOpm|x(@#VJ_^Ys|`Qqzt(Q+v9ieW>FZ*+hK%`|*X8 ztv+G(ZyGB4GM0XP>!X-r`Yy|Uf$}DeK&Vp`q%z8>l?y^8OOmF9F9o6DA)Od|d(yAmyL{CD-89=pTo83zu93Wk=PsrIu)f7Khe4nF%(SD&MW5p23F-}`B z$kR{Yyp~J5)>*jrQmf*n_NtCfkvhXVIcowHL*k)G!PZ#Q4`$O4$7B)#s%+!p)_BQ^ zxb)DHK88dQzsf|VzH7NtQVBI;hrP6I-sVeF!K4CMac6VuDVYCfDg?=-I@FwSl2_V; zLj7a>`dLQbiKDb1FxwqF8$Lh(K2F6eSWVoKVf-z;!btnP@KehjRMC}ct`lz`XQ`D$eqr+r_^B9#+k(Syff)H303yydKIMX0f? z+zGImEg)Erby;mTk?56?Y&?|FlWXraIrSoI1RsHxBKjt*$W#25eDw8Bgw%2#+p$5t z8uQhXB88}Y#;i(dbqpqrrvm*><>5C+^UoOiQ0eMm>GfByCY=;pevv&)j(9xh(oImK zLbjQcCOJe?*1%Qp(`2jHG+Qv<$d9fjoi&?3vItMYzSB7|$di4J&pxANybQOt2u6>aW5XFcsMpGSK>cgoOG{>OLMIJ*O-EUYE)04!ibK>ys^uG zGxCj@2nJa9bC+#=%sLoP8SXRuj-6*d9ps#{9NmQzQl>0zy1;;Qh(O+82^u-rjQO)@=yN#qYmf4 zYl6;a^Pg|`--2feNwIVEe`u{K`47Rfry#QeF#$01Iy;mx=G-_YSxi9Y$}pj2I!FY)Q1Zulc3dv7iq}Cg2EBN-O>%o*qGc^wao=9#8q{TL{Gz` z$E3XIKtqMxyaXu3po9Y-6*TVBKoxfP)Q$#Ek}-O3*7YorP6?}9F`Gf3gK@@@1L)Y& z-g3k-kb-OyV%N6fAdumcz~Y=81KhsL=Vum>7>{m?^P2FSSx3K^K4G%}!_U81X6`vI z627cMIg5Xk;32e^w2^{5P;R2Q*ID#3|9j6B?SMWf2zb?pAJ>@QsLybnRMKoE)_uug zpk$vfj6r;m#tj+$ap&jvP(t7w;j2}vBTDjvYBci2$K%i6LP?*cW#uk6dj1hY@r>Y0 zF+qOgM~PB0eR^X28oHzB9!N&|FoZ1mLQIz=>a4^XuLmZfp^zdL%AYezDQ>O#Q=)N| zLx7e%7`65V?x~+r)+0|e$Sbkzb`9}rThzaxhen}>l5aW5Zn-8n9wh-j)bvJ6&soC3PmFI!9e&KiuURT=CpD>K1@B(7;TT-{1uAE3qI9^`_rFONuv) z*JAZbq_sw>FnH4T=T$hk8b!(Hhli5xJ5rnE9*n2ATbTm+z>stN74kP<_!Xm(;4ZaQ zLB8k$hvZS?icSbPrM0|#kiP=1E0V8tZf-rzqInHRbrMAphJFNjt1SGG^DTI0&`!4f zlW*)oqqo+=>zjLx=GHf=ADjMai@y%AdMU7$6xh?K`T49tca3|c-JsfL&f2Yl@hxIP zN1ZPEqmMnpN|?}pV2;9=%UUz_JFld}aN1hzf^DtesFgN^yS2N8B3Xz6+LnGJ&2?Snp zu=$2CAq5ACI*|$aU1j2=8Y$v68UaNEGo8HEg-xUZn>GBd*H1pwMnAIM3{KPf5^ zqKrSh*JVw5bJC7rkrI=}b187Kx72tkG@(xbeRDwOerKyGFFDK2@nm*SZfBdQ_c<=|Mbfms! zu4#iE>n|GK2S$;NG9^j_Ktgs;jd(fq^VR4UD-GTUV8k^XB+S)xa0m?FLU6Trchb0V zdQ^=|b2_z_uK;009}i#(pYXv9{F*?vDG@ko@yIZQF*rf$2vrYF0p&g7!;}P7*2{zA zN!tw_@V2w=)sO{d)FZBV_0|)b*=jvx5^<6rPjC1}5SUbt?2X@(o5^B`Bdb6Tqh~@U zvS&?K=3MdWzF$#cq6|#Hg7E^D5#60Y0#^m_Fp0o(NybD?ntW+M`=}<(8K|QlA|tZL zl9klH$4dA@ttd;7xBD8scCCZ$$JvFvZb=r3{sX%{mLPWZ zf8iu0L|F6TD-?65P#LYVAac|en5YfiKpn*h*(;uT64W{L6FnyUp&69STMdqy3BH9( zfzyNmZhpW`F%<`*19~O@FXN{YnIcDiolUefHfHVi4n{U*^i9n1+wsO5MxWG;>Yma@ z`I{x&moGNwZwe0Z%+nnf>*6F)pRn1>pmd=P!pp97BCEc_#j88^fZD~#Z5tM7)6Yc{ zk~Sj1`q06MkWEu$A#dl40!RfiIT6Vmx2>+j^P}uJhh0EAE!R^L_ZO3d8H?}up0KZ# zKI1d{c^laAfWz}3iKZD7>w@Sg;c_UL_FSagwm#y;uLsZq9&GX@gX^HRwkYtkioFeYSpe2I#*bx%FE7BjV03 zRR8dn4LA*3UJzzD`v~901GO%&m`+_-*3dS3gU%F@E4su);qn~hROGTxAODC|Mq09J z&V<`C+x;DjXchf*`=leODzFz`EHRUtv>TA%O=a5G#9BzlpK5Vn^uagr6CWWKhL@Y-pJ6$PN-kiS_!+CQq-mR3N8F9OR)RH z@VJYOQcfW{&vP@<^3#%yC{r@$@jmte{;L4pIrX~cp4*FU2V=hM=8|)xo*r}?08Z-ddZ;=Nv#V}`5~i+k&m>>Hm=Q}cvDz^8OR_-5!kp|lW4sz=3){MI4 z@bPBBYoKpBc8NmSH$LVy*$1g4YZ~!^(o#`SR2jEWhBmJCDh4mkoDS-Qtg>XsfPU-S3rJYg? zwmb5p;wF5Hy;l+0k$+u9$wf7y?>1E$0I$?M=KugL2J6aplm=XZdZ_7D%ee0HbcdwE z?dhC$Xo(W9E83InaF>#7=&KN&Iunu-L0ZsOzl7s!yiS`vf%=Lw0PZf(P6f0Aw@n$+ zWXr6O&lyK~?FbQH|d? zPC!+Ciyz}d!_x&t*sbF&PAPES1tEJ?se)CJ_M`U&d2=Ksoo!(<#|bcT*CPa*gF=JWh=6v4f=d%$Rfxz@opz() zg3&oENt1k~CoeUZkgCPSdMSeiu5@0RwyUgPafYPS#Lr0hqo(}leIV*W*ff_ZM@2T( z&iLz~up3K>mhQH?Yt2u>m;|G4!FvOgHOk>0ECh-lSbc&-u5jq(Z0MEo=y55K`{`@0 zG24l07$R>Q=N0K4WT=aL(w&fkcC8vsTeIUz#fLq3ydsmP1aoy8AK`g?S~UYdHLs@^#_wdewtN!CDHaJDRw$`@|(*L!VUQiZ@VkI>fC1I1r~OSS|AO6 z@`#+gHzgD*z8eSyXW-o%Rw&ZF>?ZbXI}r^>hj`S)mP)Ug_0>Md(d>d=8BUWyLhfg- zhYOi>q_BVxU2RbwXGP_4Mtxwcfs&y{)N_w}#+ZO1FoxZLvr!9g?P-b(Q_chhbyd)3 zWuUu-vKoDG1WB0fPfgDyBo?2}D#;?v;&zU$^01v{)j)xvj}b5h+|PYu>A;kveCl)G zJFa>&+{rB}y4o!eN;!oH;P4}-+xX4^@8(8*3A?$gy>NjG~z5MdvOS1({&v8$}y z8>~EXR)T=c^rj)OVMKXF=}n*GpCk~#c;o|WNbnf?_t*cd6kLo+%G)Y>?GND zD=WuK{$ThNJ9j!*{#-YCCZt}#E+~aWTF)e;Js9&AcxQ{fJO7STs#`|&(=n>Zwf5Hd zIosE1acejW7fn0KVWBI1nwTX_Jmrsq(P}lb?XG9ZsT_sMhvYZycb8Y1=}MJ z#;0Tzl2byWUe+rYI2k{6YMmgxo!j-(k?9L?>0P@fy8dR*GHmvQGlm3*4s* z3ZPGux+TQETs6alHuK$AzoGCV^W`I9Sei!A{UWn(Vdlo)ei9$ze-iL8ovG=X)nSYr z3AQ(DZr~xj#_HQj-kNf@X&z;Fs%t!JEcUu<#IXwYQY3mc5HlDg#f25L3nJe0)7!OV zU=?Y>Hq-x7p1B5fP(+zZ`M-$pyLZy$a6o?c<+jW1G3dJ_I^(3cF%Nr$Ca+eC82cd}x}DZeJ@}pvPRmFp%qpt^ zKuo2*?aUYXRmbKtM?MNmH9z>^JxhO*7*Qlpb~bFPTQig@r;ORs>+xd@!>h*}rlBZ) z-Ls&^6w^Kj{p7Mz6~EF9{U;$kRX@5L)bf5Ze|NGoDjNL++yp1wjIJ#B!@g5cdKmxu z*DrFjNA))mHn=%8&#;!l?y#VgJvqTp3?@cc*S9;#@2b&A!8@$9qIEvsSQA%5?{3TE zp4C*KcsHT1F}?U>mZu@ttk+b1vR!>uptr|byyi?_%-vo@IG7id{-|$_t#5nDr(bVx z8*R}59Koo=exj8=F|EQh(h8q0@&*o_KALMa7W>sR;*kD?VJUUx#yZmM-UGPfDLQj0 z<#U%9XR(r#qis=Gv*KJ#S%Ev(bz_)Yfmqo~=q7oycxZ8>{#w$0!qbjmpBheGFB#$b zAJWF}7<&7npMKc9MFp(S&fNp)PF2y{dpUN{YvYvPcs^o=``_uVrlKfU0B`Om0$m4v>|SK~$9qS03MR!A)Y2Sh(cAZ?Oh(Q0RY2g_(h{yyf_A zZ!t<*AQlnOXw6fSwmr2xd~Q>ho6(C({B6?&dUejM${YWk69rwBIG@W(K)J~$b^|8dwlp7!sEKtC>tDAhCe@sD0 z>RHVEDD2f{0r#?RnhzPTdQLUJTgKeIya8l&Ys0T=&*sgCQXc?-5JOrMR}S9yXe;rO z*+65(m(mdLdW8_;e(dMBs}SZ8R=9_mm}k`M)~g6d0$YKW^^YnTNvGyGcmQgeO&yJX z11%Re!*X|dF0Ko);dOtj>SH|WAm&RVBYO@sSf&sG9&CQ%?($!t6x}pXXssT35Vpma z8*jJA&&N1_+zMly6)mJFXAiA@KFC81AQWBu)Ww(dxw&D-jE*xlzRqD**vWLAr5nBm zN>kS|wTZc29tmuESyC4e$f3_9!QKORT8a~{$T%hlr7@$NQ+zI)78+Z-LUFZOFbz^x z#z)Yk03WGy+aDRxpN)L3MdLkJ5u?9N$x*IvH&2dTRhfZS%|nI@sM{TaYWdNxRXgxC zY5BE)Vk)6C$!IhpM?OOpJu)N{(m+HYqJ<@ai-=j{mQmS|MtH{6ThTMp`j9twEDI@3 zN^G6g22Tq}z0;$7XM&EiH()r#vB} z15lcw=|Uy^t|*i#6i_LIktWmEfV6%XpK%?zdN~0q9^Olo=zIHxL@BPzum`H`zkrP8 zD2sgMoWXbNjktQA&CzXZWnpabJ`eTEe{R%Py5*ZTfhlAlKb9rGJ4K5}-~A8?buH!1 z+s8+Bcbzzy_5gi5P-CBCe%Lwy98u&{dLdndu90?A@)iWi!%~7dA$*UHS z9at4sU4BZCy0vzln&EukeQ)t?qN^cR4nX_}15gk6H36PA1>5r(V*8w{gh}OK`4W>W zuqp8@jK}auSG;3D4&yUVGY|PpL*ji|2$66n6QS*OkafkopeqV@1`|zT758uQtXES$ z(rp(k2z&-ki0a#$oqP{_C|ZK22W#lvIPFXH0iLl26NLDp2)ZZzWAfr2ZanQkGmTlq@F}F zQ1N%E0a+<|J`;7^)FbhPRiq3zv&pGHXbo~z_k%`kzC`a4agnfvpu|DRaK=a4;Z(_q zQ(6p3K6AY5z5dcQe#+@GaJkpHL5Wa3*2KK;LTr6V91(ASf2a~y?m!G(FPR*=?1cc5 zG=1KH?+6r5z6A?+vHo6F^ty1dd4m>D04Alt91^&@!HGSoPToMNvjeiAT^ zSR>!1vuc^kLIDLxZ8NWme` zIcXr>sW9TcE1=na;BI@hd+m9jp6ko#uhzVRkQTdQ4gH$cn+R&Q`wrp`+#?gKj#w8} za^-ZZ+(d|3jK&fcjvjrM#6DgEXB6Dn_)J+B8?I|Z=E0fPq$}dT;l$=`5A?qd6&svmPFbMxW8oZhh~%462codcc#zh;&h}0mgK=f5T>@%>ik1G_MOaB?~B= zzY6VR8m)+JR!@LS<>&|CE9RDpm8#g1NcX+3tzl2&oHw;EWU+egY1pq3!}VJKA(_as zcNOAL*bmlua>Asbxn;!)l+M0-Kk!nLP}8(M3|0RA7Gdq%biR zt4kP_j6!uxrzANFKNvMIwoSnvp|@cQ#r4S?GnVNt$%`W<<$3~BAJkI@@SHww)yZX8 z#yGWRKJA;#EiltVWXRFg#W3~VRoYFW&w0Z?i9jFD>s6#ENBjzRAeH_EPt|DGpw27-++_R~rL?7AK&qQQx3se|i+w(g-)ii(Z9H{nOFs8L`D;j)1$3ED(1l&FL}Quw zn}wJ5jd9^)5$A!A$@i>9&2lg{W<}&Rn{DN9Qa4z<{|I|}Z%cp;JoCP_lok<tyl~~D0e~|%9xlLQc4-fb46X!UD{;tyJin!u_EaWPHn-# z<0U@tTVO{(&%DHC5mtKWi*z}l9ebR-E$nSvY!E|kDT-}uhi#iRcmCXQlr4{d0|za^ z)v)6F&gol=nG%e^cO`)>Cm+Os=Ikj>`v@8EIks=)w&&tB6czd?73Y<4E~+>)bv`pL zX1%N;Cwlzg6^K(fZG2?1=y3FGjDmgu5dA7H3PbbUy({2H)|QS?`yxdj0|~P(T@IEm zL0^!%HCb|vWiw7}O`plUS$092%ohzxzeU3aiW;&Y3^5P{G6*?^0AbQ{zBfVMco_O4 zxif1lS$h$*ut=th*`PbqiC)y23F9Rwq9(0W90$jbt)v-N5FL?CU`tDnY>T)aQO=au z0g%U1)Fv!OJ-`YZOvIriX(;31%R%*h4V|vTLFzo`+U{5%Z-%^k?5^S!lT0)v$UylQ zf?nheXi7q;>0>DC=_U5 z>iEH}WQox6*Gg1$4^I;Ff4*S))u(y2B$G&7<6(lL6N#p?y82KeGpIbWijwLiO*+4? zPgEob18>rLO=u1uyaPMr6~kskF#A!KF7vlW7ZN-hAfboQ*Qj#xBo2;7rq#CS2RvVS zk>(tpk}jk+U|m%H6nOMBPOpeU;iMi<0yRN0hzO)2^32F3PdpgHj8js;#Zr$Rbk&9M zkYv21Qaaat;x-YKG_}-ZF45mq5Uta%NSYFMCt2Y6WVTmg9)<@~Ewo@nLB7)*iut0s zeA-SB!tqs*(lpcffVQX}Hw}w6@?Ctqgm!xqh>{UFJ|o5;%S%=v$K=w2eb0MxZtt<5 zt>>B{VqP&d+rHZnpwAF#!H0)t=7bh>?VjSd7j&)(;o~#1r)6U;5Rn}Wsqr-gfjU66 zP>P*3*Fo?YN`gt7h%o=Q3JVblcA(c3A_7Yj8T!PQDNB%t>N>mLb;^*ZnKIhP#Gb~` zjo4E{ZC}+-5A>uYxX(|)Nnc3i&QV&`YedA&Yyi&OB9r<|SGo_rfBs!HA*$r1ked#0 z!LaYdLT^D2RE=fdUM94&!k~wG8IQ?0B;c^- z5he{ur?bF__Yu{S$fkFbwuJ@{o;1cs@-cDsTK$qt+zc+ek!2|vXS*#3#L;?9)y{F~ zgO&72-sQE2Vmy10v!CktHD2Va-!1mzZSpwt!=3%U(-H;#wvmYzeSODno6OY8kXh# zZB#@ov&@gW=?#)PNHRvMd2TA$Ub2v0;@Rq>rgdmtlQ_R^K2Jv=!F`BeSJx)6H^TP5Y@7% zOmjo(vS}k**#seKrjM@}0^$^*lv~bXlW)m}Fq*QKvxs+PDLOr}@1L=t`JJ{pU zwL-MCsn00jnWkN#0O2++b*G@L#!!7WvmV-fGMCzn3~3sua02QZ2*VVOmLb$;P?n-X zE8PN~-Mq>i06ZMS7+RQ}XwjbBWU-Bz4}Qx$?5EeN{)};_hw^jlyJA9+Cd*^ep@m%X z4#nQ_DIk0~!i`IX<9k_r%7QRNAwIV=k4do)Kq7mlxHp)`uAE0ZJi1b!heVKfb$5*S zLjWj^7h5qYl{r6nU0E2^#9LE9&}sS?~V$?i*<+aqOd*>{tv3iY|lyDFw{ z3^S+e^gySWlxQh9(8 z2ywFHCrrjv_=N1|r5{1OW|_)P?O^290D1{&ogl8tNzAm|)*QYQKg&TMNj3DDS`UF1 zomu&cqV5B}A<_(Kp{)R_l<+{63v6X|)xP6RSc76@9}ZpGXs7V$ETK>n7!k zY@Db)^(E<}W>w!M{O zrSqFB)Hs)0Cy?LX&^L>oA1ZnP8^mC!M-=*?@94Pi&KN7dS|g$84UuyYr!dxSOPU8g zEab`BDQ@-a+ir^*=j3ZsgDt${O)^Vztux(amz^V=k+C;iJ0 z#V)Joey{(msJjq(u_N($qxj2^;^&&+3v`p`ecLZZo_p0^g#1m3_}e(e5f>_=rG8Vh z{)Ut|*CSpO^ItWG7tYYd-qh#r(U*-=Lipkt`c2LHTejlo3g(Nl{Y}mKJMwSFc{zu~ z&s&>c5EF53RD9V$#V)oCKW}hon}+Dc z8x`WO9u{|OT)?D%wWPQZDtz(U^jFJ^|9_~+`R02OCI0G=aS8NqDDhWAjZ2^x7vUiO z>a}qR^x|3?gvjM=61muVAMsZW!X;5Ju8uI3n6Baq*r=cOSRj`i}j32|Q2bRqQewFzY$lsoaUXHBwS_iDo`?|4#|N%_ofvfSPM;|IIeM8sKHU6!-MQ!iW0 zN60E?5v55#3oG|0<`2s>4ZQGlvocVJk@Z%qBjTdg|k(L)kKB5!Jk#ov&mG>O3H% z?q*B8Dmy5P_mV&k45Q?TVgv`rzONxePsCs!bFW8fd;u|;qBs+;E8})KKd-;d0irhe zR-Mo=HMXRNrj_D)Wf$k3R45;C;;@{b{|Ew#%IF*u#wHeu3v`q&$%N+HWHZ96^R*>H zX%>PC;N_A_5@q;UPigt;2c6|gm5>*LA>?Xy>rHw2*#H%i`G<6~ypW0fXzho}s8U5s zy3PnT|Lh6m7~_10SrkYTVuY@^X^LV)Ep=FU2FxPo;%ix46@`_a0!nQg$Id#1w00|N ztd+~#E3f8{dLYTWR3CMFqHT#mUkVl$XP(O%Snn6SH@2;TnU2MR>+@?(j8wBbVTyvXp@7#oXwn;k8^193r~R zI5s#JX2uBMux|H^%zL)~t8{yFWC24wlG0+ebj)vQ7540V5hF2Q297zLEvn73)Mn+B zA=2~B`T2C_f;4x%PY8yAS20rsaWa&oQNo*xU49lqkf|aX(*?$sL^4#VHVbG=Se#Z@ zeo%F}8h>R^fTiM@$`JyT9|b+ z;swP5K%b#7D-WZWi5d z9y*~l09xTh|G{Egjg5{?4jUX!(-taNOiR>e&t1 z^msJ`0lhq7hy41CeC}13Q>t!rwDN8pllL5Te(f0W@CUn^S{RPzt8Vyc4qZ;4Ry*gi zdz?OOh9KlY2*;tmYB7DO0swjfDi)P-{z*m<#z1r1c0b0e0j^3A+hnLf>``U3+DFqw zfS;cah}|%)ystyQmVTdzI{E}^)rDj*d=9dkF;6a$wOtkAb_TB(1Qg~?%?E39Iy01s ziCmAy$GLNc;$^)SMym{4bn>%_a59TzFivUQ@K|Fjka$6~gb%p-M~qrRux*+Wi#=g1 zqRj?D9kw>9FRp{%L4h3`Fv&eG7Fx$3@`M-agZ+Jkg}*MOB0Y!G@huCLt9xJ2Ki0E@ zl-g`_HnME&zuzo11GLZe#fL9-=heJ=3IKfPP%)v1ZSs`fANHb$-=qk5-N8K-{*RG$4h~kfX)Oy8$h*RVZ^A)7e?N4S3{CT zc;i6x>idD$0P1mN?N4ULB}0Os33^J+q#EP$uR0)aMqskkEywkU{N&BeI->N46ch+` z;?PdIYqNW#bgg&E4}UVrk=m6Sd@GEf`MjE3v&@m!FHS+t$Y{Q@gBTaxtr6;kH2|Kh z!nufWANO4{pbLkZpoakJOkC2S`1YO0qQG2F$be5P*{uhr+479V5_#Gp+ua6bR-J>` z2QLHSKKWbNch)BJPFV0+To*&nv=XBh=7f_YJ?w3oa>|G;%Ameo;Gq}BsJ9`SFlUg_ zl9WkJ75NTVbOequ^7fkL-M}7WO_#lX%Ol@an>KTkyH319!YG%hQl-wYbb=Hb=w2aG zQvYhMmz}4+rHcYbrLphqHJ+zQdf%pb0bxb9GGA>=lMP=>;xSDpg*^A<{TM!Y=sKeT zwco`Ls@6p|n()RlmoZ-__m3`uGxo7~p~8ncAGnJ$ejEjyzv|~T_!JTM=9ug_)Wsbdt=EHt;ELKInVj8zmYUi&>=mH zI5`B|p;gs$sEDvT+~#dX=pUXtxs`Fr+475lJ?QyM(nKDP`io>rgykQ$o6Oh(7=P(* zJf5rGFyCvx*%@s0gvPYm$f1f}7UJ|QtZ=7ye$>9md-GFME}>wC+5^rT-gXkVuBt>B z`PVuT#PHo>m9qo+5B#XhbW`q2fNg^H$_NR#Ao2;!ZsbDO%8|~Bh- zCQu1-#wf7{4+-@hFm_}=N#zXquc62O^zu3PmC5vDyFI6i(E;wqcH2*-Nf zA}#A6t74ecqRU%4A#8LW?MdDw7HUM39_+$A#`B4OZO?}nIgz__i;t*)DLNfvUOL9r zk>B|Yz&rX?O=nNeU$A|!xbIX&DBD>fY~`{?04K^Vegvuj5>YJm3QQ;yIgxv~fCODE zDOOGaR`1wBLVF6t1BI?OlyhkR^bvPLXxN$696?U^nN@Edh1t9KN5ptuSE56KcZvac zP55|d^7h3^!l5cW!&8cv!K4R3-8#^aJV2Or*^ax4e3Y!`If!By6xl~kQAZvSQiZnz z7NV@&?pu!b*y3*B(4ER*e@OZvmr7`r%B7!5kBLHaWCKhe-u@2nb&df=6}3$ZMFjs^ ze_?q*&ab6)3IPh5fMJN{eo^dZ@lx?5-u$lvdptoc1m!#Owc_&N&9@~DRg{ez6v=@2 zllQ~T!swZ}&ZRS#PD>5{m+`l(Kl&@@ped~MNdu;!u}PHOt()=~KG!=yVqwS!!6>H7eCbT6jm>JjH{)f}*hhtimp zHfoY4z7}?%&AO||KA}ewe^wOmpo+Sd)uoT5y|fB1jU;ZBA%W3>U*S7yov4$$iaJqJ zuk!O@%@1#sw(jCjXaT)fY-jcARlIjxaVrz&e#?5=c;%+Tdis1-FC8jXY4(U7;Po?5 zyuRx7mT2r17sW}{>l|M#MWJyfVG3bE8sdt+N8~6=W{>-fBUsr4G9I@_@Yu{+f;1bi zpS~U=j-J(RclN;|HR0{n*Gm1tP=8;mVe$4(F|@vaUIBzh72>E$;S-XqI2JzsT>@(C zYh@hgx@}#P%CPgZfGVHSs}x^BQCw)sePZK_TIHO9oi>`c`MDH>h|`VyJql%O7Rt_V z$ED=~CfA;`7A2BFs}?iw{4}PHBu`M5tLm);@fKb&$KwFd*#hLAhu)~75-vaJ?y1)E zkt_;V6&?PLq!tp6?BgR?1~{s+jY$9C@?&z>spSur(-h-63M#TAN;z_sDpF;D%3tsN zEIwHpKKbPeD&|Ve91r_xpSS;)=lxRoKY39msw*uLcQp8Ga{y#2zE0NzRE<%F^Z+~~ z%{sg7DW_%FBh|bmS$8L4!-kb5J&Dei8z;*;q!Zzz%d9@Cs(D9Pf)>eMe_@Sa#Z8am z6K`R|gWZ!c-aAW^HBw?eBP*Cn*a>>~U|-naSdIE~`1lwx=tvOHRY{~tCt(uL(?ITc zRj79J0ZnC341yc6V^}A_wq$T4>FfdZ4ppU=;K~vOxz+K6wf+hYV4(9(JLvgwFOJK% z6tmd!ff<1JU6$;ZvXANTVRO#JP&xIufDy7hab9`iy_m9}>>%Gwzh{iee{f8hmKiZC zSG`llljl2ns-)q6=FEOJj88;SXT`gr!LJCm8aT6>Cn9&*{gC5`euuadFTj&ieV`0( zM}0*Dh;0D?Up-Ti6xa!AMAR3xXm|6hXKIQ2t0G_rK;FI|DH(#r_l+>wmR|$wU4rHdU^P=p9SBi8b z_{WhuWO_E0(_e`&k6|^qaylB3BvJCCo5-2s^b=-5yy&Vf0Gk_idE6cEMA9k(=_-Ph zDkAH9CJ^hDSmHPC!lNq>@T~8q3f%aDFlk4Z#;wvso72pYD^48jk=|DvO4gW`6&F?Y z*JI-RqD+W$r>ep>SGqJ;&ve?>QpZb{2P}L?y}ia6e$DOX192WTV`O$+v`~rxr|2!% z(tNnnW8eo>5~OtEDyy$&=FoH)G(MlyhL-fj7jY0UXOc5~qK%)rhpM5Q*Qf!I#|rl> zlC9)&`&{MGO?2kBU7iwm2RwR5kICvV>2@&S_8aWWdRii&aRj=S5c*~QK zg0!^~!2nENOR(?T`z8sXt60)|tp`=-W|i;g<5n?}G2ZMGzqUF=QuF3_KcG`Bkp@%> zL)+n9Y|%lJFhX}4jblHW`g`3z;cg0N?aLu=NPitiv0W8&rvK#ZpKN}YFZS+)NCo3r z`^K+R?|`*OPN^9Hl9sGe0E>)M9ww(;h0MX6@$Sd&k56{BY#IFaP?-pQk1N{ezCRyFC=9Wba_@=jIFl^BBe3 z-pkM4!}dN@T=@J?c@IZ7dnocx%M=68JH9G@9=5*DcRZj%`gi<1?7*b|-1PY04ru=? ziT`Pu{O29yp$^t=K7S^pi~gTWHAAS-KN9%2`~B;ulK;BjpDmpKyp^Ag?_Y!b^VdHI z^w&UyR7B4&l|5|l*g1PRp3mn$PLYHZtbOeNYW^%_=hD*8^=JIvraxhbdy&{n2xOL>FsTGRRe zfqn-6I`scg3bmGh^mT_GSR1j&wbETAABUUR_|fICFJdgk`EJcl&N=knpJ1enAWF zqn8<;iz?c^Gza@XH(*^;Y|Wu)?qnM^X-QF4Z3MQX<@Yd#vb`@hz~x$Q`i2za5kQVH zukXz9rf2}Ih7p&W`u@>zQ8(LLb107`*=d1;4q4T^a#K{tx|93I`@K2J#_Y)xtMKO+sON29 z;lhz$IP!}L`NcqY>B?NXGM8f+RCGC%y6E=uch}^VBe-$|S0jPE@&y7FYB<0LmiN`8 zylN}biEobhn%MMabR{$y))zX~%ED0>u0S!NE?m>aP)T9MQIKPV zX>e8mBTOd6IC3)~M*(LzWa-M3u1qbbiy9l(qYhN z)56h^oweuWgnEk@0A+xodMu`?@88rv#SaS2a-?T0*9kom_eT7%XZoL`_M=nXUt}PJ zXCnhP6nudJcdQjLa57+fQ35uVa3%r!$lVJJxX1A#2JGQs?BHHxz{S2Z4&15S3oQ8a zyoiVBSbiJ@a*6*aw{Wd?fc-;&{`^q5b5GQTap<8c8h?Glq$nZ{NTcRpoM(Z%qfRJ_ zdtWwf2R}jus4E`}0_|_Z1Dt8DEi(~Z370JXlAMXlW7pK%-p0oiNxw6w!*@Xb>-)d$ z@v6|{54#6dcYIN^znh=L1;vJ}*(hLi&8BDK>)wd#y6Mg_4Qp}UZ1!bmHeKJC=a?7{ z#W@q#2va7a@H|lj$#{ywO2?Br6X(~L;_AUP52g9@X)cm63+G8LyeNWX=+K^VF+a)w z8o01SQg$VR?-mvpixwDW@ymeuVNe;bW0*91o!Z@6# zp9~yg^uBbBDGuUOafJ0dz^FaLjn0NFh$p%60t(zvx&h;6o`W!OgK@iPnz|8xhn(R1 zch9jhCX_zOikF0E1-x5U!c@a6sSfs2ZLs8aN~h7Jv!&Uj)8P0gA|qPsT^{Z$D?mG@ z?K8Y9*bkp2G4PH|566wub|e&YxmhIRAv=RgwdL+8;FVhVOQ%#523*#In3w=0%RI?| zn|(n#Gs|N|c!B{a`ey`aWn=sQT+0gY*TyYdqX-?5e%?otIGKkLEzI4F%g&(B5JHH5 z$01-6NM4>-gTD9?@s@cmkhwkgnw*_MliwB*KWuM)dT1$wN{VY7V(pGrbt!bN}IM6$qiid&*eO&P4Yf zYeV1i2g>TVnayNK2m%-kJirw3#8^}#vKk?1g?*XbQ|7|vJ_aZe_EGWxX2QPu?LoYH zjbwB;8PN(9#NKW3UsuAWLOKZC*z86Rfmf@I9>gd6Qo9Ecm)HleJ&3S>J@%CLTGwN% zNc6M0kFG3|^zg`j;7F$e*}?;)g$nNZL!!~qu~Lm%#yVmBZML7Q6P6I>GSre`{M|)} z@O24zY%@bp?QQ`0@5n-hXhwR7*X8Pm?--oLq;1H;Wz~o87%rPCAK)BtaONM941G!_ zL!Y5n$z-%M^edT+cZQzfQrpN6`sNbFef!#{McOQP#^fQ3CtdbuoIh=9ANNccd`Qveul*;>Vx@rNx|Ve__3M30Y>{170#e% z$wUz|7$AB?e;7~9M3+75>2m=I3}3OpOePwYK^K#WPGwNWWTI6WcD$2`US;rI$yBp2 z#gnOStwhMQge=k6umS=Ey&Lrk6+yCrFPH*Mk^o`uK*|FIx^cXgq#yRqkL%x(^kXsh zAHN(Bf*o+MM3Cj~>iE*Ijic@(dKtok4+-AIA6zk@RKP$79$-TU+l+5yVTW(H`ZAmg ze89ey82mdREvylAt>aFXwRs*|3H##{7D~}2`?C-M%EYJyDe|jfn>;fq?&H|sWbj2- z!v0!46>44-L@B zgz_tlk^OZ8TNym=Fi2p2P$=qXpg_~3ErL-V4bd8=KQQtUIvyCgK7&Su57%eVl2GyW znG*Kb3aIt#Gfb3VX6H_kh)4hJHYwf~IMbw140~Bq;hf#gXbpIBO4M9EB|rE+tD7<4AiVoT6DB;WXVR zi*Y2=2$zxVk8z~15iTby8slg_MYtu|t_ZiH8+CIWTqdK1n`8ZjtBQHi7>F3RfXiWR ztW!2ddZF2P=tV|mV#c3dx>pr7hSiCaZL8`U*vJ4 zaw(a~2&c$LML12dRE#5M65%r1tcr1T`WoSK^5GF~N&Yv+(dKr9TT$E<%Sf=H(Ek#%-i&M$t@E&c>fJT#Aq|?gzAw*_RZdSIi%9OkYy0g<}4IWBQUJevD;+ z4NDCa7bPS|iN(Q=pAyuNP}>Ptz4eVi9f5WUtbk$zG*PCVSwR z>{Uv!O5Dbp$zEZa$2zXmg3VRnnCz9ZJOA^3sFcZGp(DHv6pZ;(ipftYO{;^b4bA%t z(-hB3g&}7$P&oy&QGH7`w}_S4_b9bu_CVnlGVjNlk1sAdvwpzA#rmu?Ewm&4bV}>x zInM}#SwFPmIt1~terQdBV^m+pbX@6-`8TC=+C+(Hb3SMElG#ax>B0N4qE%2--w8d% zkh6Yd3tG3%X){bXhNFv-aXB96AgZJFQdA$5m*bdiR~dxL%7H;P=NJK+5M+E zE%b%SB|G6imNVOlJfE_n6gGQ%{!95E%HW~Ix6gIm;ZHP8$7k~Lcv}Mu5U$z(Ro@t; z&0>+QjmmFU8~8r8$@1GAUa&y8^0${u_~YreLu!p%w&^95 + + + + + + + + PortAudio Implementations for DirectSound + + +  +

+ + + +
+
+

+PortAudio - Release Notes

+
+ +

Link to PortAudio Home Page +

+V17 - 10/15/01

+ +
Unix OSS +
    +
  • +Set num channels back to two after device query for ALSA. This fixed a +bug in V16 that sometimes caused a failure when querying for the sample +rates. Thanks Stweart Greenhill.
  • +
+
+ +
+

+Macintosh Sound Manager

+ +
    +
  • +Use NewSndCallBackUPP() for CARBON compatibility.
  • +
+
+ +

+V16 - 9/27/01

+ +
Added Alpha implementations for ASIO, SGI, and BeOS! +
  +
  • +CPULoad is now calculated based on the time spent to generate a known number +of frames. This is more accurate than a simple percentage of real-time. +Implemented in pa_unix_oss, pa_win_wmme and pa_win_ds.
  • + +
  • +Fix dither and shift for recording PaUInt8 format data.
  • + +
  • +Added "patest_maxsines.c" which tests Pa_GetCPULoad().
  • +
    + +
    +

    +Windows WMME

    + +
      +
    • +sDevicePtrs now allocated using GlobalAlloc(). This prevents a +crash in Pa_Terminate() on Win2000. Thanks Mike Berry for finding this. +Thanks Mike Berry.
    • + +
    • +Pass process instead of thread to SetPriorityClass(). This fixes +a bug that caused the priority to not be increased. Thanks to Alberto di +Bene for spotting this.
    • +
    + +

    +Windows DirectSound

    + +
      +
    • +Casts for compiling with __MWERKS__ CodeWarrior.
    • +
    + +

    +UNIX OSS

    + +
      +
    • +Derived from Linux OSS implementation.
    • + +
    • +Numerous patches from Heiko Purnhagen, Stephen Brandon, etc.
    • + +
    • +Improved query mechanism which often bailed out unnecessarily.
    • + +
    • +Removed sNumDevices and potential related bugs,
    • + +
    • +Use getenv("PA_MIN_LATENCY_MSEC") in code to set desired latency. +User can set by entering:
    • + +
          export PA_MIN_LATENCY_MSEC=40
    + +

    +Macintosh Sound Manager

    + +
      +
    • +Pass unused event to WaitNextEvent instead of NULL to prevent Mac OSX crash. +Thanks Dominic Mazzoni.
    • + +
       
    +
    + +

    +V15 - 5/29/01

    + +
    +
      +
    • +New Linux OSS Beta
    • +
    + +

    +Windows WMME

    + +
      +
    • + sDevicePtrs now allocated based on sizeof(pointer). Was allocating +too much space.
    • + +
    • + Check for excessive numbers of channels. Some drivers reported bogus +numbers.
    • + +
    • +Apply Mike Berry's changes for CodeWarrior on PC including condition including +of memory.h, and explicit typecasting on memory allocation.
    • +
    + +

    +Macintosh Sound Manager

    + +
      +
    • +ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID.
    • + +
    • +Device Scan was crashing for anything other than siBadSoundInDevice, but +some Macs may return other errors! Caused failure to init on some G4s under +OS9.
    • + +
    • +Fix TIMEOUT in record mode.
    • + +
    • +Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON
    • +
    +
    + +

    +V14 - 2/6/01

    + +
    +
      +
    • +Added implementation for Windows MultiMedia Extensions (WMME) by Ross and +Phil
    • + +
    • +Changed Pa_StopStream() so that it waits for the buffers to drain.
    • + +
    • +Added Pa_AbortStream() that stops immediately without waiting.
    • + +
    • +Added new test: patest_stop.c to test above two mods.
    • + +
    • +Fixed Pa_StreamTime() so that it returns current play position instead +of the write position. Added "patest_sync.c" to demo audio/video sync.
    • + +
    • +Improved stability of Macintosh implementation. Added timeouts to prevent +hangs.
    • + +
    • +Added Pa_GetSampleSize( PaSampleFormat format );
    • + +
    • +Changes some "int"s to "long"s so that PA works properly on Macintosh which +often compiles using 16 bit ints.
    • + +
    • +Added Implementation Guide
    • +
    +
    + +

    +V12 - 1/9/01

    + +
    +
      +
    • +Mac now scans for and queries all devices. But it does not yet support +selecting any other than the default device.
    • + +
    • +Blocking I/O calls renamed to separate them from the PortAudio API.
    • + +
    • +Cleaned up indentation problems with tabs versus spaces.
    • + +
    • +Now attempts to correct bogus sample rate info returned from DirectSound +device queries.
    • +
    +
    + + + diff --git a/lib/portaudio/index.html b/lib/portaudio/index.html new file mode 100644 index 0000000..ff6cfee --- /dev/null +++ b/lib/portaudio/index.html @@ -0,0 +1,89 @@ + + + + + + + + + PortAudio - Cross-Platform Audio API + + +  +
    + + + +
    +
    +

    +PortAudio - Portable Audio Library

    +
    + +

    Last updated 5/6/02. +

    PortAudio is a cross platform, open-source, audio +I/O library proposed by Ross Bencina to the music-dsp +mailing list. It lets you write simple audio programs in 'C' that will +compile and run on Windows, Macintosh, Unix, BeOS. PortAudio is +intended to promote the exchange of audio synthesis software between developers +on different platforms. +

    For complete information on PortAudio and to download the latest releases, +please visit "http://www.portaudio.com". +
      +
      +

    +

    +Click here for Documentation

    + +

    +

    + +

    +Contacts and E-Mail List

    + +
      +
    • +If you are using or implementing PortAudio then please join the PortAudio +mail list generously administered by +Bill +Eldridge.
    • + +
    • +If you find bugs in one of these implementations, or have suggestions, +please e-mail them to Phil Burk.
    • + +
    • +If you make improvements to the library, please send them to us so we can +incorporate the improvements.
    • +
    + +

    +License

    +PortAudio Portable Real-Time Audio Library +
    Copyright (c) 1999-2000 Ross Bencina and Phil Burk +

    Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following conditions: +

      +
    • +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software.
    • + +
    • +Any person wishing to distribute modifications to the Software is requested +to send the modifications to the original developer so that they can be +incorporated into the canonical version.
    • +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND ON INFRINGEMENT. +
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. +
      + + diff --git a/lib/portaudio/macproj/CVS/Entries b/lib/portaudio/macproj/CVS/Entries new file mode 100644 index 0000000..a58cfb7 --- /dev/null +++ b/lib/portaudio/macproj/CVS/Entries @@ -0,0 +1,2 @@ +/portaudio.mcp/1.2/Sun Nov 10 08:04:54 2002/-kb/ +D diff --git a/lib/portaudio/macproj/CVS/Repository b/lib/portaudio/macproj/CVS/Repository new file mode 100644 index 0000000..ba1f65a --- /dev/null +++ b/lib/portaudio/macproj/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/macproj diff --git a/lib/portaudio/macproj/CVS/Root b/lib/portaudio/macproj/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/macproj/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/macproj/portaudio.mcp b/lib/portaudio/macproj/portaudio.mcp new file mode 100644 index 0000000000000000000000000000000000000000..b33679aca1f1f4f6ef1bd02133318d487a095b31 GIT binary patch literal 81930 zcmeHQ3v^V+dA_SFAt4Y5;Rk-Nzy=wxgx^GdNeBeO5?V`gz>aOSS}kZ{U)f!m1(H~K z*rs_EKjKI0lj1ndp*d|z9!}!;q;(UY#EttPX?yCnPRns~QnyK4rzgqrY3sV*H#2wl z?&`i)5+KAK_~-xcfB*a6nScKI?`ZDa*+?oC*ECJnv_Oj%xUNbIT)$e=>Q8`|3sePw zWa>(dMz@DEnOG_l3}sS#qLG|&>TJS>L0VAhDpm1*VAxNj{m82o{VZ^eqW=_lo}zyR zTr2575V%g!J;3u7{eIwjMLz?)K+)d^Ua08*0B(?U)rG)|6ulLAv7*z!jf#E}c!{F_ z1h`4jKLcJWX`TAcQS>#y=PG(9@Og?p1bn`tzYe@i(LV;hK+@H$H_L=ny$;z$g4C+- z1-@9(4+3AJ=-&svRMD>iUnc1q+9s&zR^Vnu-vPW_(Rtt%iar8-xuRbIzCzJ&0k4$w zJeGZxqC0_G6deP;Qqhk9qaQHM`!=#Qg4E`{3A|R)wY23`ihd6;%ZF4uimXkLTJ3KF zv;Ii6wAIyu)N0=VrhSp>*bdhUQmb1Je4V0c^XnD;8^AXx`m4Y~?SjZ>3Z6)L(zS}v=>r6JO2hjYV}8e zyA*vKc%!0!4ZKOx3+NZ!itYg3tmrY|Es8!2+@t8v0n?9>7EtHyg47ng1Kca=g=`;| z32EUDWZML(EqoBTU(sIyX8n;CzK)E3gVezOIv_}`fo(OYXxe;8(QLar75zN$dlgMR z+a`{R`e%;->>NBfHR8zI&fCeuL9GzkQ#rC zjMpPI{u0^!g4C8Q2HvOWAaGvM?0@?e-2r?+(QMm;ilz-dplHf|P|=?RX5TRe zNNvdrz!Qr8bKnmv`ULPt6#Z-9k4m~}KJdpBeGxEy5vhs&{vknXP2Io`EBbxFhZId4 z(^rt1sPE%~)S8|IrtOfLjv-@Nk($1P>{Ei&n*Ij((~ABXFnt$ksgCThAho6E0Y9SX zHNcN5`WE2F6iwfMT+wX9&nS8V_z6YxJ}eW`(j&;8A_e-Z$etGD$;sG1=+E>o`Y-*K z?a96p0kCh-f9bchCH<8CNxx)W=!f(_`W^j^en$VgA3%SiAJHe+f9N~xo5KM53)^D? z!1iQ2vW@9G^auJ1+mr3c_G7!Tz1U7{AKHuU!FHhiX?NP2cBXx4SK5>QL|f8V>5B^h z3jqy)MS#VCYCr%`2WSK=0W<;X0W|>nUoBuM;2glYfb#(71C{|U09**5Z&m>=2J`?f z0bB~W46qe&J0J*X1}q0KtpMnNd4PUE2yh2r05Av`0^A8`1FQpF4Y&qyEuaN(1z;`U zI>7aS8vw0a5I2DdMn^Iz)C;|pcl{y*Z}AP^Z~X3HUc&Q zx&fC1HUm}xRs-G(*bdkMxC?MMfPL^Dz!rdQ>Kp704vZ&ru|zZ!>T2zcjc9|hL^>Yr zj7KxM)=V}N(YnH!kyNrZqfxl4RUJ~4jKQA%J9n6w@_f0b2>h@w9O)kn4q<3MlFA3A zTySG_WPG>vsqR=Z9M{^zt*P;x28F?%L^_qp1yNFMAbLRC5RS)Fsbp|VG@jP_wg{R_l(%qrtv#IxUVijK|`m!A<#GCTzM^UZZu5 zXLG4U@b>8beW}c7R+HuKN+r_Jkx_^Et0XB@^^fP$G006R5sT-rh$sbMp`2zV3$bLOyV?}HatEWOG(j0ID+%iJKBrqpaG^9 zeooDvSH@(< zTgF7jV#Z&_V#Y?scgAkUa>i}ORwl-H#(N_sGd?nQF;+50GKMnlGiEYg2LVQ$W{hUc z?S3sJW%(eP+AlWiRnF@KEm zrX-Y^j!D)8WRgly=*3&Z_lLXU3`E)1J>g85ymog_DA>3CZfVkhR>KAeXiy@skfug( z$QD+RDkxYb{7)V`=O?4?<@KL_-Jy}yWKzltbrQdjm|f+N{H>Dx;lu?})2PhSVQNe^ZR+45($ zgJ|jF+(EG9N$((6D5n|(IEa>y4;(~qSbuwA0N^54GB$9LoiuE4kyj%M7b#l)@CU;rXq!zZ-+a}Q+-hkdfiP`GGuj7Y97A2hfbF2JWbADzCq}^@;<%J^jpI_xOB|Py?r~h*IAZ!M%Aq2K_^AFIwf1kR z(^DLmq8{S76m*Z{Qm#-=r#LP}J;iY;>Kez3V(vYmDC;eb7v-ILLQ%@m&83)^IBu47 zj^j$wA&yH4mpCqkJjQX8uyY(Y33`a*CTXWQZW8lgZBKDrs+2v@HI7SR_c$))T;q6A z%&Yf$kK;w%?%wMd$BT+QnA|;%OLZl2Tqa3Q2Uw?y_K{3VL-BI40vYYae zDCW^pNzMs!_RXaT-hyvYzmxkGr9Aki7fZg|{7Z66{7bUT{v|o({-wC3{$*ws{7W*6{w0qm`AI#T{s;1vB! zvP%3*%O$nHj=aZOoITDvIl5n%cOfV2Bde+w{X@&6Gt-wH7OCxCAj znEnsI`XB{(9p6+S1^9-7ZzPZce8a%E0!RVAb>$lhq`+2We4~LB;2TrEy+8^?k@2kt zQeYez-*_Me9z;gHNP)+Z@eKx2fOWi7kXqmvFyC4r1^yUWkQC@wku?ia3%m}zT+zP( zUZLp!1!j97Rk0ne5TsT`8?00`@5#3_NL8zmwFpwH>IP;#k*Y?J@f=cB9N8K{YE`Vu zT18I)v%Qh3K7ovHUy!PvMAjxqt?CHyIz@j0m~VBEs-8#2w?ar&FCx2EkXjY%c%7pE z4fuLV&x)M>z78G0=mYe0($~K5#;x#&#eX*|O6eEkbXedTI7hi(Tn)cyeD~e|+IBh$ z@0lLR(Ej7h!@ZtZ~4i$ z0H3e#A_3{bFWRyCD?qx5+Q`!h~k=v`_gK@GWqbT0r0XjYjz% zA)x7dUkaMzgTD7moU)(WsUBaCiIvz~!=c_u&Fvcdweu}dVgd2OU9Nuigm<4yyTpR~ zpjt_mO9k)f6`cz^Yag zyM&Z_A|<@?14EDIswZ83Y00snC+M!nQITgoqtRU@1(?Etuuu6{zh!#d%F_sWsb_la zRn(K7mVF9(u-QUcRoo7_0>Dqc1x}v@&c$$zN#>`o9R8hs3!H8X_W#gdcSQEaM)}Fn_Cf46SNh}$ z)#3|K{#Ai=y!Nrr{alhVugzZYmic&A)8=~B%8{m-HKnre#7H8DGiPkF*B6AJZwfOu%$kKdcta zFMaC|6F$ML zk^7`h`A)R7cm!&Ji5)LZSxwMI;r5`hUz_LP}u0BnD!%?H#27629k(q^pL`4W7MOz;U|>6`e}I*389Vh&VQW!TK9lP(co(10*Jf87 zE^Uz&jatY zr^*6;UpQ5k_U|*>E#UWs+1^5a`A(GuboC=D>VAELJ)2I|t^NDVP793Xa_RQAwmtYY zjMnUUvK8CYw52nt(eX$w+qP$PZ`;+aSGSF(BH6ZZI;J*BoSik9VyXOP&Qmm;f0=KA zIcR~>@nU|?EO$B;O;vb}&X>TGSCYxs4?(#^39qKh-fYMc7FVLE5|=i}si?->yT|EY zmxi}S{!WQ=&#`8xM(pF<`3%V4DRG8uIv3O!e$nCYlsFffbH?gm@(X{b#2K^dnXky? z7yeF(XMS_eSPe{mq4Q^2{GGvO%(T9uGtdI^8-uc)!HORqoPjl}u*m+-U=fi3PKTv)N!zGF{W&JzUfN@sKp6uGQ>XxNd(snhYnR!9*$< z<@wyrk+E=QXf5T(b3M6eCY(!UZf$Rmf!vvccmqZE@7x`=6~(ERf_N~NTpKJNMU$gz zgCnU_9Q<4yXHwanBjIdxWiXq|tZkT&<6Jn0!)PY6mb_SUx47-f;Am`jESJ497{PDZ z<{Az*%+Ew~xPE;mi0q>i^`VOZ$4YI(D=^PD>VN6LV4^Q#b^%Wk_sK5%md=7KSDXh8)w@y%7 z7YJ(V_x7*!E#OMm079X$qEFw4_?W+WSXOIZWA*$6Y7;og(f9rB5lJE5l9;rN zZ*PyDzoky!|8u}~NA-_g`lcaohpqKsg;4&Mke*+bmwGN+W_|sVzmWW@jY1DMBq~X% zQRsO}-~WiAXMM?OyMsyf3P+&lg#PFos$Q4vu)EaYTi5HBU+Vc=Tl9mSX;rTYL*5Qs z>$$ZTdeZtQj~rHdp0c~%;9Kj-grVn~`u+#jDLvmbRA=aU&F*@G zZ>?w7F6h~!A8dJBmM7@C<(!S4Ec6`K^YzD+o^{53?XXD?2M#L5qtNr3{^TzvlpZx9 zYMh^pO?pfxH|6zwM^N9txI^g~Go7>~t@T9X&@-m*&n{DXY}&nPkyKi>N$8o>FZxo@ zb4-8uU)L)=FIg7C#I!EYX4t3WZT&#A>KDNirb{eIYdw26>-o+OJ-_m}EKlq3z!-jHa$i{qu z?w;OFuu|>jfjdR$IU)^L$o*~cXbSpK^+13BP{A%_8a5+cJD3{JM50*=gvxDddfd|+ zy(Hdosn`_Hi}YC~1JhJ#A^xQ;{+(5IpIq5WNi#oRr z_TZP1M>63|Obmaj)R3}T;Tn^=>ISTtwv;xM&x;0sG!V_o!PJ6hUk-}WqX#Z{_It;Q zJ6$It3!eRMP^(onk|Q*f2MJU1Vmo=GRemXIDbJPXh79=l4FdLRY66%4)k$lvI4K3o zm1^3wx9|72&zx*jpW|D=$pWgoI*It_d<*y%IEfbU``}45uCMxxvVh+g&Zy?{3wshR z;P-`-Xk1_Q8D#-~zPiv{{(N=86crIwJ0RYnX*2O7V=gzI{7&8Fg41;xj~J{%xlr!O zVEawFu@Vh4mqAR{o*p|^JIqLWHDA@_w+UV^EtiAk@PfKNnd0|%tglc`;LI?RColhd^4 z$k$D_|58gxxy>NGh5RxS1V4hP;x6ZdB0TxS827bTi#tP ze*jz5V#?VtuIx;w<5O0kE~K8mzTORPda!&Y*puMul3*y*Wi8zo>g~2ZQ{;Nmv%QOB z`DvwLTs<&2U{lhf>VfEi;(-M<4BpVD9Yd|*Ovd7LMJ%CbWX$oJ4dHk^l}ZM;MB`~Y zL7^vNcUJQIwgOtO9u}k(Msh5W z%gK;B- zH7TqWP411_sH^w#40OMj7LYM4mzd6gqY%URBUlbMMeJe493)iCNbGWy@p96oxxJ{M zjF`J?4LgU8t(^lZ@>n-lhq_y<*xybaSRgO<;bpD6V~z&$T%J8)8~2ihb1M(qovHOe zEG;SP+>0JLy1rrk?RIW|(HTCvM%8SrQ`zX+Ue64TuKPD^4O*A9$n6VlH=eb&7-LRDt0U9Fa##1yPXs>66#)dQ6 zbDW1h#W4$OWivM|$8=6lWYbv8RgZIl3eR>|J&r@D3hL!?Eaq08Q#}^5DNn^?F&jM- z=x@fD&Z*_y+N+>v1|s-u8$&T9&BT~xdn|eFHNpJ)8*Bo&c5X#~RY4I;W|b=H$*y`l z_GDK*9(%I89!Et5Pj%CS-y5*9T_rqWXU8`Urif`%o8FsM4e5~zt2qWEbVu?)aY z$X@|rXBoved56ttl@ZZj0pYKJuy&-v)4f7unx(+>S3o$dSmCpa$zK5>?Jzkvjf!pGuvNIe z*j~>JjjsI_5LjPqYh_~;>NK-qn)`9p<8iLeRgdEoTXELiU5}%p!YJKM|=+mxr` z(Y1}933Tm>=Cs`O%)qR>C{Na30b#eYtin;TO^eBxJ`-;fsL5?zu)hLA+Iw<74AxA2 z88oBsac7_>`zs(Ox3_oBOnQdD0>a+zwDXMi9Ot1=am-@#CY&V|&p@x=db#RxE>L0Y zORgcG>?5f9OPj=VisK~Q@{1p(ED`*Ol#a{to+Xwv>5VoelPfE$u>aT#vx&{26aHhAQU;gP@fPKJQKk&yN)3x;(f4x8c zIOChpufXiFfVa3id&=vV#J9jqTfqDH1CqvX2-lv@_5If!)j#&go944tw6&f{0(xH35AR$j^*qX{P{(Ag zCl%56t*X;Mbl(Z3r^Qi;aZ>ykfN?;)BLqDm{h_8el^$bi%`61^f%_$+>OoPSnzGZ{ zsyOsKr9WBIqV(j;CCZPj>y;dbo)dchy+@RuBjwkXA6x6$+o|tsY0*Dh`)G26J>S&xuPQq~wygZR@?&c~EwAhQuGI8>m*i!6 z9$R02UHP#|kLmd_GzL9e^!@9V|2#HaeqH&oNssANC<8r*_58~rRh|jcNlVgN&v+Jk zUeiDL)H0>#nB~PLrnR0cf1>YO9YjBUOX-)FNf9M=y%SEuw$vE9dE=e6*iJ5=0w{J81XCUWUKZr_~9=E~k72M|ANC%`Y; zSg`IW_JbxrX;K1m0pzQd9^K>{tDs*eA9~FE#gGq>4?Sjn(_cW@Iq3|(lz!;WFzAKy zK22``*Gv9)D5L(GZu&W8B)|4y(2D>_O^i#6B>!%dL0e3&rValm?mJKN#n;%%|0cc- z{@+Xf@t;6&sSwvju(xTAmdJ@6olWs#7<_F4eD05G;@@)@jvK*`mGZ;C!tt_1F3xXD zP4Z)x;5eAbWoc&<|G7(XtnBdcA<*jpNcT{$vP0cA&{vbIX(M4|$`18UgT97bO?!@5 z+2J$XYwB8ZHSHK|V%Xuv=fSv6u(fXtfUoTEhtGg_qvZe97Ly&SB#-%*{s@_22NQqd zDew(DnE21}Nom-@#Qz4%Y1qNUUyX8BUzf=3F7uy8_>Z5?rqsyys}rH#Y)*S9 z)(N^to@{N0->yxhvyeCYTjSSpz6E|*&i}_EUuux~mp=epm%wh&X0E3FLlVcIPlU^N z{{Gf~fq$Oxt1TDOC%4M$e$)+opWttPbs=cAyWo#L5Bl?x|6Tf0F97MqI%HDM=HqOa zK61gQpC~;We+rsDf%LZK*@aTi!(RnGNG|y7JJJuAwb52X)G zYAHYb3(&g&NKK5R(r(MwUJjZzLTX~ayH(1+_`9IT$OWI*$vCsTc{OPCC#G%i^O{df z`9*Jnu}84A!M_Gy*=^TvgSS`mcf5{zDf`^Uei4`av0D(rUzd8${ULaazer6vWY}|Gfi~zg+Utpp;AI70Yx)AP(!cx=XqFGD>08KT++P0u+dz+#3%6T|O!{H-zq4)c zCl~x{ko`cG=L^)ik6gGT?h{xh`Op0?80-&7_kIG|QOW=Q0?_-(g&xYZO8&hoK(h}Z zeeEdXp}g<%gX|Lr1zTG(4_LL!=jbCJko=E4S@h={&Ij*%ziwC2pD+6(@IEa0k6l~z=dYvfw2w&s6N3eRZvMM3 zg7;C$UqZiC{`^;e2>N4+kA7tMbHhJ_eo*n(0xN&sH4OS8#iyStfBrMx^I^$<`mUls zuchy>jgg+~DEf2tzk$I%k8~f(Z}{`|uY&P$!PZ`cT@8O8U<_vaAuVBhDu4dUcR+uV zTrF@Z{LS#^JuidvDZ$r1`!5B5Zhq@6@IEd1JG+3TKR3Vj$Dn^x@*hAyQvSUBw^)|L zl7DndvAv%8DR_@a{*o^OD}T + +#include "portaudio.h" +#include "pa_host.h" + +#define CLIP( val, min, max ) { val = ((val) < (min)) ? min : (((val) < (max)) ? (max) : (val)); } + +/*************************************************************************/ +static void PaConvert_Float32_Int16( + float *sourceBuffer, int sourceStride, + short *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; ipast_NativeInputSampleFormat = nativeInputSampleFormat; + past->past_InputConversionSourceStride = 1; + past->past_InputConversionTargetStride = 1; + + if( nativeInputSampleFormat != past->past_InputSampleFormat ) + { + int ifDither = (past->past_Flags & paDitherOff) == 0; + past->past_InputConversionProc = PaConvert_SelectProc( nativeInputSampleFormat, + past->past_InputSampleFormat, 0, ifDither ); + if( past->past_InputConversionProc == NULL ) return paSampleFormatNotSupported; + } + else + { + past->past_InputConversionProc = NULL; /* no conversion necessary */ + } + + return paNoError; +} + +/*************************************************************************/ +PaError PaConvert_SetupOutput( internalPortAudioStream *past, + PaSampleFormat nativeOutputSampleFormat ) +{ + + past->past_NativeOutputSampleFormat = nativeOutputSampleFormat; + past->past_OutputConversionSourceStride = 1; + past->past_OutputConversionTargetStride = 1; + + if( nativeOutputSampleFormat != past->past_OutputSampleFormat ) + { + int ifDither = (past->past_Flags & paDitherOff) == 0; + int ifClip = (past->past_Flags & paClipOff) == 0; + + past->past_OutputConversionProc = PaConvert_SelectProc( past->past_OutputSampleFormat, + nativeOutputSampleFormat, ifClip, ifDither ); + if( past->past_OutputConversionProc == NULL ) return paSampleFormatNotSupported; + } + else + { + past->past_OutputConversionProc = NULL; /* no conversion necessary */ + } + + return paNoError; +} + +/************************************************************************* +** Called by host code. +** Convert input from native format to user format, +** call user code, +** then convert output to native format. +** Returns result from user callback. +*/ +long PaConvert_Process( internalPortAudioStream *past, + void *nativeInputBuffer, + void *nativeOutputBuffer ) +{ + int userResult; + void *inputBuffer = NULL; + void *outputBuffer = NULL; + + /* Get native input data. */ + if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) + { + if( past->past_InputSampleFormat == past->past_NativeInputSampleFormat ) + { + /* Already in native format so just read directly from native buffer. */ + inputBuffer = nativeInputBuffer; + } + else + { + inputBuffer = past->past_InputBuffer; + /* Convert input data to user format. */ + (*past->past_InputConversionProc)(nativeInputBuffer, past->past_InputConversionSourceStride, + inputBuffer, past->past_InputConversionTargetStride, + past->past_FramesPerUserBuffer * past->past_NumInputChannels ); + } + } + + /* Are we doing output? */ + if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) + { + outputBuffer = (past->past_OutputConversionProc == NULL) ? + nativeOutputBuffer : past->past_OutputBuffer; + } + /* + AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); + AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); + */ + /* Call user callback routine. */ + userResult = past->past_Callback( + inputBuffer, + outputBuffer, + past->past_FramesPerUserBuffer, + past->past_FrameCount, + past->past_UserData ); + + /* Advance frame counter for timestamp. */ + past->past_FrameCount += past->past_FramesPerUserBuffer; // FIXME - should this be in here? + + /* Convert to native format if necessary. */ + if( (past->past_OutputConversionProc != NULL ) && (outputBuffer != NULL) ) + { + (*past->past_OutputConversionProc)( outputBuffer, past->past_OutputConversionSourceStride, + nativeOutputBuffer, past->past_OutputConversionTargetStride, + past->past_FramesPerUserBuffer * past->past_NumOutputChannels ); + } + + return userResult; +} diff --git a/lib/portaudio/pa_common/pa_host.h b/lib/portaudio/pa_common/pa_host.h new file mode 100644 index 0000000..3d7befc --- /dev/null +++ b/lib/portaudio/pa_common/pa_host.h @@ -0,0 +1,189 @@ +#ifndef PA_HOST_H +#define PA_HOST_H + +/* + * $Id: pa_host.h,v 1.5 2003/03/02 08:01:34 dmazzoni Exp $ + * Host dependant internal API for PortAudio + * + * Author: Phil Burk + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.softsynth.com/portaudio/ + * DirectSound and Macintosh Implementation + * Copyright (c) 1999-2000 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#ifndef SUPPORT_AUDIO_CAPTURE +#define SUPPORT_AUDIO_CAPTURE (1) +#endif + +#ifndef int32 + typedef long int32; +#endif +#ifndef uint32 + typedef unsigned long uint32; +#endif +#ifndef int16 + typedef short int16; +#endif +#ifndef uint16 + typedef unsigned short uint16; +#endif + +/* Used to convert between various sample formats. */ +typedef void (PortAudioConverter)( + void *inputBuffer, int inputStride, + void *outputBuffer, int outputStride, + int numSamples ); + +#define PA_MAGIC (0x18273645) + +/************************************************************************************/ +/****************** Structures ******************************************************/ +/************************************************************************************/ + +typedef struct internalPortAudioStream +{ + uint32 past_Magic; /* ID for struct to catch bugs. */ + + /* Begin user specified information. */ + uint32 past_FramesPerUserBuffer; + uint32 past_NumUserBuffers; + double past_SampleRate; /* Closest supported sample rate. */ + int past_NumInputChannels; + int past_NumOutputChannels; + PaDeviceID past_InputDeviceID; + PaDeviceID past_OutputDeviceID; + PaSampleFormat past_InputSampleFormat; + PaSampleFormat past_OutputSampleFormat; + PortAudioCallback *past_Callback; + void *past_UserData; + uint32 past_Flags; + /* End user specified information. */ + + void *past_DeviceData; + PaSampleFormat past_NativeOutputSampleFormat; + PaSampleFormat past_NativeInputSampleFormat; + + /* Flags for communicating between foreground and background. */ + volatile int past_IsActive; /* Background is still playing. */ + volatile int past_StopSoon; /* Background should keep playing when buffers empty. */ + volatile int past_StopNow; /* Background should stop playing now. */ + /* These buffers are used when the native format does not match the user format. */ + void *past_InputBuffer; + uint32 past_InputBufferSize; /* Size in bytes of the input buffer. */ + void *past_OutputBuffer; + uint32 past_OutputBufferSize; + /* Measurements */ + uint32 past_NumCallbacks; + PaTimestamp past_FrameCount; /* Frames output to buffer. */ + /* For measuring CPU utilization. */ + double past_AverageInsideCount; + double past_AverageTotalCount; + double past_Usage; + int past_IfLastExitValid; + /* Format Conversion */ + /* These are setup by PaConversion_Setup() */ + PortAudioConverter *past_InputConversionProc; + int past_InputConversionSourceStride; + int past_InputConversionTargetStride; + PortAudioConverter *past_OutputConversionProc; + int past_OutputConversionSourceStride; + int past_OutputConversionTargetStride; +} +internalPortAudioStream; + +/************************************************************************************/ +/******** These functions must be provided by a platform implementation. ************/ +/************************************************************************************/ + +PaError PaHost_Init( void ); +PaError PaHost_Term( void ); + +PaError PaHost_OpenStream( internalPortAudioStream *past ); +PaError PaHost_CloseStream( internalPortAudioStream *past ); + +PaError PaHost_StartOutput( internalPortAudioStream *past ); +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ); +PaError PaHost_StartInput( internalPortAudioStream *past ); +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ); +PaError PaHost_StartEngine( internalPortAudioStream *past ); +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ); +PaError PaHost_StreamActive( internalPortAudioStream *past ); + +void *PaHost_AllocateFastMemory( long numBytes ); +void PaHost_FreeFastMemory( void *addr, long numBytes ); + +/* This only called if PA_VALIDATE_RATE IS CALLED. */ +PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate, + double *closestFrameRatePtr ); + +/**********************************************************************/ +/************ Common Utility Routines provided by PA ******************/ +/**********************************************************************/ + +/* PaHost_IsInitialized() returns non-zero if PA is initialized, 0 otherwise */ +int PaHost_IsInitialized( void ); + +internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream ); + +int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, + int numRates, double frameRate ); + +long Pa_CallConvertInt16( internalPortAudioStream *past, + short *nativeInputBuffer, + short *nativeOutputBuffer ); + +/* Calculate 2 LSB dither signal with a triangular distribution. +** Ranged properly for adding to a 32 bit 1.31 fixed point value prior to >>15. +** Range of output is +/- 65535 +** Multiply by PA_DITHER_SCALE to get a float between -2.0 and 2.0. */ +#define PA_DITHER_BITS (15) +#define PA_DITHER_SCALE (1.0f / ((1< +#include +#include +#include + +/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ +#ifdef _WIN32 +#ifndef __MWERKS__ +#include +#endif /* __MWERKS__ */ +#else /* !_WIN32 */ +#include +#endif /* _WIN32 */ + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +/* The reason we might NOT want to validate the rate before opening the stream + * is because many DirectSound drivers lie about the rates they actually support. + */ +#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */ + +/* +O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion +*/ + +#ifndef FALSE + #define FALSE (0) + #define TRUE (!FALSE) +#endif + +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */ + +static PaError Pa_KillStream( PortAudioStream *stream, int abort ); + +/***********************************************************************/ +int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate ) +{ + double err, minErr = allowableError; + int i, bestFit = -1; + + for( i=0; inumSampleRates == -1 ) + { + /* Is it out of range? */ + if( (requestedFrameRate < pdi->sampleRates[0]) || + (requestedFrameRate > pdi->sampleRates[1]) ) + { + return paInvalidSampleRate; + } + + *closestFrameRatePtr = requestedFrameRate; + } + else + { + bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate ); + if( bestRateIndex < 0 ) return paInvalidSampleRate; + *closestFrameRatePtr = pdi->sampleRates[bestRateIndex]; + } + return paNoError; +} + +/*************************************************************************/ +PaError Pa_OpenStream( + PortAudioStream** streamPtrPtr, + PaDeviceID inputDeviceID, + int numInputChannels, + PaSampleFormat inputSampleFormat, + void *inputDriverInfo, + PaDeviceID outputDeviceID, + int numOutputChannels, + PaSampleFormat outputSampleFormat, + void *outputDriverInfo, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + unsigned long streamFlags, + PortAudioCallback *callback, + void *userData ) +{ + internalPortAudioStream *past = NULL; + PaError result = paNoError; + int bitsPerInputSample; + int bitsPerOutputSample; + /* Print passed parameters. */ + DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n", + streamPtrPtr, inputDeviceID, numInputChannels, + inputSampleFormat, inputDriverInfo )); + DBUG((" %d, %d, %d, %p, /* output */\n", + outputDeviceID, numOutputChannels, + outputSampleFormat, outputDriverInfo )); + DBUG((" %g, %d, %d, 0x%x, , %p )\n", + sampleRate, framesPerBuffer, numberOfBuffers, + streamFlags, userData )); + + /* Check for parameter errors. */ + if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag; + if( streamPtrPtr == NULL ) return paBadStreamPtr; + if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */ + if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */ + if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId; + if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) + { + return paInvalidDeviceId; + } + if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount; + +#if SUPPORT_AUDIO_CAPTURE + if( inputDeviceID >= 0 ) + { + PaError size = Pa_GetSampleSize( inputSampleFormat ); + if( size < 0 ) return size; + bitsPerInputSample = 8 * size; + if( (numInputChannels <= 0) ) return paInvalidChannelCount; + } +#else + if( inputDeviceID >= 0 ) + { + return paInvalidChannelCount; + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + else + { + if( numInputChannels > 0 ) return paInvalidChannelCount; + bitsPerInputSample = 0; + } + + if( outputDeviceID >= 0 ) + { + PaError size = Pa_GetSampleSize( outputSampleFormat ); + if( size < 0 ) return size; + bitsPerOutputSample = 8 * size; + if( (numOutputChannels <= 0) ) return paInvalidChannelCount; + } + else + { + if( numOutputChannels > 0 ) return paInvalidChannelCount; + bitsPerOutputSample = 0; + } + + if( callback == NULL ) return paNullCallback; + + /* Allocate and clear stream structure. */ + past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) ); + if( past == NULL ) return paInsufficientMemory; + memset( past, 0, sizeof(internalPortAudioStream) ); + AddTraceMessage("Pa_OpenStream: past", (long) past ); + + past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */ + past->past_FramesPerUserBuffer = framesPerBuffer; + past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() MUST CHECK FOR ZERO! */ + past->past_Callback = callback; + past->past_UserData = userData; + past->past_OutputSampleFormat = outputSampleFormat; + past->past_InputSampleFormat = inputSampleFormat; + past->past_OutputDeviceID = outputDeviceID; + past->past_InputDeviceID = inputDeviceID; + past->past_NumInputChannels = numInputChannels; + past->past_NumOutputChannels = numOutputChannels; + past->past_Flags = streamFlags; + + /* Check for absurd sample rates. */ + if( (sampleRate < 1000.0) || (sampleRate > 200000.0) ) + { + result = paInvalidSampleRate; + goto cleanup; + } + + /* Allocate buffers that may be used for format conversion from user to native buffers. */ + if( numInputChannels > 0 ) + { + +#if PA_VALIDATE_RATE + result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate ); + if( result < 0 ) + { + goto cleanup; + } +#else + past->past_SampleRate = sampleRate; +#endif + /* Allocate single Input buffer for passing formatted samples to user callback. */ + past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8); + past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize); + if( past->past_InputBuffer == NULL ) + { + result = paInsufficientMemory; + goto cleanup; + } + } + else + { + past->past_InputBuffer = NULL; + } + + /* Allocate single Output buffer. */ + if( numOutputChannels > 0 ) + { +#if PA_VALIDATE_RATE + result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate ); + if( result < 0 ) + { + goto cleanup; + } +#else + past->past_SampleRate = sampleRate; +#endif + past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8); + past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize); + if( past->past_OutputBuffer == NULL ) + { + result = paInsufficientMemory; + goto cleanup; + } + } + else + { + past->past_OutputBuffer = NULL; + } + + result = PaHost_OpenStream( past ); + if( result < 0 ) goto cleanup; + + *streamPtrPtr = (void *) past; + + return result; + +cleanup: + if( past != NULL ) Pa_CloseStream( past ); + *streamPtrPtr = NULL; + return result; +} + + +/*************************************************************************/ +PaError Pa_OpenDefaultStream( PortAudioStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PortAudioCallback *callback, + void *userData ) +{ + return Pa_OpenStream( + stream, + ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice), + numInputChannels, sampleFormat, NULL, + ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice), + numOutputChannels, sampleFormat, NULL, + sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData ); +} + +/*************************************************************************/ +PaError Pa_CloseStream( PortAudioStream* stream) +{ + PaError result; + internalPortAudioStream *past; + + DBUG(("Pa_CloseStream()\n")); + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + + Pa_AbortStream( past ); + result = PaHost_CloseStream( past ); + + if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize ); + if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize ); + PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) ); + + return result; +} + +/*************************************************************************/ +PaError Pa_StartStream( PortAudioStream *stream ) +{ + PaError result = paHostError; + internalPortAudioStream *past; + + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + + past->past_FrameCount = 0.0; + + if( past->past_NumInputChannels > 0 ) + { + result = PaHost_StartInput( past ); + DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + } + + if( past->past_NumOutputChannels > 0 ) + { + result = PaHost_StartOutput( past ); + DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + } + + result = PaHost_StartEngine( past ); + DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + + return paNoError; + +error: + return result; +} + +/*************************************************************************/ +PaError Pa_StopStream( PortAudioStream *stream ) +{ + return Pa_KillStream( stream, 0 ); +} + +/*************************************************************************/ +PaError Pa_AbortStream( PortAudioStream *stream ) +{ + return Pa_KillStream( stream, 1 ); +} + +/*************************************************************************/ +static PaError Pa_KillStream( PortAudioStream *stream, int abort ) +{ + PaError result = paNoError; + internalPortAudioStream *past; + + DBUG(("Pa_StopStream().\n")); + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + + if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) ) + { + result = PaHost_StopEngine( past, abort ); + DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + } + + if( past->past_NumInputChannels > 0 ) + { + result = PaHost_StopInput( past, abort ); + DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result)); + if( result != paNoError ) goto error; + } + + if( past->past_NumOutputChannels > 0 ) + { + result = PaHost_StopOutput( past, abort ); + DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result)); + if( result != paNoError ) goto error; + } + +error: + past->past_Usage = 0; + past->past_IfLastExitValid = 0; + + return result; +} + +/*************************************************************************/ +PaError Pa_StreamActive( PortAudioStream *stream ) +{ + internalPortAudioStream *past; + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + return PaHost_StreamActive( past ); +} + +/*************************************************************************/ +const char *Pa_GetErrorText( PaError errnum ) +{ + const char *msg; + + switch(errnum) + { + case paNoError: msg = "Success"; break; + case paHostError: msg = "Host error."; break; + case paInvalidChannelCount: msg = "Invalid number of channels."; break; + case paInvalidSampleRate: msg = "Invalid sample rate."; break; + case paInvalidDeviceId: msg = "Invalid device ID."; break; + case paInvalidFlag: msg = "Invalid flag."; break; + case paSampleFormatNotSupported: msg = "Sample format not supported"; break; + case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break; + case paInsufficientMemory: msg = "Insufficient memory."; break; + case paBufferTooBig: msg = "Buffer too big."; break; + case paBufferTooSmall: msg = "Buffer too small."; break; + case paNullCallback: msg = "No callback routine specified."; break; + case paBadStreamPtr: msg = "Invalid stream pointer."; break; + case paTimedOut : msg = "Wait Timed Out."; break; + case paInternalError: msg = "Internal PortAudio Error."; break; + case paDeviceUnavailable: msg = "Device Unavailable."; break; + default: msg = "Illegal error number."; break; + } + return msg; +} + +/* + Get CPU Load as a fraction of total CPU time. + A value of 0.5 would imply that PortAudio and the sound generating + callback was consuming roughly 50% of the available CPU time. + The amount may vary depending on CPU load. + This function may be called from the callback function. +*/ +double Pa_GetCPULoad( PortAudioStream* stream) +{ + internalPortAudioStream *past; + if( stream == NULL ) return (double) paBadStreamPtr; + past = (internalPortAudioStream *) stream; + return past->past_Usage; +} + +/*************************************************************************/ +internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream ) +{ + internalPortAudioStream* result = (internalPortAudioStream*) stream; + + if( result == NULL || result->past_Magic != PA_MAGIC ) + return NULL; + else + return result; +} + +/************************************************************* +** Calculate 2 LSB dither signal with a triangular distribution. +** Ranged properly for adding to a 32 bit integer prior to >>15. +** Range of output is +/- 32767 +*/ +#define PA_DITHER_BITS (15) +#define PA_DITHER_SCALE (1.0f / ((1<>DITHER_SHIFT) + (((long)randSeed2)>>DITHER_SHIFT); + /* High pass filter to reduce audibility. */ + highPass = current - previous; + previous = current; + return highPass; +} + +/************************************************************************* +** Called by host code. +** Convert input from Int16, call user code, then convert output +** to Int16 format for native use. +** Assumes host native format is paInt16. +** Returns result from user callback. +*/ +long Pa_CallConvertInt16( internalPortAudioStream *past, + short *nativeInputBuffer, + short *nativeOutputBuffer ) +{ + long temp; + int userResult; + unsigned int i; + void *inputBuffer = NULL; + void *outputBuffer = NULL; + +#if SUPPORT_AUDIO_CAPTURE + /* Get native data from DirectSound. */ + if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) + { + /* Convert from native format to PA format. */ + unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels; + switch(past->past_InputSampleFormat) + { + + case paFloat32: + { + float *inBufPtr = (float *) past->past_InputBuffer; + inputBuffer = past->past_InputBuffer; + for( i=0; ipast_InputBuffer; + inputBuffer = past->past_InputBuffer; + for( i=0; ipast_InputBuffer; + inputBuffer = past->past_InputBuffer; + if( past->past_Flags & paDitherOff ) + { + for( i=0; i> 8); + } + } + else + { + for( i=0; i> 8; /* PLB20010820 */ + temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); + inBufPtr[i] = (char)(temp >> 8); + } + } + break; + } + + case paUInt8: + { + /* Convert 16 bit data to 8 bit unsigned chars */ + unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer; + inputBuffer = past->past_InputBuffer; + if( past->past_Flags & paDitherOff ) + { + for( i=0; i> 8) + 0x80); + } + } + else + { + /* If you dither then you have to clip because dithering could push the signal out of range! */ + for( i=0; i> 8; /* PLB20010820 */ + temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); + inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */ + } + } + break; + } + + default: + break; + } + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + + /* Are we doing output time? */ + if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) + { + /* May already be in native format so just write directly to native buffer. */ + outputBuffer = (past->past_OutputSampleFormat == paInt16) ? + nativeOutputBuffer : past->past_OutputBuffer; + } + /* + AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); + AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); + */ + /* Call user callback routine. */ + userResult = past->past_Callback( + inputBuffer, + outputBuffer, + past->past_FramesPerUserBuffer, + past->past_FrameCount, + past->past_UserData ); + + past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer; + + /* Convert to native format if necessary. */ + if( outputBuffer != NULL ) + { + unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels; + switch(past->past_OutputSampleFormat) + { + case paFloat32: + { + float *outBufPtr = (float *) past->past_OutputBuffer; + if( past->past_Flags & paDitherOff ) + { + if( past->past_Flags & paClipOff ) /* NOTHING */ + { + for( i=0; i 0x7FFF) ? 0x7FFF : temp)); + } + } + } + else + { + /* If you dither then you have to clip because dithering could push the signal out of range! */ + for( i=0; i 0x7FFF) ? 0x7FFF : temp)); + } + } + break; + } + + case paInt32: + { + int *outBufPtr = (int *) past->past_OutputBuffer; + if( past->past_Flags & paDitherOff ) + { + for( i=0; i> 16 ); + } + } + else + { + for( i=0; i> 1) + PaConvert_TriangularDither(); + temp = temp >> 15; + *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); + } + } + break; + } + + case paInt8: + { + char *outBufPtr = (char *) past->past_OutputBuffer; + for( i=0; ipast_OutputBuffer; + for( i=0; i 0 ) return paNoError; + ResetTraceMessages(); + return PaHost_Init(); +} + +PaError Pa_Terminate( void ) +{ + PaError result = paNoError; + + if( gInitCount == 0 ) return paNoError; + else if( --gInitCount == 0 ) + { + result = PaHost_Term(); + DumpTraceMessages(); + } + return result; +} + +int PaHost_IsInitialized() +{ + return gInitCount; +} + +/*************************************************************************/ +PaError Pa_GetSampleSize( PaSampleFormat format ) +{ + int size; + switch(format ) + { + + case paUInt8: + case paInt8: + size = 1; + break; + + case paInt16: + size = 2; + break; + + case paPackedInt24: + size = 3; + break; + + case paFloat32: + case paInt32: + case paInt24: + size = 4; + break; + + default: + size = paSampleFormatNotSupported; + break; + } + return (PaError) size; +} + + diff --git a/lib/portaudio/pa_common/pa_trace.c b/lib/portaudio/pa_common/pa_trace.c new file mode 100644 index 0000000..3a9ef02 --- /dev/null +++ b/lib/portaudio/pa_common/pa_trace.c @@ -0,0 +1,83 @@ +/* + * $Id: pa_trace.c,v 1.5 2003/03/02 08:01:34 dmazzoni Exp $ + * Portable Audio I/O Library Trace Facility + * Store trace information in real-time for later printing. + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2000 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "pa_trace.h" + +#if TRACE_REALTIME_EVENTS + +static char *traceTextArray[MAX_TRACE_RECORDS]; +static int traceIntArray[MAX_TRACE_RECORDS]; +static int traceIndex = 0; +static int traceBlock = 0; + +/*********************************************************************/ +void ResetTraceMessages() +{ + traceIndex = 0; +} + +/*********************************************************************/ +void DumpTraceMessages() +{ + int i; + int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS; + + printf("DumpTraceMessages: traceIndex = %d\n", traceIndex ); + for( i=0; i +#include +#include +#include +#include + +/* Mac specific includes */ +#include "OSUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +#ifndef FALSE + #define FALSE (0) + #define TRUE (!FALSE) +#endif + +/* #define TARGET_API_MAC_CARBON (1) */ + +/* + * Define maximum CPU load that will be allowed. User callback will + * be skipped if load exceeds this limit. This is to prevent the Mac + * from hanging when the CPU is hogged by the sound thread. + * On my PowerBook G3, the mac hung when I used 94% of CPU ( usage = 0.94 ). + */ +#define PA_MAX_USAGE_ALLOWED (0.92) + +/* Debugging output macros. */ +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +#define MAC_PHYSICAL_FRAMES_PER_BUFFER (512) /* Minimum number of stereo frames per SoundManager double buffer. */ +#define MAC_VIRTUAL_FRAMES_PER_BUFFER (4096) /* Need this many when using Virtual Memory for recording. */ +#define PA_MIN_NUM_HOST_BUFFERS (2) +#define PA_MAX_NUM_HOST_BUFFERS (16) /* Do not exceed!! */ +#define PA_MAX_DEVICE_INFO (32) + +/* Conversions for 16.16 fixed point code. */ +#define DoubleToUnsignedFixed(x) ((UnsignedFixed) ((x) * 65536.0)) +#define UnsignedFixedToDouble(fx) (((double)(fx)) * (1.0/(1<<16))) + +/************************************************************************************/ +/****************** Structures ******************************************************/ +/************************************************************************************/ +/* Use for passing buffers from input callback to output callback for processing. */ +typedef struct MultiBuffer +{ + char *buffers[PA_MAX_NUM_HOST_BUFFERS]; + int numBuffers; + int nextWrite; + int nextRead; +} +MultiBuffer; + +/* Define structure to contain all Macintosh specific data. */ +typedef struct PaHostSoundControl +{ + UInt64 pahsc_EntryCount; + double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ + + /* Use char instead of Boolean for atomic operation. */ + volatile char pahsc_IsRecording; /* Recording in progress. Set by foreground. Cleared by background. */ + volatile char pahsc_StopRecording; /* Signal sent to background. */ + volatile char pahsc_IfInsideCallback; + /* Input */ + SPB pahsc_InputParams; + SICompletionUPP pahsc_InputCompletionProc; + MultiBuffer pahsc_InputMultiBuffer; + int32 pahsc_BytesPerInputHostBuffer; + int32 pahsc_InputRefNum; + /* Output */ + CmpSoundHeader pahsc_SoundHeaders[PA_MAX_NUM_HOST_BUFFERS]; + int32 pahsc_BytesPerOutputHostBuffer; + SndChannelPtr pahsc_Channel; + SndCallBackUPP pahsc_OutputCompletionProc; + int32 pahsc_NumOutsQueued; + int32 pahsc_NumOutsPlayed; + PaTimestamp pahsc_NumFramesDone; + UInt64 pahsc_WhenFramesDoneIncremented; + /* Init Time -------------- */ + int32 pahsc_NumHostBuffers; + int32 pahsc_FramesPerHostBuffer; + int32 pahsc_UserBuffersPerHostBuffer; + int32 pahsc_MinFramesPerHostBuffer; /* Can vary depending on virtual memory usage. */ +} +PaHostSoundControl; + +/* Mac specific device information. */ +typedef struct internalPortAudioDevice +{ + long pad_DeviceRefNum; + long pad_DeviceBufferSize; + Component pad_Identifier; + PaDeviceInfo pad_Info; +} +internalPortAudioDevice; + +/************************************************************************************/ +/****************** Data ************************************************************/ +/************************************************************************************/ +static int sNumDevices = 0; +static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 }; +static int32 sPaHostError = 0; +static int sDefaultOutputDeviceID; +static int sDefaultInputDeviceID; + +/************************************************************************************/ +/****************** Prototypes ******************************************************/ +/************************************************************************************/ +static PaError PaMac_TimeSlice( internalPortAudioStream *past, int16 *macOutputBufPtr ); +static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr ); +static PaError PaMac_RecordNext( internalPortAudioStream *past ); +static void PaMac_StartLoadCalculation( internalPortAudioStream *past ); +static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerBuffer, double sampleRate ); +static double *PaMac_GetSampleRatesFromHandle ( int numRates, Handle h ); +static PaError PaMac_ScanInputDevices( void ); +static PaError PaMac_ScanOutputDevices( void ); +static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad ); +static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad ); +static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader ); +static void PaMac_EndLoadCalculation( internalPortAudioStream *past ); +static void PaMac_PlayNext ( internalPortAudioStream *past, int index ); +static long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index ); +static pascal void PaMac_InputCompletionProc(SPBPtr recParams); +static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCmd); +static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index ); +long PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); +static int Mac_IsVirtualMemoryOn( void ); +static void PToCString(unsigned char* inString, char* outString); +static void CToPString(char *inString, unsigned char* outString); +char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf ); +char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf ); +int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf ); +int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf ); +int MultiBuffer_IsWriteable( MultiBuffer *mbuf ); +int MultiBuffer_IsReadable( MultiBuffer *mbuf ); +void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf ); +void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf ); + +/************************************************************************* +** Simple FIFO index control for multiple buffers. +** Read and Write indices range from 0 to 2N-1. +** This allows us to distinguish between full and empty. +*/ +char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf ) +{ + return mbuf->buffers[mbuf->nextWrite % mbuf->numBuffers]; +} +char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf ) +{ + return mbuf->buffers[mbuf->nextRead % mbuf->numBuffers]; +} +int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf ) +{ + return mbuf->nextRead % mbuf->numBuffers; +} +int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf ) +{ + return mbuf->nextWrite % mbuf->numBuffers; +} + +int MultiBuffer_IsWriteable( MultiBuffer *mbuf ) +{ + int bufsFull = mbuf->nextWrite - mbuf->nextRead; + if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers); + return (bufsFull < mbuf->numBuffers); +} +int MultiBuffer_IsReadable( MultiBuffer *mbuf ) +{ + int bufsFull = mbuf->nextWrite - mbuf->nextRead; + if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers); + return (bufsFull > 0); +} +void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf ) +{ + int temp = mbuf->nextRead + 1; + mbuf->nextRead = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp; +} +void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf ) +{ + int temp = mbuf->nextWrite + 1; + mbuf->nextWrite = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp; +} + +/************************************************************************* +** String Utility by Chris Rolfe +*/ +static void PToCString(unsigned char* inString, char* outString) +{ + long i; + for(i=0; i 255) + len = 255; + + /* Length is stored in first char of Pascal string */ + outString[0] = (unsigned char)len; + for(i=0; isampleRates; + if( (rates != NULL) ) free( rates ); /* MEM_011 */ + dev->sampleRates = NULL; + if( dev->name != NULL ) free( (void *) dev->name ); /* MEM_010 */ + dev->name = NULL; + } + sNumDevices = 0; + return paNoError; +} + +/************************************************************************* + PaHost_Init() is the library initialization function - call this before + using the library. +*/ +PaError PaHost_Init( void ) +{ + PaError err; + NumVersionVariant version; + + version.parts = SndSoundManagerVersion(); + DBUG(("SndSoundManagerVersion = 0x%x\n", version.whole)); + + /* Have we already initialized the device info? */ + err = (PaError) Pa_CountDevices(); + if( err < 0 ) return err; + else return paNoError; +} + +/************************************************************************* + PaMac_ScanOutputDevices() queries the properties of all output devices. +*/ +static PaError PaMac_ScanOutputDevices( void ) +{ + PaError err; + Component identifier=0; + ComponentDescription criteria = { kSoundOutputDeviceType, 0, 0, 0, 0 }; + long numComponents, i; + + /* Search the system linked list for output components */ + numComponents = CountComponents (&criteria); + identifier = 0; + sDefaultOutputDeviceID = sNumDevices; /* FIXME - query somehow */ + for (i = 0; i < numComponents; i++) + { + /* passing nil returns first matching component. */ + identifier = FindNextComponent( identifier, &criteria); + sDevices[sNumDevices].pad_Identifier = identifier; + + /* Set up for default OUTPUT devices. */ + err = PaMac_QueryOutputDeviceInfo( identifier, &sDevices[sNumDevices] ); + if( err < 0 ) return err; + else sNumDevices++; + + } + + return paNoError; +} + +/************************************************************************* + PaMac_ScanInputDevices() queries the properties of all input devices. +*/ +static PaError PaMac_ScanInputDevices( void ) +{ + Str255 deviceName; + int count; + Handle iconHandle; + PaError err; + OSErr oserr; + count = 1; + sDefaultInputDeviceID = sNumDevices; /* FIXME - query somehow */ /* PLB20010415 - was setting sDefaultOutputDeviceID */ + while(true) + { + /* Thanks Chris Rolfe and Alberto Ricci for this trick. */ + oserr = SPBGetIndexedDevice(count++, deviceName, &iconHandle); + DBUG(("PaMac_ScanInputDevices: SPBGetIndexedDevice returned %d\n", oserr )); +#if 1 + /* PLB20010415 - was giving error for anything other than siBadSoundInDevice, but some Macs may return other errors! */ + if(oserr != noErr) break; /* Some type of error is expected when count > devices */ +#else + if(oserr == siBadSoundInDevice) + { /* it's expected when count > devices */ + oserr = noErr; + break; + } + if(oserr != noErr) + { + ERR_RPT(("ERROR: SPBGetIndexedDevice(%d,,) returned %d\n", count-1, oserr )); + sPaHostError = oserr; + return paHostError; + } +#endif + DisposeHandle(iconHandle); /* Don't need the icon */ + + err = PaMac_QueryInputDeviceInfo( deviceName, &sDevices[sNumDevices] ); + DBUG(("PaMac_ScanInputDevices: PaMac_QueryInputDeviceInfo returned %d\n", err )); + if( err < 0 ) return err; + else if( err == 1 ) sNumDevices++; + } + + return paNoError; +} + +/* Sample rate info returned by using siSampleRateAvailable selector in SPBGetDeviceInfo() */ +/* Thanks to Chris Rolfe for help with this query. */ +#pragma options align=mac68k +typedef struct +{ + int16 numRates; + UnsignedFixed (**rates)[]; /* Handle created by SPBGetDeviceInfo */ +} +SRateInfo; +#pragma options align=reset + +/************************************************************************* +** PaMac_QueryOutputDeviceInfo() +** Query information about a named output device. +** Clears contents of ipad and writes info based on queries. +** Return one if OK, +** zero if device cannot be used, +** or negative error. +*/ +static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad ) +{ + int len; + OSErr err; + PaDeviceInfo *dev = &ipad->pad_Info; + SRateInfo srinfo = {0}; + int numRates; + ComponentDescription tempD; + Handle nameH=nil, infoH=nil, iconH=nil; + + memset( ipad, 0, sizeof(internalPortAudioDevice) ); + + dev->structVersion = 1; + dev->maxInputChannels = 0; + dev->maxOutputChannels = 2; + dev->nativeSampleFormats = paInt16; /* FIXME - query to see if 24 or 32 bit data can be handled. */ + + /* Get sample rates supported. */ + err = GetSoundOutputInfo(identifier, siSampleRateAvailable, (Ptr) &srinfo); + if(err != noErr) + { + ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetSoundOutputInfo siSampleRateAvailable returned %d\n", err )); + goto error; + } + numRates = srinfo.numRates; + DBUG(("PaMac_QueryOutputDeviceInfo: srinfo.numRates = %d\n", srinfo.numRates )); + if( numRates == 0 ) + { + dev->numSampleRates = -1; + numRates = 2; + } + else + { + dev->numSampleRates = numRates; + } + dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates ); + if(dev->sampleRates == NULL) + { + DBUG(("PaMac_QueryOutputDeviceInfo: PaMac_GetSampleRatesFromHandle alloc failed.\n")); + return paInsufficientMemory; + } + + /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */ + DisposeHandle((Handle) srinfo.rates); + + /* Device name */ + /* we pass an existing handle for the component name; + we don't care about the info (type, subtype, etc.) or icon, so set them to nil */ + DBUG(("PaMac_QueryOutputDeviceInfo: get component name.\n")); + infoH = nil; + iconH = nil; + nameH = NewHandle(0); + if(nameH == nil) return paInsufficientMemory; + err = GetComponentInfo(identifier, &tempD, nameH, infoH, iconH); + if (err) + { + ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetComponentInfo returned %d\n", err )); + goto error; + } + /* Cast as uchar* so that we get a proper pascal string length. */ + len = ((unsigned char *)(*nameH))[0] + 1; /* PLB20020612 - fix allocation error on Mac 8600 */ + DBUG(("PaMac_QueryOutputDeviceInfo: new len = %d\n", len )); + + dev->name = (char *) malloc(len); /* MEM_010 */ + if( dev->name == NULL ) + { + DisposeHandle(nameH); + return paInsufficientMemory; + } + else + { + PToCString((unsigned char *)(*nameH), (char *) dev->name); + DisposeHandle(nameH); + } + + DBUG(("PaMac_QueryOutputDeviceInfo: dev->name = %s\n", dev->name )); + return paNoError; + +error: + sPaHostError = err; + return paHostError; + +} + +/************************************************************************* +** PaMac_QueryInputDeviceInfo() +** Query information about a named input device. +** Clears contents of ipad and writes info based on queries. +** Return one if OK, +** zero if device cannot be used, +** or negative error. +*/ +static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad ) +{ + PaError result = paNoError; + int len; + OSErr err; + long mRefNum = 0; + long tempL; + int16 tempS; + Fixed tempF; + PaDeviceInfo *dev = &ipad->pad_Info; + SRateInfo srinfo = {0}; + int numRates; + + memset( ipad, 0, sizeof(internalPortAudioDevice) ); + dev->maxOutputChannels = 0; + + /* Open device based on name. If device is in use, it may not be able to open in write mode. */ + err = SPBOpenDevice( deviceName, siWritePermission, &mRefNum); + if (err) + { + /* If device is in use, it may not be able to open in write mode so try read mode. */ + err = SPBOpenDevice( deviceName, siReadPermission, &mRefNum); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBOpenDevice returned %d\n", err )); + sPaHostError = err; + return paHostError; + } + } + + /* Define macros for printing out device info. */ +#define PrintDeviceInfo(selector,var) \ + err = SPBGetDeviceInfo(mRefNum, selector, (Ptr) &var); \ + if (err) { \ + DBUG(("query %s failed\n", #selector )); \ + }\ + else { \ + DBUG(("query %s = 0x%x\n", #selector, var )); \ + } + + PrintDeviceInfo( siContinuous, tempS ); + PrintDeviceInfo( siAsync, tempS ); + PrintDeviceInfo( siNumberChannels, tempS ); + PrintDeviceInfo( siSampleSize, tempS ); + PrintDeviceInfo( siSampleRate, tempF ); + PrintDeviceInfo( siChannelAvailable, tempS ); + PrintDeviceInfo( siActiveChannels, tempL ); + PrintDeviceInfo( siDeviceBufferInfo, tempL ); + + err = SPBGetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL); + if (err == 0) DBUG(("%s = 0x%x\n", "siActiveChannels", tempL )); + /* Can we use this device? */ + err = SPBGetDeviceInfo(mRefNum, siAsync, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siAsync returned %d\n", err )); + goto error; + } + if( tempS == 0 ) goto useless; /* Does not support async recording so forget about it. */ + + err = SPBGetDeviceInfo(mRefNum, siChannelAvailable, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siChannelAvailable returned %d\n", err )); + goto error; + } + dev->maxInputChannels = tempS; + + /* Get sample rates supported. */ + err = SPBGetDeviceInfo(mRefNum, siSampleRateAvailable, (Ptr) &srinfo); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleRateAvailable returned %d\n", err )); + goto error; + } + + numRates = srinfo.numRates; + DBUG(("numRates = 0x%x\n", numRates )); + if( numRates == 0 ) + { + dev->numSampleRates = -1; + numRates = 2; + } + else + { + dev->numSampleRates = numRates; + } + dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates ); + /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */ + DisposeHandle((Handle) srinfo.rates); + + /* Get size of device buffer. */ + err = SPBGetDeviceInfo(mRefNum, siDeviceBufferInfo, (Ptr) &tempL); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siDeviceBufferInfo returned %d\n", err )); + goto error; + } + ipad->pad_DeviceBufferSize = tempL; + DBUG(("siDeviceBufferInfo = %d\n", tempL )); + + /* Set format based on sample size. */ + err = SPBGetDeviceInfo(mRefNum, siSampleSize, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleSize returned %d\n", err )); + goto error; + } + switch( tempS ) + { + case 0x0020: + dev->nativeSampleFormats = paInt32; /* FIXME - warning, code probably won't support this! */ + break; + case 0x0010: + default: /* FIXME - What about other formats? */ + dev->nativeSampleFormats = paInt16; + break; + } + DBUG(("nativeSampleFormats = %d\n", dev->nativeSampleFormats )); + + /* Device name */ + len = deviceName[0] + 1; /* Get length of Pascal string */ + dev->name = (char *) malloc(len); /* MEM_010 */ + if( dev->name == NULL ) + { + result = paInsufficientMemory; + goto cleanup; + } + PToCString(deviceName, (char *) dev->name); + DBUG(("deviceName = %s\n", dev->name )); + result = (PaError) 1; + /* All done so close up device. */ +cleanup: + if( mRefNum ) SPBCloseDevice(mRefNum); + return result; + +error: + if( mRefNum ) SPBCloseDevice(mRefNum); + sPaHostError = err; + return paHostError; + +useless: + if( mRefNum ) SPBCloseDevice(mRefNum); + return (PaError) 0; +} + +/************************************************************************* +** Allocate a double array and fill it with listed sample rates. +*/ +static double * PaMac_GetSampleRatesFromHandle ( int numRates, Handle h ) +{ + OSErr err = noErr; + SInt8 hState; + int i; + UnsignedFixed *fixedRates; + double *rates = (double *) malloc( numRates * sizeof(double) ); /* MEM_011 */ + if( rates == NULL ) return NULL; + /* Save and restore handle state as suggested by TechNote at: + http://developer.apple.com/technotes/tn/tn1122.html + */ + hState = HGetState (h); + if (!(err = MemError ())) + { + HLock (h); + if (!(err = MemError ( ))) + { + fixedRates = (UInt32 *) *h; + for( i=0; i= Pa_CountDevices()) ) return NULL; + return &sDevices[id].pad_Info; +} +/*************************************************************************/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + return sDefaultInputDeviceID; +} + +/*************************************************************************/ +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + return sDefaultOutputDeviceID; +} + +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +static void PaMac_StartLoadCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + UnsignedWide widePad; + if( pahsc == NULL ) return; + /* Query system timer for usage analysis and to prevent overuse of CPU. */ + Microseconds( &widePad ); + pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad ); +} + +/****************************************************************************** +** Measure fractional CPU load based on real-time it took to calculate +** buffers worth of output. +*/ +/**************************************************************************/ +static void PaMac_EndLoadCalculation( internalPortAudioStream *past ) +{ + UnsignedWide widePad; + UInt64 currentCount; + long usecsElapsed; + double newUsage; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + + /* Measure CPU utilization during this callback. Note that this calculation + ** assumes that we had the processor the whole time. + */ +#define LOWPASS_COEFFICIENT_0 (0.95) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + Microseconds( &widePad ); + currentCount = UnsignedWideToUInt64( widePad ); + + usecsElapsed = (long) U64Subtract(currentCount, pahsc->pahsc_EntryCount); + + /* Use inverse because it is faster than the divide. */ + newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer; + + past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + +} + +/*********************************************************************** +** Called by Pa_StartStream() +*/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + pahsc->pahsc_IsRecording = 0; + pahsc->pahsc_StopRecording = 0; + pahsc->pahsc_InputMultiBuffer.nextWrite = 0; + pahsc->pahsc_InputMultiBuffer.nextRead = 0; + return PaMac_RecordNext( past ); +} + +/*********************************************************************** +** Called by Pa_StopStream(). +** May be called during error recovery or cleanup code +** so protect against NULL pointers. +*/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ + int32 timeOutMsec; + PaError result = paNoError; + OSErr err = 0; + long mRefNum; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + + (void) abort; + + mRefNum = pahsc->pahsc_InputRefNum; + + DBUG(("PaHost_StopInput: mRefNum = %d\n", mRefNum )); + if( mRefNum ) + { + DBUG(("PaHost_StopInput: pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording )); + if( pahsc->pahsc_IsRecording ) + { + /* PLB20010420 - Fix TIMEOUT in record mode. */ + pahsc->pahsc_StopRecording = 1; /* Request that we stop recording. */ + err = SPBStopRecording(mRefNum); + DBUG(("PaHost_StopInput: then pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording )); + + /* Calculate timeOut longer than longest time it could take to play one buffer. */ + timeOutMsec = (int32) ((1500.0 * pahsc->pahsc_FramesPerHostBuffer) / past->past_SampleRate); + /* Keep querying sound channel until it is no longer busy playing. */ + while( !err && pahsc->pahsc_IsRecording && (timeOutMsec > 0)) + { + Pa_Sleep(20); + timeOutMsec -= 20; + } + if( timeOutMsec <= 0 ) + { + ERR_RPT(("PaHost_StopInput: timed out!\n")); + return paTimedOut; + } + } + } + if( err ) + { + sPaHostError = err; + result = paHostError; + } + + DBUG(("PaHost_StopInput: finished.\n", mRefNum )); + return result; +} + +/***********************************************************************/ +static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader ) +{ + sndHeader->numChannels = past->past_NumOutputChannels; + sndHeader->sampleRate = DoubleToUnsignedFixed(past->past_SampleRate); + sndHeader->loopStart = 0; + sndHeader->loopEnd = 0; + sndHeader->encode = cmpSH; + sndHeader->baseFrequency = kMiddleC; + sndHeader->markerChunk = nil; + sndHeader->futureUse2 = nil; + sndHeader->stateVars = nil; + sndHeader->leftOverSamples = nil; + sndHeader->compressionID = 0; + sndHeader->packetSize = 0; + sndHeader->snthID = 0; + sndHeader->sampleSize = 8 * sizeof(int16); // FIXME - might be 24 or 32 bits some day; + sndHeader->sampleArea[0] = 0; + sndHeader->format = kSoundNotCompressed; +} + +static void SetFramesDone( PaHostSoundControl *pahsc, PaTimestamp framesDone ) +{ + UnsignedWide now; + Microseconds( &now ); + pahsc->pahsc_NumFramesDone = framesDone; + pahsc->pahsc_WhenFramesDoneIncremented = UnsignedWideToUInt64( now ); +} + +/***********************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + SndCommand pauseCommand; + SndCommand resumeCommand; + int i; + OSErr error; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + if( pahsc->pahsc_Channel == NULL ) return paInternalError; + + past->past_StopSoon = 0; + past->past_IsActive = 1; + pahsc->pahsc_NumOutsQueued = 0; + pahsc->pahsc_NumOutsPlayed = 0; + + SetFramesDone( pahsc, 0.0 ); + + /* Pause channel so it does not do back ground processing while we are still filling the queue. */ + pauseCommand.cmd = pauseCmd; + pauseCommand.param1 = pauseCommand.param2 = 0; + error = SndDoCommand (pahsc->pahsc_Channel, &pauseCommand, true); + if (noErr != error) goto exit; + + /* Queue all of the buffers so we start off full. */ + for (i = 0; ipahsc_NumHostBuffers; i++) + { + PaMac_PlayNext( past, i ); + } + + /* Resume channel now that the queue is full. */ + resumeCommand.cmd = resumeCmd; + resumeCommand.param1 = resumeCommand.param2 = 0; + error = SndDoImmediate( pahsc->pahsc_Channel, &resumeCommand ); + if (noErr != error) goto exit; + + return paNoError; +exit: + past->past_IsActive = 0; + sPaHostError = error; + ERR_RPT(("Error in PaHost_StartOutput: SndDoCommand returned %d\n", error )); + return paHostError; +} + +/*******************************************************************/ +long PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return (long) (pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer); +} + +/*********************************************************************** +** Called by Pa_StopStream(). +** May be called during error recovery or cleanup code +** so protect against NULL pointers. +*/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + int32 timeOutMsec; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + if( pahsc->pahsc_Channel == NULL ) return paNoError; + + DBUG(("PaHost_StopOutput()\n")); + if( past->past_IsActive == 0 ) return paNoError; + + /* Set flags for callback function to see. */ + if( abort ) past->past_StopNow = 1; + past->past_StopSoon = 1; + /* Calculate timeOut longer than longest time it could take to play all buffers. */ + timeOutMsec = (int32) ((1500.0 * PaHost_GetTotalBufferFrames( past )) / past->past_SampleRate); + /* Keep querying sound channel until it is no longer busy playing. */ + while( past->past_IsActive && (timeOutMsec > 0)) + { + Pa_Sleep(20); + timeOutMsec -= 20; + } + if( timeOutMsec <= 0 ) + { + ERR_RPT(("PaHost_StopOutput: timed out!\n")); + return paTimedOut; + } + else return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + (void) past; /* Prevent unused variable warnings. */ + return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + (void) past; /* Prevent unused variable warnings. */ + (void) abort; /* Prevent unused variable warnings. */ + return paNoError; +} +/***********************************************************************/ +PaError PaHost_StreamActive( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return (PaError) ( past->past_IsActive + pahsc->pahsc_IsRecording ); +} +int Mac_IsVirtualMemoryOn( void ) +{ + long attr; + OSErr result = Gestalt( gestaltVMAttr, &attr ); + DBUG(("gestaltVMAttr : 0x%x\n", attr )); + return ((attr >> gestaltVMHasPagingControl ) & 1); +} + +/******************************************************************* +* Determine number of host Buffers +* and how many User Buffers we can put into each host buffer. +*/ +static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + int32 minNumBuffers; + int32 minFramesPerHostBuffer; + int32 minTotalFrames; + int32 userBuffersPerHostBuffer; + int32 framesPerHostBuffer; + int32 numHostBuffers; + + minFramesPerHostBuffer = pahsc->pahsc_MinFramesPerHostBuffer; + minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7; + DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer )); + + /* Determine number of user buffers based on minimum latency. */ + /* PLB20020417 I used to call Pa_GetMinNumBuffers() which doesn't take into account the + ** variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will + ** gove lower latency when virtual memory is turned off. */ + /* minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); WRONG */ + minNumBuffers = PaMac_GetMinNumBuffers( minFramesPerHostBuffer, past->past_FramesPerUserBuffer, past->past_SampleRate ); + + past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers; + DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); + minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer; + + /* We cannot make the buffers too small because they may not get serviced quickly enough. */ + if( (int32) past->past_FramesPerUserBuffer < minFramesPerHostBuffer ) + { + userBuffersPerHostBuffer = + (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) / + past->past_FramesPerUserBuffer; + } + else + { + userBuffersPerHostBuffer = 1; + } + framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer; + + /* Calculate number of host buffers needed. Round up to cover minTotalFrames. */ + numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; + /* Make sure we have enough host buffers. */ + if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS) + { + numHostBuffers = PA_MIN_NUM_HOST_BUFFERS; + } + else + { + /* If we have too many host buffers, try to put more user buffers in a host buffer. */ + while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) + { + userBuffersPerHostBuffer += 1; + framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer; + numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; + } + } + + pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer; + pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer; + pahsc->pahsc_NumHostBuffers = numHostBuffers; + DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); +} + +/*******************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + OSErr err; + PaError result = paHostError; + PaHostSoundControl *pahsc; + int i; + /* Allocate and initialize host data. */ + pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); + if( pahsc == NULL ) + { + return paInsufficientMemory; + } + past->past_DeviceData = (void *) pahsc; + + /* If recording, and virtual memory is turned on, then use bigger buffers to prevent glitches. */ + if( (past->past_NumInputChannels > 0) && Mac_IsVirtualMemoryOn() ) + { + pahsc->pahsc_MinFramesPerHostBuffer = MAC_VIRTUAL_FRAMES_PER_BUFFER; + } + else + { + pahsc->pahsc_MinFramesPerHostBuffer = MAC_PHYSICAL_FRAMES_PER_BUFFER; + } + + PaHost_CalcNumHostBuffers( past ); + + /* Setup constants for CPU load measurement. */ + pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer); + + /* ------------------ OUTPUT */ + if( past->past_NumOutputChannels > 0 ) + { + /* Create sound channel to which we can send commands. */ + pahsc->pahsc_Channel = 0L; + err = SndNewChannel(&pahsc->pahsc_Channel, sampledSynth, 0, nil); /* FIXME - use kUseOptionalOutputDevice if not default. */ + if(err != 0) + { + ERR_RPT(("Error in PaHost_OpenStream: SndNewChannel returned 0x%x\n", err )); + goto error; + } + + /* Install our callback function pointer straight into the sound channel structure */ + /* Use new CARBON name for callback procedure. */ +#if TARGET_API_MAC_CARBON + pahsc->pahsc_OutputCompletionProc = NewSndCallBackUPP(PaMac_OutputCompletionProc); +#else + pahsc->pahsc_OutputCompletionProc = NewSndCallBackProc(PaMac_OutputCompletionProc); +#endif + + pahsc->pahsc_Channel->callBack = pahsc->pahsc_OutputCompletionProc; + + pahsc->pahsc_BytesPerOutputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumOutputChannels * sizeof(int16); + for (i = 0; ipahsc_NumHostBuffers; i++) + { + char *buf = (char *)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer); + if (buf == NULL) + { + ERR_RPT(("Error in PaHost_OpenStream: could not allocate output buffer. Size = \n", pahsc->pahsc_BytesPerOutputHostBuffer )); + goto memerror; + } + + PaMac_InitSoundHeader( past, &pahsc->pahsc_SoundHeaders[i] ); + pahsc->pahsc_SoundHeaders[i].samplePtr = buf; + pahsc->pahsc_SoundHeaders[i].numFrames = (unsigned long) pahsc->pahsc_FramesPerHostBuffer; + + } + } +#ifdef SUPPORT_AUDIO_CAPTURE + /* ------------------ INPUT */ + /* Use double buffer scheme that matches output. */ + if( past->past_NumInputChannels > 0 ) + { + int16 tempS; + long tempL; + Fixed tempF; + long mRefNum; + Str255 namePString; +#if TARGET_API_MAC_CARBON + pahsc->pahsc_InputCompletionProc = NewSICompletionUPP((SICompletionProcPtr)PaMac_InputCompletionProc); +#else + pahsc->pahsc_InputCompletionProc = NewSICompletionProc((ProcPtr)PaMac_InputCompletionProc); +#endif + pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumInputChannels * sizeof(int16); + for (i = 0; ipahsc_NumHostBuffers; i++) + { + char *buf = (char *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer); + if ( buf == NULL ) + { + ERR_RPT(("PaHost_OpenStream: could not allocate input buffer. Size = \n", pahsc->pahsc_BytesPerInputHostBuffer )); + goto memerror; + } + pahsc->pahsc_InputMultiBuffer.buffers[i] = buf; + } + pahsc->pahsc_InputMultiBuffer.numBuffers = pahsc->pahsc_NumHostBuffers; + + // err = SPBOpenDevice( (const unsigned char *) &noname, siWritePermission, &mRefNum); + CToPString((char *)sDevices[past->past_InputDeviceID].pad_Info.name, namePString); + err = SPBOpenDevice(namePString, siWritePermission, &mRefNum); + + if (err) goto error; + pahsc->pahsc_InputRefNum = mRefNum; + DBUG(("PaHost_OpenStream: mRefNum = %d\n", mRefNum )); + + /* Set input device characteristics. */ + tempS = 1; + err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siContinuous returned %d\n", err )); + goto error; + } + + tempL = 0x03; + err = SPBSetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL); + if (err) + { + DBUG(("PaHost_OpenStream: setting siActiveChannels returned 0x%x. Error ignored.\n", err )); + } + + /* PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. */ + tempS = past->past_NumInputChannels; + err = SPBSetDeviceInfo(mRefNum, siNumberChannels, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siNumberChannels returned %d\n", err )); + goto error; + } + + tempF = ((unsigned long)past->past_SampleRate) << 16; + err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF); + if (err) + { + ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siSampleRate returned %d\n", err )); + goto error; + } + + /* Setup record-parameter block */ + pahsc->pahsc_InputParams.inRefNum = mRefNum; + pahsc->pahsc_InputParams.milliseconds = 0; // not used + pahsc->pahsc_InputParams.completionRoutine = pahsc->pahsc_InputCompletionProc; + pahsc->pahsc_InputParams.interruptRoutine = 0; + pahsc->pahsc_InputParams.userLong = (long) past; + pahsc->pahsc_InputParams.unused1 = 0; + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + DBUG(("PaHost_OpenStream: complete.\n")); + return paNoError; + +error: + PaHost_CloseStream( past ); + ERR_RPT(("PaHost_OpenStream: sPaHostError = 0x%x.\n", err )); + sPaHostError = err; + return paHostError; + +memerror: + PaHost_CloseStream( past ); + return paInsufficientMemory; +} + +/*********************************************************************** +** Called by Pa_CloseStream(). +** May be called during error recovery or cleanup code +** so protect against NULL pointers. +*/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaError result = paNoError; + OSErr err = 0; + int i; + PaHostSoundControl *pahsc; + + DBUG(("PaHost_CloseStream( 0x%x )\n", past )); + + if( past == NULL ) return paBadStreamPtr; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + + if( past->past_NumOutputChannels > 0 ) + { + /* TRUE means flush now instead of waiting for quietCmd to be processed. */ + if( pahsc->pahsc_Channel != NULL ) SndDisposeChannel(pahsc->pahsc_Channel, TRUE); + { + for (i = 0; ipahsc_NumHostBuffers; i++) + { + Ptr p = (Ptr) pahsc->pahsc_SoundHeaders[i].samplePtr; + if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerOutputHostBuffer ); + } + } + } + + if( past->past_NumInputChannels > 0 ) + { + if( pahsc->pahsc_InputRefNum ) + { + err = SPBCloseDevice(pahsc->pahsc_InputRefNum); + pahsc->pahsc_InputRefNum = 0; + if( err ) + { + sPaHostError = err; + result = paHostError; + } + } + { + for (i = 0; ipahsc_InputMultiBuffer.numBuffers; i++) + { + Ptr p = (Ptr) pahsc->pahsc_InputMultiBuffer.buffers[i]; + if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerInputHostBuffer ); + } + } + } + + past->past_DeviceData = NULL; + PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); + + DBUG(("PaHost_CloseStream: complete.\n", past )); + return result; +} +/*************************************************************************/ +int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +{ +/* We use the MAC_VIRTUAL_FRAMES_PER_BUFFER because we might be recording. +** This routine doesn't have enough information to determine the best value +** and is being depracated. */ + return PaMac_GetMinNumBuffers( MAC_VIRTUAL_FRAMES_PER_BUFFER, framesPerUserBuffer, sampleRate ); +} +/*************************************************************************/ +static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerUserBuffer, double sampleRate ) +{ + int minUserPerHost = ( minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer; + int numBufs = PA_MIN_NUM_HOST_BUFFERS * minUserPerHost; + if( numBufs < PA_MIN_NUM_HOST_BUFFERS ) numBufs = PA_MIN_NUM_HOST_BUFFERS; + (void) sampleRate; + return numBufs; +} + +/*************************************************************************/ +void Pa_Sleep( int32 msec ) +{ + EventRecord event; + int32 sleepTime, endTime; + /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */ + sleepTime = ((msec * 60) + 999) / 1000; + if( sleepTime < 1 ) sleepTime = 1; + endTime = TickCount() + sleepTime; + do + { + DBUGX(("Sleep for %d ticks.\n", sleepTime )); + /* Use WaitNextEvent() to sleep without getting events. */ + /* PLB20010907 - Pass unused event to WaitNextEvent instead of NULL to prevent + * Mac OSX crash. Thanks Dominic Mazzoni. */ + WaitNextEvent( 0, &event, sleepTime, NULL ); + sleepTime = endTime - TickCount(); + } + while( sleepTime > 0 ); +} +/*************************************************************************/ +int32 Pa_GetHostError( void ) +{ + int32 err = sPaHostError; + sPaHostError = 0; + return err; +} + +/************************************************************************* + * Allocate memory that can be accessed in real-time. + * This may need to be held in physical memory so that it is not + * paged to virtual memory. + * This call MUST be balanced with a call to PaHost_FreeFastMemory(). + */ +void *PaHost_AllocateFastMemory( long numBytes ) +{ + void *addr = NewPtrClear( numBytes ); + if( (addr == NULL) || (MemError () != 0) ) return NULL; + +#if (TARGET_API_MAC_CARBON == 0) + if( HoldMemory( addr, numBytes ) != noErr ) + { + DisposePtr( (Ptr) addr ); + return NULL; + } +#endif + return addr; +} + +/************************************************************************* + * Free memory that could be accessed in real-time. + * This call MUST be balanced with a call to PaHost_AllocateFastMemory(). + */ +void PaHost_FreeFastMemory( void *addr, long numBytes ) +{ + if( addr == NULL ) return; +#if TARGET_API_MAC_CARBON + (void) numBytes; +#else + UnholdMemory( addr, numBytes ); +#endif + DisposePtr( (Ptr) addr ); +} + +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + PaTimestamp framesDone1; + PaTimestamp framesDone2; + UInt64 whenIncremented; + UnsignedWide now; + UInt64 now64; + long microsElapsed; + long framesElapsed; + + PaHostSoundControl *pahsc; + internalPortAudioStream *past = (internalPortAudioStream *) stream; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + +/* Capture information from audio thread. + * We have to be careful that we don't get interrupted in the middle. + * So we grab the pahsc_NumFramesDone twice and make sure it didn't change. + */ + do + { + framesDone1 = pahsc->pahsc_NumFramesDone; + whenIncremented = pahsc->pahsc_WhenFramesDoneIncremented; + framesDone2 = pahsc->pahsc_NumFramesDone; + } while( framesDone1 != framesDone2 ); + + /* Calculate how many microseconds have elapsed and convert to frames. */ + Microseconds( &now ); + now64 = UnsignedWideToUInt64( now ); + microsElapsed = U64Subtract( now64, whenIncremented ); + framesElapsed = microsElapsed * past->past_SampleRate * 0.000001; + + return framesDone1 + framesElapsed; +} + +/************************************************************************** +** Callback for Input, SPBRecord() +*/ +int gRecordCounter = 0; +int gPlayCounter = 0; +pascal void PaMac_InputCompletionProc(SPBPtr recParams) +{ + PaError result = paNoError; + int finished = 1; + internalPortAudioStream *past; + PaHostSoundControl *pahsc; + + gRecordCounter += 1; /* debug hack to see if engine running */ + + /* Get our PA data from Mac structure. */ + past = (internalPortAudioStream *) recParams->userLong; + if( past == NULL ) return; + + if( past->past_Magic != PA_MAGIC ) + { + AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, past", (long) past ); + AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, magic", (long) past->past_Magic ); + goto error; + } + pahsc = (PaHostSoundControl *) past->past_DeviceData; + past->past_NumCallbacks += 1; + + /* Have we been asked to stop recording? */ + if( (recParams->error == abortErr) || pahsc->pahsc_StopRecording ) goto error; + + /* If there are no output channels, then we need to call the user callback function from here. + * Otherwise we will call the user code during the output completion routine. + */ + if(past->past_NumOutputChannels == 0) + { + SetFramesDone( pahsc, + pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer ); + result = PaMac_CallUserLoop( past, NULL ); + } + + /* Did user code ask us to stop? If not, issue another recording request. */ + if( (result == paNoError) && (pahsc->pahsc_StopRecording == 0) ) + { + result = PaMac_RecordNext( past ); + if( result != paNoError ) pahsc->pahsc_IsRecording = 0; + } + else goto error; + + return; + +error: + pahsc->pahsc_IsRecording = 0; + pahsc->pahsc_StopRecording = 0; + return; +} + +/*********************************************************************** +** Called by either input or output completion proc. +** Grabs input data if any present, and calls PA conversion code, +** that in turn calls user code. +*/ +static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr ) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + int16 *inPtr = NULL; + int i; + + + /* Advance read index for sound input FIFO here, independantly of record/write process. */ + if(past->past_NumInputChannels > 0) + { + if( MultiBuffer_IsReadable( &pahsc->pahsc_InputMultiBuffer ) ) + { + inPtr = (int16 *) MultiBuffer_GetNextReadBuffer( &pahsc->pahsc_InputMultiBuffer ); + MultiBuffer_AdvanceReadIndex( &pahsc->pahsc_InputMultiBuffer ); + } + } + + /* Call user code enough times to fill buffer. */ + if( (inPtr != NULL) || (outPtr != NULL) ) + { + PaMac_StartLoadCalculation( past ); /* CPU usage */ + +#ifdef PA_MAX_USAGE_ALLOWED + /* If CPU usage exceeds limit, skip user callback to prevent hanging CPU. */ + if( past->past_Usage > PA_MAX_USAGE_ALLOWED ) + { + past->past_FrameCount += (PaTimestamp) pahsc->pahsc_FramesPerHostBuffer; + } + else +#endif + { + + for( i=0; ipahsc_UserBuffersPerHostBuffer; i++ ) + { + result = (PaError) Pa_CallConvertInt16( past, inPtr, outPtr ); + if( result != 0) + { + /* Recording might be in another process, so tell it to stop with a flag. */ + pahsc->pahsc_StopRecording = pahsc->pahsc_IsRecording; + break; + } + /* Advance sample pointers. */ + if(inPtr != NULL) inPtr += past->past_FramesPerUserBuffer * past->past_NumInputChannels; + if(outPtr != NULL) outPtr += past->past_FramesPerUserBuffer * past->past_NumOutputChannels; + } + } + + PaMac_EndLoadCalculation( past ); + } + return result; +} + +/*********************************************************************** +** Setup next recording buffer in FIFO and issue recording request to Snd Input Manager. +*/ +static PaError PaMac_RecordNext( internalPortAudioStream *past ) +{ + PaError result = paNoError; + OSErr err; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + /* Get pointer to next buffer to record into. */ + pahsc->pahsc_InputParams.bufferPtr = MultiBuffer_GetNextWriteBuffer( &pahsc->pahsc_InputMultiBuffer ); + + /* Advance write index if there is room. Otherwise keep writing same buffer. */ + if( MultiBuffer_IsWriteable( &pahsc->pahsc_InputMultiBuffer ) ) + { + MultiBuffer_AdvanceWriteIndex( &pahsc->pahsc_InputMultiBuffer ); + } + + AddTraceMessage("PaMac_RecordNext: bufferPtr", (long) pahsc->pahsc_InputParams.bufferPtr ); + AddTraceMessage("PaMac_RecordNext: nextWrite", pahsc->pahsc_InputMultiBuffer.nextWrite ); + + /* Setup parameters and issue an asynchronous recording request. */ + pahsc->pahsc_InputParams.bufferLength = pahsc->pahsc_BytesPerInputHostBuffer; + pahsc->pahsc_InputParams.count = pahsc->pahsc_BytesPerInputHostBuffer; + err = SPBRecord(&pahsc->pahsc_InputParams, true); + if( err ) + { + AddTraceMessage("PaMac_RecordNext: SPBRecord error ", err ); + sPaHostError = err; + result = paHostError; + } + else + { + pahsc->pahsc_IsRecording = 1; + } + return result; +} + +/************************************************************************** +** Callback for Output Playback() +** Return negative error, 0 to continue, 1 to stop. +*/ +long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index ) +{ + PaHostSoundControl *pahsc; + long result = 0; + int finished = 1; + char *outPtr; + + gPlayCounter += 1; /* debug hack */ + + past->past_NumCallbacks += 1; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return -1; + /* Are we nested?! */ + if( pahsc->pahsc_IfInsideCallback ) return 0; + pahsc->pahsc_IfInsideCallback = 1; + /* Get pointer to buffer to fill. */ + outPtr = pahsc->pahsc_SoundHeaders[index].samplePtr; + /* Combine with any sound input, and call user callback. */ + result = PaMac_CallUserLoop( past, (int16 *) outPtr ); + + pahsc->pahsc_IfInsideCallback = 0; + return result; +} + +/************************************************************************************* +** Called by SoundManager when ready for another buffer. +*/ +static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCallBackCmd) +{ + internalPortAudioStream *past; + PaHostSoundControl *pahsc; + (void) theChannel; + (void) theCallBackCmd; + + /* Get our data from Mac structure. */ + past = (internalPortAudioStream *) theCallBackCmd->param2; + if( past == NULL ) return; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + pahsc->pahsc_NumOutsPlayed += 1; + + SetFramesDone( pahsc, + pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer ); + + PaMac_BackgroundManager( past, theCallBackCmd->param1 ); +} + +/*******************************************************************/ +static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index ) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + /* Has someone asked us to abort by calling Pa_AbortStream()? */ + if( past->past_StopNow ) + { + SndCommand command; + /* Clear the queue of any pending commands. */ + command.cmd = flushCmd; + command.param1 = command.param2 = 0; + SndDoImmediate( pahsc->pahsc_Channel, &command ); + /* Then stop currently playing buffer, if any. */ + command.cmd = quietCmd; + SndDoImmediate( pahsc->pahsc_Channel, &command ); + past->past_IsActive = 0; + } + /* Has someone asked us to stop by calling Pa_StopStream() + * OR has a user callback returned '1' to indicate finished. + */ + else if( past->past_StopSoon ) + { + if( (pahsc->pahsc_NumOutsQueued - pahsc->pahsc_NumOutsPlayed) <= 0 ) + { + past->past_IsActive = 0; /* We're finally done. */ + } + } + else + { + PaMac_PlayNext( past, index ); + } + return result; +} + +/************************************************************************************* +** Fill next buffer with sound and queue it for playback. +*/ +static void PaMac_PlayNext ( internalPortAudioStream *past, int index ) +{ + OSErr error; + long result; + SndCommand playCmd; + SndCommand callbackCmd; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + /* If this was the last buffer, or abort requested, then just be done. */ + if ( past->past_StopSoon ) goto done; + + /* Load buffer with sound. */ + result = PaMac_FillNextOutputBuffer ( past, index ); + if( result > 0 ) past->past_StopSoon = 1; /* Stop generating audio but wait until buffers play. */ + else if( result < 0 ) goto done; + + /* Play the next buffer. */ + playCmd.cmd = bufferCmd; + playCmd.param1 = 0; + playCmd.param2 = (long) &pahsc->pahsc_SoundHeaders[ index ]; + error = SndDoCommand (pahsc->pahsc_Channel, &playCmd, true ); + if( error != noErr ) goto gotError; + + /* Ask for a callback when it is done. */ + callbackCmd.cmd = callBackCmd; + callbackCmd.param1 = index; + callbackCmd.param2 = (long)past; + error = SndDoCommand (pahsc->pahsc_Channel, &callbackCmd, true ); + if( error != noErr ) goto gotError; + pahsc->pahsc_NumOutsQueued += 1; + + return; + +gotError: + sPaHostError = error; +done: + return; +} diff --git a/lib/portaudio/pa_mac/patest_devinfo.c b/lib/portaudio/pa_mac/patest_devinfo.c new file mode 100644 index 0000000..94e26ed --- /dev/null +++ b/lib/portaudio/pa_mac/patest_devinfo.c @@ -0,0 +1 @@ +/* * Test calling SPBSetDeviceInfo() * Should small parameters less than 4 bytes long be passed through a pointer, or directly? * * According to an Applet TechNote at: * http://developer.apple.com/technotes/tn/tn1048.html * * ------------- begin quote -------------- * Trouble with Void Pointers * The SetSoundOutputInfo, SndSetInfo, and SPBSetDeviceInfo all take a void* as their last parameter. * Because the last parameter of these calls is a void*, it looks as if you need to pass a pointer * to the value you are setting, but this is not the case if what you are passing is 4 bytes or * less in size. If you are passing a parameter that is larger than four bytes, you must pass a * pointer to it. * * If you are calling GetSoundOutputInfo, SndGetInfo, or SPBGetDeviceInfo and * it is returning a value that is 4 bytes in size or less, it does not return a pointer to * that value, it just returns the value. * ------------ end quote ------------------ * * But my experiments on a PowerComputing Mac clone running Mac OS 8.1 indicate that * the TechNote is wrong and that a pointer should be passed to SPBSetDeviceInfo. * * Author: Phil Burk * Copyright 2000 Phil Burk * This code may be used freely for any purpose. */ #include #include #include #include #include /* Mac specific includes */ #include "OSUtils.h" #include #include #include #include #include #include #include #include #include /* Change this to 0 or 1 to use the direct parameter method, or the indirect pointer method. */ #define USE_POINTER (1) /* Debugging output macros. */ #define PRINT(x) { printf x; fflush(stdout); } #define ERR_RPT(x) PRINT(x) #define DBUG(x) PRINT(x) static int TestSettingDeviceInfo( const unsigned char *deviceName ); int main( void ); int main( void ) { unsigned char noname = 0; int result = TestSettingDeviceInfo( (const unsigned char *) &noname ); PRINT(("Test complete. result = %d\n", result )); return 0; } /************************************************************************* ** TestSettingDeviceInfo() ** Query information about a named input device. */ static int TestSettingDeviceInfo( const unsigned char *deviceName ) { OSErr err; long mRefNum = 0; long tempL; short tempS; Fixed tempF; err = SPBOpenDevice( deviceName, siWritePermission, &mRefNum); if (err) { PRINT(("Cound not open device!\n")); return -1; } /* Define macros for printing out device info. */ #define PrintDeviceInfo(selector,var) \ err = SPBGetDeviceInfo(mRefNum, selector, (Ptr) &var); \ if (err) { \ PRINT(("query %s failed\n", #selector )); \ }\ else { \ PRINT(("query %s = 0x%x\n", #selector, var )); \ } PrintDeviceInfo( siContinuous, tempS ); PrintDeviceInfo( siAsync, tempS ); PrintDeviceInfo( siNumberChannels, tempS ); PrintDeviceInfo( siSampleSize, tempS ); PrintDeviceInfo( siSampleRate, tempF ); PrintDeviceInfo( siChannelAvailable, tempS ); PrintDeviceInfo( siActiveChannels, tempL ); PrintDeviceInfo( siDeviceBufferInfo, tempL ); #if USE_POINTER /* Continuous Mode ---------- */ PRINT(("Attempt to set siContinuous to 1 using pointer method.\n")); tempS = 1; err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS); if (err) PRINT(("setting siContinuous using pointer failed\n")); PrintDeviceInfo( siContinuous, tempS ); PRINT(("Attempt to set siContinuous to 0 using pointer method.\n")); tempS = 1; err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS); if (err) PRINT(("setting siContinuous using pointer failed\n")); PrintDeviceInfo( siContinuous, tempS ); #else PRINT(("Attempt to set siContinuous to 1 using direct method.\n")); err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) 1); if (err) PRINT(("setting siContinuous using direct failed\n")); PrintDeviceInfo( siContinuous, tempS ); PRINT(("Attempt to set siContinuous to 0 using direct method.\n")); err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) 0); if (err) PRINT(("setting siContinuous using direct failed\n")); PrintDeviceInfo( siContinuous, tempS ); #endif /* Sample rate ----------- */ #if USE_POINTER PRINT(("Attempt to set siSampleRate to 44100 using pointer method.\n")); tempF = ((unsigned long)44100) << 16; err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF); if (err) PRINT(("setting siSampleRate using pointer failed\n")); tempF = 0; PrintDeviceInfo( siSampleRate, tempF ); PRINT(("Attempt to set siSampleRate to 22050 using pointer method.\n")); tempF = ((unsigned long)22050) << 16; err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF); if (err) PRINT(("setting siSampleRate using pointer failed\n")); tempF = 0; PrintDeviceInfo( siSampleRate, tempF ); #else PRINT(("Attempt to set siSampleRate to 44100 using direct method.\n")); err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) (((unsigned long)44100) << 16)); if (err) PRINT(("setting siSampleRate using direct failed\n")); PrintDeviceInfo( siSampleRate, tempF ); PRINT(("Attempt to set siSampleRate to 22050 using direct method.\n")); err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) (((unsigned long)44100) << 16)); if (err) PRINT(("setting siSampleRate using direct failed\n")); PrintDeviceInfo( siSampleRate, tempF ); #endif /* All done so close up device. */ if( mRefNum ) SPBCloseDevice(mRefNum); return 0; error: if( mRefNum ) SPBCloseDevice(mRefNum); return -1; } \ No newline at end of file diff --git a/lib/portaudio/pa_mac_core/CVS/Entries b/lib/portaudio/pa_mac_core/CVS/Entries new file mode 100644 index 0000000..9c9f721 --- /dev/null +++ b/lib/portaudio/pa_mac_core/CVS/Entries @@ -0,0 +1,3 @@ +/Makefile/1.4/Fri Oct 18 05:38:13 2002// +/pa_mac_core.c/1.9/Mon Sep 22 05:15:18 2003// +D diff --git a/lib/portaudio/pa_mac_core/CVS/Repository b/lib/portaudio/pa_mac_core/CVS/Repository new file mode 100644 index 0000000..21e40f8 --- /dev/null +++ b/lib/portaudio/pa_mac_core/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/pa_mac_core diff --git a/lib/portaudio/pa_mac_core/CVS/Root b/lib/portaudio/pa_mac_core/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/pa_mac_core/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/pa_mac_core/Makefile b/lib/portaudio/pa_mac_core/Makefile new file mode 100644 index 0000000..58e8674 --- /dev/null +++ b/lib/portaudio/pa_mac_core/Makefile @@ -0,0 +1,20 @@ +# Make PortAudio Library for Mac OS X / CoreAudio + +CC = cc + +CFLAGS = -g -I../pa_common -I../pablio +PASRC = ../pa_common/pa_lib.c ../pa_common/pa_convert.c \ + ../pablio/ringbuffer.c pa_mac_core.c +PAINC = ../pa_common/portaudio.h +PAOBJ = ../pa_common/pa_lib.o ../pa_common/pa_convert.o \ + ../pablio/ringbuffer.o pa_mac_core.o + +portaudio.a: $(PAOBJ) + ar ruv portaudio.a $(PAOBJ) + ranlib portaudio.a + +clean: + rm -f portaudio.a *.o + +%.o: %.c $(PAINC) Makefile + $(CC) -c $(CFLAGS) $< -o $@ diff --git a/lib/portaudio/pa_mac_core/pa_mac_core.c b/lib/portaudio/pa_mac_core/pa_mac_core.c new file mode 100644 index 0000000..cfe80a2 --- /dev/null +++ b/lib/portaudio/pa_mac_core/pa_mac_core.c @@ -0,0 +1,1915 @@ +/* + * $Id: pa_mac_core.c,v 1.9 2003/09/22 05:15:18 dmazzoni Exp $ + * pa_mac_core.c + * Implementation of PortAudio for Mac OS X Core Audio + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Authors: Ross Bencina and Phil Burk + * Copyright (c) 1999-2002 Ross Bencina and Phil Burk + * + * Theory of Operation + * + * This code uses the HAL (Hardware Access Layer) of the Apple CoreAudio library. + * This is the layer closes to the hardware. + * The HAL layer only supports the native HW supported sample rates. + * So if the chip only supports 44100 Hz, then the HAL only supports 44100. + * To provide other rates we use the handy Apple AudioConverter which provides + * sample rate conversion, mono-to-stereo conversion, and buffer size adaptation. + * + * There are four modes of operation: + * PA_MODE_OUTPUT_ONLY, + * PA_MODE_INPUT_ONLY, + * PA_MODE_IO_ONE_DEVICE, + * PA_MODE_IO_TWO_DEVICES + * + * The processing pipeline for PA_MODE_IO_ONE_DEVICE is in one thread: + * + * PaOSX_CoreAudioIOCallback() input buffers -> RingBuffer -> input.AudioConverter -> + * PortAudio callback -> output.AudioConverter -> PaOSX_CoreAudioIOCallback() output buffers + * + * For two separate devices, we have to use two separate callbacks. + * We pass data between them using a RingBuffer FIFO. + * The processing pipeline for PA_MODE_IO_TWO_DEVICES is split into two threads: + * + * PaOSX_CoreAudioInputCallback() input buffers -> RingBuffer + * + * RingBuffer -> input.AudioConverter -> + * PortAudio callback -> output.AudioConverter -> PaOSX_CoreAudioIOCallback() output buffers + * + * License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * CHANGE HISTORY: + + 3.29.2001 - Phil Burk - First pass... converted from Window MME code with help from Darren. + 3.30.2001 - Darren Gibbs - Added more support for dynamically querying device info. + 12.7.2001 - Gord Peters - Tweaks to compile on PA V17 and OS X 10.1 + 2.7.2002 - Darren and Phil - fixed isInput so GetProperty works better, + fixed device queries for numChannels and sampleRates, + one CoreAudio device now maps to separate input and output PaDevices, + audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices). + 2.22.2002 - Stephane Letz - Explicit cast needed for compilation with Code Warrior 7 + 3.19.2002 - Phil Burk - Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file. + Return error if opened in mono mode cuz not supported. [Supported 10.12.2002] + Add support for Pa_GetCPULoad(); + Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!) + Check for invalid sample rates and return an error. + Check for getenv("PA_MIN_LATENCY_MSEC") to set latency externally. + Better error checking for invalid channel counts and invalid devices. + 3.29.2002 - Phil Burk - Fixed Pa_GetCPULoad() for small buffers. + 3.31.2002 - Phil Burk - Use getrusage() instead of gettimeofday() for CPU Load calculation. + 10.12.2002 - Phil Burk - Use AudioConverter to allow wide range of sample rates, and mono. + Use FIFO (from pablio/rinbuffer.h) so that we can pull data through converter. + Added PaOSX_FixVolumeScalar() to make iMic audible. + 10.17.2002 - Phil Burk - Support full duplex between two different devices. + Name internal functions PaOSX_* + Dumped useless PA_MIN_LATENCY_MSEC environment variable. + Use kAudioDevicePropertyStreamFormatMatch to determine max channels. + 02.03.2003 - Phil Burk - always use AudioConverters so that we can adapt when format changes. + Synchronize with device when format changes. + +TODO: +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" +#include "ringbuffer.h" + +/************************************************* Constants ********/ +#define SET_DEVICE_BUFFER_SIZE (1) + +/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */ +#define PA_TRACE_RUN (0) +#define PA_TRACE_START_STOP (0) + +#define PA_MIN_LATENCY_MSEC (20) /* FIXME */ +#define MIN_TIMEOUT_MSEC (3000) + +#define PRINT(x) { printf x; fflush(stdout); } +#define PRINT_ERR( msg, err ) PRINT(( msg ": error = 0x%0lX = '%s'\n", (err), ErrorToString(err)) ) +#define DBUG(x) /* PRINT(x) */ +#define DBUGBACK(x) /* if( sMaxBackgroundErrorMessages-- > 0 ) PRINT(x) */ +#define DBUGX(x) + +// define value of isInput passed to CoreAudio routines +#define IS_INPUT (true) +#define IS_OUTPUT (false) + +typedef enum PaDeviceMode +{ + PA_MODE_OUTPUT_ONLY, + PA_MODE_INPUT_ONLY, + PA_MODE_IO_ONE_DEVICE, + PA_MODE_IO_TWO_DEVICES +} PaDeviceMode; + +#define PA_USING_OUTPUT (pahsc->mode != PA_MODE_INPUT_ONLY) +#define PA_USING_INPUT (pahsc->mode != PA_MODE_OUTPUT_ONLY) + +/************************************************************** + * Information needed by PortAudio specific to a CoreAudio device. + */ +typedef struct PaHostInOut_s +{ + AudioDeviceID audioDeviceID; /* CoreAudio specific ID */ + int bytesPerUserNativeBuffer; /* User buffer size in native host format. Depends on numChannels. */ + AudioConverterRef converter; + void *converterBuffer; + int numChannels; +} PaHostInOut; + +/************************************************************** + * Structure for internal host specific stream data. + * This is allocated on a per stream basis. + */ +typedef struct PaHostSoundControl +{ + PaHostInOut input; + PaHostInOut output; + AudioDeviceID primaryDeviceID; + PaDeviceMode mode; + RingBuffer ringBuffer; + char *ringBufferData; + Boolean formatListenerCalled; + /* For measuring CPU utilization. */ + struct rusage entryRusage; + double inverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ +} PaHostSoundControl; + +/************************************************************** + * Structure for internal extended device info query. + * There will be one or two PortAudio devices for each Core Audio device: + * one input and or one output. + */ +typedef struct PaHostDeviceInfo +{ + PaDeviceInfo paInfo; + AudioDeviceID audioDeviceID; +} +PaHostDeviceInfo; + +/************************************************* Shared Data ********/ +/* FIXME - put Mutex around this shared data. */ +static int sNumPaDevices = 0; /* Total number of PaDeviceInfos */ +static int sNumInputDevices = 0; /* Total number of input PaDeviceInfos */ +static int sNumOutputDevices = 0; +static int sNumCoreDevices = 0; +static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs +static PaHostDeviceInfo *sDeviceInfos = NULL; +static int sDefaultInputDeviceID = paNoDevice; +static int sDefaultOutputDeviceID = paNoDevice; +static int sSavedHostError = 0; + +static const double supportedSampleRateRange[] = { 8000.0, 96000.0 }; /* FIXME - go to double HW rate. */ +static const char sMapperSuffixInput[] = " - Input"; +static const char sMapperSuffixOutput[] = " - Output"; + +/* Debug support. */ +//static int sMaxBackgroundErrorMessages = 100; +//static int sCoverageCounter = 1; // used to check code coverage during validation + +/* We index the input devices first, then the output devices. */ +#define LOWEST_INPUT_DEVID (0) +#define HIGHEST_INPUT_DEVID (sNumInputDevices - 1) +#define LOWEST_OUTPUT_DEVID (sNumInputDevices) +#define HIGHEST_OUTPUT_DEVID (sNumPaDevices - 1) + +/************************************************* Macros ********/ + +/************************************************* Prototypes **********/ + +static PaError PaOSX_QueryDevices( void ); +static int PaOSX_ScanDevices( Boolean isInput ); +static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); +static PaDeviceID PaOSX_QueryDefaultInputDevice( void ); +static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ); +static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ); + +static OSStatus PAOSX_DevicePropertyListener (AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData); + +/**********************************************************************/ +/* OS X errors are 4 character ID that can be printed. + * Note that uses a static pad so result must be printed immediately. + */ +static OSStatus statusText[2] = { 0, 0 }; +static const char *ErrorToString( OSStatus err ) +{ + const char *str; + + switch (err) + { + case kAudioHardwareUnspecifiedError: + str = "kAudioHardwareUnspecifiedError"; + break; + case kAudioHardwareNotRunningError: + str = "kAudioHardwareNotRunningError"; + break; + case kAudioHardwareUnknownPropertyError: + str = "kAudioHardwareUnknownPropertyError"; + break; + case kAudioDeviceUnsupportedFormatError: + str = "kAudioDeviceUnsupportedFormatError"; + break; + case kAudioHardwareBadPropertySizeError: + str = "kAudioHardwareBadPropertySizeError"; + break; + case kAudioHardwareIllegalOperationError: + str = "kAudioHardwareIllegalOperationError"; + break; + default: + statusText[0] = err; + str = (const char *)statusText; + break; + } + + return str; +} + +/**********************************************************************/ +static unsigned long RoundUpToNextPowerOf2( unsigned long n ) +{ + long numBits = 0; + if( ((n-1) & n) == 0) return n; /* Already Power of two. */ + while( n > 0 ) + { + n= n>>1; + numBits++; + } + return (1<past_DeviceData; + if( pahsc == NULL ) return; + /* Query user CPU timer for usage analysis and to prevent overuse of CPU. */ + getrusage( RUSAGE_SELF, &pahsc->entryRusage ); +} + +static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB ) +{ + long secs = timeA->tv_sec - timeB->tv_sec; + long usecs = secs * 1000000; + usecs += (timeA->tv_usec - timeB->tv_usec); + return usecs; +} + +/****************************************************************************** +** Measure fractional CPU load based on real-time it took to calculate +** buffers worth of output. +*/ +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + struct rusage currentRusage; + long usecsElapsed; + double newUsage; + +#define LOWPASS_COEFFICIENT_0 (0.95) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + + if( getrusage( RUSAGE_SELF, ¤tRusage ) == 0 ) + { + usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->entryRusage.ru_utime ); + + /* Use inverse because it is faster than the divide. */ + newUsage = usecsElapsed * pahsc->inverseMicrosPerHostBuffer; + + past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + } +} +/****************************************** END CPU UTILIZATION *******/ + +/************************************************************************/ +static PaDeviceID PaOSX_QueryDefaultInputDevice( void ) +{ + OSStatus err = noErr; + UInt32 count; + int i; + AudioDeviceID tempDeviceID = kAudioDeviceUnknown; + PaDeviceID defaultDeviceID = paNoDevice; + + // get the default output device for the HAL + // it is required to pass the size of the data to be returned + count = sizeof(tempDeviceID); + err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID); + if (err != noErr) goto error; + + // scan input devices to see which one matches this device + defaultDeviceID = paNoDevice; + for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ ) + { + DBUG(("PaOSX_QueryDefaultInputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); + if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) + { + defaultDeviceID = i; + break; + } + } +error: + return defaultDeviceID; +} + +/************************************************************************/ +static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ) +{ + OSStatus err = noErr; + UInt32 count; + int i; + AudioDeviceID tempDeviceID = kAudioDeviceUnknown; + PaDeviceID defaultDeviceID = paNoDevice; + + // get the default output device for the HAL + // it is required to pass the size of the data to be returned + count = sizeof(tempDeviceID); + err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID); + if (err != noErr) goto error; + + // scan output devices to see which one matches this device + defaultDeviceID = paNoDevice; + for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ ) + { + DBUG(("PaOSX_QueryDefaultOutputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); + if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) + { + defaultDeviceID = i; + break; + } + } +error: + return defaultDeviceID; +} + +/******************************************************************/ +static PaError PaOSX_QueryDevices( void ) +{ + OSStatus err = noErr; + UInt32 outSize; + Boolean outWritable; + int numBytes; + + // find out how many Core Audio devices there are, if any + outSize = sizeof(outWritable); + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable); + if (err != noErr) + { + PRINT_ERR("Couldn't get info about list of audio devices", err); + sSavedHostError = err; + return paHostError; + } + + // calculate the number of device available + sNumCoreDevices = outSize / sizeof(AudioDeviceID); + + // Bail if there aren't any devices + if (sNumCoreDevices < 1) + { + PRINT(("No Devices Available")); + return paHostError; + } + + // make space for the devices we are about to get + sCoreDeviceIDs = (AudioDeviceID *)malloc(outSize); + + // get an array of AudioDeviceIDs + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sCoreDeviceIDs); + if (err != noErr) + { + PRINT_ERR("Couldn't get list of audio device IDs", err); + sSavedHostError = err; + return paHostError; + } + + // Allocate structures to hold device info pointers. + // There will be a maximum of two Pa devices per Core Audio device, input and/or output. + numBytes = sNumCoreDevices * 2 * sizeof(PaHostDeviceInfo); + sDeviceInfos = (PaHostDeviceInfo *) PaHost_AllocateFastMemory( numBytes ); + if( sDeviceInfos == NULL ) return paInsufficientMemory; + + // Scan all the Core Audio devices to see which support input and allocate a + // PaHostDeviceInfo structure for each one. + DBUG(("PaOSX_QueryDevices: scan for input ======================\n")); + PaOSX_ScanDevices( IS_INPUT ); + sNumInputDevices = sNumPaDevices; + // Now scan all the output devices. + DBUG(("PaOSX_QueryDevices: scan for output ======================\n")); + PaOSX_ScanDevices( IS_OUTPUT ); + sNumOutputDevices = sNumPaDevices - sNumInputDevices; + + // Figure out which of the devices that we scanned is the default device. + sDefaultInputDeviceID = PaOSX_QueryDefaultInputDevice(); + sDefaultOutputDeviceID = PaOSX_QueryDefaultOutputDevice(); + + return paNoError; +} + + +/*************************************************************************/ +/* Query a device for its sample rate. + * @return positive rate or 0.0 on error. + */ +static Float64 PaOSX_GetDeviceSampleRate( AudioDeviceID deviceID, Boolean isInput ) +{ + OSStatus err = noErr; + AudioStreamBasicDescription formatDesc; + UInt32 dataSize; + dataSize = sizeof(formatDesc); + err = AudioDeviceGetProperty( deviceID, 0, isInput, + kAudioDevicePropertyStreamFormat, &dataSize, &formatDesc); + if( err != noErr ) return 0.0; + else return formatDesc.mSampleRate; +} + +/*************************************************************************/ +/* Allocate a string containing the device name. */ +static char *PaOSX_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) +{ + OSStatus err = noErr; + UInt32 outSize; + Boolean outWritable; + char *deviceName = nil; + + // query size of name + err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, &outWritable); + if (err == noErr) + { + deviceName = (char*)malloc( outSize + 1); + if( deviceName ) + { + err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, deviceName); + if (err != noErr) + PRINT_ERR("Couldn't get audio device name", err); + } + } + + return deviceName; +} + +/************************************************************************* +** Scan all of the Core Audio devices to see which support selected +** input or output mode. +** Changes sNumDevices, and fills in sDeviceInfos. +*/ +static int PaOSX_ScanDevices( Boolean isInput ) +{ + int coreDeviceIndex; + int result; + PaHostDeviceInfo *hostDeviceInfo; + int numAdded = 0; + + for( coreDeviceIndex=0; coreDeviceIndex 0 ) + { + sNumPaDevices += 1; // bump global counter if we got one + numAdded += 1; + } + else if( result < 0 ) return result; + } + return numAdded; +} + + +/************************************************************************* +** Try to fill in the device info for this device. +** Return 1 if a good device that PA can use. +** Return 0 if not appropriate +** or return negative error. +** +*/ +static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) +{ + OSStatus err; + UInt32 outSize; + AudioStreamBasicDescription formatDesc; + AudioDeviceID devID; + PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; + + deviceInfo->structVersion = 1; + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + + deviceInfo->sampleRates = supportedSampleRateRange; // because we use sample rate converter to get continuous rates + deviceInfo->numSampleRates = -1; + + devID = sCoreDeviceIDs[ coreDeviceIndex ]; + hostDeviceInfo->audioDeviceID = devID; + DBUG(("PaOSX_QueryDeviceInfo: coreDeviceIndex = %d, devID = %d, isInput = %d\n", + coreDeviceIndex, devID, isInput )); + + // Get data format info from the device. + outSize = sizeof(formatDesc); + err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); + // This just may not be an appropriate device for input or output so leave quietly. + if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; + + DBUG(("PaOSX_QueryDeviceInfo: mFormatID = 0x%x\n", formatDesc.mFormatID)); + DBUG(("PaOSX_QueryDeviceInfo: kAudioFormatLinearPCM = 0x%x\n", kAudioFormatLinearPCM)); + DBUG(("PaOSX_QueryDeviceInfo: mFormatFlags = 0x%x\n", formatDesc.mFormatFlags)); + DBUG(("PaOSX_QueryDeviceInfo: kLinearPCMFormatFlagIsFloat = 0x%x\n", kLinearPCMFormatFlagIsFloat)); + + // Right now the Core Audio headers only define one formatID: LinearPCM + // Apparently LinearPCM must be Float32 for now. + if( (formatDesc.mFormatID == kAudioFormatLinearPCM) && + ((formatDesc.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) ) + { + deviceInfo->nativeSampleFormats = paFloat32; + } + else + { + PRINT(("PaOSX_QueryDeviceInfo: ERROR - not LinearPCM & Float32!!!\n")); + return paSampleFormatNotSupported; + } + + // Determine maximum number of channels supported. + memset( &formatDesc, 0, sizeof(formatDesc)); + formatDesc.mChannelsPerFrame = 256; // FIXME - what about device with > 256 channels + outSize = sizeof(formatDesc); + err = AudioDeviceGetProperty( devID, 0, + isInput, kAudioDevicePropertyStreamFormatMatch, &outSize, &formatDesc); + if( err != noErr ) + { + PRINT_ERR("PaOSX_QueryDeviceInfo: Could not get device format match", err); + sSavedHostError = err; + return paHostError; + } + + if( isInput ) + { + deviceInfo->maxInputChannels = formatDesc.mChannelsPerFrame; + } + else + { + deviceInfo->maxOutputChannels = formatDesc.mChannelsPerFrame; + } + + // Get the device name + deviceInfo->name = PaOSX_DeviceNameFromID( devID, isInput ); + return 1; + +error: + return 0; +} + +/**********************************************************************/ +static PaError PaOSX_MaybeQueryDevices( void ) +{ + if( sNumPaDevices == 0 ) + { + return PaOSX_QueryDevices(); + } + return 0; +} + +static char zeroPad[256] = { 0 }; + +/********************************************************************** +** This is the proc that supplies the data to the AudioConverterFillBuffer call. +** We can pass back arbitrarily sized blocks so if the FIFO region is split +** just pass back the first half. +*/ +static OSStatus PaOSX_InputConverterCallbackProc (AudioConverterRef inAudioConverter, + UInt32* outDataSize, + void** outData, + void* inUserData) +{ + internalPortAudioStream *past = (internalPortAudioStream *) inUserData; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + void *dataPtr1; + long size1; + void *dataPtr2; + long size2; + + /* Pass contiguous region from FIFO directly to converter. */ + RingBuffer_GetReadRegions( &pahsc->ringBuffer, *outDataSize, + &dataPtr1, &size1, &dataPtr2, &size2 ); + + if( size1 > 0 ) + { + *outData = dataPtr1; + *outDataSize = size1; + RingBuffer_AdvanceReadIndex( &pahsc->ringBuffer, size1 ); + DBUGX(("PaOSX_InputConverterCallbackProc: read %ld bytes from FIFO.\n", size1 )); + } + else + { + DBUGBACK(("PaOSX_InputConverterCallbackProc: got no data!\n")); + *outData = zeroPad; /* Give it zero data to keep it happy. */ + *outDataSize = sizeof(zeroPad); + } + return noErr; +} + +/***************************************************************************** +** Get audio input, if any, from passed in buffer, or from converter or from FIFO, +** then run PA callback and output data. +*/ +static OSStatus PaOSX_LoadAndProcess( internalPortAudioStream *past, + void *inputBuffer, void *outputBuffer ) +{ + OSStatus err = noErr; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + if( past->past_StopSoon ) + { + if( outputBuffer ) + { + /* Clear remainder of audio buffer if we are waiting for stop. */ + AddTraceMessage("PaOSX_LoadAndProcess: zero rest of wave buffer ", i ); + memset( outputBuffer, 0, pahsc->output.bytesPerUserNativeBuffer ); + } + } + else + { + /* Do we need data from the converted input? */ + if( PA_USING_INPUT ) + { + UInt32 size = pahsc->input.bytesPerUserNativeBuffer; + err = AudioConverterFillBuffer( + pahsc->input.converter, + PaOSX_InputConverterCallbackProc, + past, + &size, + pahsc->input.converterBuffer); + if( err != noErr ) return err; + inputBuffer = pahsc->input.converterBuffer; + } + + /* Fill part of audio converter buffer by converting input to user format, + * calling user callback, then converting output to native format. */ + if( PaConvert_Process( past, inputBuffer, outputBuffer )) + { + past->past_StopSoon = 1; + } + } + return err; +} + +/***************************************************************************** +** This is the proc that supplies the data to the AudioConverterFillBuffer call +*/ +static OSStatus PaOSX_OutputConverterCallbackProc (AudioConverterRef inAudioConverter, + UInt32* outDataSize, + void** outData, + void* inUserData) +{ + internalPortAudioStream *past = (internalPortAudioStream *) inUserData; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + *outData = pahsc->output.converterBuffer; + *outDataSize = pahsc->output.bytesPerUserNativeBuffer; + + return PaOSX_LoadAndProcess ( past, pahsc->input.converterBuffer, pahsc->output.converterBuffer ); +} + +/********************************************************************** +** Fill any available output buffers and use any available +** input buffers by calling user callback. +** Will set past->past_StopSoon if user callback indicates that it is finished. +*/ +static OSStatus PaOSX_HandleInputOutput( internalPortAudioStream *past, + const AudioBufferList* inInputData, + AudioBufferList* outOutputData ) +{ + OSStatus err = noErr; + char *inputNativeBufferfPtr = NULL; + char *outputNativeBufferfPtr = NULL; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + /* If we are using output, then we need an empty output buffer. */ + if( outOutputData->mNumberBuffers > 0 ) + { + outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData; + } + + if( inInputData->mNumberBuffers > 0 ) + { + inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData; + + /* Write to FIFO here if we are only using this callback. */ + if( (pahsc->mode == PA_MODE_INPUT_ONLY) || (pahsc->mode == PA_MODE_IO_ONE_DEVICE) ) + { + long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); + long numBytes = inInputData->mBuffers[0].mDataByteSize; + if( numBytes <= writeRoom ) + { + RingBuffer_Write( &pahsc->ringBuffer, inputNativeBufferfPtr, numBytes ); + DBUGBACK(("PaOSX_HandleInputOutput: wrote %ld bytes to FIFO.\n", inInputData->mBuffers[0].mDataByteSize)); + } // FIXME else drop samples on floor, remember overflow??? + } + } + + if( pahsc->mode == PA_MODE_INPUT_ONLY ) + { + /* Generate user buffers as long as we have a half full input FIFO. */ + long halfSize = pahsc->ringBuffer.bufferSize / 2; + while( (RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= halfSize) && + (past->past_StopSoon == 0) ) + { + err = PaOSX_LoadAndProcess ( past, NULL, NULL ); + if( err != noErr ) goto error; + } + } + else + { + UInt32 size = outOutputData->mBuffers[0].mDataByteSize; + err = AudioConverterFillBuffer( + pahsc->output.converter, + PaOSX_OutputConverterCallbackProc, + past, + &size, + outputNativeBufferfPtr); + if( err != noErr ) + { + PRINT_ERR("PaOSX_HandleInputOutput: AudioConverterFillBuffer failed", err); + goto error; + } + } + +error: + return err; +} + +/****************************************************************** + * This callback is used when two separate devices are used for input and output. + * This often happens when using USB devices which present as two devices: input and output. + * It just writes its data to a FIFO so that it can be read by the main callback + * proc PaOSX_CoreAudioIOCallback(). + */ +static OSStatus PaOSX_CoreAudioInputCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, + void* contextPtr) +{ + internalPortAudioStream *past = (internalPortAudioStream *) contextPtr; + PaHostSoundControl *pahsc; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + /* If there is a FIFO for input then write to it. */ + if( pahsc->ringBufferData != NULL ) + { + long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); + long numBytes = inInputData->mBuffers[0].mDataByteSize; + if( numBytes <= writeRoom ) + { + RingBuffer_Write( &pahsc->ringBuffer, inInputData->mBuffers[0].mData, inInputData->mBuffers[0].mDataByteSize ); + } + else + { + DBUGBACK(("PaOSX_CoreAudioInputCallback: FIFO too full to write!\n")); + } + } + + return noErr; +} + +/****************************************************************** + * This is the primary callback for CoreAudio. + * It can handle input and/or output for a single device. + * It takes input from CoreAudio, converts it and passes it to the + * PortAudio user callback. Then takes the PA results and passes it + * back to CoreAudio. + */ +static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, + void* contextPtr) +{ + OSStatus err = noErr; + internalPortAudioStream *past; + PaHostSoundControl *pahsc; + past = (internalPortAudioStream *) contextPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + /* Has someone asked us to abort by calling Pa_AbortStream()? */ + if( past->past_StopNow ) + { + past->past_IsActive = 0; /* Will cause thread to return. */ + } + /* Has someone asked us to stop by calling Pa_StopStream() + * OR has a user callback returned '1' to indicate finished. + */ + else if( past->past_StopSoon ) + { + // FIXME - Pretend all done. Should wait for audio to play out but CoreAudio latency very low. + past->past_IsActive = 0; /* Will cause thread to return. */ + } + else + { + /* use time stamp from CoreAudio if valid */ + + /* dmazzoni: this is unreliable! + + if( inOutputTime->mFlags & kAudioTimeStampSampleTimeValid) + { + past->past_FrameCount = inOutputTime->mSampleTime; + } + else if( inInputTime->mFlags & kAudioTimeStampSampleTimeValid) + { + past->past_FrameCount = inInputTime->mSampleTime; + } + + */ + + /* Measure CPU load. */ + Pa_StartUsageCalculation( past ); + past->past_NumCallbacks += 1; + + /* Process full input buffer and fill up empty output buffers. */ + err = PaOSX_HandleInputOutput( past, inInputData, outOutputData ); + + Pa_EndUsageCalculation( past ); + } + + if( err != 0 ) DBUG(("PaOSX_CoreAudioIOCallback: returns %ld.\n", err )); + + return err; +} + +/*******************************************************************/ +/** Attempt to set device sample rate. + * This is not critical because we use an AudioConverter but we may + * get better fidelity if we can avoid resampling. + * + * Only set format once because some devices take time to settle. + * Return flag indicating whether format changed so we know whether to wait + * for DevicePropertyListener to get called. + * + * @return negative error, zero if no change, or one if changed successfully. + */ +static PaError PaOSX_SetFormat( AudioDeviceID devID, Boolean isInput, + double desiredRate, int desiredNumChannels ) +{ + AudioStreamBasicDescription formatDesc; + PaError result = 0; + OSStatus err; + UInt32 dataSize; + Float64 originalRate; + int originalChannels; + + /* Get current device format. This is critical because if we pass + * zeros for unspecified fields then the iMic device gets switched to a 16 bit + * integer format!!! I don't know if this is a Mac bug or not. But it only + * started happening when I upgraded from OS X V10.1 to V10.2 (Jaguar). + */ + dataSize = sizeof(formatDesc); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyStreamFormat, &dataSize, &formatDesc); + if( err != noErr ) + { + PRINT_ERR("PaOSX_SetFormat: Could not get format.", err); + sSavedHostError = err; + return paHostError; + } + + originalRate = formatDesc.mSampleRate; + originalChannels = formatDesc.mChannelsPerFrame; + + // Is it already set to the correct format? + if( (originalRate != desiredRate) || (originalChannels != desiredNumChannels) ) + { + DBUG(("PaOSX_SetFormat: try to change sample rate to %f.\n", desiredRate )); + DBUG(("PaOSX_SetFormat: try to set number of channels to %d\n", desiredNumChannels)); + + formatDesc.mSampleRate = desiredRate; + formatDesc.mChannelsPerFrame = desiredNumChannels; + formatDesc.mBytesPerFrame = formatDesc.mChannelsPerFrame * sizeof(float); + formatDesc.mBytesPerPacket = formatDesc.mBytesPerFrame * formatDesc.mFramesPerPacket; + + err = AudioDeviceSetProperty( devID, 0, 0, + isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + if (err != noErr) + { + /* Could not set to desired rate so query for closest match. */ + dataSize = sizeof(formatDesc); + err = AudioDeviceGetProperty( devID, 0, + isInput, kAudioDevicePropertyStreamFormatMatch, &dataSize, &formatDesc); + + DBUG(("PaOSX_SetFormat: closest rate is %f.\n", formatDesc.mSampleRate )); + DBUG(("PaOSX_SetFormat: closest numChannels is %d.\n", formatDesc.mChannelsPerFrame )); + // Set to closest if different from original. + if( (err == noErr) && + ((originalRate != formatDesc.mSampleRate) || + (originalChannels != formatDesc.mChannelsPerFrame)) ) + { + err = AudioDeviceSetProperty( devID, 0, 0, + isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + if( err == noErr ) result = 1; + } + } + else result = 1; + } + + return result; +} + +/******************************************************************* + * Check volume level of device. If below threshold, then set to newLevel. + * Using volume instead of decibels because decibel range varies by device. + */ +static void PaOSX_FixVolumeScalars( AudioDeviceID devID, Boolean isInput, + int numChannels, double threshold, double newLevel ) +{ + OSStatus err = noErr; + UInt32 dataSize; + int iChannel; + +/* The master channel is 0. Left and right are channels 1 and 2. */ +/* Fix volume. */ + for( iChannel = 0; iChannel<=numChannels; iChannel++ ) + { + Float32 fdata32; + dataSize = sizeof( fdata32 ); + err = AudioDeviceGetProperty( devID, iChannel, isInput, + kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); + + printf("devID=%d\n", devID); + + printf("Channel=%d input=%d volume=%f\n", + iChannel, (int)isInput, fdata32); + + if( err == noErr ) + { + DBUG(("kAudioDevicePropertyVolumeScalar for channel %d = %f\n", iChannel, fdata32)); + if( fdata32 <= (Float32) threshold ) + { + dataSize = sizeof( fdata32 ); + fdata32 = (Float32) newLevel; + + printf("Channel=%d input=%d new volume=%f\n", + iChannel, (int)isInput, fdata32); + + err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, + kAudioDevicePropertyVolumeScalar, dataSize, &fdata32 ); + if( err != noErr ) + { + PRINT(("Warning: audio volume is very low and could not be turned up.\n")); + } + else + { + PRINT(("Volume for audio channel %d was <= %4.2f so set to %4.2f by PortAudio!\n", + iChannel, threshold, newLevel )); + } + } + } + } +/* Unmute if muted. */ + for( iChannel = 0; iChannel<=numChannels; iChannel++ ) + { + UInt32 uidata32; + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, iChannel, isInput, + kAudioDevicePropertyMute, &dataSize, &uidata32 ); + if( err == noErr ) + { + DBUG(("uidata32 for channel %d = %ld\n", iChannel, uidata32)); + if( uidata32 == 1 ) // muted? + { + dataSize = sizeof( uidata32 ); + uidata32 = 0; // unmute + err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, + kAudioDevicePropertyMute, dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT(("Warning: audio is muted and could not be unmuted!\n")); + } + else + { + PRINT(("Audio channel %d was unmuted by PortAudio!\n", iChannel )); + } + } + } + } + +} + +#if 0 +static void PaOSX_DumpDeviceInfo( AudioDeviceID devID, Boolean isInput ) +{ + OSStatus err = noErr; + UInt32 dataSize; + UInt32 uidata32; + Float32 fdata32; + AudioValueRange audioRange; + + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyLatency, &dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading kAudioDevicePropertyLatency", err); + return; + } + PRINT(("kAudioDevicePropertyLatency = %d\n", (int)uidata32 )); + + dataSize = sizeof( fdata32 ); + err = AudioDeviceGetProperty( devID, 1, isInput, + kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading kAudioDevicePropertyVolumeScalar", err); + return; + } + PRINT(("kAudioDevicePropertyVolumeScalar = %f\n", fdata32 )); + + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferSize, &dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size", err); + return; + } + PRINT(("kAudioDevicePropertyBufferSize = %d bytes\n", (int)uidata32 )); + + dataSize = sizeof( audioRange ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferSizeRange, &dataSize, &audioRange ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size range", err); + return; + } + PRINT(("kAudioDevicePropertyBufferSizeRange = %g to %g bytes\n", audioRange.mMinimum, audioRange.mMaximum )); + + dataSize = sizeof( uidata32 ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferFrameSize, &dataSize, &uidata32 ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size", err); + return; + } + PRINT(("kAudioDevicePropertyBufferFrameSize = %d frames\n", (int)uidata32 )); + + dataSize = sizeof( audioRange ); + err = AudioDeviceGetProperty( devID, 0, isInput, + kAudioDevicePropertyBufferFrameSizeRange, &dataSize, &audioRange ); + if( err != noErr ) + { + PRINT_ERR("Error reading buffer size range", err); + return; + } + PRINT(("kAudioDevicePropertyBufferFrameSizeRange = %g to %g frames\n", audioRange.mMinimum, audioRange.mMaximum )); + + return; +} +#endif + +/*******************************************************************/ +static OSStatus PAOSX_DevicePropertyListener (AudioDeviceID inDevice, + UInt32 inChannel, + Boolean isInput, + AudioDevicePropertyID inPropertyID, + void* inClientData) +{ + PaHostSoundControl *pahsc; + internalPortAudioStream *past; + UInt32 dataSize; + OSStatus err = noErr; + AudioStreamBasicDescription userStreamFormat, hardwareStreamFormat; + Boolean updateInverseMicros; + Boolean updateConverter; + + past = (internalPortAudioStream *) inClientData; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + DBUG(("PAOSX_DevicePropertyListener: called with propertyID = 0x%0X\n", (unsigned int) inPropertyID )); + + updateInverseMicros = (inDevice == pahsc->primaryDeviceID) && + ((inPropertyID == kAudioDevicePropertyStreamFormat) || + (inPropertyID == kAudioDevicePropertyBufferFrameSize)); + + updateConverter = (inPropertyID == kAudioDevicePropertyStreamFormat); + + // Sample rate needed for both. + if( updateConverter || updateInverseMicros ) + { + + /* Get target device format */ + dataSize = sizeof(hardwareStreamFormat); + err = AudioDeviceGetProperty(inDevice, 0, isInput, + kAudioDevicePropertyStreamFormat, &dataSize, &hardwareStreamFormat); + if( err != noErr ) + { + PRINT_ERR("PAOSX_DevicePropertyListener: Could not get device format", err); + sSavedHostError = err; + goto error; + } + } + + if( updateConverter ) + { + DBUG(("PAOSX_DevicePropertyListener: HW rate = %f\n", hardwareStreamFormat.mSampleRate )); + DBUG(("PAOSX_DevicePropertyListener: user rate = %f\n", past->past_SampleRate )); + + /* Set source user format. */ + userStreamFormat = hardwareStreamFormat; + userStreamFormat.mSampleRate = past->past_SampleRate; // sample rate of the user synthesis code + userStreamFormat.mChannelsPerFrame = (isInput) ? past->past_NumInputChannels : past->past_NumOutputChannels; // the number of channels in each frame + + userStreamFormat.mBytesPerFrame = userStreamFormat.mChannelsPerFrame * sizeof(float); + userStreamFormat.mBytesPerPacket = userStreamFormat.mBytesPerFrame * userStreamFormat.mFramesPerPacket; + + if( isInput ) + { + if( pahsc->input.converter != NULL ) + { + verify_noerr(AudioConverterDispose (pahsc->input.converter)); + } + + // Convert from hardware format to user format. + err = AudioConverterNew ( + &hardwareStreamFormat, + &userStreamFormat, + &pahsc->input.converter ); + if( err != noErr ) + { + PRINT_ERR("Could not create input format converter", err); + sSavedHostError = err; + goto error; + } + } + else + { + if( pahsc->output.converter != NULL ) + { + verify_noerr(AudioConverterDispose (pahsc->output.converter)); + } + + // Convert from user format to hardware format. + err = AudioConverterNew ( + &userStreamFormat, + &hardwareStreamFormat, + &pahsc->output.converter ); + if( err != noErr ) + { + PRINT_ERR("Could not create output format converter", err); + sSavedHostError = err; + goto error; + } + } + } + + if( updateInverseMicros ) + { + // Update coefficient used to calculate CPU Load based on sampleRate and bufferSize. + UInt32 ioBufferSize; + dataSize = sizeof(ioBufferSize); + err = AudioDeviceGetProperty( inDevice, 0, isInput, + kAudioDevicePropertyBufferFrameSize, &dataSize, + &ioBufferSize); + if( err == noErr ) + { + pahsc->inverseMicrosPerHostBuffer = hardwareStreamFormat.mSampleRate / + (1000000.0 * ioBufferSize); + } + } + +error: + pahsc->formatListenerCalled = true; + return err; +} + +/* Allocate FIFO between Device callback and Converter callback so that device can push data +* and converter can pull data. +*/ +static PaError PaOSX_CreateInputRingBuffer( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + OSStatus err = noErr; + UInt32 dataSize; + double sampleRateRatio; + long numBytes; + UInt32 framesPerHostBuffer; + UInt32 bytesForDevice; + UInt32 bytesForUser; + AudioStreamBasicDescription formatDesc; + + dataSize = sizeof(formatDesc); + err = AudioDeviceGetProperty( pahsc->input.audioDeviceID, 0, IS_INPUT, + kAudioDevicePropertyStreamFormat, &dataSize, &formatDesc); + if( err != noErr ) + { + PRINT_ERR("PaOSX_CreateInputRingBuffer: Could not get I/O buffer size.\n", err); + sSavedHostError = err; + return paHostError; + } + + // If device is delivering audio faster than being consumed then buffer must be bigger. + sampleRateRatio = formatDesc.mSampleRate / past->past_SampleRate; + + // Get size of CoreAudio IO buffers. + dataSize = sizeof(framesPerHostBuffer); + err = AudioDeviceGetProperty( pahsc->input.audioDeviceID, 0, IS_INPUT, + kAudioDevicePropertyBufferFrameSize, &dataSize, + &framesPerHostBuffer); + if( err != noErr ) + { + PRINT_ERR("PaOSX_CreateInputRingBuffer: Could not get I/O buffer size.\n", err); + sSavedHostError = err; + return paHostError; + } + + bytesForDevice = framesPerHostBuffer * formatDesc.mChannelsPerFrame * sizeof(Float32) * 2; + + bytesForUser = past->past_FramesPerUserBuffer * past->past_NumInputChannels * + sizeof(Float32) * 3 * sampleRateRatio; + + // Ring buffer should be large enough to consume audio input from device, + // and to deliver a complete user buffer. + numBytes = (bytesForDevice > bytesForUser) ? bytesForDevice : bytesForUser; + + numBytes = RoundUpToNextPowerOf2( numBytes ); + + DBUG(("PaOSX_CreateInputRingBuffer: FIFO numBytes = %ld\n", numBytes)); + pahsc->ringBufferData = PaHost_AllocateFastMemory( numBytes ); + if( pahsc->ringBufferData == NULL ) + { + return paInsufficientMemory; + } + RingBuffer_Init( &pahsc->ringBuffer, numBytes, pahsc->ringBufferData ); + // make it look full at beginning + RingBuffer_AdvanceWriteIndex( &pahsc->ringBuffer, numBytes ); + + return paNoError; +} + +/****************************************************************** + * Try to set the I/O bufferSize of the device. + * Scale the size by the ratio of the sample rates so that the converter will have + * enough data to operate on. + */ +static OSStatus PaOSX_SetDeviceBufferSize( AudioDeviceID devID, Boolean isInput, int framesPerUserBuffer, Float64 sampleRateRatio ) +{ + UInt32 dataSize; + UInt32 ioBufferSize; + int scaler; + + scaler = (int) sampleRateRatio; + if( sampleRateRatio > (Float64) scaler ) scaler += 1; + DBUG(("PaOSX_SetDeviceBufferSize: buffer size scaler = %d\n", scaler )); + ioBufferSize = framesPerUserBuffer * scaler; + + // Limit buffer size to reasonable value. + if( ioBufferSize < 128 ) ioBufferSize = 128; + + dataSize = sizeof(ioBufferSize); + return AudioDeviceSetProperty( devID, 0, 0, isInput, + kAudioDevicePropertyBufferFrameSize, dataSize, + &ioBufferSize); +} + + +/*******************************************************************/ +static PaError PaOSX_OpenCommonDevice( internalPortAudioStream *past, + PaHostInOut *inOut, Boolean isInput ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + PaError result = paNoError; + OSStatus err = noErr; + Float64 deviceRate; + + /* dmazzoni: this is not needed because we use PortMixer + PaOSX_FixVolumeScalars( inOut->audioDeviceID, isInput, + inOut->numChannels, -0.1, 0.9 ); + */ + + // The HW device format changes are asynchronous. + // So we don't know when or if the PAOSX_DevicePropertyListener() will + // get called. To be safe, call the listener now to forcibly create the converter. + if( inOut->converter == NULL ) + { + err = PAOSX_DevicePropertyListener (inOut->audioDeviceID, + 0, isInput, kAudioDevicePropertyStreamFormat, past); + if (err != kAudioHardwareNoError) + { + PRINT_ERR("PaOSX_OpenCommonDevice: PAOSX_DevicePropertyListener failed.\n", err); + sSavedHostError = err; + return paHostError; + } + } + + // Add listener for when format changed by other apps. + DBUG(("PaOSX_OpenCommonDevice: call AudioDeviceAddPropertyListener()\n" )); + err = AudioDeviceAddPropertyListener( inOut->audioDeviceID, 0, isInput, + kAudioDevicePropertyStreamFormat, + (AudioDevicePropertyListenerProc) PAOSX_DevicePropertyListener, past ); + if (err != noErr) + { + return -1; // FIXME + } + + // Only change format if current HW format is different. + // Don't bother to check result because we are going to use an AudioConverter anyway. + pahsc->formatListenerCalled = false; + result = PaOSX_SetFormat( inOut->audioDeviceID, isInput, past->past_SampleRate, inOut->numChannels ); + // Synchronize with device because format changes put some devices into unusable mode. + if( result > 0 ) + { + const int sleepDurMsec = 50; + int spinCount = MIN_TIMEOUT_MSEC / sleepDurMsec; + while( !pahsc->formatListenerCalled && (spinCount > 0) ) + { + Pa_Sleep( sleepDurMsec ); // FIXME - use a semaphore or signal + spinCount--; + } + if( !pahsc->formatListenerCalled ) + { + PRINT(("PaOSX_OpenCommonDevice: timed out waiting for device format to settle.\n")); + } + result = 0; + } + +#if SET_DEVICE_BUFFER_SIZE + // Try to set the I/O bufferSize of the device. + { + Float64 ratio; + deviceRate = PaOSX_GetDeviceSampleRate( inOut->audioDeviceID, isInput ); + if( deviceRate <= 0.0 ) deviceRate = past->past_SampleRate; + ratio = deviceRate / past->past_SampleRate ; + err = PaOSX_SetDeviceBufferSize( inOut->audioDeviceID, isInput, + past->past_FramesPerUserBuffer, ratio ); + if( err != noErr ) + { + DBUG(("PaOSX_OpenCommonDevice: Could not set I/O buffer size.\n")); + } + } +#endif + + /* Allocate an input buffer because we need it between the user callback and the converter. */ + inOut->converterBuffer = PaHost_AllocateFastMemory( inOut->bytesPerUserNativeBuffer ); + if( inOut->converterBuffer == NULL ) + { + return paInsufficientMemory; + } + + return result; +} + +/*******************************************************************/ +static PaError PaOSX_OpenInputDevice( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + const PaHostDeviceInfo *hostDeviceInfo; + PaError result = paNoError; + + DBUG(("PaOSX_OpenInputDevice: -------------\n")); + + if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) || + (past->past_InputDeviceID > HIGHEST_INPUT_DEVID) ) + { + return paInvalidDeviceId; + } + hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID]; + + if( past->past_NumInputChannels > hostDeviceInfo->paInfo.maxInputChannels ) + { + return paInvalidChannelCount; /* Too many channels! */ + } + pahsc->input.numChannels = past->past_NumInputChannels; + + // setup PA conversion procedure + result = PaConvert_SetupInput( past, paFloat32 ); + + result = PaOSX_OpenCommonDevice( past, &pahsc->input, IS_INPUT ); + if( result != paNoError ) goto error; + + // Allocate a ring buffer so we can push in data from device, and pull through AudioConverter. + result = PaOSX_CreateInputRingBuffer( past ); + if( result != paNoError ) goto error; + +error: + return result; +} + +/*******************************************************************/ +static PaError PaOSX_OpenOutputDevice( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + const PaHostDeviceInfo *hostDeviceInfo; + PaError result = paNoError; + + DBUG(("PaOSX_OpenOutputDevice: -------------\n")); + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + // Validate DeviceID + DBUG(("PaOSX_OpenOutputDevice: deviceID = 0x%x\n", past->past_OutputDeviceID)); + if( (past->past_OutputDeviceID < LOWEST_OUTPUT_DEVID) || + (past->past_OutputDeviceID > HIGHEST_OUTPUT_DEVID) ) + { + return paInvalidDeviceId; + } + hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID]; + + // Validate number of channels. + if( past->past_NumOutputChannels > hostDeviceInfo->paInfo.maxOutputChannels ) + { + return paInvalidChannelCount; /* Too many channels! */ + } + pahsc->output.numChannels = past->past_NumOutputChannels; + + // setup conversion procedure + result = PaConvert_SetupOutput( past, paFloat32 ); + if( result != paNoError ) goto error; + + result = PaOSX_OpenCommonDevice( past, &pahsc->output, IS_OUTPUT ); + if( result != paNoError ) goto error; + +error: + return result; +} + +/******************************************************************* +* Determine how many User Buffers we can put into our CoreAudio stream buffer. +* Uses: +* past->past_FramesPerUserBuffer, etc. +* Sets: +* past->past_NumUserBuffers +* pahsc->input.bytesPerUserNativeBuffer +* pahsc->output.bytesPerUserNativeBuffer +*/ +static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = ( PaHostSoundControl *)past->past_DeviceData; + + // Determine number of user buffers based strictly on minimum reasonable buffer size. + // We ignore the Pa_OpenStream numBuffer parameter because CoreAudio has a big + // mix buffer and handles latency automatically. + past->past_NumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + + // calculate buffer sizes in bytes + pahsc->input.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * + Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; + pahsc->output.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * + Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; + + DBUG(("PaOSX_CalcNumHostBuffers: past_NumUserBuffers = %ld\n", past->past_NumUserBuffers )); + DBUG(("PaOSX_CalcNumHostBuffers: input.bytesPerUserNativeBuffer = %d\n", pahsc->input.bytesPerUserNativeBuffer )); + DBUG(("PaOSX_CalcNumHostBuffers: output.bytesPerUserNativeBuffer = %d\n", pahsc->output.bytesPerUserNativeBuffer )); +} + +/*****************************************************************************/ +/************** Internal Host API ********************************************/ +/*****************************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc; + Boolean useInput; + Boolean useOutput; + + assert( past->past_Magic == PA_MAGIC ); + + /* Allocate and initialize host data. */ + pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl)); + if( pahsc == NULL ) + { + result = paInsufficientMemory; + goto error; + } + memset( pahsc, 0, sizeof(PaHostSoundControl) ); + past->past_DeviceData = (void *) pahsc; + pahsc->primaryDeviceID = kAudioDeviceUnknown; + pahsc->input.audioDeviceID = kAudioDeviceUnknown; + pahsc->output.audioDeviceID = kAudioDeviceUnknown; + + PaOSX_CalcHostBufferSize( past ); + + useOutput = (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0); + useInput = (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0); + + // Set device IDs and determine IO Device mode. + if( useOutput ) + { + pahsc->output.audioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + pahsc->primaryDeviceID = pahsc->output.audioDeviceID; + if( useInput ) + { + pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + pahsc->mode = ( pahsc->input.audioDeviceID != pahsc->primaryDeviceID ) ? + PA_MODE_IO_TWO_DEVICES : PA_MODE_IO_ONE_DEVICE; + } + else + { + pahsc->mode = PA_MODE_OUTPUT_ONLY; + } + } + else + { + /* Just input, not output. */ + pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + pahsc->primaryDeviceID = pahsc->input.audioDeviceID; + pahsc->mode = PA_MODE_INPUT_ONLY; + } + + DBUG(("outputDeviceID = %ld\n", pahsc->output.audioDeviceID )); + DBUG(("inputDeviceID = %ld\n", pahsc->input.audioDeviceID )); + DBUG(("primaryDeviceID = %ld\n", pahsc->primaryDeviceID )); + + /* ------------------ OUTPUT */ + if( useOutput ) + { + result = PaOSX_OpenOutputDevice( past ); + if( result < 0 ) goto error; + } + + /* ------------------ INPUT */ + if( useInput ) + { + result = PaOSX_OpenInputDevice( past ); + if( result < 0 ) goto error; + } + + return result; + +error: + PaHost_CloseStream( past ); + return result; +} + +/*************************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + return 0; +} + +/*************************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + return 0; +} + +/*************************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + OSStatus err = noErr; + PaError result = paNoError; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + past->past_StopSoon = 0; + past->past_StopNow = 0; + past->past_IsActive = 1; + + past->past_FrameCount = 0; + +/* If full duplex and using two separate devices then start input device. */ + if( pahsc->mode == PA_MODE_IO_TWO_DEVICES ) + { + // Associate an IO proc with the device and pass a pointer to the audio data context + err = AudioDeviceAddIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback, past); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc secondary failed", err ); + goto error; + } + + // start playing sound through the device + err = AudioDeviceStart(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceStart secondary failed", err ); + PRINT(("The program may succeed if you run it again!\n")); + goto error; + } + } + + // Associate an IO proc with the device and pass a pointer to the audio data context + err = AudioDeviceAddIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback, past); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc primary failed", err ); + goto error; + } + + // start playing sound through the device + err = AudioDeviceStart(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); + if (err != noErr) + { + PRINT_ERR("PaHost_StartEngine: AudioDeviceStart primary failed", err ); + PRINT(("The program may succeed if you run it again!\n")); + goto error; + } + + return result; + +error: + sSavedHostError = err; + return paHostError; +} + +/*************************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + OSStatus err = noErr; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + (void) abort; + + /* Tell background thread to stop generating more data and to let current data play out. */ + past->past_StopSoon = 1; + /* If aborting, tell background thread to stop NOW! */ + if( abort ) past->past_StopNow = 1; + past->past_IsActive = 0; + +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StopOutput: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); +#endif + + // FIXME - we should ask proc to stop instead of stopping abruptly + err = AudioDeviceStop(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); + if (err != noErr) + { + goto error; + } + + err = AudioDeviceRemoveIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); + if (err != noErr) goto error; + +/* If full duplex and using two separate devices then stop second input device. */ + if( pahsc->mode == PA_MODE_IO_TWO_DEVICES ) + { + err = AudioDeviceStop(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); + if (err != noErr) goto error; + err = AudioDeviceRemoveIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); + if (err != noErr) goto error; + } + + return paNoError; + +error: + sSavedHostError = err; + return paHostError; +} + +/*************************************************************************/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + +/*******************************************************************/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + + //PaOSX_DumpDeviceInfo( sDeviceInfos[past->past_OutputDeviceID].audioDeviceID, IS_OUTPUT ); + +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); +#endif + // Stop Listener callbacks ASAP before dismantling stream. + if( PA_USING_INPUT ) + { + AudioDeviceRemovePropertyListener( pahsc->input.audioDeviceID, 0, IS_INPUT, + kAudioDevicePropertyStreamFormat, + (AudioDevicePropertyListenerProc) PAOSX_DevicePropertyListener ); + } + + if( PA_USING_OUTPUT ) + { + AudioDeviceRemovePropertyListener( pahsc->output.audioDeviceID, 0, IS_OUTPUT, + kAudioDevicePropertyStreamFormat, + (AudioDevicePropertyListenerProc) PAOSX_DevicePropertyListener ); + } + + if( pahsc->output.converterBuffer != NULL ) + { + PaHost_FreeFastMemory( pahsc->output.converterBuffer, pahsc->output.bytesPerUserNativeBuffer ); + } + if( pahsc->input.converterBuffer != NULL ) + { + PaHost_FreeFastMemory( pahsc->input.converterBuffer, pahsc->input.bytesPerUserNativeBuffer ); + } + if( pahsc->ringBufferData != NULL ) + { + PaHost_FreeFastMemory( pahsc->ringBufferData, pahsc->ringBuffer.bufferSize ); + } + if( pahsc->output.converter != NULL ) + { + verify_noerr(AudioConverterDispose (pahsc->output.converter)); + } + if( pahsc->input.converter != NULL ) + { + verify_noerr(AudioConverterDispose (pahsc->input.converter)); + } + + free( pahsc ); + past->past_DeviceData = NULL; + + return paNoError; +} + +/********************************************************************** +** Initialize Host dependant part of API. +*/ +PaError PaHost_Init( void ) +{ + return PaOSX_MaybeQueryDevices(); +} + +/************************************************************************* +** Cleanup device info. +*/ +PaError PaHost_Term( void ) +{ + int i; + + if( sDeviceInfos != NULL ) + { + for( i=0; ipast_DeviceData; + if( pahsc == NULL ) return paInternalError; + return (PaError) past->past_IsActive; +} + +/*****************************************************************************/ +/************** External User API ********************************************/ +/*****************************************************************************/ + +/********************************************************************** +** Query devices and use result. +*/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + PaError result = PaOSX_MaybeQueryDevices(); + if( result < 0 ) return result; + return sDefaultInputDeviceID; +} + +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + PaError result = PaOSX_MaybeQueryDevices(); + if( result < 0 ) return result; + return sDefaultOutputDeviceID; +} + + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Because CoreAudio manages latency, this just selects +** a reasonably small number of buffers. +*/ +int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) +{ + int minBuffers; + double denominator; + int minLatencyMsec = PA_MIN_LATENCY_MSEC; + denominator = 1000.0 * framesPerBuffer; + minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); + if( minBuffers < 1 ) minBuffers = 1; + return minBuffers; +} + +/*************************************************************************/ +void Pa_Sleep( long msec ) +{ + usleep( msec * 1000 ); +} + +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + AudioTimeStamp timeStamp; + PaTimestamp streamTime; + PaHostSoundControl *pahsc; + internalPortAudioStream *past = (internalPortAudioStream *) stream; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + /* dmazzoni: this is unreliable + AudioDeviceGetCurrentTime(pahsc->primaryDeviceID, &timeStamp); + streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ? + timeStamp.mSampleTime : past->past_FrameCount; + */ + + return past->past_FrameCount; + + return streamTime; +} + +/************************************************************************************/ +long Pa_GetHostError() +{ + return sSavedHostError; +} + +/*************************************************************************/ +int Pa_CountDevices() +{ + if( sNumPaDevices <= 0 ) Pa_Initialize(); + return sNumPaDevices; +} + +/************************************************************************* +** PaDeviceInfo structures have already been created +** so just return the pointer. +** +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + if( id < 0 || id >= sNumPaDevices ) + return NULL; + + return &sDeviceInfos[id].paInfo; +} + + diff --git a/lib/portaudio/pa_tests/CVS/Entries b/lib/portaudio/pa_tests/CVS/Entries new file mode 100644 index 0000000..1fe5778 --- /dev/null +++ b/lib/portaudio/pa_tests/CVS/Entries @@ -0,0 +1,30 @@ +/debug_dual.c/1.3/Sun Mar 2 08:01:39 2003// +/debug_multi_in.c/1.2/Sun Mar 2 08:01:39 2003// +/debug_multi_out.c/1.2/Sun Mar 2 08:01:39 2003// +/debug_record.c/1.2/Sun Mar 2 08:01:40 2003// +/debug_sine.c/1.3/Sun Mar 2 08:01:40 2003// +/debug_test1.c/1.2/Sun Mar 2 08:01:40 2003// +/pa_devs.c/1.2/Sun Mar 2 08:01:40 2003// +/pa_fuzz.c/1.3/Sun Mar 2 08:01:40 2003// +/pa_minlat.c/1.2/Sun Mar 2 08:01:40 2003// +/paqa_devs.c/1.2/Sun Mar 2 08:01:40 2003// +/paqa_errs.c/1.2/Sun Mar 2 08:01:41 2003// +/patest1.c/1.3/Sun Mar 2 08:01:41 2003// +/patest_clip.c/1.2/Sun Mar 2 08:01:41 2003// +/patest_dither.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_latency.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_leftright.c/1.3/Sun Mar 2 08:01:42 2003// +/patest_longsine.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_many.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_maxsines.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_pink.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_record.c/1.3/Sun Mar 2 08:01:42 2003// +/patest_ringmix.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_saw.c/1.2/Sun Mar 2 08:01:42 2003// +/patest_sine.c/1.3/Sun Mar 2 08:01:43 2003// +/patest_sine8.c/1.2/Sun Mar 2 08:01:43 2003// +/patest_sine_time.c/1.3/Sun Mar 2 08:01:43 2003// +/patest_stop.c/1.2/Sun Mar 2 08:01:43 2003// +/patest_sync.c/1.2/Sun Mar 2 08:01:43 2003// +/patest_wire.c/1.3/Sun Mar 2 08:01:43 2003// +D diff --git a/lib/portaudio/pa_tests/CVS/Repository b/lib/portaudio/pa_tests/CVS/Repository new file mode 100644 index 0000000..9cee589 --- /dev/null +++ b/lib/portaudio/pa_tests/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/pa_tests diff --git a/lib/portaudio/pa_tests/CVS/Root b/lib/portaudio/pa_tests/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/pa_tests/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/pa_tests/debug_dual.c b/lib/portaudio/pa_tests/debug_dual.c new file mode 100644 index 0000000..ff734b1 --- /dev/null +++ b/lib/portaudio/pa_tests/debug_dual.c @@ -0,0 +1,183 @@ +/* + * $Id: debug_dual.c,v 1.3 2003/03/02 08:01:39 dmazzoni Exp $ + * debug_dual.c + * Try to open TWO streams on separate cards. + * Play a sine sweep using the Portable Audio api for several seconds. + * Hacked test for debugging PA. + * + * Author: Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define DEV_ID_1 (13) +#define DEV_ID_2 (15) +#define NUM_SECONDS (8) +#define SLEEP_DUR (800) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#if 0 +#define MIN_LATENCY_MSEC (200) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#else +#define NUM_BUFFERS (0) +#endif +#define MIN_FREQ (100.0f) +#define MAX_FREQ (4000.0f) +#define FREQ_SCALAR (1.00002f) +#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float left_phase; + float right_phase; +} +paTestData; +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ); +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + + for( i=0; ileft_phase); /* left */ + *out++ = LookupSine(data, data->right_phase); /* right */ + data->left_phase += data->phase_increment; + if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; + data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ + if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; + /* sweep frequency then start over. */ + data->phase_increment *= FREQ_SCALAR; + if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ); + } + return 0; +} + +PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, + paTestData *data ); +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream1, *stream2; + PaError err; + paTestData DATA1, DATA2; + printf("PortAudio Test: DUAL sine sweep. ask for %d buffers\n", NUM_BUFFERS ); + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = TestStart( &stream1, DEV_ID_1, &DATA1 ); + if( err != paNoError ) goto error; + err = TestStart( &stream2, DEV_ID_2, &DATA2 ); + if( err != paNoError ) goto error; + printf("Hit ENTER\n"); + getchar(); + err = Pa_StopStream( stream1 ); + if( err != paNoError ) goto error; + err = Pa_StopStream( stream2 ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} +PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, paTestData *data ) +{ + PortAudioStream *stream; + PaError err; + int i; + /* initialise sinusoidal wavetable */ + for( i=0; isine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ); + } + data->sine[TABLE_SIZE] = data->sine[0]; // set guard point + data->left_phase = data->right_phase = 0.0; + data->phase_increment = CalcPhaseIncrement(MIN_FREQ); + printf("PortAudio Test: output device = %d\n", devID ); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + devID, + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + NUM_BUFFERS, /* number of buffers, if zero then use default minimum */ + paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + *streamPtr = stream; + return 0; +error: + return err; +} diff --git a/lib/portaudio/pa_tests/debug_multi_in.c b/lib/portaudio/pa_tests/debug_multi_in.c new file mode 100644 index 0000000..a51235b --- /dev/null +++ b/lib/portaudio/pa_tests/debug_multi_in.c @@ -0,0 +1,179 @@ +/* + * $Id: debug_multi_in.c,v 1.2 2003/03/02 08:01:39 dmazzoni Exp $ + * debug_multi_in.c + * Pass output from each of multiple channels + * to a stereo output using the Portable Audio api. + * Hacked test for debugging PA. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "portaudio.h" +//#define INPUT_DEVICE_NAME ("EWS88 MT Interleaved Rec") +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +//#define OUTPUT_DEVICE (18) +#define SAMPLE_RATE (22050) +#define FRAMES_PER_BUFFER (256) +#define MIN_LATENCY_MSEC (400) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +typedef struct +{ + int liveChannel; + int numChannels; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float *in = (float*)inputBuffer; + int i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( in == NULL ) return 0; + for( i=0; i<(int)framesPerBuffer; i++ ) + { + /* Copy one channel of input to output. */ + *out++ = in[data->liveChannel]; + *out++ = in[data->liveChannel]; + in += data->numChannels; + } + return 0; +} +/*******************************************************************/ +int PaFindDeviceByName( const char *name ) +{ + int i; + int numDevices; + const PaDeviceInfo *pdi; + int len = strlen( name ); + PaDeviceID result = paNoDevice; + numDevices = Pa_CountDevices(); + for( i=0; iname, len ) == 0 ) + { + result = i; + break; + } + } + return result; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + PaDeviceID inputDevice; + const PaDeviceInfo *pdi; + printf("PortAudio Test: input signal from each channel. %d buffers\n", NUM_BUFFERS ); + data.liveChannel = 0; + err = Pa_Initialize(); + if( err != paNoError ) goto error; +#ifdef INPUT_DEVICE_NAME + printf("Try to use device: %s\n", INPUT_DEVICE_NAME ); + inputDevice = PaFindDeviceByName(INPUT_DEVICE_NAME); + if( inputDevice == paNoDevice ) + { + printf("Could not find %s. Using default instead.\n", INPUT_DEVICE_NAME ); + inputDevice = Pa_GetDefaultInputDeviceID(); + } +#else + printf("Using default input device.\n"); + inputDevice = Pa_GetDefaultInputDeviceID(); +#endif + pdi = Pa_GetDeviceInfo( inputDevice ); + if( pdi == NULL ) + { + printf("Could not get device info!\n"); + goto error; + } + data.numChannels = pdi->maxInputChannels; + printf("Input Device name is %s\n", pdi->name ); + printf("Input Device has %d channels.\n", pdi->maxInputChannels); + err = Pa_OpenStream( + &stream, + inputDevice, + pdi->maxInputChannels, + paFloat32, /* 32 bit floating point input */ + NULL, + OUTPUT_DEVICE, + 2, + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + NUM_BUFFERS, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + data.liveChannel = 0; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + for( i=0; i +#include +#include "portaudio.h" + +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#define FREQ_INCR (300.0 / SAMPLE_RATE) +#define MAX_CHANNELS (64) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +typedef struct +{ + int numChannels; + double phases[MAX_CHANNELS]; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int frameIndex, channelIndex; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) + { + for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) + { + /* Output sine wave on every channel. */ + *out++ = (float) sin(data->phases[channelIndex]); + + /* Play each channel at a higher frequency. */ + data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); + if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); + } + } + + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + const PaDeviceInfo *pdi; + paTestData data = {0}; + printf("PortAudio Test: output sine wave on each channel.\n" ); + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE ); + data.numChannels = pdi->maxOutputChannels; + if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS; + printf("Number of Channels = %d\n", data.numChannels ); + + err = Pa_OpenStream( + &stream, + paNoDevice, /* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + OUTPUT_DEVICE, + data.numChannels, + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Hit ENTER to stop sound.\n"); + fflush(stdout); + getchar(); + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + Pa_CloseStream( stream ); + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/lib/portaudio/pa_tests/debug_record.c b/lib/portaudio/pa_tests/debug_record.c new file mode 100644 index 0000000..2f1b6ce --- /dev/null +++ b/lib/portaudio/pa_tests/debug_record.c @@ -0,0 +1,338 @@ +/* + * $Id: debug_record.c,v 1.2 2003/03/02 08:01:40 dmazzoni Exp $ + * patest_record.c + * Record input into an array. + * Save array to a file. + * Playback recorded data. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define SAMPLE_RATE (22050) +#define NUM_SECONDS (6) +#define NUM_CHANNELS (2) +#define FRAMES_PER_BUFFER (64) +/* #define DITHER_FLAG (paDitherOff) */ +#define DITHER_FLAG (0) + +/* Select sample format. */ +#if 1 +#define PA_SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#define SAMPLE_SILENCE (0.0f) + +#elif 0 +#define PA_SAMPLE_TYPE paInt32 +typedef long SAMPLE; +#define SAMPLE_SILENCE (0) + +#elif 0 +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#define SAMPLE_SILENCE (0) + +#elif 0 +#define PA_SAMPLE_TYPE paInt8 +typedef char SAMPLE; +#define SAMPLE_SILENCE (0) + +#else +#define PA_SAMPLE_TYPE paUInt8 +typedef unsigned char SAMPLE; +#define SAMPLE_SILENCE (128) + +#endif + +typedef struct +{ + int frameIndex; /* Index into sample array. */ + int maxFrameIndex; + SAMPLE *recordedSamples; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int recordCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = (SAMPLE*)inputBuffer; + SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; + long framesToCalc; + long i; + int finished; + unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; + + (void) outputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + framesToCalc = framesLeft; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + finished = 0; + } + if( inputBuffer == NULL ) + { + for( i=0; iframeIndex += framesToCalc; + return finished; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int playCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; + SAMPLE *wptr = (SAMPLE*)outputBuffer; + unsigned int i; + int finished; + unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; + (void) inputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + /* final buffer... */ + for( i=0; iframeIndex += framesLeft; + finished = 1; + } + else + { + for( i=0; iframeIndex += framesPerBuffer; + finished = 0; + } + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int totalFrames; + int numSamples; + int numBytes; + SAMPLE max, average, val; + + printf("debug_record.c, sampleRate = %d, numChannels = %d\n", + SAMPLE_RATE, NUM_CHANNELS ); + fflush(stdout); + + data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */ + data.frameIndex = 0; + numSamples = totalFrames * NUM_CHANNELS; + + numBytes = numSamples * sizeof(SAMPLE); + data.recordedSamples = (SAMPLE *) malloc( numBytes ); + if( data.recordedSamples == NULL ) + { + printf("Could not allocate record array.\n"); + exit(1); + } + for( i=0; i max ) + { + max = val; + } + average += val; + } + + average = average / numSamples; + + if( PA_SAMPLE_TYPE == paFloat32 ) + { + printf("sample max amplitude = %f\n", (double) max ); + printf("sample average = %f\n", (double) average ); + } + else + { + printf("sample max amplitude = %d\n", (int) max ); + printf("sample average = %d\n", (int) average ); + } + + /* Write recorded data to a file. */ +#if 0 + { + FILE *fid; + fid = fopen("recorded.raw", "wb"); + if( fid == NULL ) + { + printf("Could not open file."); + } + else + { + fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid ); + fclose( fid ); + printf("Wrote data to 'recorded.raw'\n"); + } + } +#endif + + /* Playback recorded data. -------------------------------------------- */ + data.frameIndex = 0; + printf("Begin playback.\n"); fflush(stdout); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* NO input */ + PA_SAMPLE_TYPE, + NULL, + Pa_GetDefaultOutputDeviceID(), + NUM_CHANNELS, /* stereo output */ + PA_SAMPLE_TYPE, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + playCallback, + &data ); + if( err != paNoError ) goto error; + + if( stream ) + { + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + printf("Start playback!!\n"); fflush(stdout); + + while( Pa_StreamActive( stream ) ) + { + Pa_Sleep(1000); + printf("index = %d\n", data.frameIndex ); fflush(stdout); + } + + printf("Stop playback!!\n"); fflush(stdout); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + printf("Done.\n"); fflush(stdout); + } + free( data.recordedSamples ); + + Pa_Terminate(); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; +} diff --git a/lib/portaudio/pa_tests/debug_sine.c b/lib/portaudio/pa_tests/debug_sine.c new file mode 100644 index 0000000..1b95388 --- /dev/null +++ b/lib/portaudio/pa_tests/debug_sine.c @@ -0,0 +1,201 @@ +/* + * $Id: debug_sine.c,v 1.3 2003/03/02 08:01:40 dmazzoni Exp $ + * debug_sine.c + * Play a sine sweep using the Portable Audio api for several seconds. + * Hacked test for debugging PA. + * + * Author: Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +//#define OUTPUT_DEVICE (11) +#define NUM_SECONDS (8) +#define SLEEP_DUR (800) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (4096) + +#define MSEC_PER_BUFFER (1000 * FRAMES_PER_BUFFER / SAMPLE_RATE) + +#if 0 +#define MIN_LATENCY_MSEC (200) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#else +#define NUM_BUFFERS (0) +#endif + +#define MIN_FREQ (100.0f) +#define MAX_FREQ (4000.0f) +#define FREQ_SCALAR (1.00002f) +#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float left_phase; + float right_phase; + unsigned int framesToGo; +} +paTestData; +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ); +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int framesToCalc; + int i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( data->framesToGo < framesPerBuffer ) + { + framesToCalc = data->framesToGo; + data->framesToGo = 0; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + data->framesToGo -= framesPerBuffer; + } + + for( i=0; ileft_phase); /* left */ + *out++ = LookupSine(data, data->right_phase); /* right */ + data->left_phase += data->phase_increment; + if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; + data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ + if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; + /* sweep frequency then start over. */ + data->phase_increment *= FREQ_SCALAR; + if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ); + } + /* zero remainder of final buffer */ + for( ; i<(int)framesPerBuffer; i++ ) + { + *out++ = 0; /* left */ + *out++ = 0; /* right */ + } + // Pa_Sleep( 3 * MSEC_PER_BUFFER / 4 ); + // Pa_Sleep( MSEC_PER_BUFFER / 3 ); + + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int totalSamps; + printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS ); + printf("MSEC_PER_BUFFER = %d\n", MSEC_PER_BUFFER ); + + /* initialise sinusoidal wavetable */ + for( i=0; i + Modifications: + April 5th, 2001 - PLB - Check for NULL inputBuffer. +*/ +#include +#include +#include "portaudio.h" +#ifndef M_PI +#define M_PI (3.14159265) +#endif +typedef struct +{ + float sine[100]; + int phase; + int sampsToGo; +} +patest1data; +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long bufferFrames, + PaTimestamp outTime, void *userData ) +{ + patest1data *data = (patest1data*)userData; + float *in = (float*)inputBuffer; + float *out = (float*)outputBuffer; + int framesToCalc = bufferFrames; + unsigned long i; + int finished = 0; + if(inputBuffer == NULL) return 0; + if( data->sampsToGo < bufferFrames ) + { + finished = 1; + } + for( i=0; iphase >= 100 ) + data->phase = 0; + } + data->sampsToGo -= bufferFrames; + /* zero remainder of final buffer if not already done */ + for( ; i +#include +#include "portaudio.h" + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i,j; + int numDevices; + const PaDeviceInfo *pdi; + PaError err; + Pa_Initialize(); + numDevices = Pa_CountDevices(); + if( numDevices < 0 ) + { + printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } + printf("Number of devices = %d\n", numDevices ); + for( i=0; iname ); + printf("Max Inputs = %d", pdi->maxInputChannels ); + printf(", Max Outputs = %d\n", pdi->maxOutputChannels ); + if( pdi->numSampleRates == -1 ) + { + printf("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] ); + } + else + { + printf("Sample Rates ="); + for( j=0; jnumSampleRates; j++ ) + { + printf(" %8.2f,", pdi->sampleRates[j] ); + } + printf("\n"); + } + printf("Native Sample Formats = "); + if( pdi->nativeSampleFormats & paInt8 ) printf("paInt8, "); + if( pdi->nativeSampleFormats & paUInt8 ) printf("paUInt8, "); + if( pdi->nativeSampleFormats & paInt16 ) printf("paInt16, "); + if( pdi->nativeSampleFormats & paInt32 ) printf("paInt32, "); + if( pdi->nativeSampleFormats & paFloat32 ) printf("paFloat32, "); + if( pdi->nativeSampleFormats & paInt24 ) printf("paInt24, "); + if( pdi->nativeSampleFormats & paPackedInt24 ) printf("paPackedInt24, "); + printf("\n"); + } + Pa_Terminate(); + + printf("----------------------------------------------\n"); + return 0; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/lib/portaudio/pa_tests/pa_fuzz.c b/lib/portaudio/pa_tests/pa_fuzz.c new file mode 100644 index 0000000..206df4b --- /dev/null +++ b/lib/portaudio/pa_tests/pa_fuzz.c @@ -0,0 +1,156 @@ +/* + * $Id: pa_fuzz.c,v 1.3 2003/03/02 08:01:40 dmazzoni Exp $ + * pa_fuzz.c + * Distort input like a fuzz boz. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +/* +** Note that many of the older ISA sound cards on PCs do NOT support +** full duplex audio (simultaneous record and playback). +** And some only support full duplex at lower sample rates. +*/ +#define SAMPLE_RATE (44100) +#define PA_SAMPLE_TYPE paFloat32 +#define FRAMES_PER_BUFFER (64) + +typedef float SAMPLE; + +float CubicAmplifier( float input ); +static int fuzzCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + +/* Non-linear amplifier with soft distortion curve. */ +float CubicAmplifier( float input ) +{ + float output, temp; + if( input < 0.0 ) + { + temp = input + 1.0f; + output = (temp * temp * temp) - 1.0f; + } + else + { + temp = input - 1.0f; + output = (temp * temp * temp) + 1.0f; + } + + return output; +} +#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x)))) + +static int gNumNoInputs = 0; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int fuzzCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + SAMPLE *out = (SAMPLE*)outputBuffer; + SAMPLE *in = (SAMPLE*)inputBuffer; + unsigned int i; + (void) outTime; /* Prevent unused variable warnings. */ + (void) userData; + + if( inputBuffer == NULL ) + { + for( i=0; i +#include +#include +#include "portaudio.h" + +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TWOPI (M_PI * 2.0) + +#define DEFAULT_BUFFER_SIZE (64) + +typedef struct +{ + double left_phase; + double right_phase; +} +paTestData; + +/* Very simple synthesis routine to generate two sine waves. */ +static int paminlatCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + double left_phaseInc = 0.02; + double right_phaseInc = 0.06; + + double left_phase = data->left_phase; + double right_phase = data->right_phase; + + for( i=0; i TWOPI ) left_phase -= TWOPI; + *out++ = (float) sin( left_phase ); + + right_phase += right_phaseInc; + if( right_phase > TWOPI ) right_phase -= TWOPI; + *out++ = (float) sin( right_phase ); + } + + data->left_phase = left_phase; + data->right_phase = right_phase; + return 0; +} +void main( int argc, char **argv ); +void main( int argc, char **argv ) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int go; + int numBuffers = 0; + int minBuffers = 0; + int framesPerBuffer; + double sampleRate = 44100.0; + char str[256]; + printf("paminlat - Determine minimum latency for your computer.\n"); + printf(" usage: paminlat {framesPerBuffer}\n"); + printf(" for example: paminlat 256\n"); + printf("Adjust your stereo until you hear a smooth tone in each speaker.\n"); + printf("Then try to find the smallest number of buffers that still sounds smooth.\n"); + printf("Note that the sound will stop momentarily when you change the number of buffers.\n"); + /* Get bufferSize from command line. */ + framesPerBuffer = ( argc > 1 ) ? atol( argv[1] ) : DEFAULT_BUFFER_SIZE; + printf("Frames per buffer = %d\n", framesPerBuffer ); + + data.left_phase = data.right_phase = 0.0; + err = Pa_Initialize(); + if( err != paNoError ) goto error; + /* Ask PortAudio for the recommended minimum number of buffers. */ + numBuffers = minBuffers = Pa_GetMinNumBuffers( framesPerBuffer, sampleRate ); + printf("NumBuffers set to %d based on a call to Pa_GetMinNumBuffers()\n", numBuffers ); + /* Try different numBuffers in a loop. */ + go = 1; + while( go ) + { + + printf("Latency = framesPerBuffer * numBuffers = %d * %d = %d frames = %d msecs.\n", + framesPerBuffer, numBuffers, framesPerBuffer*numBuffers, + (int)((1000 * framesPerBuffer * numBuffers) / sampleRate) ); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + sampleRate, + framesPerBuffer, + numBuffers, /* number of buffers */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + paminlatCallback, + &data ); + if( err != paNoError ) goto error; + if( stream == NULL ) goto error; + /* Start audio. */ + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + /* Ask user for a new number of buffers. */ + printf("\nMove windows around to see if the sound glitches.\n"); + printf("NumBuffers currently %d, enter new number, or 'q' to quit: ", numBuffers ); + gets( str ); + if( str[0] == 'q' ) go = 0; + else + { + numBuffers = atol( str ); + if( numBuffers < minBuffers ) + { + printf( "numBuffers below minimum of %d! Set to minimum!!!\n", minBuffers ); + numBuffers = minBuffers; + } + } + /* Stop sound until ENTER hit. */ + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + } + printf("A good setting for latency would be somewhat higher than\n"); + printf("the minimum latency that worked.\n"); + printf("PortAudio: Test finished.\n"); + Pa_Terminate(); + return; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); +} diff --git a/lib/portaudio/pa_tests/paqa_devs.c b/lib/portaudio/pa_tests/paqa_devs.c new file mode 100644 index 0000000..cc2d3dd --- /dev/null +++ b/lib/portaudio/pa_tests/paqa_devs.c @@ -0,0 +1,322 @@ +/* + * $Id: paqa_devs.c,v 1.2 2003/03/02 08:01:40 dmazzoni Exp $ + * paqa_devs.c + * Self Testing Quality Assurance app for PortAudio + * Try to open each device and run through all the + * possible configurations. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#include "pa_trace.h" +/****************************************** Definitions ***********/ +#define MODE_INPUT (0) +#define MODE_OUTPUT (1) +typedef struct PaQaData +{ + unsigned long framesLeft; + int numChannels; + int bytesPerSample; + int mode; + short sawPhase; + PaSampleFormat format; +} PaQaData; + +/****************************************** Prototypes ***********/ +static void TestDevices( int mode ); +static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels ); +static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels, PaSampleFormat format ); +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + +/****************************************** Globals ***********/ +static int gNumPassed = 0; +static int gNumFailed = 0; + +/****************************************** Macros ***********/ +/* Print ERROR if it fails. Tally success or failure. */ +/* Odd do-while wrapper seems to be needed for some compilers. */ +#define EXPECT(_exp) \ + do \ + { \ + if ((_exp)) {\ + /* printf("SUCCESS for %s\n", #_exp ); */ \ + gNumPassed++; \ + } \ + else { \ + printf("ERROR - 0x%x - %s for %s\n", result, \ + ((result == 0) ? "-" : Pa_GetErrorText(result)), \ + #_exp ); \ + gNumFailed++; \ + goto error; \ + } \ + } while(0) +/*******************************************************************/ +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + unsigned long i; + short phase; + PaQaData *data = (PaQaData *) userData; + (void) inputBuffer; + (void) outTime; + + /* Play simple sawtooth wave. */ + if( data->mode == MODE_OUTPUT ) + { + phase = data->sawPhase; + switch( data->format ) + { + case paFloat32: + { + float *out = (float *) outputBuffer; + for( i=0; inumChannels == 2 ) + { + *out++ = (float) (phase * (1.0 / 32768.0)); + } + } + } + break; + + case paInt32: + { + int *out = (int *) outputBuffer; + for( i=0; inumChannels == 2 ) + { + *out++ = ((int) phase ) << 16; + } + } + } + break; + case paInt16: + { + short *out = (short *) outputBuffer; + for( i=0; inumChannels == 2 ) + { + *out++ = phase; + } + } + } + break; + + default: + { + unsigned char *out = (unsigned char *) outputBuffer; + unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; + for( i=0; isawPhase = phase; + } + /* Are we through yet? */ + if( data->framesLeft > framesPerBuffer ) + { + AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft ); + data->framesLeft -= framesPerBuffer; + return 0; + } + else + { + AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft ); + data->framesLeft = 0; + return 1; + } +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError result; + EXPECT( ((result=Pa_Initialize()) == 0) ); + printf("Test OUTPUT ---------------\n"); + TestDevices( MODE_OUTPUT ); + printf("Test INPUT ---------------\n"); + TestDevices( MODE_INPUT ); +error: + Pa_Terminate(); + printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed ); +} +/******************************************************************* +* Try each output device, through its full range of capabilities. */ +static void TestDevices( int mode ) +{ + int id,jc,kr; + int maxChannels; + const PaDeviceInfo *pdi; + int numDevices = Pa_CountDevices(); + /* Iterate through all devices. */ + for( id=0; idmaxInputChannels : pdi->maxOutputChannels; + for( jc=1; jc<=maxChannels; jc++ ) + { + printf("Name = %s\n", pdi->name ); + /* Try each legal sample rate. */ + if( pdi->numSampleRates == -1 ) + { + double low, high; + low = pdi->sampleRates[0]; + high = pdi->sampleRates[1]; + if( low < 8000.0 ) low = 8000.0; + TestFormats( mode, id, low, jc ); +#define TESTSR(sr) {if(((sr)>=low) && ((sr)<=high)) TestFormats( mode, id, (sr), jc ); } + + TESTSR(11025.0); + TESTSR(22050.0); + TESTSR(34567.0); + TESTSR(44100.0); + TestFormats( mode, id, high, jc ); + } + else + { + for( kr=0; krnumSampleRates; kr++ ) + { + TestFormats( mode, id, pdi->sampleRates[kr], jc ); + } + } + } + } +} +/*******************************************************************/ +static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels ) +{ + TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); /* */ + TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); /* */ + TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); /* */ +} +/*******************************************************************/ +static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels, PaSampleFormat format ) +{ + PortAudioStream *stream = NULL; + PaError result; + PaQaData myData; +#define FRAMES_PER_BUFFER (64) + printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %d -------\n", + ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT", + deviceID, sampleRate, numChannels, format); + fflush(stdout); + /* Setup data for synthesis thread. */ + myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */ + myData.numChannels = numChannels; + myData.mode = mode; + myData.format = format; + switch( format ) + { + case paFloat32: + case paInt32: + case paInt24: + myData.bytesPerSample = 4; + break; + case paPackedInt24: + myData.bytesPerSample = 3; + break; + default: + myData.bytesPerSample = 2; + break; + } + EXPECT( ((result = Pa_OpenStream( + &stream, + ( mode == MODE_INPUT ) ? deviceID : paNoDevice, + ( mode == MODE_INPUT ) ? numChannels : 0, + format, + NULL, + ( mode == MODE_OUTPUT ) ? deviceID : paNoDevice, + ( mode == MODE_OUTPUT ) ? numChannels : 0, + format, + NULL, + sampleRate, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + QaCallback, + &myData ) + ) == 0) ); + if( stream ) + { + PaTimestamp oldStamp, newStamp; + unsigned long oldFrames; + int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400; + int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); + int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate); + if( msec < minDelay ) msec = minDelay; + printf("msec = %d\n", msec); /**/ + EXPECT( ((result=Pa_StartStream( stream )) == 0) ); + /* Check to make sure PortAudio is advancing timeStamp. */ + result = paNoError; + oldStamp = Pa_StreamTime(stream); + fflush(stdout); + Pa_Sleep(msec); + newStamp = Pa_StreamTime(stream); + printf("oldStamp = %g,newStamp = %g\n", oldStamp, newStamp ); /**/ + EXPECT( (oldStamp < newStamp) ); + /* Check to make sure callback is decrementing framesLeft. */ + oldFrames = myData.framesLeft; + Pa_Sleep(msec); + printf("oldFrames = %d, myData.framesLeft = %d\n", oldFrames, myData.framesLeft ); /**/ + EXPECT( (oldFrames > myData.framesLeft) ); + EXPECT( ((result=Pa_CloseStream( stream )) == 0) ); + stream = NULL; + } +error: + if( stream != NULL ) Pa_CloseStream( stream ); + fflush(stdout); + return result; +} diff --git a/lib/portaudio/pa_tests/paqa_errs.c b/lib/portaudio/pa_tests/paqa_errs.c new file mode 100644 index 0000000..a5f4726 --- /dev/null +++ b/lib/portaudio/pa_tests/paqa_errs.c @@ -0,0 +1,330 @@ +/* + * $Id: paqa_errs.c,v 1.2 2003/03/02 08:01:41 dmazzoni Exp $ + * paqa_devs.c + * Self Testing Quality Assurance app for PortAudio + * Do lots of bad things to test error reporting. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +/****************************************** Definitions ***********/ +#define MODE_INPUT (0) +#define MODE_OUTPUT (1) +#define FRAMES_PER_BUFFER (64) +#define SAMPLE_RATE (44100.0) +#define NUM_BUFFERS (0) +typedef struct PaQaData +{ + unsigned long framesLeft; + int numChannels; + int bytesPerSample; + int mode; +} +PaQaData; +/****************************************** Prototypes ***********/ +static void TestDevices( int mode ); +static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels ); +static int TestBadOpens( void ); +static int TestBadActions( void ); +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/****************************************** Globals ***********/ +static int gNumPassed = 0; +static int gNumFailed = 0; +/****************************************** Macros ***********/ +/* Print ERROR if it fails. Tally success or failure. */ +/* Odd do-while wrapper seems to be needed for some compilers. */ +#define EXPECT( msg, _exp) \ + do \ + { \ + if ((_exp)) {\ + gNumPassed++; \ + } \ + else { \ + printf("\nERROR %s\n - 0x%x - %s for %s\n", (msg), result, Pa_GetErrorText(result), #_exp ); \ + gNumFailed++; \ + goto error; \ + } \ + } while(0) +#define HOPEFOR( msg, _exp) \ + do \ + { \ + if ((_exp)) {\ + gNumPassed++; \ + } \ + else { \ + printf("\nERROR %s\n - 0x%x - %s for %s\n", (msg), result, Pa_GetErrorText(result), #_exp ); \ + gNumFailed++; \ + } \ + } while(0) +/*******************************************************************/ +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + unsigned long i; + unsigned char *out = (unsigned char *) outputBuffer; + PaQaData *data = (PaQaData *) userData; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + /* Zero out buffer so we don't hear terrible noise. */ + if( data->mode == MODE_OUTPUT ) + { + unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; + for( i=0; iframesLeft > framesPerBuffer ) + { + data->framesLeft -= framesPerBuffer; + return 0; + } + else + { + data->framesLeft = 0; + return 1; + } +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError result; + EXPECT( "init", ((result=Pa_Initialize()) == 0) ); + TestBadActions(); + TestBadOpens(); +error: + Pa_Terminate(); + printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed ); + return 0; +} +/*******************************************************************/ +static int TestBadOpens( void ) +{ + PortAudioStream *stream = NULL; + PaError result; + PaQaData myData; + /* Setup data for synthesis thread. */ + myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */ + myData.numChannels = 1; + myData.mode = MODE_OUTPUT; + HOPEFOR( "No devices specified.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + HOPEFOR( "Out of range input device specified.",( + (result = Pa_OpenStream( + &stream, + Pa_CountDevices(), 0, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + + HOPEFOR( "Out of range output device specified.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_CountDevices(), 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + HOPEFOR( "Zero input channels.",( + (result = Pa_OpenStream( + &stream, + Pa_GetDefaultInputDeviceID(), 0, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + HOPEFOR( "Zero output channels.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + HOPEFOR( "Nonzero input channels but no device.",( + (result = Pa_OpenStream( + &stream, + Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL, + paNoDevice, 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + + HOPEFOR( "Nonzero output channels but no device.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 2, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + HOPEFOR( "NULL stream pointer.",( + (result = Pa_OpenStream( + NULL, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paBadStreamPtr) ); + HOPEFOR( "Low sample rate.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + 1.0, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidSampleRate) ); + HOPEFOR( "High sample rate.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + 10000000.0, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidSampleRate) ); + HOPEFOR( "NULL callback.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + NULL, + &myData ) + ) == paNullCallback) ); + HOPEFOR( "Bad flag.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + (1<<3), + QaCallback, + &myData ) + ) == paInvalidFlag) ); + +#if 1 /* FIXME - this is legal for some implementations. */ + HOPEFOR( "Use input device as output device.",( + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + + HOPEFOR( "Use output device as input device.",( + (result = Pa_OpenStream( + &stream, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); +#endif + + if( stream != NULL ) Pa_CloseStream( stream ); + return result; +} +/*******************************************************************/ +static int TestBadActions( void ) +{ + PortAudioStream *stream = NULL; + PaError result; + PaQaData myData; + /* Setup data for synthesis thread. */ + myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */ + myData.numChannels = 1; + myData.mode = MODE_OUTPUT; + /* Default output. */ + EXPECT( "TestBadActions", ((result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == 0) ); + HOPEFOR( "start", ((result = Pa_StartStream( NULL )) == paBadStreamPtr) ); + HOPEFOR( "stop", ((result = Pa_StopStream( NULL )) == paBadStreamPtr) ); + HOPEFOR( "active?", ((result = Pa_StreamActive( NULL )) == paBadStreamPtr) ); + HOPEFOR( "close", ((result = Pa_CloseStream( NULL )) == paBadStreamPtr) ); + HOPEFOR( "time?", ((result = (PaError)Pa_StreamTime( NULL )) != 0) ); + HOPEFOR( "CPULoad?", ((result = (PaError)Pa_GetCPULoad( NULL )) != 0) ); +error: + if( stream != NULL ) Pa_CloseStream( stream ); + return result; +} diff --git a/lib/portaudio/pa_tests/patest1.c b/lib/portaudio/pa_tests/patest1.c new file mode 100644 index 0000000..17ba89b --- /dev/null +++ b/lib/portaudio/pa_tests/patest1.c @@ -0,0 +1,114 @@ +/* + $Id: patest1.c,v 1.3 2003/03/02 08:01:41 dmazzoni Exp $ + patest1.c + Ring modulate the audio input with a sine wave for 20 seconds + using the Portable Audio api + Author: Ross Bencina + Modifications: + April 5th, 2001 - PLB - Check for NULL inputBuffer. +*/ +#include +#include +#include "portaudio.h" +#ifndef M_PI +#define M_PI (3.14159265) +#endif +typedef struct +{ + float sine[100]; + int phase; + int sampsToGo; +} +patest1data; +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long bufferFrames, + PaTimestamp outTime, void *userData ) +{ + patest1data *data = (patest1data*)userData; + float *in = (float*)inputBuffer; + float *out = (float*)outputBuffer; + int framesToCalc = bufferFrames; + unsigned long i; + int finished = 0; + /* Check to see if any input data is available. */ + if(inputBuffer == NULL) return 0; + if( data->sampsToGo < bufferFrames ) + { + framesToCalc = data->sampsToGo; + finished = 1; + } + for( i=0; isine[data->phase]; /* left */ + *out++ = *in++ * data->sine[data->phase++]; /* right */ + if( data->phase >= 100 ) + data->phase = 0; + } + data->sampsToGo -= framesToCalc; + /* zero remainder of final buffer if not already done */ + for( ; i +#include +#include "portaudio.h" +#define NUM_SECONDS (4) +#define SAMPLE_RATE (44100) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct paTestData +{ + float sine[TABLE_SIZE]; + float amplitude; + int left_phase; + int right_phase; +} +paTestData; +PaError PlaySine( paTestData *data, unsigned long flags, float amplitude ); +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int sineCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float amplitude = data->amplitude; + unsigned int i; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + for( i=0; isine[data->left_phase]; /* left */ + *out++ = amplitude * data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + paTestData DATA; + int i; + printf("PortAudio Test: output sine wave with and without clipping.\n"); + /* initialise sinusoidal wavetable */ + for( i=0; ileft_phase = data->right_phase = 0; + data->amplitude = amplitude; + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + 1024, + 0, /* number of buffers, if zero then use default minimum */ + flags, /* we won't output out of range samples so don't bother clipping them */ + sineCallback, + data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + Pa_Sleep( NUM_SECONDS * 1000 ); + printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) ); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + return paNoError; +error: + return err; +} diff --git a/lib/portaudio/pa_tests/patest_dither.c b/lib/portaudio/pa_tests/patest_dither.c new file mode 100644 index 0000000..de75cf2 --- /dev/null +++ b/lib/portaudio/pa_tests/patest_dither.c @@ -0,0 +1,152 @@ +/* + * $Id: patest_dither.c,v 1.2 2003/03/02 08:01:42 dmazzoni Exp $ + * patest_dither.c + * Attempt to hear difference between dithered and non-dithered signal. + * This only has an effect if the native format is 16 bit. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (4) +#define SAMPLE_RATE (44100) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct paTestData +{ + float sine[TABLE_SIZE]; + float amplitude; + int left_phase; + int right_phase; +} +paTestData; +PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude ); +static int sineCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int sineCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float amplitude = data->amplitude; + unsigned int i; + (void) outTime; + (void) inputBuffer; + for( i=0; isine[data->left_phase]; /* left */ + *out++ = amplitude * data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + paTestData DATA; + int i; + float amplitude = 32.0 / (1<<15); + printf("PortAudio Test: output EXTREMELY QUIET sine wave with and without dithering.\n"); + /* initialise sinusoidal wavetable */ + for( i=0; ileft_phase = data->right_phase = 0; + data->amplitude = amplitude; + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + 1024, + 0, /* number of buffers, if zero then use default minimum */ + flags, /* we won't output out of range samples so don't bother clipping them */ + sineCallback, + (void *)data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + Pa_Sleep( NUM_SECONDS * 1000 ); + printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) ); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + return paNoError; +error: + return err; +} diff --git a/lib/portaudio/pa_tests/patest_latency.c b/lib/portaudio/pa_tests/patest_latency.c new file mode 100644 index 0000000..5b1eb1e --- /dev/null +++ b/lib/portaudio/pa_tests/patest_latency.c @@ -0,0 +1,176 @@ +/* + * $Id: patest_latency.c,v 1.2 2003/03/02 08:01:42 dmazzoni Exp $ + * Hear the latency caused by big buffers. + * Play a sine wave and change frequency based on letter input. + * + * Author: Phil Burk , and Darren Gibbs + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (64) + +#if 0 +#define MIN_LATENCY_MSEC (2000) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#else +#define NUM_BUFFERS (0) +#endif + +#define MIN_FREQ (100.0f) +#define CalcPhaseIncrement(freq) ((freq)/SAMPLE_RATE) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float left_phase; + float right_phase; +} +paTestData; +float LookupSine( paTestData *data, float phase ); +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; ileft_phase); /* left */ + *out++ = LookupSine(data, data->right_phase); /* right */ + data->left_phase += data->phase_increment; + if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; + data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ + if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int done = 0; + printf("PortAudio Test: enter letter then hit ENTER. numBuffers = %d\n", NUM_BUFFERS ); + /* initialise sinusoidal wavetable */ + for( i=0; i + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (8) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (512) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; + int toggle; + int countDown; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; itoggle ) + { + *out++ = data->sine[data->left_phase]; /* left */ + *out++ = 0; /* right */ + } + else + { + *out++ = 0; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + } + + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + + if( data->countDown < 0 ) + { + data->countDown = SAMPLE_RATE; + data->toggle = !data->toggle; + } + data->countDown -= framesPerBuffer; + + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int timeout; + + printf("Play different tone sine waves that alternate between left and right channel.\n"); + printf("The low tone should be on the left channel.\n"); + + /* initialise sinusoidal wavetable */ + for( i=0; i 0 ) + { + Pa_Sleep( 300 ); + timeout -= 1; + } + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/lib/portaudio/pa_tests/patest_longsine.c b/lib/portaudio/pa_tests/patest_longsine.c new file mode 100644 index 0000000..6f97e9d --- /dev/null +++ b/lib/portaudio/pa_tests/patest_longsine.c @@ -0,0 +1,137 @@ +/* + * $Id: patest_longsine.c,v 1.2 2003/03/02 08:01:42 dmazzoni Exp $ + * patest_longsine.c + * Play a sine wave using the Portable Audio api until ENTER hit. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define SAMPLE_RATE (44100) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + return 0; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + printf("PortAudio Test: output sine wave.\n"); + + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (1) +#define SAMPLE_RATE (44100) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct +{ + short sine[TABLE_SIZE]; + int left_phase; + int right_phase; + unsigned int sampsToGo; +} +paTestData; +PaError TestOnce( void ); +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + short *out = (short*)outputBuffer; + unsigned int i; + int finished = 0; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + if( data->sampsToGo < framesPerBuffer ) + { + /* final buffer... */ + + for( i=0; isampsToGo; i++ ) + { + *out++ = data->sine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + /* zero remainder of final buffer */ + for( ; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + data->sampsToGo -= framesPerBuffer; + } + return finished; +} +/*******************************************************************/ +#ifdef MACINTOSH +int main(void); +int main(void) +{ + int i; + PaError err; + int numLoops = 10; + printf("Loop %d times.\n", numLoops ); + for( i=0; i 1 ) + { + numLoops = atoi(argv[1]); + } + for( i=0; i + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define MAX_SINES (500) +#define MAX_USAGE (0.8) +#define SAMPLE_RATE (44100) +#define FREQ_TO_PHASE_INC(freq) (freq/(float)SAMPLE_RATE) + +#define MIN_PHASE_INC FREQ_TO_PHASE_INC(200.0f) +#define MAX_PHASE_INC (MIN_PHASE_INC * (1 << 5)) + +#define FRAMES_PER_BUFFER (512) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TWOPI (M_PI * 2.0) + +#define TABLE_SIZE (512) + +typedef struct paTestData +{ + int numSines; + float sine[TABLE_SIZE + 1]; /* add one for guard point for interpolation */ + float phases[MAX_SINES]; +} +paTestData; + +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ); +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float outSample; + float scaler; + int numForScale; + unsigned long i; + int j; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + +/* Detemine amplitude scaling factor */ + numForScale = data->numSines; + if( numForScale < 8 ) numForScale = 8; /* prevent pops at beginning */ + scaler = 1.0f / numForScale; + + for( i=0; inumSines; j++ ) + { + /* Advance phase of next oscillator. */ + phase = data->phases[j]; + phase += phaseInc; + if( phase >= 1.0 ) phase -= 1.0; + + output += LookupSine(data, phase); + data->phases[j] = phase; + + phaseInc *= 1.02f; + if( phaseInc > MAX_PHASE_INC ) phaseInc = MIN_PHASE_INC; + } + + outSample = (float) (output * scaler); + *out++ = outSample; /* Left */ + *out++ = outSample; /* Right */ + } + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i; + PortAudioStream *stream; + PaError err; + paTestData data = {0}; + double load; + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include "portaudio.h" +#define PINK_MAX_RANDOM_ROWS (30) +#define PINK_RANDOM_BITS (24) +#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS) +typedef struct +{ + long pink_Rows[PINK_MAX_RANDOM_ROWS]; + long pink_RunningSum; /* Used to optimize summing of generators. */ + int pink_Index; /* Incremented each sample. */ + int pink_IndexMask; /* Index wrapped by ANDing with this mask. */ + float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */ +} +PinkNoise; +/* Prototypes */ +static unsigned long GenerateRandomNumber( void ); +void InitializePinkNoise( PinkNoise *pink, int numRows ); +float GeneratePinkNoise( PinkNoise *pink ); +/************************************************************/ +/* Calculate pseudo-random 32 bit number based on linear congruential method. */ +static unsigned long GenerateRandomNumber( void ) +{ + /* Change this seed for different random sequences. */ + static unsigned long randSeed = 22222; + randSeed = (randSeed * 196314165) + 907633515; + return randSeed; +} +/************************************************************/ +/* Setup PinkNoise structure for N rows of generators. */ +void InitializePinkNoise( PinkNoise *pink, int numRows ) +{ + int i; + long pmax; + pink->pink_Index = 0; + pink->pink_IndexMask = (1<pink_Scalar = 1.0f / pmax; + /* Initialize rows. */ + for( i=0; ipink_Rows[i] = 0; + pink->pink_RunningSum = 0; +} +#define PINK_MEASURE +#ifdef PINK_MEASURE +float pinkMax = -999.0; +float pinkMin = 999.0; +#endif +/* Generate Pink noise values between -1.0 and +1.0 */ +float GeneratePinkNoise( PinkNoise *pink ) +{ + long newRandom; + long sum; + float output; + /* Increment and mask index. */ + pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask; + /* If index is zero, don't update any random values. */ + if( pink->pink_Index != 0 ) + { + /* Determine how many trailing zeros in PinkIndex. */ + /* This algorithm will hang if n==0 so test first. */ + int numZeros = 0; + int n = pink->pink_Index; + while( (n & 1) == 0 ) + { + n = n >> 1; + numZeros++; + } + /* Replace the indexed ROWS random value. + * Subtract and add back to RunningSum instead of adding all the random + * values together. Only one changes each time. + */ + pink->pink_RunningSum -= pink->pink_Rows[numZeros]; + newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; + pink->pink_RunningSum += newRandom; + pink->pink_Rows[numZeros] = newRandom; + } + + /* Add extra white noise value. */ + newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; + sum = pink->pink_RunningSum + newRandom; + /* Scale to range of -1.0 to 0.9999. */ + output = pink->pink_Scalar * sum; +#ifdef PINK_MEASURE + /* Check Min/Max */ + if( output > pinkMax ) pinkMax = output; + else if( output < pinkMin ) pinkMin = output; +#endif + return output; +} +/*******************************************************************/ +#define PINK_TEST +#ifdef PINK_TEST +/* Context for callback routine. */ +typedef struct +{ + PinkNoise leftPink; + PinkNoise rightPink; + unsigned int sampsToGo; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + int finished; + int i; + int numFrames; + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + /* Are we almost at end. */ + if( data->sampsToGo < framesPerBuffer ) + { + numFrames = data->sampsToGo; + finished = 1; + } + else + { + numFrames = framesPerBuffer; + finished = 0; + } + for( i=0; ileftPink ); + *out++ = GeneratePinkNoise( &data->rightPink ); + } + data->sampsToGo -= numFrames; + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int totalSamps; + /* Initialize two pink noise signals with different numbers of rows. */ + InitializePinkNoise( &data.leftPink, 12 ); + InitializePinkNoise( &data.rightPink, 16 ); + /* Look at a few values. */ + { + int i; + float pink; + for( i=0; i<20; i++ ) + { + pink = GeneratePinkNoise( &data.leftPink ); + printf("Pink = %f\n", pink ); + } + } + data.sampsToGo = totalSamps = 8*44100; /* Play for a few seconds. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + /* Open a stereo PortAudio stream so we can hear the result. */ + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + 44100., + 2048, /* 46 msec buffers */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + printf("Waiting for sound to finish.\n"); + while( Pa_StreamActive( stream ) ) + { + Pa_Sleep(100); /* SPIN! */ + } + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; +#ifdef PINK_MEASURE + printf("Pink min = %f, max = %f\n", pinkMin, pinkMax ); +#endif + Pa_Terminate(); + return 0; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return 0; +} +#endif /* PINK_TEST */ diff --git a/lib/portaudio/pa_tests/patest_record.c b/lib/portaudio/pa_tests/patest_record.c new file mode 100644 index 0000000..614621e --- /dev/null +++ b/lib/portaudio/pa_tests/patest_record.c @@ -0,0 +1,323 @@ +/* + * $Id: patest_record.c,v 1.3 2003/03/02 08:01:42 dmazzoni Exp $ + * patest_record.c + * Record input into an array. + * Save array to a file. + * Playback recorded data. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +/* #define SAMPLE_RATE (17932) /* Test failure to open with this value. */ +#define SAMPLE_RATE (22050) +#define NUM_SECONDS (5) +#define NUM_CHANNELS (2) +/* #define DITHER_FLAG (paDitherOff) /**/ +#define DITHER_FLAG (0) /**/ +#define FRAMES_PER_BUFFER (1024) + +/* Select sample format. */ +#if 1 +#define PA_SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#define SAMPLE_SILENCE (0.0f) +#elif 0 +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#define SAMPLE_SILENCE (0) +#elif 0 +#define PA_SAMPLE_TYPE paInt8 +typedef char SAMPLE; +#define SAMPLE_SILENCE (0) +#else +#define PA_SAMPLE_TYPE paUInt8 +typedef unsigned char SAMPLE; +#define SAMPLE_SILENCE (128) + +#endif + +typedef struct +{ + int frameIndex; /* Index into sample array. */ + int maxFrameIndex; + SAMPLE *recordedSamples; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int recordCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = (SAMPLE*)inputBuffer; + SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; + long framesToCalc; + long i; + int finished; + unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; + + (void) outputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + framesToCalc = framesLeft; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + finished = 0; + } + if( inputBuffer == NULL ) + { + for( i=0; iframeIndex += framesToCalc; + return finished; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int playCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; + SAMPLE *wptr = (SAMPLE*)outputBuffer; + unsigned int i; + int finished; + unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; + (void) inputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + /* final buffer... */ + for( i=0; iframeIndex += framesLeft; + finished = 1; + } + else + { + for( i=0; iframeIndex += framesPerBuffer; + finished = 0; + } + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int totalFrames; + int numSamples; + int numBytes; + SAMPLE max, average, val; + printf("patest_record.c\n"); fflush(stdout); + + data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */ + data.frameIndex = 0; + numSamples = totalFrames * NUM_CHANNELS; + + numBytes = numSamples * sizeof(SAMPLE); + data.recordedSamples = (SAMPLE *) malloc( numBytes ); + if( data.recordedSamples == NULL ) + { + printf("Could not allocate record array.\n"); + exit(1); + } + for( i=0; i max ) + { + max = val; + } + average += val; + } + + average = average / numSamples; + + if( PA_SAMPLE_TYPE == paFloat32 ) + { + printf("sample max amplitude = %f\n", max ); + printf("sample average = %f\n", average ); + } + else + { + printf("sample max amplitude = %d\n", max ); + printf("sample average = %d\n", average ); + } + + /* Write recorded data to a file. */ +#if 0 + { + FILE *fid; + fid = fopen("recorded.raw", "wb"); + if( fid == NULL ) + { + printf("Could not open file."); + } + else + { + fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid ); + fclose( fid ); + printf("Wrote data to 'recorded.raw'\n"); + } + } +#endif + + /* Playback recorded data. -------------------------------------------- */ + data.frameIndex = 0; + printf("Begin playback.\n"); fflush(stdout); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* NO input */ + PA_SAMPLE_TYPE, + NULL, + Pa_GetDefaultOutputDeviceID(), + NUM_CHANNELS, /* stereo output */ + PA_SAMPLE_TYPE, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + playCallback, + &data ); + if( err != paNoError ) goto error; + + if( stream ) + { + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + printf("Waiting for playback to finish.\n"); fflush(stdout); + + while( Pa_StreamActive( stream ) ) Pa_Sleep(100); + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + printf("Done.\n"); fflush(stdout); + } + free( data.recordedSamples ); + + Pa_Terminate(); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; +} diff --git a/lib/portaudio/pa_tests/patest_ringmix.c b/lib/portaudio/pa_tests/patest_ringmix.c new file mode 100644 index 0000000..097d577 --- /dev/null +++ b/lib/portaudio/pa_tests/patest_ringmix.c @@ -0,0 +1,41 @@ +/* $Id: patest_ringmix.c,v 1.2 2003/03/02 08:01:42 dmazzoni Exp $ */ + +#include "stdio.h" +#include "portaudio.h" +/* This will be called asynchronously by the PortAudio engine. */ +static int myCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, PaTimestamp outTime, void *userData ) +{ + float *out = (float *) outputBuffer; + float *in = (float *) inputBuffer; + float leftInput, rightInput; + unsigned int i; + if( inputBuffer == NULL ) return 0; + /* Read input buffer, process data, and fill output buffer. */ + for( i=0; i +#include +#include "portaudio.h" +#define NUM_SECONDS (4) +#define SAMPLE_RATE (44100) +typedef struct +{ + float left_phase; + float right_phase; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + /* Cast data passed through stream to our structure. */ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; ileft_phase; /* left */ + *out++ = data->right_phase; /* right */ + /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */ + data->left_phase += 0.01f; + /* When signal reaches top, drop back down. */ + if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f; + /* higher pitch so we can distinguish left and right. */ + data->right_phase += 0.03f; + if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f; + } + return 0; +} +/*******************************************************************/ +static paTestData data; +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + printf("PortAudio Test: output sawtooth wave.\n"); + /* Initialize our data for use by callback. */ + data.left_phase = data.right_phase = 0.0; + /* Initialize library before making any other calls. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + /* Open an audio I/O stream. */ + err = Pa_OpenDefaultStream( + &stream, + 0, /* no input channels */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + SAMPLE_RATE, + 256, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + /* Sleep for several seconds. */ + Pa_Sleep(NUM_SECONDS*1000); + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/lib/portaudio/pa_tests/patest_sine.c b/lib/portaudio/pa_tests/patest_sine.c new file mode 100644 index 0000000..80ae639 --- /dev/null +++ b/lib/portaudio/pa_tests/patest_sine.c @@ -0,0 +1,141 @@ +/* + * $Id: patest_sine.c,v 1.3 2003/03/02 08:01:43 dmazzoni Exp $ + * patest_sine.c + * Play a sine wave using the Portable Audio api for several seconds. + * + * Authors: + * Ross Bencina + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define NUM_SECONDS (10) +#define SAMPLE_RATE (44100) +#define AMPLITUDE (0.9) +#define FRAMES_PER_BUFFER (64) +#define OUTPUT_DEVICE Pa_GetDefaultOutputDeviceID() +//#define OUTPUT_DEVICE (2) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d, devID = %d\n", + SAMPLE_RATE, FRAMES_PER_BUFFER, OUTPUT_DEVICE); + /* initialise sinusoidal wavetable */ + for( i=0; i + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (8) +#define SAMPLE_RATE (44100) +#define TEST_UNSIGNED (1) +#if TEST_UNSIGNED +#define TEST_FORMAT paUInt8 +#else +#define TEST_FORMAT paInt8 +#endif +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct +{ +#if TEST_UNSIGNED + unsigned char sine[TABLE_SIZE]; +#else + char sine[TABLE_SIZE]; +#endif + int left_phase; + int right_phase; + unsigned int framesToGo; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + char *out = (char*)outputBuffer; + int i; + int framesToCalc; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( data->framesToGo < framesPerBuffer ) + { + framesToCalc = data->framesToGo; + data->framesToGo = 0; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + data->framesToGo -= framesPerBuffer; + } + + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + /* zero remainder of final buffer */ + for( ; i<(int)framesPerBuffer; i++ ) + { +#if TEST_UNSIGNED + *out++ = (unsigned char) 0x80; /* left */ + *out++ = (unsigned char) 0x80; /* right */ +#else + *out++ = 0; /* left */ + *out++ = 0; /* right */ +#endif + + } + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int totalSamps; +#if TEST_UNSIGNED + printf("PortAudio Test: output UNsigned 8 bit sine wave.\n"); +#else + printf("PortAudio Test: output signed 8 bit sine wave.\n"); +#endif + /* initialise sinusoidal wavetable */ + for( i=0; i + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (8) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (64) +#define NUM_BUFFERS (0) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; + int framesToGo; + volatile PaTimestamp outTime; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int i; + int framesToCalc; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + data->outTime = outTime; + + if( data->framesToGo < framesPerBuffer ) + { + framesToCalc = data->framesToGo; + data->framesToGo = 0; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + data->framesToGo -= framesPerBuffer; + } + + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + /* zero remainder of final buffer */ + for( ; i<(int)framesPerBuffer; i++ ) + { + *out++ = 0; /* left */ + *out++ = 0; /* right */ + } + return finished; +} +/*******************************************************************/ +static void ReportStreamTime( PortAudioStream *stream, paTestData *data ); +static void ReportStreamTime( PortAudioStream *stream, paTestData *data ) +{ + PaTimestamp streamTime, latency, outTime; + + streamTime = Pa_StreamTime( stream ); + outTime = data->outTime; + if( outTime < 0.0 ) + { + printf("Stream time = %8.1f\n", streamTime ); + } + else + { + latency = outTime - streamTime; + printf("Stream time = %8.1f, outTime = %8.1f, latency = %8.1f\n", + streamTime, outTime, latency ); + } + fflush(stdout); +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData DATA; + int i; + int totalSamps; + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + /* initialise sinusoidal wavetable */ + for( i=0; i + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +#define SLEEP_DUR (200) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#define LATENCY_MSEC (3000) +#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#define FRAMES_PER_NOTE (SAMPLE_RATE/2) +#define MAX_REPEATS (2) +#define FUNDAMENTAL (400.0f / SAMPLE_RATE) +#define NOTE_0 (FUNDAMENTAL * 1.0f / 1.0f) +#define NOTE_1 (FUNDAMENTAL * 5.0f / 4.0f) +#define NOTE_2 (FUNDAMENTAL * 4.0f / 3.0f) +#define NOTE_3 (FUNDAMENTAL * 3.0f / 2.0f) +#define NOTE_4 (FUNDAMENTAL * 2.0f / 1.0f) +#define MODE_FINISH (0) +#define MODE_STOP (1) +#define MODE_ABORT (2) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float waveform[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float phase; + float *tune; + int notesPerTune; + int frameCounter; + int noteCounter; + int repeatCounter; + PaTimestamp outTime; + int stopMode; + int done; +} +paTestData; +/************* Prototypes *****************************/ +int TestStopMode( paTestData *data ); +float LookupWaveform( paTestData *data, float phase ); +/****************************************************** + * Convert phase between 0.0 and 1.0 to waveform value + * using linear interpolation. + */ +float LookupWaveform( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->waveform[index]; + float hi = data->waveform[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float value; + unsigned int i = 0; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + data->outTime = outTime; + if( !data->done ) + { + for( i=0; iframeCounter >= FRAMES_PER_NOTE ) + { + data->noteCounter += 1; + data->frameCounter = 0; + /* Are we done with this tune? */ + if( data->noteCounter >= data->notesPerTune ) + { + data->noteCounter = 0; + data->repeatCounter += 1; + /* Are we totally done? */ + if( data->repeatCounter >= MAX_REPEATS ) + { + data->done = 1; + if( data->stopMode == MODE_FINISH ) + { + finished = 1; + break; + } + } + } + data->phase_increment = data->tune[data->noteCounter]; + } + value = LookupWaveform(data, data->phase); + *out++ = value; /* left */ + *out++ = value; /* right */ + data->phase += data->phase_increment; + if( data->phase >= 1.0f ) data->phase -= 1.0f; + + data->frameCounter += 1; + } + } + /* zero remainder of final buffer */ + for( ; idone = 0; + data->phase = 0.0; + data->frameCounter = 0; + data->noteCounter = 0; + data->repeatCounter = 0; + data->phase_increment = data->tune[data->noteCounter]; + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + OUTPUT_DEVICE, + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + NUM_BUFFERS, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + if( data->stopMode == MODE_FINISH ) + { + while( Pa_StreamActive( stream ) ) + { + /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, + data->noteCounter, data->repeatCounter ); + fflush(stdout); /**/ + Pa_Sleep( SLEEP_DUR ); + } + } + else + { + while( data->repeatCounter < MAX_REPEATS ) + { + /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, + data->noteCounter, data->repeatCounter ); + fflush(stdout); /**/ + Pa_Sleep( SLEEP_DUR ); + } + } + if( data->stopMode == MODE_ABORT ) + { + printf("Call Pa_AbortStream()\n"); + err = Pa_AbortStream( stream ); + } + else + { + printf("Call Pa_StopStream()\n"); + err = Pa_StopStream( stream ); + } + if( err != paNoError ) goto error; + printf("Call Pa_CloseStream()\n"); fflush(stdout); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/lib/portaudio/pa_tests/patest_sync.c b/lib/portaudio/pa_tests/patest_sync.c new file mode 100644 index 0000000..5f38dc1 --- /dev/null +++ b/lib/portaudio/pa_tests/patest_sync.c @@ -0,0 +1,227 @@ +/* + * $Id: patest_sync.c,v 1.2 2003/03/02 08:01:43 dmazzoni Exp $ + * patest_sync.c + * Test time stamping and synchronization of audio and video. + * A high latency is used so we can hear the difference in time. + * Random durations are used so we know we are hearing the right beep + * and not the one before or after. + * + * Sequence of events: + * Foreground requests a beep. + * Background randomly schedules a beep. + * Foreground waits for the beep to be heard based on Pa_StreamTime(). + * Foreground outputs video (printf) in sync with audio. + * Repeat. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_BEEPS (6) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#define BEEP_DURATION (1000) +#define LATENCY_MSEC (2000) +#define SLEEP_MSEC (10) +#define TIMEOUT_MSEC ((3 * LATENCY_MSEC) / (2 * SLEEP_MSEC)) +#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#define STATE_BKG_IDLE (0) +#define STATE_BKG_PENDING (1) +#define STATE_BKG_BEEPING (2) +typedef struct +{ + float left_phase; + float right_phase; + int state; + int requestBeep; /* Set by foreground, cleared by background. */ + PaTimestamp beepTime; + int beepCount; +} +paTestData; +static unsigned long GenerateRandomNumber( void ); +/************************************************************/ +/* Calculate pseudo-random 32 bit number based on linear congruential method. */ +static unsigned long GenerateRandomNumber( void ) +{ + static unsigned long randSeed = 22222; /* Change this for different random sequences. */ + randSeed = (randSeed * 196314165) + 907633515; + return randSeed; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + /* Cast data passed through stream to our structure. */ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + (void) inputBuffer; + + for( i=0; istate ) + { + case STATE_BKG_IDLE: + /* Schedule beep at some random time in the future. */ + if( data->requestBeep ) + { + int random = GenerateRandomNumber() >> 14; + data->beepTime = outTime + (i + random + (SAMPLE_RATE/4)); + data->state = STATE_BKG_PENDING; + data->requestBeep = 0; + data->left_phase = data->right_phase = 0.0; + } + *out++ = 0.0; /* left */ + *out++ = 0.0; /* right */ + break; + case STATE_BKG_PENDING: + if( (outTime + i) >= data->beepTime ) + { + data->state = STATE_BKG_BEEPING; + data->beepCount = BEEP_DURATION; + } + *out++ = 0.0; /* left */ + *out++ = 0.0; /* right */ + break; + case STATE_BKG_BEEPING: + if( data->beepCount <= 0 ) + { + data->state = STATE_BKG_IDLE; + *out++ = 0.0; /* left */ + *out++ = 0.0; /* right */ + } + else + { + /* Play sawtooth wave. */ + *out++ = data->left_phase; /* left */ + *out++ = data->right_phase; /* right */ + /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */ + data->left_phase += 0.01f; + /* When signal reaches top, drop back down. */ + if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f; + /* higher pitch so we can distinguish left and right. */ + data->right_phase += 0.03f; + if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f; + } + data->beepCount -= 1; + break; + default: + data->state = STATE_BKG_IDLE; + break; + } + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData DATA; + int i, timeout; + PaTimestamp previousTime; + printf("PortAudio Test: you should see BEEP at the same time you hear it.\n"); + printf("Wait for a few seconds random delay between BEEPs.\n"); + printf("BEEP %d times.\n", NUM_BEEPS ); + /* Initialize our DATA for use by callback. */ + DATA.left_phase = DATA.right_phase = 0.0; + DATA.state = STATE_BKG_IDLE; + DATA.requestBeep = 0; + /* Initialize library before making any other calls. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + /* Open an audio I/O stream. */ + err = Pa_OpenDefaultStream( + &stream, + 0, /* no input channels */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + SAMPLE_RATE, + FRAMES_PER_BUFFER, + NUM_BUFFERS, + patestCallback, + &DATA ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + previousTime = Pa_StreamTime( stream ); + for( i=0; i 0 ) ) Pa_Sleep(SLEEP_MSEC); + if( timeout <= 0 ) + { + fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" ); + goto error; + } + /* Wait for scheduled beep time. */ + timeout = TIMEOUT_MSEC + (10000/SLEEP_MSEC); + while( (Pa_StreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) ) + { + Pa_Sleep(SLEEP_MSEC); + } + if( timeout <= 0 ) + { + fprintf( stderr, "Timed out waiting for time. Now = %g, Beep for %g.\n", + Pa_StreamTime( stream ), DATA.beepTime ); + goto error; + } + /* Beep should be sounding now so print synchronized BEEP. */ + printf("BEEP"); + fflush(stdout); + printf(" at %d, delta = %d\n", + (long) DATA.beepTime, (long) (DATA.beepTime - previousTime) ); + fflush(stdout); + previousTime = DATA.beepTime; + } + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/lib/portaudio/pa_tests/patest_wire.c b/lib/portaudio/pa_tests/patest_wire.c new file mode 100644 index 0000000..229365b --- /dev/null +++ b/lib/portaudio/pa_tests/patest_wire.c @@ -0,0 +1,151 @@ +/* + * $Id: patest_wire.c,v 1.3 2003/03/02 08:01:43 dmazzoni Exp $ + * patest_wire.c + * + * Pass input directly to output. + * Note that some HW devices, for example many ISA audio cards + * on PCs, do NOT support full duplex! For a PC, you normally need + * a PCI based audio card such as the SBLive. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define INPUT_DEVICE Pa_GetDefaultInputDeviceID() +#define OUTPUT_DEVICE Pa_GetDefaultOutputDeviceID() + +/* +** Note that many of the older ISA sound cards on PCs do NOT support +** full duplex audio (simultaneous record and playback). +** And some only support full duplex at lower sample rates. +*/ +#define SAMPLE_RATE (22050) +#define FRAMES_PER_BUFFER (64) + +#if 1 +#define PA_SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#else +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#endif +static int wireCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int wireCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + SAMPLE *out = (SAMPLE*)outputBuffer; + SAMPLE *in = (SAMPLE*)inputBuffer; + unsigned int i; + (void) outTime; + + /* This may get called with NULL inputBuffer during initial setup. */ + if( inputBuffer == NULL ) + { + for( i=0; i ;-) + added 24k and 16k to ratesToTry[] + fixed Pa_GetInternalDevice() + changed DEVICE_NAME_BASE from /dev/audio to /dev/dsp + handled SNDCTL_DSP_SPEED in Pq_QueryDevice() more graceful + fixed Pa_StreamTime() for paqa_errs.c + fixed numCannel=2 oddity and error handling in Pa_SetupDeviceFormat() + grep also for HP20010922 ... + PLB20010924 - Phil - merged Heiko's changes + removed sNumDevices and potential related bugs, + use getenv("PA_MIN_LATENCY_MSEC") to set desired latency, + simplify CPU Load calculation by comparing real-time to framesPerBuffer, + always close device when querying even if error occurs, + PLB20010927 - Phil - Improved negotiation for numChannels. + SG20011005 - Stewart Greenhill - set numChannels back to reasonable value after query. + DH20010115 - David Herring - fixed uninitialized handle. + + DM20020218 - Dominic Mazzoni - Try to open in nonblocking mode first, in case + the device is already open. New implementation of + Pa_StreamTime that uses SNDCTL_DSP_GETOPTR but + uses our own counter to avoid wraparound. + PLB20020222 - Phil Burk - Added WatchDog proc if audio running at high priority. + Check error return from read() and write(). + Check CPU endianness instead of assuming Little Endian. + 20020621 - pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by + Augustus Saunders. Return values from usleep() ignored by Sam Bayer + because not cross-platform compatible (at least until we get configure + going). Pa_SetupDeviceFormat split into input and output sides to + reflect capabilities of Solaris. + + 20030206 - Martin Rohrbach - various mods for Solaris + + 20030410 - Bjorn Dittmer-Roche - fixed numerous problems associated with pthread_t + +TODO +O- put semaphore lock around shared data? +O- handle native formats better +O- handle stereo-only device better ??? +O- what if input and output of a device capabilities differ (e.g. es1371) ??? +*/ + + +#include "pa_unix.h" + +typedef void *(*pthread_function_t)(void *); + +/************************************************* Shared Data ********/ +/* FIXME - put Mutex around this shared data. */ +static internalPortAudioDevice *sDeviceList = NULL; +static int sDefaultInputDeviceID = paNoDevice; +static int sDefaultOutputDeviceID = paNoDevice; +static int sPaHostError = 0; + +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + /* Query system timer for usage analysis and to prevent overuse of CPU. */ + gettimeofday( &pahsc->pahsc_EntryTime, NULL ); +} + +static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB ) +{ + long secs = timeA->tv_sec - timeB->tv_sec; + long usecs = secs * 1000000; + usecs += (timeA->tv_usec - timeB->tv_usec); + return usecs; +} + +/****************************************************************************** +** Measure fractional CPU load based on real-time it took to calculate +** buffers worth of output. +*/ +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + struct timeval currentTime; + long usecsElapsed; + double newUsage; + +#define LOWPASS_COEFFICIENT_0 (0.95) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + + if( gettimeofday( ¤tTime, NULL ) == 0 ) + { + usecsElapsed = SubtractTime_AminusB( ¤tTime, &pahsc->pahsc_EntryTime ); + /* Use inverse because it is faster than the divide. */ + newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer; + + past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + + } +} +/****************************************** END CPU UTILIZATION *******/ + +/********************************************************************* + * Determines the number of available devices by trying to open + * each "/dev/dsp#" or "/dsp/audio#" in order until it fails. + * Add each working device to a singly linked list of devices. + */ +PaError Pa_QueryDevices( void ) +{ + internalPortAudioDevice *pad, *lastPad; + int go = 1; + int numDevices = 0; + PaError testResult; + PaError result = paNoError; + char *envdev; + + sDefaultInputDeviceID = paNoDevice; + sDefaultOutputDeviceID = paNoDevice; + + lastPad = NULL; + + while( go ) + { + /* Allocate structure to hold device info. */ + pad = (internalPortAudioDevice *) + PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) ); + if( pad == NULL ) return paInsufficientMemory; + memset( pad, 0, sizeof(internalPortAudioDevice) ); + + /* Build name for device. */ + if( numDevices == 0 ) + { + sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE); + } + else + { + sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices ); + } + + DBUG(("Try device %s\n", pad->pad_DeviceName )); + testResult = Pa_QueryDevice( pad->pad_DeviceName, pad ); + DBUG(("Pa_QueryDevice returned %d\n", testResult )); + if( testResult != paNoError ) + { + if( lastPad == NULL ) + { + result = testResult; /* No good devices! */ + } + go = 0; + PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) ); + } + else + { + numDevices += 1; + /* Add to linked list of devices. */ + if( lastPad ) + { + lastPad->pad_Next = pad; + } + else + { + sDeviceList = pad; /* First element in linked list. */ + } + lastPad = pad; + } + } + + /* I'm sitting at a SunRay1 and I neither have /dev/audio# nor /dev/dsp#. + Instead, the correct audio device is stored in the environment variable + AUDIODEV and/or UTAUDIODEV, so check these devices as well if we haven't + checked them yet above - MR */ + + DBUG(("Checking for AUDIODEV and UTAUDIODEV\n")); + envdev = getenv("AUDIODEV"); + if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE)) { + result = paNoError; + + /* Allocate structure to hold device info. */ + pad = (internalPortAudioDevice *) + PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) ); + if( pad == NULL ) return paInsufficientMemory; + memset( pad, 0, sizeof(internalPortAudioDevice) ); + + /* Build name for device. */ + strcpy(pad->pad_DeviceName, envdev); + + DBUG(("Try device %s\n", pad->pad_DeviceName )); + testResult = Pa_QueryDevice( pad->pad_DeviceName, pad ); + DBUG(("Pa_QueryDevice returned %d\n", testResult )); + if( testResult != paNoError ) + { + if( lastPad == NULL ) + { + result = testResult; /* No good devices! */ + } + PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) ); + } + else + { + numDevices += 1; + /* Add to linked list of devices. */ + if( lastPad ) + { + lastPad->pad_Next = pad; + } + else + { + sDeviceList = pad; /* First element in linked list. */ + } + lastPad = pad; + } + } + + envdev = getenv("UTAUDIODEV"); + if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE) && getenv("AUDIODEV") != NULL && strcmp(envdev, getenv("AUDIODEV"))) { + result = paNoError; + + /* Allocate structure to hold device info. */ + pad = (internalPortAudioDevice *) + PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) ); + if( pad == NULL ) return paInsufficientMemory; + memset( pad, 0, sizeof(internalPortAudioDevice) ); + + /* Build name for device. */ + strcpy(pad->pad_DeviceName, envdev); + + DBUG(("Try device %s\n", pad->pad_DeviceName )); + testResult = Pa_QueryDevice( pad->pad_DeviceName, pad ); + DBUG(("Pa_QueryDevice returned %d\n", testResult )); + if( testResult != paNoError ) + { + if( lastPad == NULL ) + { + result = testResult; /* No good devices! */ + } + PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) ); + } + else + { + numDevices += 1; + /* Add to linked list of devices. */ + if( lastPad ) + { + lastPad->pad_Next = pad; + } + else + { + sDeviceList = pad; /* First element in linked list. */ + } + lastPad = pad; + } + } + + return result; +} + +/*************************************************************************/ +int Pa_CountDevices() +{ + int numDevices = 0; + internalPortAudioDevice *pad; + + if( sDeviceList == NULL ) Pa_Initialize(); + /* Count devices in list. */ + pad = sDeviceList; + while( pad != NULL ) + { + pad = pad->pad_Next; + numDevices++; + } + + return numDevices; +} + +/*************************************************************************/ +internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id ) +{ + internalPortAudioDevice *pad; + if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; + pad = sDeviceList; + while( id > 0 ) + { + pad = pad->pad_Next; + id--; + } + return pad; +} + +/*************************************************************************/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + internalPortAudioDevice *pad; + if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; + pad = Pa_GetInternalDevice( id ); + return &pad->pad_Info ; +} + +static PaError Pa_MaybeQueryDevices( void ) +{ + if( sDeviceList == NULL ) + { + return Pa_QueryDevices(); + } + return 0; +} + +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + /* return paNoDevice; */ + return 0; +} + +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + return 0; +} + +/********************************************************************** +** Make sure that we have queried the device capabilities. +*/ + +PaError PaHost_Init( void ) +{ + return Pa_MaybeQueryDevices(); +} + +/******************************************************************************************* + * The ol' Canary in a Coal Mine trick. + * Just update the time periodically. + * Runs at low priority so if audio thread runs wild, this thread will get starved + * and the watchdog will detect it. + */ + +#define SCHEDULER_POLICY SCHED_RR +#define WATCHDOG_MAX_SECONDS (3) +#define WATCHDOG_INTERVAL_USEC (1000000) + +static int PaHost_CanaryProc( PaHostSoundControl *pahsc ) +{ + int result = 0; + +#ifdef GNUSTEP + GSRegisterCurrentThread(); /* SB20010904 */ +#endif + + while( pahsc->pahsc_CanaryRun) { + usleep( WATCHDOG_INTERVAL_USEC ); + gettimeofday( &pahsc->pahsc_CanaryTime, NULL ); + } + + DBUG(("PaHost_CanaryProc: exiting.\n")); + +#ifdef GNUSTEP + GSUnregisterCurrentThread(); /* SB20010904 */ +#endif + + return result; +} + +/******************************************************************************************* + * Monitor audio thread and lower its it if it hogs the CPU. + * To prevent getting killed, the audio thread must update a + * variable with a timer value. + * If the value is not recent enough, then the + * thread will get killed. + */ + +static PaError PaHost_WatchDogProc( PaHostSoundControl *pahsc ) +{ + struct sched_param schp = { 0 }; + int maxPri; + +#ifdef GNUSTEP + GSRegisterCurrentThread(); /* SB20010904 */ +#endif + +/* Run at a priority level above audio thread so we can still run if it hangs. */ +/* Rise more than 1 because of rumored off-by-one scheduler bugs. */ + schp.sched_priority = pahsc->pahsc_AudioPriority + 4; + maxPri = sched_get_priority_max(SCHEDULER_POLICY); + if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri; + + if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0) + { + ERR_RPT(("PaHost_WatchDogProc: cannot set watch dog priority!\n")); + goto killAudio; + } + + /* Compare watchdog time with audio and canary thread times. */ + /* Sleep for a while or until thread cancelled. */ + while( pahsc->pahsc_WatchDogRun ) + { + + int delta; + struct timeval currentTime; + + usleep( WATCHDOG_INTERVAL_USEC ); + gettimeofday( ¤tTime, NULL ); + + /* If audio thread is not advancing, then it must be hung so kill it. */ + delta = currentTime.tv_sec - pahsc->pahsc_EntryTime.tv_sec; + DBUG(("PaHost_WatchDogProc: audio delta = %d\n", delta )); + if( delta > WATCHDOG_MAX_SECONDS ) + { + goto killAudio; + } + + /* If canary died, then lower audio priority and halt canary. */ + delta = currentTime.tv_sec - pahsc->pahsc_CanaryTime.tv_sec; + if( delta > WATCHDOG_MAX_SECONDS ) + { + ERR_RPT(("PaHost_WatchDogProc: canary died!\n")); + goto lowerAudio; + } + } + + DBUG(("PaHost_WatchDogProc: exiting.\n")); +#ifdef GNUSTEP + GSUnregisterCurrentThread(); /* SB20010904 */ +#endif + return 0; + +lowerAudio: + { + struct sched_param schat = { 0 }; + if( sched_setscheduler(pahsc->pahsc_AudioThreadPID, SCHED_OTHER, &schat) != 0) + { + ERR_RPT(("PaHost_WatchDogProc: failed to lower audio priority. errno = %d\n", errno )); + /* Fall through into killing audio thread. */ + } + else + { + ERR_RPT(("PaHost_WatchDogProc: lowered audio priority to prevent hogging of CPU.\n")); + goto cleanup; + } + } + +killAudio: + ERR_RPT(("PaHost_WatchDogProc: killing hung audio thread!\n")); + pthread_kill( pahsc->pahsc_AudioThread, SIGKILL ); + +cleanup: + pahsc->pahsc_CanaryRun = 0; + DBUG(("PaHost_WatchDogProc: cancel Canary\n")); + pthread_cancel( pahsc->pahsc_CanaryThread ); + DBUG(("PaHost_WatchDogProc: join Canary\n")); + pthread_join( pahsc->pahsc_CanaryThread, NULL ); + DBUG(("PaHost_WatchDogProc: forget Canary\n")); + pahsc->pahsc_IsCanaryThreadValid = 0; + +#ifdef GNUSTEP + GSUnregisterCurrentThread(); /* SB20010904 */ +#endif + return 0; +} + +/*******************************************************************************************/ +static void PaHost_StopWatchDog( PaHostSoundControl *pahsc ) +{ +/* Cancel WatchDog thread if there is one. */ + if( pahsc->pahsc_IsWatchDogThreadValid ) + { + pahsc->pahsc_WatchDogRun = 0; + DBUG(("PaHost_StopWatchDog: cancel WatchDog\n")); + pthread_cancel( pahsc->pahsc_WatchDogThread ); + pthread_join( pahsc->pahsc_WatchDogThread, NULL ); + pahsc->pahsc_IsWatchDogThreadValid = 0; + } +/* Cancel Canary thread if there is one. */ + if( pahsc->pahsc_IsCanaryThreadValid ) + { + pahsc->pahsc_CanaryRun = 0; + DBUG(("PaHost_StopWatchDog: cancel Canary\n")); + pthread_cancel( pahsc->pahsc_CanaryThread ); + DBUG(("PaHost_StopWatchDog: join Canary\n")); + pthread_join( pahsc->pahsc_CanaryThread, NULL ); + pahsc->pahsc_IsCanaryThreadValid = 0; + } +} + +/*******************************************************************************************/ +static PaError PaHost_StartWatchDog( PaHostSoundControl *pahsc ) +{ + int hres; + PaError result = 0; + + /* The watch dog watches for these timer updates */ + gettimeofday( &pahsc->pahsc_EntryTime, NULL ); + gettimeofday( &pahsc->pahsc_CanaryTime, NULL ); + + /* Launch a canary thread to detect priority abuse. */ + pahsc->pahsc_CanaryRun = 1; + hres = pthread_create(&(pahsc->pahsc_CanaryThread), + NULL /*pthread_attr_t * attr*/, + (pthread_function_t)PaHost_CanaryProc, pahsc); + if( hres != 0 ) + { + pahsc->pahsc_IsCanaryThreadValid = 0; + result = paHostError; + sPaHostError = hres; + goto error; + } + pahsc->pahsc_IsCanaryThreadValid = 1; + + /* Launch a watchdog thread to prevent runaway audio thread. */ + pahsc->pahsc_WatchDogRun = 1; + hres = pthread_create(&(pahsc->pahsc_WatchDogThread), + NULL /*pthread_attr_t * attr*/, + (pthread_function_t)PaHost_WatchDogProc, pahsc); + if( hres != 0 ) + { + pahsc->pahsc_IsWatchDogThreadValid = 0; + result = paHostError; + sPaHostError = hres; + goto error; + } + pahsc->pahsc_IsWatchDogThreadValid = 1; + return result; + +error: + PaHost_StopWatchDog( pahsc ); + return result; +} + +/******************************************************************************************* + * Bump priority of audio thread if running with superuser priveledges. + * if priority bumped then launch a watchdog. + */ +static PaError PaHost_BoostPriority( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + PaError result = paNoError; + struct sched_param schp = { 0 }; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + + pahsc->pahsc_AudioThreadPID = getpid(); + DBUG(("PaHost_BoostPriority: audio PID = %d\n", pahsc->pahsc_AudioThreadPID )); + + /* Choose a priority in the middle of the range. */ + pahsc->pahsc_AudioPriority = (sched_get_priority_max(SCHEDULER_POLICY) - + sched_get_priority_min(SCHEDULER_POLICY)) / 2; + schp.sched_priority = pahsc->pahsc_AudioPriority; + + if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0) + { + DBUG(("PortAudio: only superuser can use real-time priority.\n")); + } + else + { + DBUG(("PortAudio: audio callback priority set to level %d!\n", schp.sched_priority)); + /* We are running at high priority so we should have a watchdog in case audio goes wild. */ + result = PaHost_StartWatchDog( pahsc ); + } + + return result; +} + +/*******************************************************************************************/ +static PaError Pa_AudioThreadProc( internalPortAudioStream *past ) +{ + PaError result; + PaHostSoundControl *pahsc; + ssize_t bytes_read, bytes_written; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + +#ifdef GNUSTEP + GSRegisterCurrentThread(); /* SB20010904 */ +#endif + + result = PaHost_BoostPriority( past ); + if( result < 0 ) goto error; + + past->past_IsActive = 1; + DBUG(("entering thread.\n")); + + while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) ) + { + /* Read data from device */ + if(pahsc->pahsc_NativeInputBuffer) + { + unsigned int totalread = 0; + DBUG(("Pa_AudioThreadProc: attempt to read %d bytes\n", pahsc->pahsc_BytesPerInputBuffer)); + do + { + bytes_read = read(pahsc->pahsc_InputHandle, + (char *)pahsc->pahsc_NativeInputBuffer + totalread, + pahsc->pahsc_BytesPerInputBuffer - totalread); + + if (bytes_read < 0) + { + ERR_RPT(("PortAudio: read interrupted!\n")); + break; + } + + totalread += bytes_read; + } while( totalread < pahsc->pahsc_BytesPerInputBuffer); + } + + /* Convert 16 bit native data to user data and call user routine. */ + DBUG(("converting...\n")); + Pa_StartUsageCalculation( past ); + result = Pa_CallConvertInt16( past, + pahsc->pahsc_NativeInputBuffer, + pahsc->pahsc_NativeOutputBuffer ); + Pa_EndUsageCalculation( past ); + if( result != 0) + { + DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n", + result)); + break; + } + + /* Write data to device. */ + if( pahsc->pahsc_NativeOutputBuffer ) + { + unsigned int totalwritten = 0; + do + { + bytes_written = write(pahsc->pahsc_OutputHandle, + (void *)pahsc->pahsc_NativeOutputBuffer, + pahsc->pahsc_BytesPerOutputBuffer); + if( bytes_written < 0 ) + { + ERR_RPT(("PortAudio: write interrupted!")); + break; + } + + totalwritten += bytes_written; + } while( totalwritten < pahsc->pahsc_BytesPerOutputBuffer); + } + + Pa_UpdateStreamTime(pahsc); + } + DBUG(("Pa_AudioThreadProc: left audio loop.\n")); + + past->past_IsActive = 0; + PaHost_StopWatchDog( pahsc ); + +error: + DBUG(("leaving audio thread.\n")); +#ifdef GNUSTEP + GSUnregisterCurrentThread(); /* SB20010904 */ +#endif + return result; +} + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the cshrc file. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") + +int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) +{ + int minBuffers; + int minLatencyMsec = MIN_LATENCY_MSEC; + char *minLatencyText = getenv(PA_LATENCY_ENV_NAME); + if( minLatencyText != NULL ) + { + PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText )); + minLatencyMsec = atoi( minLatencyText ); + if( minLatencyMsec < 1 ) minLatencyMsec = 1; + else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000; + } + + minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer )); + if( minBuffers < 2 ) minBuffers = 2; + return minBuffers; +} + +/*******************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc; + unsigned int minNumBuffers; + internalPortAudioDevice *pad; + DBUG(("PaHost_OpenStream() called.\n" )); + + /* Allocate and initialize host data. */ + pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl)); + if( pahsc == NULL ) + { + result = paInsufficientMemory; + goto error; + } + memset( pahsc, 0, sizeof(PaHostSoundControl) ); + past->past_DeviceData = (void *) pahsc; + + pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */ + pahsc->pahsc_InputHandle = BAD_DEVICE_ID; + pahsc->pahsc_IsAudioThreadValid = 0; + pahsc->pahsc_IsWatchDogThreadValid = 0; + + /* Allocate native buffers. */ + pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer * + past->past_NumInputChannels * sizeof(short); + if( past->past_NumInputChannels > 0) + { + pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer); + if( pahsc->pahsc_NativeInputBuffer == NULL ) + { + result = paInsufficientMemory; + goto error; + } + } + pahsc->pahsc_BytesPerOutputBuffer = past->past_FramesPerUserBuffer * + past->past_NumOutputChannels * sizeof(short); + if( past->past_NumOutputChannels > 0) + { + pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer); + if( pahsc->pahsc_NativeOutputBuffer == NULL ) + { + result = paInsufficientMemory; + goto error; + } + } + + /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */ + minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers; + + pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer); + DBUG(("past_SampleRate = %g\n", past->past_SampleRate )); + DBUG(("past_FramesPerUserBuffer = %d\n", past->past_FramesPerUserBuffer )); + DBUG(("pahsc_InverseMicrosPerBuffer = %g\n", pahsc->pahsc_InverseMicrosPerBuffer )); + + /* ------------------------- OPEN DEVICE -----------------------*/ + + /* just output */ + if (past->past_OutputDeviceID == past->past_InputDeviceID) + { + + if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) ) + { + pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); + DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName )); + + /* dmazzoni: test it first in nonblocking mode to + make sure the device is not busy */ + pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDWR|O_NONBLOCK); + if(pahsc->pahsc_InputHandle==-1) + { + ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName )); + result = paHostError; + goto error; + } + close(pahsc->pahsc_InputHandle); + + pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle = + open(pad->pad_DeviceName,O_RDWR); + if(pahsc->pahsc_InputHandle==-1) + { + ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName )); + result = paHostError; + goto error; + } + Pa_SetLatency( pahsc->pahsc_OutputHandle, + past->past_NumUserBuffers, past->past_FramesPerUserBuffer, + past->past_NumOutputChannels ); + result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle, + past->past_NumOutputChannels, (int)past->past_SampleRate ); + } + } + else + { + if (past->past_NumOutputChannels > 0) + { + pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); + DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName )); + /* dmazzoni: test it first in nonblocking mode to + make sure the device is not busy */ + pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY|O_NONBLOCK); + if(pahsc->pahsc_OutputHandle==-1) + { + ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName )); + result = paHostError; + goto error; + } + close(pahsc->pahsc_OutputHandle); + + pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY); + if(pahsc->pahsc_OutputHandle==-1) + { + ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName )); + result = paHostError; + goto error; + } + Pa_SetLatency( pahsc->pahsc_OutputHandle, + past->past_NumUserBuffers, past->past_FramesPerUserBuffer, + past->past_NumOutputChannels ); + result = Pa_SetupOutputDeviceFormat( pahsc->pahsc_OutputHandle, + past->past_NumOutputChannels, (int)past->past_SampleRate ); + } + + if (past->past_NumInputChannels > 0) + { + pad = Pa_GetInternalDevice( past->past_InputDeviceID ); + DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName )); + /* dmazzoni: test it first in nonblocking mode to + make sure the device is not busy */ + pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY|O_NONBLOCK); + if(pahsc->pahsc_InputHandle==-1) + { + ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName )); + result = paHostError; + goto error; + } + close(pahsc->pahsc_InputHandle); + + pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY); + if(pahsc->pahsc_InputHandle==-1) + { + ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName )); + result = paHostError; + goto error; + } + Pa_SetLatency( pahsc->pahsc_InputHandle, /* DH20010115 - was OutputHandle! */ + past->past_NumUserBuffers, past->past_FramesPerUserBuffer, + past->past_NumInputChannels ); + result = Pa_SetupInputDeviceFormat( pahsc->pahsc_InputHandle, + past->past_NumInputChannels, (int)past->past_SampleRate ); + } + } + + + DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result )); + return result; + +error: + ERR_RPT(("PaHost_OpenStream: ERROR - result = %d\n", result )); + PaHost_CloseStream( past ); + return result; +} + +/*************************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + PaError result = paNoError; + int hres; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + past->past_StopSoon = 0; + past->past_StopNow = 0; + past->past_IsActive = 1; + + /* Use pthread_create() instead of __clone() because: + * - pthread_create also works for other UNIX systems like Solaris, + * - the Java HotSpot VM crashes in pthread_setcanceltype() when using __clone() + */ + hres = pthread_create(&(pahsc->pahsc_AudioThread), + NULL /*pthread_attr_t * attr*/, + (pthread_function_t)Pa_AudioThreadProc, past); + if( hres != 0 ) + { + result = paHostError; + sPaHostError = hres; + pahsc->pahsc_IsAudioThreadValid = 0; + goto error; + } + pahsc->pahsc_IsAudioThreadValid = 1; + +error: + return result; +} + +/*************************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + int hres; + PaError result = paNoError; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + if( pahsc == NULL ) return paNoError; + + /* Tell background thread to stop generating more data and to let current data play out. */ + past->past_StopSoon = 1; + /* If aborting, tell background thread to stop NOW! */ + if( abort ) past->past_StopNow = 1; + + /* Join thread to recover memory resources. */ + if( pahsc->pahsc_IsAudioThreadValid ) + { + /* This check is needed for GNUSTEP - SB20010904 */ + if ( !pthread_equal( pahsc->pahsc_AudioThread, pthread_self() ) ) + { + hres = pthread_join( pahsc->pahsc_AudioThread, NULL ); + } + else + { + DBUG(("Play thread was stopped from itself - can't do pthread_join()\n")); + hres = 0; + } + + if( hres != 0 ) + { + result = paHostError; + sPaHostError = hres; + } + pahsc->pahsc_IsAudioThreadValid = 0; + } + + past->past_IsActive = 0; + + return result; +} + +/*************************************************************************/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + +/*******************************************************************/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + + if( pahsc->pahsc_OutputHandle != BAD_DEVICE_ID ) + { + int err = 0; + DBUG(("PaHost_CloseStream: attempt to close output device handle = %d\n", + pahsc->pahsc_OutputHandle )); + + Pa_FlushStream(pahsc->pahsc_OutputHandle); + + err = close(pahsc->pahsc_OutputHandle); + if( err < 0 ) + { + ERR_RPT(("PaHost_CloseStream: warning, closing output device failed.\n")); + } + } + + if( (pahsc->pahsc_InputHandle != BAD_DEVICE_ID) && + (pahsc->pahsc_InputHandle != pahsc->pahsc_OutputHandle) ) + { + int err = 0; + DBUG(("PaHost_CloseStream: attempt to close input device handle = %d\n", + pahsc->pahsc_InputHandle )); + + Pa_FlushStream(pahsc->pahsc_InputHandle); + + err = close(pahsc->pahsc_InputHandle); + if( err < 0 ) + { + ERR_RPT(("PaHost_CloseStream: warning, closing input device failed.\n")); + } + } + pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; + pahsc->pahsc_InputHandle = BAD_DEVICE_ID; + + if( pahsc->pahsc_NativeInputBuffer ) + { + free( pahsc->pahsc_NativeInputBuffer ); + pahsc->pahsc_NativeInputBuffer = NULL; + } + if( pahsc->pahsc_NativeOutputBuffer ) + { + free( pahsc->pahsc_NativeOutputBuffer ); + pahsc->pahsc_NativeOutputBuffer = NULL; + } + + free( pahsc ); + past->past_DeviceData = NULL; + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_Term( void ) +{ + /* Free all of the linked devices. */ + internalPortAudioDevice *pad, *nextPad; + pad = sDeviceList; + while( pad != NULL ) + { + nextPad = pad->pad_Next; + DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName )); + PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) ); + pad = nextPad; + } + sDeviceList = NULL; + return 0; +} + +/************************************************************************* + * Sleep for the requested number of milliseconds. + */ +void Pa_Sleep( long msec ) +{ +#if 0 + struct timeval timeout; + timeout.tv_sec = msec / 1000; + timeout.tv_usec = (msec % 1000) * 1000; + select( 0, NULL, NULL, NULL, &timeout ); +#else + long usecs = msec * 1000; + usleep( usecs ); +#endif +} + +/************************************************************************* + * Allocate memory that can be accessed in real-time. + * This may need to be held in physical memory so that it is not + * paged to virtual memory. + * This call MUST be balanced with a call to PaHost_FreeFastMemory(). + */ +void *PaHost_AllocateFastMemory( long numBytes ) +{ + void *addr = malloc( numBytes ); /* FIXME - do we need physical, wired, non-virtual memory? */ + if( addr != NULL ) memset( addr, 0, numBytes ); + return addr; +} + +/************************************************************************* + * Free memory that could be accessed in real-time. + * This call MUST be balanced with a call to PaHost_AllocateFastMemory(). + */ +void PaHost_FreeFastMemory( void *addr, long numBytes ) +{ + if( addr != NULL ) free( addr ); +} + + +/***********************************************************************/ +PaError PaHost_StreamActive( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + return (PaError) (past->past_IsActive != 0); +} + +/***********************************************************************/ +long Pa_GetHostError( void ) +{ + return (long) sPaHostError; +} diff --git a/lib/portaudio/pa_unix_oss/pa_unix.h b/lib/portaudio/pa_unix_oss/pa_unix.h new file mode 100644 index 0000000..fd58636 --- /dev/null +++ b/lib/portaudio/pa_unix_oss/pa_unix.h @@ -0,0 +1,141 @@ +/* + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * Linux OSS Implementation by douglas repetto and Phil Burk + * + * Copyright (c) 1999-2000 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* Modification history: + 20020621: pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by + Augustus Saunders. See pa_unix.c for previous history. */ + +/* + PROPOSED - should we add this to "portaudio.h". Problem with + Pa_QueryDevice() not having same driver name os Pa_OpenStream(). + + A PaDriverInfo structure can be passed to the underlying device + on the Pa_OpenStream() call. The contents and interpretation of + the structure is determined by the PA implementation. +*/ +typedef struct PaDriverInfo /* PROPOSED */ +{ + /* Size of structure. Allows driver to extend the structure without breaking existing applications. */ + int size; + /* Can be used to request a specific device name. */ + const char *name; + unsigned long data; +} +PaDriverInfo; + +#include +#include +/* #include */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +#define BAD_DEVICE_ID (-1) + +#define MIN_LATENCY_MSEC (100) +#define MIN_TIMEOUT_MSEC (100) +#define MAX_TIMEOUT_MSEC (1000) + +/************************************************* Definitions ********/ +#ifdef __linux__ + #define DEVICE_NAME_BASE "/dev/dsp" +#else + #define DEVICE_NAME_BASE "/dev/audio" +#endif + +#define MAX_CHARS_DEVNAME (32) +#define MAX_SAMPLE_RATES (10) +typedef struct internalPortAudioDevice +{ + struct internalPortAudioDevice *pad_Next; /* Singly linked list. */ + double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */ + char pad_DeviceName[MAX_CHARS_DEVNAME]; + PaDeviceInfo pad_Info; +} +internalPortAudioDevice; + +/* Define structure to contain all OSS and Linux specific data. */ +typedef struct PaHostSoundControl +{ + int pahsc_OutputHandle; + int pahsc_InputHandle; + int pahsc_AudioPriority; /* priority of background audio thread */ + pthread_t pahsc_AudioThread; /* background audio thread */ + int pahsc_IsAudioThreadValid; /* Is pahsc_AudioThread valid?*/ pid_t pahsc_AudioThreadPID; /* background audio thread */ + pthread_t pahsc_WatchDogThread; /* highest priority thread that protects system */ + int pahsc_IsWatchDogThreadValid; /* Is pahsc_WatchDogThread valid?*/ + int pahsc_WatchDogRun; /* Ask WatchDog to stop. */ + pthread_t pahsc_CanaryThread; /* low priority thread that detects abuse by audio */ + int pahsc_IsCanaryThreadValid; /* Is pahsc_CanaryThread valid?*/ + struct timeval pahsc_CanaryTime; + int pahsc_CanaryRun; /* Ask Canary to stop. */ + short *pahsc_NativeInputBuffer; + short *pahsc_NativeOutputBuffer; + unsigned int pahsc_BytesPerInputBuffer; /* native buffer size in bytes */ + unsigned int pahsc_BytesPerOutputBuffer; /* native buffer size in bytes */ + /* For measuring CPU utilization. */ + struct timeval pahsc_EntryTime; + double pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */ + + /* For calculating stream time */ + int pahsc_LastPosPtr; + double pahsc_LastStreamBytes; +} +PaHostSoundControl; + +/************************************************* Prototypes **********/ + +internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id ); +PaError Pa_QueryDevices( void ); +PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad ); +PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate ); +PaError Pa_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate ); +PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate ); +void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame ); +void Pa_UpdateStreamTime(PaHostSoundControl *pahsc); +int Pa_FlushStream(int devHandle); diff --git a/lib/portaudio/pa_unix_oss/pa_unix_oss.c b/lib/portaudio/pa_unix_oss/pa_unix_oss.c new file mode 100644 index 0000000..697cfd0 --- /dev/null +++ b/lib/portaudio/pa_unix_oss/pa_unix_oss.c @@ -0,0 +1,393 @@ +/* + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * Linux OSS Implementation by douglas repetto and Phil Burk + * + * Copyright (c) 1999-2000 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* Modification history: + 20020621: pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by + Augustus Saunders. See pa_unix.c for previous history. Pa_FlushStream + added by Augustus Saunders for Solaris compatibility. + PLB20021018 - Fill device info table with actual sample rates instead of wished for rates. + - Allow stream to open if sample rate within 10% of desired rate. +*/ + +#include "pa_unix.h" + +#ifdef __linux__ +#include +#else +#include /* JH20010905 */ +#endif + + +#ifndef AFMT_S16_NE +#define AFMT_S16_NE Get_AFMT_S16_NE() +/********************************************************************* + * Some versions of OSS do not define AFMT_S16_NE. So check CPU. + * PowerPC is Big Endian. X86 is Little Endian. + */ +int Get_AFMT_S16_NE( void ) +{ + long testData = 1; + char *ptr = (char *) &testData; + int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */ + return isLittle ? AFMT_S16_LE : AFMT_S16_BE; +} +#endif /* AFMT_S16_NE */ + + +/********************************************************************* + * Try to open the named device. + * If it opens, try to set various rates and formats and fill in + * the device info structure. + */ +PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad ) +{ + int result = paHostError; + int tempDevHandle; + int numChannels, maxNumChannels; + int format; + int numSampleRates; + int sampleRate; + int numRatesToTry; + int lastRate; + int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; + int i; + + /* douglas: + we have to do this querying in a slightly different order. apparently + some sound cards will give you different info based on their settings. + e.g. a card might give you stereo at 22kHz but only mono at 44kHz. + the correct order for OSS is: format, channels, sample rate + + */ + if ( (tempDevHandle = open(deviceName,O_WRONLY|O_NONBLOCK)) == -1 ) + { + DBUG(("Pa_QueryDevice: could not open %s\n", deviceName )); + return paHostError; + } + + /* Ask OSS what formats are supported by the hardware. */ + pad->pad_Info.nativeSampleFormats = 0; + + if (ioctl(tempDevHandle, SNDCTL_DSP_GETFMTS, &format) == -1) + { + ERR_RPT(("Pa_QueryDevice: could not get format info\n" )); + goto error; + } + if( format & AFMT_U8 ) pad->pad_Info.nativeSampleFormats |= paUInt8; + if( format & AFMT_S16_NE ) pad->pad_Info.nativeSampleFormats |= paInt16; + + /* Negotiate for the maximum number of channels for this device. PLB20010927 + * Consider up to 16 as the upper number of channels. + * Variable numChannels should contain the actual upper limit after the call. + * Thanks to John Lazzaro and Heiko Purnhagen for suggestions. + */ + maxNumChannels = 0; + for( numChannels = 1; numChannels <= 16; numChannels++ ) + { + int temp = numChannels; + DBUG(("Pa_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels )) + if(ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 ) + { + /* ioctl() failed so bail out if we already have stereo */ + if( numChannels > 2 ) break; + } + else + { + /* ioctl() worked but bail out if it does not support numChannels. + * We don't want to leave gaps in the numChannels supported. + */ + if( (numChannels > 2) && (temp != numChannels) ) break; + DBUG(("Pa_QueryDevice: temp = %d\n", temp )) + if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */ + } + } + + /* The above negotiation may fail for an old driver so try this older technique. */ + if( maxNumChannels < 1 ) + { + int stereo = 1; + if(ioctl(tempDevHandle, SNDCTL_DSP_STEREO, &stereo) < 0) + { + maxNumChannels = 1; + } + else + { + maxNumChannels = (stereo) ? 2 : 1; + } + DBUG(("Pa_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels )) + } + + pad->pad_Info.maxOutputChannels = maxNumChannels; + DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels)) + + /* During channel negotiation, the last ioctl() may have failed. This can + * also cause sample rate negotiation to fail. Hence the following, to return + * to a supported number of channels. SG20011005 */ + { + int temp = maxNumChannels; + if( temp > 2 ) temp = 2; /* use most reasonable default value */ + ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp); + } + + /* FIXME - for now, assume maxInputChannels = maxOutputChannels. + * Eventually do separate queries for O_WRONLY and O_RDONLY + */ + pad->pad_Info.maxInputChannels = pad->pad_Info.maxOutputChannels; + + DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", + pad->pad_Info.maxInputChannels)) + + + /* Determine available sample rates by trying each one and seeing result. + * OSS often supports funky rates such as 44188 instead of 44100! + */ + numSampleRates = 0; + lastRate = 0; + numRatesToTry = sizeof(ratesToTry)/sizeof(int); + for (i = 0; i < numRatesToTry; i++) + { + sampleRate = ratesToTry[i]; + + if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) >= 0 ) /* PLB20010817 */ + { + /* Use whatever rate OSS tells us. PLB20021018 */ + if (sampleRate != lastRate) + { + DBUG(("Pa_QueryDevice: adding sample rate: %d\n", sampleRate)) + pad->pad_SampleRates[numSampleRates] = (float)sampleRate; + numSampleRates++; + lastRate = sampleRate; + } + else + { + DBUG(("Pa_QueryDevice: dang - got sample rate %d again!\n", sampleRate)) + } + } + } + + DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates)) + if (numSampleRates==0) /* HP20010922 */ + { + /* Desparate attempt to keep running even though no good rates found! */ + ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed). Force 44100 Hz\n" )); + pad->pad_SampleRates[numSampleRates++] = 44100; + } + + pad->pad_Info.numSampleRates = numSampleRates; + pad->pad_Info.sampleRates = pad->pad_SampleRates; /* use pointer to embedded array */ + + pad->pad_Info.name = deviceName; + + result = paNoError; + +error: + /* We MUST close the handle here or we won't be able to reopen it later!!! */ + close(tempDevHandle); + + return result; +} + +/*******************************************************************************************/ +PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate ) +{ + PaError result = paNoError; + int tmp; + + /* Set format, channels, and rate in this order to keep OSS happy. */ + /* Set data format. FIXME - handle more native formats. */ + tmp = AFMT_S16_NE; + if( ioctl(devHandle,SNDCTL_DSP_SETFMT,&tmp) == -1) + { + ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SETFMT\n" )); + return paHostError; + } + if( tmp != AFMT_S16_NE ) + { + ERR_RPT(("Pa_SetupDeviceFormat: HW does not support AFMT_S16_NE\n" )); + return paHostError; + } + + + /* Set number of channels. */ + tmp = numChannels; + if (ioctl(devHandle, SNDCTL_DSP_CHANNELS, &numChannels) == -1) + { + ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_CHANNELS\n" )); + return paHostError; + } + if( tmp != numChannels) + { + ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d channels\n", numChannels )); + return paHostError; + } + + /* Set playing frequency. */ + tmp = sampleRate; + if( ioctl(devHandle,SNDCTL_DSP_SPEED,&tmp) == -1) + { + ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SPEED\n" )); + return paHostError; + } + else if( tmp != sampleRate ) + { + int percentError = abs( (100 * (sampleRate - tmp)) / sampleRate ); + PRINT(("Pa_SetupDeviceFormat: warning - requested sample rate = %d Hz - closest = %d\n", + sampleRate, tmp )); + /* Allow sample rate within 10% off of requested rate. PLB20021018 + * Sometimes OSS uses a funky rate like 44188 instead of 44100. + */ + if( percentError > 10 ) + { + ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d Hz sample rate\n",sampleRate )); + return paHostError; + } + } + + return result; +} + +PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate ) +{ + return Pa_SetupDeviceFormat(devHandle, numChannels, sampleRate); +} + +PaError Pa_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate ) +{ + return Pa_SetupDeviceFormat(devHandle, numChannels, sampleRate); +} + + +/******************************************************************************************* +** Set number of fragments and size of fragments to achieve desired latency. +*/ + +static int CalcHigherLogTwo( int n ) +{ + int log2 = 0; + while( (1< 8 ) + { + numBuffers = (numBuffers + 1) >> 1; + framesPerBuffer = framesPerBuffer << 1; + } + + /* calculate size of buffers in bytes */ + bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME - other sizes? */ + + /* Calculate next largest power of two */ + powerOfTwo = CalcHigherLogTwo( bufferSize ); + DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n", + numBuffers, framesPerBuffer, powerOfTwo )); + + /* Encode info into a single int */ + tmp=(numBuffers<<16) + powerOfTwo; + + if(ioctl(devHandle,SNDCTL_DSP_SETFRAGMENT,&tmp) == -1) + { + ERR_RPT(("Pa_SetLatency: could not SNDCTL_DSP_SETFRAGMENT\n" )); + /* Don't return an error. Best to just continue and hope for the best. */ + ERR_RPT(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n", + numBuffers, framesPerBuffer, powerOfTwo )); + } +} + +/***********************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + internalPortAudioStream *past = (internalPortAudioStream *) stream; + PaHostSoundControl *pahsc; + + count_info info; + unsigned int delta; + unsigned int numchan; + + if( past == NULL ) return paBadStreamPtr; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + if( pahsc->pahsc_NativeOutputBuffer ) + { + ioctl(pahsc->pahsc_OutputHandle, SNDCTL_DSP_GETOPTR, &info); + numchan = past->past_NumOutputChannels; + } + else + { + ioctl(pahsc->pahsc_InputHandle, SNDCTL_DSP_GETIPTR, &info); + numchan = past->past_NumInputChannels; + } + + delta = info.bytes - pahsc->pahsc_LastPosPtr; + + if (delta > 0x000FFFFF) + delta = 0; + + return (pahsc->pahsc_LastStreamBytes + delta) / (numchan * sizeof(short)); +} + +void Pa_UpdateStreamTime(PaHostSoundControl *pahsc) +{ + count_info info; + unsigned int delta; + + /* Update current stream time (using a double so that + we don't wrap around like info.bytes does) */ + if( pahsc->pahsc_NativeOutputBuffer ) + { + ioctl(pahsc->pahsc_OutputHandle, SNDCTL_DSP_GETOPTR, &info); + } + else + { + ioctl(pahsc->pahsc_InputHandle, SNDCTL_DSP_GETIPTR, &info); + } + delta = info.bytes - pahsc->pahsc_LastPosPtr; + + if (delta <= 0x000FFFFF) { + pahsc->pahsc_LastStreamBytes += delta; + pahsc->pahsc_LastPosPtr = info.bytes; + } +} + +PaError Pa_FlushStream(int devHandle) +{ + /* AS: This doesn't do anything under OSS; it was added for Solaris.*/ + + return paNoError; +} diff --git a/lib/portaudio/pa_win_ds/CVS/Entries b/lib/portaudio/pa_win_ds/CVS/Entries new file mode 100644 index 0000000..2ddbec6 --- /dev/null +++ b/lib/portaudio/pa_win_ds/CVS/Entries @@ -0,0 +1,5 @@ +/dsound_wrapper.c/1.3/Sun Mar 2 08:01:45 2003// +/dsound_wrapper.h/1.3/Sun Mar 2 08:01:45 2003// +/pa_dsound.c/1.3/Sun Mar 2 08:01:45 2003// +/portaudio.def/1.3/Sun Mar 2 08:01:45 2003// +D diff --git a/lib/portaudio/pa_win_ds/CVS/Repository b/lib/portaudio/pa_win_ds/CVS/Repository new file mode 100644 index 0000000..a413b25 --- /dev/null +++ b/lib/portaudio/pa_win_ds/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/pa_win_ds diff --git a/lib/portaudio/pa_win_ds/CVS/Root b/lib/portaudio/pa_win_ds/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/pa_win_ds/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/pa_win_ds/dsound_wrapper.c b/lib/portaudio/pa_win_ds/dsound_wrapper.c new file mode 100644 index 0000000..4f9367c --- /dev/null +++ b/lib/portaudio/pa_win_ds/dsound_wrapper.c @@ -0,0 +1,466 @@ +/* + * $Id: dsound_wrapper.c,v 1.3 2003/03/02 08:01:45 dmazzoni Exp $ + * Simplified DirectSound interface. + * + * Author: Phil Burk & Robert Marsanyi + * + * PortAudio Portable Real-Time Audio Library + * For more information see: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#define INITGUID // Needed to build IID_IDirectSoundNotify. See objbase.h for info. +#include +#include +#include "dsound_wrapper.h" +#include "pa_trace.h" + +/************************************************************************************/ +void DSW_Term( DSoundWrapper *dsw ) +{ + // Cleanup the sound buffers + if (dsw->dsw_OutputBuffer) + { + IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); + IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer ); + dsw->dsw_OutputBuffer = NULL; + } +#if SUPPORT_AUDIO_CAPTURE + if (dsw->dsw_InputBuffer) + { + IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); + IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer ); + dsw->dsw_InputBuffer = NULL; + } + if (dsw->dsw_pDirectSoundCapture) + { + IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture ); + dsw->dsw_pDirectSoundCapture = NULL; + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + if (dsw->dsw_pDirectSound) + { + IDirectSound_Release( dsw->dsw_pDirectSound ); + dsw->dsw_pDirectSound = NULL; + } +} +/************************************************************************************/ +HRESULT DSW_Init( DSoundWrapper *dsw ) +{ + memset( dsw, 0, sizeof(DSoundWrapper) ); + return 0; +} +/************************************************************************************/ +HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) +{ + // Create the DS object + HRESULT hr = DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL ); + if( hr != DS_OK ) return hr; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer ) +{ + DWORD dwDataLen; + DWORD playCursor; + HRESULT result; + LPDIRECTSOUNDBUFFER pPrimaryBuffer; + HWND hWnd; + HRESULT hr; + WAVEFORMATEX wfFormat; + DSBUFFERDESC primaryDesc; + DSBUFFERDESC secondaryDesc; + unsigned char* pDSBuffData; + LARGE_INTEGER counterFrequency; + dsw->dsw_OutputSize = bytesPerBuffer; + dsw->dsw_OutputRunning = FALSE; + dsw->dsw_OutputUnderflows = 0; + dsw->dsw_FramesWritten = 0; + dsw->dsw_BytesPerFrame = nChannels * sizeof(short); + // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the + // applications's window. Also if that window is closed before the Buffer is closed + // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.) + // So we will use GetDesktopWindow() which was suggested by Miller Puckette. + // hWnd = GetForegroundWindow(); + hWnd = GetDesktopWindow(); + // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz. + // Exclusize also prevents unexpected sounds from other apps during a performance. + if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound, + hWnd, DSSCL_EXCLUSIVE)) != DS_OK) + { + return hr; + } + // ----------------------------------------------------------------------- + // Create primary buffer and set format just so we can specify our custom format. + // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz. + // Setup the primary buffer description + ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC)); + primaryDesc.dwSize = sizeof(DSBUFFERDESC); + primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth + primaryDesc.dwBufferBytes = 0; + primaryDesc.lpwfxFormat = NULL; + // Create the buffer + if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, + &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result; + // Define the buffer format + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = nChannels; + wfFormat.nSamplesPerSec = nFrameRate; + wfFormat.wBitsPerSample = 8 * sizeof(short); + wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8; + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; /* No extended format info. */ + // Set the primary buffer's format + if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result; + // ---------------------------------------------------------------------- + // Setup the secondary buffer description + ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC)); + secondaryDesc.dwSize = sizeof(DSBUFFERDESC); + secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; + secondaryDesc.dwBufferBytes = bytesPerBuffer; + secondaryDesc.lpwfxFormat = &wfFormat; + // Create the secondary buffer + if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, + &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result; + // Lock the DS buffer + if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData, + &dwDataLen, NULL, 0, 0)) != DS_OK) return result; + // Zero the DS buffer + ZeroMemory(pDSBuffData, dwDataLen); + // Unlock the DS buffer + if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result; + if( QueryPerformanceFrequency( &counterFrequency ) ) + { + int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short)); + dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate; + AddTraceMessage("dsw_CounterTicksPerBuffer = %d\n", dsw->dsw_CounterTicksPerBuffer.LowPart ); + } + else + { + dsw->dsw_CounterTicksPerBuffer.QuadPart = 0; + } + // Let DSound set the starting write position because if we set it to zero, it looks like the + // buffer is full to begin with. This causes a long pause before sound starts when using large buffers. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset ); + if( hr != DS_OK ) + { + return hr; + } + dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerFrame; + /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */ + return DS_OK; +} + +/************************************************************************************/ +HRESULT DSW_StartOutput( DSoundWrapper *dsw ) +{ + HRESULT hr; + QueryPerformanceCounter( &dsw->dsw_LastPlayTime ); + dsw->dsw_LastPlayCursor = 0; + dsw->dsw_FramesPlayed = 0; + hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 ); + if( hr != DS_OK ) + { + return hr; + } + // Start the buffer playback in a loop. + if( dsw->dsw_OutputBuffer != NULL ) + { + hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING ); + if( hr != DS_OK ) + { + return hr; + } + dsw->dsw_OutputRunning = TRUE; + } + + return 0; +} +/************************************************************************************/ +HRESULT DSW_StopOutput( DSoundWrapper *dsw ) +{ + // Stop the buffer playback + if( dsw->dsw_OutputBuffer != NULL ) + { + dsw->dsw_OutputRunning = FALSE; + return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ) +{ + HRESULT hr; + DWORD playCursor; + DWORD writeCursor; + long numBytesEmpty; + long playWriteGap; + // Query to see how much room is in buffer. + // Note: Even though writeCursor is not used, it must be passed to prevent DirectSound from dieing + // under WinNT. The Microsoft documentation says we can pass NULL but apparently not. + // Thanks to Max Rheiner for the fix. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); + if( hr != DS_OK ) + { + return hr; + } + AddTraceMessage("playCursor", playCursor); + AddTraceMessage("dsw_WriteOffset", dsw->dsw_WriteOffset); + // Determine size of gap between playIndex and WriteIndex that we cannot write into. + playWriteGap = writeCursor - playCursor; + if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap + /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */ + /* Attempt to detect playCursor wrap-around and correct it. */ + if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) ) + { + /* How much time has elapsed since last check. */ + LARGE_INTEGER currentTime; + LARGE_INTEGER elapsedTime; + long bytesPlayed; + long bytesExpected; + long buffersWrapped; + QueryPerformanceCounter( ¤tTime ); + elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart; + dsw->dsw_LastPlayTime = currentTime; + /* How many bytes does DirectSound say have been played. */ + bytesPlayed = playCursor - dsw->dsw_LastPlayCursor; + if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap + dsw->dsw_LastPlayCursor = playCursor; + /* Calculate how many bytes we would have expected to been played by now. */ + bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart); + buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize; + if( buffersWrapped > 0 ) + { + AddTraceMessage("playCursor wrapped! bytesPlayed", bytesPlayed ); + AddTraceMessage("playCursor wrapped! bytesExpected", bytesExpected ); + playCursor += (buffersWrapped * dsw->dsw_OutputSize); + bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize); + } + /* Maintain frame output cursor. */ + dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerFrame); + } + numBytesEmpty = playCursor - dsw->dsw_WriteOffset; + if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset + /* Have we underflowed? */ + if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) ) + { + if( dsw->dsw_OutputRunning ) + { + dsw->dsw_OutputUnderflows += 1; + AddTraceMessage("underflow detected! numBytesEmpty", numBytesEmpty ); + } + dsw->dsw_WriteOffset = writeCursor; + numBytesEmpty = dsw->dsw_OutputSize - playWriteGap; + } + *bytesEmpty = numBytesEmpty; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + long bytesEmpty; + hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed + if (hr != DS_OK) return hr; + if( bytesEmpty == 0 ) return DS_OK; + // Lock free space in the DS + hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy the buffer into the DS + ZeroMemory(lpbuf1, dwsize1); + if(lpbuf2 != NULL) + { + ZeroMemory(lpbuf2, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerFrame; + } + return hr; +} + +/************************************************************************************/ +HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + // Lock free space in the DS + hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy the buffer into the DS + CopyMemory(lpbuf1, buf, dwsize1); + if(lpbuf2 != NULL) + { + CopyMemory(lpbuf2, buf+dwsize1, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerFrame; + } + return hr; +} + +/************************************************************************************/ +DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ) +{ + DWORD status; + if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK) + return( DSERR_INVALIDPARAM ); + else + return( status ); +} + +#if SUPPORT_AUDIO_CAPTURE +/* These routines are used to support audio input. + * Do NOT compile these calls when using NT4 because it does + * not support the entry points. + */ +/************************************************************************************/ +HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) +{ + HRESULT hr = DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); + if( hr != DS_OK ) return hr; + return hr; +} +/************************************************************************************/ +HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer ) +{ + DSCBUFFERDESC captureDesc; + WAVEFORMATEX wfFormat; + HRESULT result; + // Define the buffer format + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = nChannels; + wfFormat.nSamplesPerSec = nFrameRate; + wfFormat.wBitsPerSample = 8 * sizeof(short); + wfFormat.nBlockAlign = wfFormat.nChannels * (wfFormat.wBitsPerSample / 8); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; /* No extended format info. */ + dsw->dsw_InputSize = bytesPerBuffer; + // ---------------------------------------------------------------------- + // Setup the secondary buffer description + ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC)); + captureDesc.dwSize = sizeof(DSCBUFFERDESC); + captureDesc.dwFlags = 0; + captureDesc.dwBufferBytes = bytesPerBuffer; + captureDesc.lpwfxFormat = &wfFormat; + // Create the capture buffer + if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture, + &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result; + dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer + return DS_OK; +} + +/************************************************************************************/ +HRESULT DSW_StartInput( DSoundWrapper *dsw ) +{ + // Start the buffer playback + if( dsw->dsw_InputBuffer != NULL ) + { + return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_StopInput( DSoundWrapper *dsw ) +{ + // Stop the buffer playback + if( dsw->dsw_InputBuffer != NULL ) + { + return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ) +{ + HRESULT hr; + DWORD capturePos; + DWORD readPos; + long filled; + // Query to see how much data is in buffer. + // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly + // so let's pass a pointer just to be safe. + hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos ); + if( hr != DS_OK ) + { + return hr; + } + filled = readPos - dsw->dsw_ReadOffset; + if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset + *bytesFilled = filled; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + // Lock free space in the DS + hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy from DS to the buffer + CopyMemory( buf, lpbuf1, dwsize1); + if(lpbuf2 != NULL) + { + CopyMemory( buf+dwsize1, lpbuf2, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize; + IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + } + return hr; +} + +#endif /* SUPPORT_AUDIO_CAPTURE */ diff --git a/lib/portaudio/pa_win_ds/dsound_wrapper.h b/lib/portaudio/pa_win_ds/dsound_wrapper.h new file mode 100644 index 0000000..13961fb --- /dev/null +++ b/lib/portaudio/pa_win_ds/dsound_wrapper.h @@ -0,0 +1,106 @@ +#ifndef __DSOUND_WRAPPER_H +#define __DSOUND_WRAPPER_H +/* + * $Id: dsound_wrapper.h,v 1.3 2003/03/02 08:01:45 dmazzoni Exp $ + * Simplified DirectSound interface. + * + * Author: Phil Burk & Robert Marsanyi + * + * For PortAudio Portable Real-Time Audio Library + * For more information see: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#if !defined(BOOL) +#define BOOL short +#endif +#ifndef SUPPORT_AUDIO_CAPTURE +#define SUPPORT_AUDIO_CAPTURE (1) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define DSW_NUM_POSITIONS (4) +#define DSW_NUM_EVENTS (5) +#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS) + +typedef struct +{ + /* Output */ + LPDIRECTSOUND dsw_pDirectSound; + LPDIRECTSOUNDBUFFER dsw_OutputBuffer; + DWORD dsw_WriteOffset; /* last write position */ + INT dsw_OutputSize; + INT dsw_BytesPerFrame; + /* Try to detect play buffer underflows. */ + LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */ + LARGE_INTEGER dsw_LastPlayTime; + UINT dsw_LastPlayCursor; + UINT dsw_OutputUnderflows; + BOOL dsw_OutputRunning; + /* use double which lets us can play for several thousand years with enough precision */ + double dsw_FramesWritten; + double dsw_FramesPlayed; +#if SUPPORT_AUDIO_CAPTURE + /* Input */ + LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture; + LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer; + UINT dsw_ReadOffset; /* last read position */ + UINT dsw_InputSize; +#endif /* SUPPORT_AUDIO_CAPTURE */ + +} +DSoundWrapper; +HRESULT DSW_Init( DSoundWrapper *dsw ); +void DSW_Term( DSoundWrapper *dsw ); +HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, + int nChannels, int bufSize ); +HRESULT DSW_StartOutput( DSoundWrapper *dsw ); +HRESULT DSW_StopOutput( DSoundWrapper *dsw ); +DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ); +HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ); +HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ); +HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ); +HRESULT DSW_Enumerate( DSoundWrapper *dsw ); + +#if SUPPORT_AUDIO_CAPTURE +HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, + int nChannels, int bufSize ); +HRESULT DSW_StartInput( DSoundWrapper *dsw ); +HRESULT DSW_StopInput( DSoundWrapper *dsw ); +HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ); +HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ); +#endif /* SUPPORT_AUDIO_CAPTURE */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* __DSOUND_WRAPPER_H */ diff --git a/lib/portaudio/pa_win_ds/pa_dsound.c b/lib/portaudio/pa_win_ds/pa_dsound.c new file mode 100644 index 0000000..197b97f --- /dev/null +++ b/lib/portaudio/pa_win_ds/pa_dsound.c @@ -0,0 +1,1042 @@ +/* + * $Id: pa_dsound.c,v 1.3 2003/03/02 08:01:45 dmazzoni Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * + * Copyright (c) 1999-2000 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +/* Modifications + * 7/19/01 Mike Berry - casts for compiling with __MWERKS__ CodeWarrior + * 9/27/01 Phil Burk - use number of frames instead of real-time for CPULoad calculation. + * 4/19/02 Phil Burk - Check for Win XP for system latency calculation. + */ +/* Compiler flags: + SUPPORT_AUDIO_CAPTURE - define this flag if you want to SUPPORT_AUDIO_CAPTURE + */ + +#include +#include +#ifndef __MWERKS__ +#include +#include +#endif //__MWERKS__ +#include +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" +#include "dsound_wrapper.h" + +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +#define PA_USE_HIGH_LATENCY (0) +#if PA_USE_HIGH_LATENCY +#define PA_WIN_9X_LATENCY (500) +#define PA_WIN_NT_LATENCY (600) +#else +#define PA_WIN_9X_LATENCY (140) +#define PA_WIN_NT_LATENCY (280) +#endif + +#define PA_WIN_WDM_LATENCY (120) + +/* Trigger an underflow for testing purposes. Should normally be (0). */ +#define PA_SIMULATE_UNDERFLOW (0) +#if PA_SIMULATE_UNDERFLOW +static gUnderCallbackCounter = 0; +#define UNDER_START_GAP (10) +#define UNDER_STOP_GAP (UNDER_START_GAP + 4) +#endif + +/************************************************* Definitions ********/ +typedef struct internalPortAudioStream internalPortAudioStream; +typedef struct internalPortAudioDevice +{ + GUID pad_GUID; + GUID *pad_lpGUID; + double pad_SampleRates[10]; /* for pointing to from pad_Info FIXME?!*/ + PaDeviceInfo pad_Info; +} +internalPortAudioDevice; + +/* Define structure to contain all DirectSound and Windows specific data. */ +typedef struct PaHostSoundControl +{ + DSoundWrapper pahsc_DSoundWrapper; + MMRESULT pahsc_TimerID; + BOOL pahsc_IfInsideCallback; /* Test for reentrancy. */ + short *pahsc_NativeBuffer; + unsigned int pahsc_BytesPerBuffer; /* native buffer size in bytes */ + double pahsc_ValidFramesWritten; + int pahsc_FramesPerDSBuffer; + /* For measuring CPU utilization. */ + LARGE_INTEGER pahsc_EntryCount; + double pahsc_InverseTicksPerUserBuffer; +} +PaHostSoundControl; + +/************************************************* Shared Data ********/ +/* FIXME - put Mutex around this shared data. */ +static int sNumDevices = 0; +static int sDeviceIndex = 0; +static internalPortAudioDevice *sDevices = NULL; +static int sDefaultInputDeviceID = paNoDevice; +static int sDefaultOutputDeviceID = paNoDevice; +static int sEnumerationError; +static int sPaHostError = 0; +/************************************************* Prototypes **********/ +static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id ); +static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); +static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); +static Pa_QueryDevices( void ); +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, + DWORD dwUser, DWORD dw1, DWORD dw2); + +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + /* Query system timer for usage analysis and to prevent overuse of CPU. */ + QueryPerformanceCounter( &pahsc->pahsc_EntryCount ); +} + +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + LARGE_INTEGER CurrentCount = { 0, 0 }; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + /* + ** Measure CPU utilization during this callback. Note that this calculation + ** assumes that we had the processor the whole time. + */ +#define LOWPASS_COEFFICIENT_0 (0.9) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + if( QueryPerformanceCounter( &CurrentCount ) ) + { + LONGLONG InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart; + double newUsage = InsideCount * pahsc->pahsc_InverseTicksPerUserBuffer; + past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + } +} + +/****************************************** END CPU UTILIZATION *******/ +static PaError Pa_QueryDevices( void ) +{ + int numBytes; + sDefaultInputDeviceID = paNoDevice; + sDefaultOutputDeviceID = paNoDevice; + /* Enumerate once just to count devices. */ + sNumDevices = 0; // for default device + DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL ); +#if SUPPORT_AUDIO_CAPTURE + DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL ); +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* Allocate structures to hold device info. */ + numBytes = sNumDevices * sizeof(internalPortAudioDevice); + sDevices = (internalPortAudioDevice *)PaHost_AllocateFastMemory( numBytes ); /* MEM */ + if( sDevices == NULL ) return paInsufficientMemory; + /* Enumerate again to fill in structures. */ + sDeviceIndex = 0; + sEnumerationError = 0; + DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)0 ); +#if SUPPORT_AUDIO_CAPTURE + if( sEnumerationError != paNoError ) return sEnumerationError; + sEnumerationError = 0; + DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)1 ); +#endif /* SUPPORT_AUDIO_CAPTURE */ + return sEnumerationError; +} +/************************************************************************************/ +long Pa_GetHostError() +{ + return sPaHostError; +} +/************************************************************************************ +** Just count devices so we know how much memory to allocate. +*/ +static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + sNumDevices++; + return TRUE; +} +/************************************************************************************ +** Extract capabilities info from each device. +*/ +static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + HRESULT hr; + LPDIRECTSOUND lpDirectSound; +#if SUPPORT_AUDIO_CAPTURE + LPDIRECTSOUNDCAPTURE lpDirectSoundCapture; +#endif /* SUPPORT_AUDIO_CAPTURE */ + int isInput = (int) lpContext; /* Passed from Pa_CountDevices() */ + internalPortAudioDevice *pad; + + if( sDeviceIndex >= sNumDevices ) + { + sEnumerationError = paInternalError; + return FALSE; + } + pad = &sDevices[sDeviceIndex]; + /* Copy GUID to static array. Set pointer. */ + if( lpGUID == NULL ) + { + pad->pad_lpGUID = NULL; + } + else + { + memcpy( &pad->pad_GUID, lpGUID, sizeof(GUID) ); + pad->pad_lpGUID = &pad->pad_GUID; + } + pad->pad_Info.sampleRates = pad->pad_SampleRates; /* Point to array. */ + /* Allocate room for descriptive name. */ + if( lpszDesc != NULL ) + { + int len = strlen(lpszDesc); + pad->pad_Info.name = (char *)malloc( len+1 ); + if( pad->pad_Info.name == NULL ) + { + sEnumerationError = paInsufficientMemory; + return FALSE; + } + memcpy( (void *) pad->pad_Info.name, lpszDesc, len+1 ); + } +#if SUPPORT_AUDIO_CAPTURE + if( isInput ) + { + /********** Input ******************************/ + DSCCAPS caps; + if( lpGUID == NULL ) sDefaultInputDeviceID = sDeviceIndex; + hr = DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL ); + if( hr != DS_OK ) + { + pad->pad_Info.maxInputChannels = 0; + DBUG(("Cannot create Capture for %s. Result = 0x%x\n", lpszDesc, hr )); + } + else + { + /* Query device characteristics. */ + caps.dwSize = sizeof(caps); + IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps ); + /* printf("caps.dwFormats = 0x%x\n", caps.dwFormats ); */ + pad->pad_Info.maxInputChannels = caps.dwChannels; + /* Determine sample rates from flags. */ + if( caps.dwChannels == 2 ) + { + int index = 0; + if( caps.dwFormats & WAVE_FORMAT_1S16) pad->pad_SampleRates[index++] = 11025.0; + if( caps.dwFormats & WAVE_FORMAT_2S16) pad->pad_SampleRates[index++] = 22050.0; + if( caps.dwFormats & WAVE_FORMAT_4S16) pad->pad_SampleRates[index++] = 44100.0; + pad->pad_Info.numSampleRates = index; + } + else if( caps.dwChannels == 1 ) + { + int index = 0; + if( caps.dwFormats & WAVE_FORMAT_1M16) pad->pad_SampleRates[index++] = 11025.0; + if( caps.dwFormats & WAVE_FORMAT_2M16) pad->pad_SampleRates[index++] = 22050.0; + if( caps.dwFormats & WAVE_FORMAT_4M16) pad->pad_SampleRates[index++] = 44100.0; + pad->pad_Info.numSampleRates = index; + } + else pad->pad_Info.numSampleRates = 0; + IDirectSoundCapture_Release( lpDirectSoundCapture ); + } + } + else +#endif /* SUPPORT_AUDIO_CAPTURE */ + + { + /********** Output ******************************/ + DSCAPS caps; + if( lpGUID == NULL ) sDefaultOutputDeviceID = sDeviceIndex; + /* Create interfaces for each object. */ + hr = DirectSoundCreate( lpGUID, &lpDirectSound, NULL ); + if( hr != DS_OK ) + { + pad->pad_Info.maxOutputChannels = 0; + DBUG(("Cannot create dsound for %s. Result = 0x%x\n", lpszDesc, hr )); + } + else + { + /* Query device characteristics. */ + caps.dwSize = sizeof(caps); + IDirectSound_GetCaps( lpDirectSound, &caps ); + pad->pad_Info.maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; + /* Get sample rates. */ + pad->pad_SampleRates[0] = (double) caps.dwMinSecondarySampleRate; + pad->pad_SampleRates[1] = (double) caps.dwMaxSecondarySampleRate; + if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) pad->pad_Info.numSampleRates = -1; + else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate ) + { + if( caps.dwMinSecondarySampleRate == 0 ) + { + /* + ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !! + ** But it supports continuous sampling. + ** So fake range of rates, and hope it really supports it. + */ + pad->pad_SampleRates[0] = 11025.0f; + pad->pad_SampleRates[1] = 48000.0f; + pad->pad_Info.numSampleRates = -1; /* continuous range */ + + DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex )); + } + else + { + pad->pad_Info.numSampleRates = 1; + } + } + else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) ) + { + /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000. + ** But we know that they really support a range of rates! + ** So when we see a ridiculous set of rates, assume it is a range. + */ + pad->pad_Info.numSampleRates = -1; + DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex )); + } + else pad->pad_Info.numSampleRates = 2; + IDirectSound_Release( lpDirectSound ); + } + } + pad->pad_Info.nativeSampleFormats = paInt16; + sDeviceIndex++; + return( TRUE ); +} +/*************************************************************************/ +int Pa_CountDevices() +{ + if( sNumDevices <= 0 ) Pa_Initialize(); + return sNumDevices; +} +static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id ) +{ + if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; + return &sDevices[id]; +} +/*************************************************************************/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + internalPortAudioDevice *pad; + if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; + pad = Pa_GetInternalDevice( id ); + return &pad->pad_Info ; +} +static PaError Pa_MaybeQueryDevices( void ) +{ + if( sNumDevices == 0 ) + { + return Pa_QueryDevices(); + } + return 0; +} +/************************************************************************* +** Returns recommended device ID. +** On the PC, the recommended device can be specified by the user by +** setting an environment variable. For example, to use device #1. +** +** set PA_RECOMMENDED_OUTPUT_DEVICE=1 +** +** The user should first determine the available device ID by using +** the supplied application "pa_devs". +*/ +#define PA_ENV_BUF_SIZE (32) +#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE") +#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE") +static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName ) +{ + DWORD hresult; + char envbuf[PA_ENV_BUF_SIZE]; + PaDeviceID recommendedID = paNoDevice; + /* Let user determine default device by setting environment variable. */ + hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + recommendedID = atoi( envbuf ); + } + return recommendedID; +} +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME ); + if( result < 0 ) + { + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultInputDeviceID; + } + return result; +} +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME ); + if( result < 0 ) + { + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultOutputDeviceID; + } + return result; +} +/********************************************************************** +** Make sure that we have queried the device capabilities. +*/ +PaError PaHost_Init( void ) +{ +#if PA_SIMULATE_UNDERFLOW + PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n")); +#endif + return Pa_MaybeQueryDevices(); +} +static PaError Pa_TimeSlice( internalPortAudioStream *past ) +{ + PaError result = 0; + long bytesEmpty = 0; + long bytesFilled = 0; + long bytesToXfer = 0; + long numChunks; + HRESULT hresult; + PaHostSoundControl *pahsc; + short *nativeBufPtr; + past->past_NumCallbacks += 1; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + /* How much input data is available? */ +#if SUPPORT_AUDIO_CAPTURE + if( past->past_NumInputChannels > 0 ) + { + DSW_QueryInputFilled( &pahsc->pahsc_DSoundWrapper, &bytesFilled ); + bytesToXfer = bytesFilled; + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* How much output room is available? */ + if( past->past_NumOutputChannels > 0 ) + { + DSW_QueryOutputSpace( &pahsc->pahsc_DSoundWrapper, &bytesEmpty ); + bytesToXfer = bytesEmpty; + } + AddTraceMessage( "bytesEmpty ", bytesEmpty ); + /* Choose smallest value if both are active. */ + if( (past->past_NumInputChannels > 0) && (past->past_NumOutputChannels > 0) ) + { + bytesToXfer = ( bytesFilled < bytesEmpty ) ? bytesFilled : bytesEmpty; + } + /* printf("bytesFilled = %d, bytesEmpty = %d, bytesToXfer = %d\n", + bytesFilled, bytesEmpty, bytesToXfer); + */ + /* Quantize to multiples of a buffer. */ + numChunks = bytesToXfer / pahsc->pahsc_BytesPerBuffer; + if( numChunks > (long)(past->past_NumUserBuffers/2) ) + { + numChunks = (long)past->past_NumUserBuffers/2; + } + else if( numChunks < 0 ) + { + numChunks = 0; + } + AddTraceMessage( "numChunks ", numChunks ); + nativeBufPtr = pahsc->pahsc_NativeBuffer; + if( numChunks > 0 ) + { + while( numChunks-- > 0 ) + { + /* Measure usage based on time to process one user buffer. */ + Pa_StartUsageCalculation( past ); +#if SUPPORT_AUDIO_CAPTURE + /* Get native data from DirectSound. */ + if( past->past_NumInputChannels > 0 ) + { + hresult = DSW_ReadBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer ); + if( hresult < 0 ) + { + ERR_RPT(("DirectSound ReadBlock failed, hresult = 0x%x\n",hresult)); + sPaHostError = hresult; + break; + } + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* Convert 16 bit native data to user data and call user routine. */ + result = Pa_CallConvertInt16( past, nativeBufPtr, nativeBufPtr ); + if( result != 0) break; + /* Pass native data to DirectSound. */ + if( past->past_NumOutputChannels > 0 ) + { + /* static short DEBUGHACK = 0; + DEBUGHACK += 0x0049; + nativeBufPtr[0] = DEBUGHACK; /* Make buzz to see if DirectSound still running. */ + hresult = DSW_WriteBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer ); + if( hresult < 0 ) + { + ERR_RPT(("DirectSound WriteBlock failed, result = 0x%x\n",hresult)); + sPaHostError = hresult; + break; + } + } + Pa_EndUsageCalculation( past ); + } + } + return result; +} +/*******************************************************************/ +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + internalPortAudioStream *past; + PaHostSoundControl *pahsc; +#if PA_SIMULATE_UNDERFLOW + gUnderCallbackCounter++; + if( (gUnderCallbackCounter >= UNDER_START_GAP) && + (gUnderCallbackCounter <= UNDER_STOP_GAP) ) + { + if( gUnderCallbackCounter == UNDER_START_GAP) + { + AddTraceMessage("Begin stall: gUnderCallbackCounter =======", gUnderCallbackCounter ); + } + if( gUnderCallbackCounter == UNDER_STOP_GAP) + { + AddTraceMessage("End stall: gUnderCallbackCounter =======", gUnderCallbackCounter ); + } + return; + } +#endif + past = (internalPortAudioStream *) dwUser; + if( past == NULL ) return; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + if( !pahsc->pahsc_IfInsideCallback && past->past_IsActive ) + { + if( past->past_StopNow ) + { + past->past_IsActive = 0; + } + else if( past->past_StopSoon ) + { + DSoundWrapper *dsw = &pahsc->pahsc_DSoundWrapper; + if( past->past_NumOutputChannels > 0 ) + { + DSW_ZeroEmptySpace( dsw ); + AddTraceMessage("Pa_TimerCallback: waiting - written ", (int) dsw->dsw_FramesWritten ); + AddTraceMessage("Pa_TimerCallback: waiting - played ", (int) dsw->dsw_FramesPlayed ); + /* clear past_IsActive when all sound played */ + if( dsw->dsw_FramesPlayed >= past->past_FrameCount ) + { + past->past_IsActive = 0; + } + } + else + { + past->past_IsActive = 0; + } + } + else + { + pahsc->pahsc_IfInsideCallback = 1; + if( Pa_TimeSlice( past ) != 0) /* Call time slice independant of timing method. */ + { + past->past_StopSoon = 1; + } + pahsc->pahsc_IfInsideCallback = 0; + } + } +} +/*******************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + HRESULT hr; + PaError result = paNoError; + PaHostSoundControl *pahsc; + int numBytes, maxChannels; + unsigned int minNumBuffers; + internalPortAudioDevice *pad; + DSoundWrapper *dsw; + /* Allocate and initialize host data. */ + pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */ + if( pahsc == NULL ) + { + result = paInsufficientMemory; + goto error; + } + memset( pahsc, 0, sizeof(PaHostSoundControl) ); + past->past_DeviceData = (void *) pahsc; + pahsc->pahsc_TimerID = 0; + dsw = &pahsc->pahsc_DSoundWrapper; + DSW_Init( dsw ); + /* Allocate native buffer. */ + maxChannels = ( past->past_NumOutputChannels > past->past_NumInputChannels ) ? + past->past_NumOutputChannels : past->past_NumInputChannels; + pahsc->pahsc_BytesPerBuffer = past->past_FramesPerUserBuffer * maxChannels * sizeof(short); + if( maxChannels > 0 ) + { + pahsc->pahsc_NativeBuffer = (short *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerBuffer); /* MEM */ + if( pahsc->pahsc_NativeBuffer == NULL ) + { + result = paInsufficientMemory; + goto error; + } + } + else + { + result = paInvalidChannelCount; + goto error; + } + + DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); + minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers; + numBytes = pahsc->pahsc_BytesPerBuffer * past->past_NumUserBuffers; + if( numBytes < DSBSIZE_MIN ) + { + result = paBufferTooSmall; + goto error; + } + if( numBytes > DSBSIZE_MAX ) + { + result = paBufferTooBig; + goto error; + } + pahsc->pahsc_FramesPerDSBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; + { + int msecLatency = (int) ((pahsc->pahsc_FramesPerDSBuffer * 1000) / past->past_SampleRate); + PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", pahsc->pahsc_FramesPerDSBuffer, msecLatency )); + } + /* ------------------ OUTPUT */ + if( (past->past_OutputDeviceID >= 0) && (past->past_NumOutputChannels > 0) ) + { + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID)); + pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); + hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL ); + /* If this fails, then try each output device until we find one that works. */ + if( hr != DS_OK ) + { + int i; + ERR_RPT(("Creation of requested Audio Output device '%s' failed.\n", + ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) )); + for( i=0; ipad_Info.maxOutputChannels >= past->past_NumOutputChannels ) + { + DBUG(("Try device '%s' instead.\n", pad->pad_Info.name )); + hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL ); + if( hr == DS_OK ) + { + ERR_RPT(("Using device '%s' instead.\n", pad->pad_Info.name )); + break; + } + } + } + } + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n")); + result = paHostError; + sPaHostError = hr; + goto error; + } + hr = DSW_InitOutputBuffer( dsw, + (unsigned long) (past->past_SampleRate + 0.5), + past->past_NumOutputChannels, numBytes ); + DBUG(("DSW_InitOutputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + result = paHostError; + sPaHostError = hr; + goto error; + } + past->past_FrameCount = pahsc->pahsc_DSoundWrapper.dsw_FramesWritten; + } +#if SUPPORT_AUDIO_CAPTURE + /* ------------------ INPUT */ + if( (past->past_InputDeviceID >= 0) && (past->past_NumInputChannels > 0) ) + { + pad = Pa_GetInternalDevice( past->past_InputDeviceID ); + hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); + /* If this fails, then try each input device until we find one that works. */ + if( hr != DS_OK ) + { + int i; + ERR_RPT(("Creation of requested Audio Capture device '%s' failed.\n", + ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) )); + for( i=0; ipad_Info.maxInputChannels >= past->past_NumInputChannels ) + { + PRINT(("Try device '%s' instead.\n", pad->pad_Info.name )); + hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); + if( hr == DS_OK ) break; + } + } + } + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n")); + result = paHostError; + sPaHostError = hr; + goto error; + } + hr = DSW_InitInputBuffer( dsw, + (unsigned long) (past->past_SampleRate + 0.5), + past->past_NumInputChannels, numBytes ); + DBUG(("DSW_InitInputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr)); + result = paHostError; + sPaHostError = hr; + goto error; + } + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* Calculate scalar used in CPULoad calculation. */ + { + LARGE_INTEGER frequency; + if( QueryPerformanceFrequency( &frequency ) == 0 ) + { + pahsc->pahsc_InverseTicksPerUserBuffer = 0.0; + } + else + { + pahsc->pahsc_InverseTicksPerUserBuffer = past->past_SampleRate / + ( (double)frequency.QuadPart * past->past_FramesPerUserBuffer ); + DBUG(("pahsc_InverseTicksPerUserBuffer = %g\n", pahsc->pahsc_InverseTicksPerUserBuffer )); + } + } + return result; +error: + PaHost_CloseStream( past ); + return result; +} +/*************************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + HRESULT hr; + PaHostSoundControl *pahsc; + PaError result = paNoError; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + /* Give user callback a chance to pre-fill buffer. */ + result = Pa_TimeSlice( past ); + if( result != paNoError ) return result; // FIXME - what if finished? + hr = DSW_StartOutput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paHostError; + sPaHostError = hr; + goto error; + } +error: + return result; +} +/*************************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + PaError result = paNoError; +#if SUPPORT_AUDIO_CAPTURE + HRESULT hr; + PaHostSoundControl *pahsc; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + hr = DSW_StartInput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("Pa_StartStream: DSW_StartInput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paHostError; + sPaHostError = hr; + goto error; + } +error: +#endif /* SUPPORT_AUDIO_CAPTURE */ + return result; +} +/*************************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + PaError result = paNoError; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + past->past_StopNow = 0; + past->past_StopSoon = 0; + past->past_IsActive = 1; + /* Create timer that will wake us up so we can fill the DSound buffer. */ + { + int msecPerBuffer; + int resolution; + int bufsPerInterrupt; + + DBUG(("PaHost_StartEngine: past_NumUserBuffers = %d\n", past->past_NumUserBuffers)); + /* Decide how often to wake up and fill the buffers. */ + if( past->past_NumUserBuffers == 2 ) + { + /* Generate two timer interrupts per user buffer. */ + msecPerBuffer = (500 * past->past_FramesPerUserBuffer) / (int) past->past_SampleRate; + } + else + { + if ( past->past_NumUserBuffers >= 16 ) bufsPerInterrupt = past->past_NumUserBuffers/8; + else if ( past->past_NumUserBuffers >= 8 ) bufsPerInterrupt = 2; + else bufsPerInterrupt = 1; + + msecPerBuffer = 1000 * (bufsPerInterrupt * past->past_FramesPerUserBuffer) / (int) past->past_SampleRate; + + DBUG(("PaHost_StartEngine: bufsPerInterrupt = %d\n", bufsPerInterrupt)); + } + + DBUG(("PaHost_StartEngine: msecPerBuffer = %d\n", msecPerBuffer)); + + if( msecPerBuffer < 10 ) msecPerBuffer = 10; + else if( msecPerBuffer > 100 ) msecPerBuffer = 100; + DBUG(("PaHost_StartEngine: clipped msecPerBuffer = %d\n", msecPerBuffer)); + + resolution = msecPerBuffer/4; + pahsc->pahsc_TimerID = timeSetEvent( msecPerBuffer, resolution, (LPTIMECALLBACK) Pa_TimerCallback, + (DWORD) past, TIME_PERIODIC ); + } + if( pahsc->pahsc_TimerID == 0 ) + { + past->past_IsActive = 0; + result = paHostError; + sPaHostError = 0; + goto error; + } +error: + return result; +} +/*************************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + int timeoutMsec; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + if( abort ) past->past_StopNow = 1; + past->past_StopSoon = 1; + /* Set timeout at 20% beyond maximum time we might wait. */ + timeoutMsec = (int) (1200.0 * pahsc->pahsc_FramesPerDSBuffer / past->past_SampleRate); + while( past->past_IsActive && (timeoutMsec > 0) ) + { + Sleep(10); + timeoutMsec -= 10; + } + if( pahsc->pahsc_TimerID != 0 ) + { + timeKillEvent(pahsc->pahsc_TimerID); /* Stop callback timer. */ + pahsc->pahsc_TimerID = 0; + } + return paNoError; +} +/*************************************************************************/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ +#if SUPPORT_AUDIO_CAPTURE + HRESULT hr; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + (void) abort; + hr = DSW_StopInput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("DSW_StopInput() result is %x\n", hr)); +#endif /* SUPPORT_AUDIO_CAPTURE */ + return paNoError; +} +/*************************************************************************/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + HRESULT hr; + PaHostSoundControl *pahsc; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + (void) abort; + hr = DSW_StopOutput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("DSW_StopOutput() result is %x\n", hr)); + return paNoError; +} +/*******************************************************************/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + DSW_Term( &pahsc->pahsc_DSoundWrapper ); + if( pahsc->pahsc_NativeBuffer ) + { + PaHost_FreeFastMemory( pahsc->pahsc_NativeBuffer, pahsc->pahsc_BytesPerBuffer ); /* MEM */ + pahsc->pahsc_NativeBuffer = NULL; + } + PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); /* MEM */ + past->past_DeviceData = NULL; + return paNoError; +} + +/* Set minimal latency based on whether NT or Win95. + * NT has higher latency. + */ +static int PaHost_GetMinSystemLatency( void ) +{ + int minLatencyMsec; + /* Set minimal latency based on whether NT or other OS. + * NT has higher latency. + */ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof( osvi ); + GetVersionEx( &osvi ); + DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); + DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); + DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); + /* Check for NT */ + if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) + { + minLatencyMsec = PA_WIN_NT_LATENCY; + } + else if(osvi.dwMajorVersion >= 5) + { + minLatencyMsec = PA_WIN_WDM_LATENCY; + } + else + { + minLatencyMsec = PA_WIN_9X_LATENCY; + } + return minLatencyMsec; +} + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the AUTOEXEC.BAT file and reboot. +** If the environment variable is not set, then the latency will be determined +** based on the OS. Windows NT has higher latency than Win95. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") +int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ) +{ + char envbuf[PA_ENV_BUF_SIZE]; + DWORD hresult; + int minLatencyMsec = 0; + double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate; + int minBuffers; + /* Let user determine minimal latency by setting environment variable. */ + hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + minLatencyMsec = atoi( envbuf ); + } + else + { + minLatencyMsec = PaHost_GetMinSystemLatency(); +#if PA_USE_HIGH_LATENCY + PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); +#endif + + } + minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer)); + if( minBuffers < 2 ) minBuffers = 2; + return minBuffers; +} +/*************************************************************************/ +PaError PaHost_Term( void ) +{ + int i; + /* Free names allocated during enumeration. */ + for( i=0; ipast_DeviceData; + if( pahsc == NULL ) return paInternalError; + return (PaError) (past->past_IsActive); +} +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + DSoundWrapper *dsw; + internalPortAudioStream *past = (internalPortAudioStream *) stream; + PaHostSoundControl *pahsc; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + dsw = &pahsc->pahsc_DSoundWrapper; + return dsw->dsw_FramesPlayed; +} diff --git a/lib/portaudio/pa_win_ds/portaudio.def b/lib/portaudio/pa_win_ds/portaudio.def new file mode 100644 index 0000000..8012b99 --- /dev/null +++ b/lib/portaudio/pa_win_ds/portaudio.def @@ -0,0 +1,28 @@ +LIBRARY PortAudio +DESCRIPTION 'PortAudio Portable interface to audio HW' + +EXPORTS + ; Explicit exports can go here + Pa_Initialize @1 + Pa_Terminate @2 + Pa_GetHostError @3 + Pa_GetErrorText @4 + Pa_CountDevices @5 + Pa_GetDefaultInputDeviceID @6 + Pa_GetDefaultOutputDeviceID @7 + Pa_GetDeviceInfo @8 + Pa_OpenStream @9 + Pa_OpenDefaultStream @10 + Pa_CloseStream @11 + Pa_StartStream @12 + Pa_StopStream @13 + Pa_StreamActive @14 + Pa_StreamTime @15 + Pa_GetCPULoad @16 + Pa_GetMinNumBuffers @17 + Pa_Sleep @18 + + ;123456789012345678901234567890123456 + ;000000000111111111122222222223333333 + + diff --git a/lib/portaudio/pa_win_wmme/CVS/Entries b/lib/portaudio/pa_win_wmme/CVS/Entries new file mode 100644 index 0000000..4abc4fb --- /dev/null +++ b/lib/portaudio/pa_win_wmme/CVS/Entries @@ -0,0 +1,3 @@ +/Makefile/1.1/Mon Sep 22 09:00:33 2003// +/pa_win_wmme.c/1.8/Sun Mar 2 08:01:46 2003// +D diff --git a/lib/portaudio/pa_win_wmme/CVS/Repository b/lib/portaudio/pa_win_wmme/CVS/Repository new file mode 100644 index 0000000..f9807fc --- /dev/null +++ b/lib/portaudio/pa_win_wmme/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/pa_win_wmme diff --git a/lib/portaudio/pa_win_wmme/CVS/Root b/lib/portaudio/pa_win_wmme/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/pa_win_wmme/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/pa_win_wmme/Makefile b/lib/portaudio/pa_win_wmme/Makefile new file mode 100644 index 0000000..acb2902 --- /dev/null +++ b/lib/portaudio/pa_win_wmme/Makefile @@ -0,0 +1,21 @@ +# Make PortAudio Library for Unix/OSS + +CC = gcc + +override CFLAGS += -g -O -ansi -I../pa_common +PASRC = ../pa_common/pa_lib.c pa_win_wmme.c +PAINC = ../pa_common/portaudio.h +PAOBJ = ../pa_common/pa_lib.o pa_win_wmme.o + +portaudio.a: $(PAOBJ) + ar ruv portaudio.a $(PAOBJ) + ranlib portaudio.a +# cp portaudio.a $(DEST) + +clean: + rm -f portaudio.a $(PAOBJ) + +distclean: clean + +%.o: %.c $(PAINC) Makefile + $(CC) -c $(CFLAGS) $< -o $@ diff --git a/lib/portaudio/pa_win_wmme/pa_win_wmme.c b/lib/portaudio/pa_win_wmme/pa_win_wmme.c new file mode 100644 index 0000000..fed24d6 --- /dev/null +++ b/lib/portaudio/pa_win_wmme/pa_win_wmme.c @@ -0,0 +1,1709 @@ +/* + * $Id: pa_win_wmme.c,v 1.8 2003/03/02 08:01:46 dmazzoni Exp $ + * pa_win_wmme.c + * Implementation of PortAudio for Windows MultiMedia Extensions (WMME) + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Authors: Ross Bencina and Phil Burk + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtainingF + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +/* + All memory allocations and frees are marked with MEM for quick review. +*/ + +/* Modification History: + PLB = Phil Burk + JM = Julien Maillard + RDB = Ross Bencina + PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer) + PLB20010413 - check for excessive numbers of channels + PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC + including condition including of memory.h, + and explicit typecasting on memory allocation + PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory + PLB20010816 - pass process instead of thread to SetPriorityClass() + PLB20010927 - use number of frames instead of real-time for CPULoad calculation. + JM20020118 - prevent hung thread when buffers underflow. + PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount + RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init + RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices + refactoring, renaming and fixed a few edge case bugs + PLB20020612 - added 8000.0 Hz to custom sampling rates array +*/ + +#include +#include +#include +#include +#include +#include +/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ +#ifndef __MWERKS__ +#include +#include +#endif /* __MWERKS__ */ +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +/************************************************* Constants ********/ +#define PA_TRACK_MEMORY (0) + +#define PA_USE_TIMER_CALLBACK (0) /* Select between two options for background task. 0=thread, 1=timer */ +/* Switches for debugging. */ +#define PA_SIMULATE_UNDERFLOW (0) /* Set to one to force an underflow of the output buffer. */ + +/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */ +#define PA_TRACE_RUN (0) +#define PA_TRACE_START_STOP (1) + +#define PA_USE_HIGH_LATENCY (0) /* For debugging glitches. */ + +#if PA_USE_HIGH_LATENCY + #define PA_MIN_MSEC_PER_HOST_BUFFER (100) + #define PA_MAX_MSEC_PER_HOST_BUFFER (300) /* Do not exceed unless user buffer exceeds */ + #define PA_MIN_NUM_HOST_BUFFERS (4) + #define PA_MAX_NUM_HOST_BUFFERS (16) /* OK to exceed if necessary */ + #define PA_WIN_9X_LATENCY (400) +#else + #define PA_MIN_MSEC_PER_HOST_BUFFER (10) + #define PA_MAX_MSEC_PER_HOST_BUFFER (100) /* Do not exceed unless user buffer exceeds */ + #define PA_MIN_NUM_HOST_BUFFERS (3) + #define PA_MAX_NUM_HOST_BUFFERS (16) /* OK to exceed if necessary */ + #define PA_WIN_9X_LATENCY (200) +#endif +#define MIN_TIMEOUT_MSEC (1000) + +/* +** Use higher latency for NT because it is even worse at real-time +** operation than Win9x. +*/ +#define PA_WIN_NT_LATENCY (PA_WIN_9X_LATENCY * 2) +#define PA_WIN_WDM_LATENCY (PA_WIN_9X_LATENCY) + +#if PA_SIMULATE_UNDERFLOW +static gUnderCallbackCounter = 0; +#define UNDER_SLEEP_AT (40) +#define UNDER_SLEEP_FOR (500) +#endif + +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) /**/ +#define DBUGX(x) /* PRINT(x) */ +/************************************************* Definitions ********/ +/************************************************************** + * Structure for internal host specific stream data. + * This is allocated on a per stream basis. + */ +typedef struct PaWMMEStreamData +{ + /* Input -------------- */ + HWAVEIN hWaveIn; + WAVEHDR *inputBuffers; + int currentInputBuffer; + int bytesPerHostInputBuffer; + int bytesPerUserInputBuffer; /* native buffer size in bytes */ + /* Output -------------- */ + HWAVEOUT hWaveOut; + WAVEHDR *outputBuffers; + int currentOutputBuffer; + int bytesPerHostOutputBuffer; + int bytesPerUserOutputBuffer; /* native buffer size in bytes */ + /* Run Time -------------- */ + PaTimestamp framesPlayed; + long lastPosition; /* used to track frames played. */ + /* For measuring CPU utilization. */ + LARGE_INTEGER entryCount; + double inverseTicksPerHostBuffer; + /* Init Time -------------- */ + int numHostBuffers; + int framesPerHostBuffer; + int userBuffersPerHostBuffer; + CRITICAL_SECTION streamLock; /* Mutext to prevent threads from colliding. */ + INT streamLockInited; +#if PA_USE_TIMER_CALLBACK + BOOL ifInsideCallback; /* Test for reentrancy. */ + MMRESULT timerID; +#else + HANDLE abortEvent; + int abortEventInited; + HANDLE bufferEvent; + int bufferEventInited; + HANDLE engineThread; + DWORD engineThreadID; +#endif +} +PaWMMEStreamData; +/************************************************* Shared Data ********/ +/* FIXME - put Mutex around this shared data. */ +static int sNumInputDevices = 0; +static int sNumOutputDevices = 0; +static int sNumDevices = 0; +static PaDeviceInfo **sDevicePtrs = NULL; +static int sDefaultInputDeviceID = paNoDevice; +static int sDefaultOutputDeviceID = paNoDevice; +static int sPaHostError = 0; +static const char sMapperSuffixInput[] = " - Input"; +static const char sMapperSuffixOutput[] = " - Output"; + +#if PA_TRACK_MEMORY +static int sNumAllocations = 0; +#endif + +/************************************************* Macros ********/ +/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */ +#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1)) +/************************************************* Prototypes **********/ + +void Pa_InitializeNumDevices( void ); +PaError Pa_AllocateDevicePtrs( void ); + +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, + DWORD dwUser, DWORD dw1, DWORD dw2); +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); +static PaError PaHost_UpdateStreamTime( PaWMMEStreamData *wmmeStreamData ); +static PaError PaHost_BackgroundManager( internalPortAudioStream *past ); + +static void *PaHost_AllocateTrackedMemory( long numBytes ); +static void PaHost_FreeTrackedMemory( void *addr ); + +/*******************************************************************/ +static PaError PaHost_AllocateWMMEStreamData( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + PaWMMEStreamData *wmmeStreamData; + + wmmeStreamData = (PaWMMEStreamData *) PaHost_AllocateFastMemory(sizeof(PaWMMEStreamData)); /* MEM */ + if( wmmeStreamData == NULL ) + { + result = paInsufficientMemory; + goto error; + } + memset( wmmeStreamData, 0, sizeof(PaWMMEStreamData) ); + stream->past_DeviceData = (void *) wmmeStreamData; + + return result; + +error: + return result; +} + +/*******************************************************************/ +static void PaHost_FreeWMMEStreamData( internalPortAudioStream *internalStream ) +{ + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) internalStream->past_DeviceData; + + PaHost_FreeFastMemory( wmmeStreamData, sizeof(PaWMMEStreamData) ); /* MEM */ + internalStream->past_DeviceData = NULL; +} +/*************************************************************************/ +static PaWMMEStreamData* PaHost_GetWMMEStreamData( internalPortAudioStream* internalStream ) +{ + PaWMMEStreamData *result = NULL; + + if( internalStream != NULL ) + { + result = (PaWMMEStreamData *) internalStream->past_DeviceData; + } + return result; +} +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +/* FIXME: the cpu usage code should be factored out into a common module */ +static void Pa_InitializeCpuUsageScalar( internalPortAudioStream *stream ) +{ + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + + LARGE_INTEGER frequency; + if( QueryPerformanceFrequency( &frequency ) == 0 ) + { + wmmeStreamData->inverseTicksPerHostBuffer = 0.0; + } + else + { + wmmeStreamData->inverseTicksPerHostBuffer = stream->past_SampleRate / + ( (double)frequency.QuadPart * stream->past_FramesPerUserBuffer * wmmeStreamData->userBuffersPerHostBuffer ); + DBUG(("inverseTicksPerHostBuffer = %g\n", wmmeStreamData->inverseTicksPerHostBuffer )); + } +} +static void Pa_StartUsageCalculation( internalPortAudioStream *stream ) +{ + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + + if( wmmeStreamData == NULL ) return; + /* Query system timer for usage analysis and to prevent overuse of CPU. */ + QueryPerformanceCounter( &wmmeStreamData->entryCount ); +} +static void Pa_EndUsageCalculation( internalPortAudioStream *stream ) +{ + LARGE_INTEGER CurrentCount; + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + + if( wmmeStreamData == NULL ) return; + /* + * Measure CPU utilization during this callback. Note that this calculation + * assumes that we had the processor the whole time. + */ +#define LOWPASS_COEFFICIENT_0 (0.9) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + if( QueryPerformanceCounter( &CurrentCount ) ) + { + LONGLONG InsideCount = CurrentCount.QuadPart - wmmeStreamData->entryCount.QuadPart; + double newUsage = InsideCount * wmmeStreamData->inverseTicksPerHostBuffer; + stream->past_Usage = (LOWPASS_COEFFICIENT_0 * stream->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + } +} +/****************************************** END CPU UTILIZATION *******/ + +static void Pa_InitializeNumDevices( void ) +{ + sNumInputDevices = waveInGetNumDevs(); + if( sNumInputDevices > 0 ) + { + sNumInputDevices += 1; /* add one extra for the WAVE_MAPPER */ + sDefaultInputDeviceID = 0; + } + else + { + sDefaultInputDeviceID = paNoDevice; + } + + sNumOutputDevices = waveOutGetNumDevs(); + if( sNumOutputDevices > 0 ) + { + sNumOutputDevices += 1; /* add one extra for the WAVE_MAPPER */ + sDefaultOutputDeviceID = sNumInputDevices; + } + else + { + sDefaultOutputDeviceID = paNoDevice; + } + + sNumDevices = sNumInputDevices + sNumOutputDevices; +} + +static PaError Pa_AllocateDevicePtrs( void ) +{ + int numBytes; + int i; + + /* Allocate structures to hold device info. */ + /* PLB20010402 - was allocating too much memory. */ + /* numBytes = sNumDevices * sizeof(PaDeviceInfo); // PLB20010402 */ + + if( sNumDevices > 0 ) + { + numBytes = sNumDevices * sizeof(PaDeviceInfo *); /* PLB20010402 */ + sDevicePtrs = (PaDeviceInfo **) PaHost_AllocateTrackedMemory( numBytes ); /* MEM */ + if( sDevicePtrs == NULL ) return paInsufficientMemory; + + for( i = 0; i < sNumDevices; i++ ) + sDevicePtrs[i] = NULL; /* RDB20020417 explicitly set each ptr to NULL */ + } + else + { + sDevicePtrs = NULL; + } + + return paNoError; +} +/*************************************************************************/ +long Pa_GetHostError() +{ + return sPaHostError; +} +/*************************************************************************/ +int Pa_CountDevices() +{ + if( PaHost_IsInitialized() ) + return sNumDevices; + else + return 0; +} +/************************************************************************* + * If a PaDeviceInfo structure has not already been created, + * then allocate one and fill it in for the selected device. + * + * We create one extra input and one extra output device for the WAVE_MAPPER. + * [Does anyone know how to query the default device and get its name?] + */ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ +#define NUM_STANDARDSAMPLINGRATES 3 /* 11025, 22050, 44100 */ + static DWORD customSamplingRates[] = { 8000, 32000, 48000, 64000, 88200, 96000 }; +#define NUM_CUSTOMSAMPLINGRATES (sizeof(customSamplingRates)/sizeof(DWORD)) +#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES) + + PaDeviceInfo *deviceInfo; + double *sampleRates; /* non-const ptr */ + int i; + char *s; + + if( id < 0 || id >= sNumDevices ) + return NULL; + if( sDevicePtrs[ id ] != NULL ) + { + return sDevicePtrs[ id ]; + } + deviceInfo = (PaDeviceInfo *)PaHost_AllocateTrackedMemory( sizeof(PaDeviceInfo) ); /* MEM */ + if( deviceInfo == NULL ) return NULL; + deviceInfo->structVersion = 1; + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + deviceInfo->numSampleRates = 0; + sampleRates = (double*)PaHost_AllocateTrackedMemory( MAX_NUMSAMPLINGRATES * sizeof(double) ); /* MEM */ + deviceInfo->sampleRates = sampleRates; + deviceInfo->nativeSampleFormats = paInt16; /* should query for higher bit depths below */ + if( id < sNumInputDevices ) + { + /* input device */ + int inputMmID = PaDeviceIdToWinId(id); + WAVEINCAPS wic; + if( waveInGetDevCaps( inputMmID, &wic, sizeof( WAVEINCAPS ) ) != MMSYSERR_NOERROR ) + goto error; + + /* Append I/O suffix to WAVE_MAPPER device. */ + if( inputMmID == WAVE_MAPPER ) + { + s = (char *) PaHost_AllocateTrackedMemory( strlen( wic.szPname ) + 1 + sizeof(sMapperSuffixInput) ); /* MEM */ + strcpy( s, wic.szPname ); + strcat( s, sMapperSuffixInput ); + } + else + { + s = (char *) PaHost_AllocateTrackedMemory( strlen( wic.szPname ) + 1 ); /* MEM */ + strcpy( s, wic.szPname ); + } + deviceInfo->name = s; + deviceInfo->maxInputChannels = wic.wChannels; + /* Sometimes a device can return a rediculously large number of channels. + * This happened with an SBLive card on a Windows ME box. + * If that happens, then force it to 2 channels. PLB20010413 + */ + if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) ) + { + ERR_RPT(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels )); + deviceInfo->maxInputChannels = 2; + } + /* Add a sample rate to the list if we can do stereo 16 bit at that rate + * based on the format flags. */ + if( wic.dwFormats & WAVE_FORMAT_1M16 ||wic.dwFormats & WAVE_FORMAT_1S16 ) + sampleRates[ deviceInfo->numSampleRates++ ] = 11025.; + if( wic.dwFormats & WAVE_FORMAT_2M16 ||wic.dwFormats & WAVE_FORMAT_2S16 ) + sampleRates[ deviceInfo->numSampleRates++ ] = 22050.; + if( wic.dwFormats & WAVE_FORMAT_4M16 ||wic.dwFormats & WAVE_FORMAT_4S16 ) + sampleRates[ deviceInfo->numSampleRates++ ] = 44100.; + /* Add a sample rate to the list if we can do stereo 16 bit at that rate + * based on opening the device successfully. */ + for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ ) + { + WAVEFORMATEX wfx; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nSamplesPerSec = customSamplingRates[i]; + wfx.wBitsPerSample = 16; + wfx.cbSize = 0; /* ignored */ + wfx.nChannels = (WORD)deviceInfo->maxInputChannels; + wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short); + wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short)); + if( waveInOpen( NULL, inputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR ) + { + sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i]; + } + } + + } + else if( id - sNumInputDevices < sNumOutputDevices ) + { + /* output device */ + int outputMmID = PaDeviceIdToWinId(id); + WAVEOUTCAPS woc; + if( waveOutGetDevCaps( outputMmID, &woc, sizeof( WAVEOUTCAPS ) ) != MMSYSERR_NOERROR ) + goto error; + /* Append I/O suffix to WAVE_MAPPER device. */ + if( outputMmID == WAVE_MAPPER ) + { + s = (char *) PaHost_AllocateTrackedMemory( strlen( woc.szPname ) + 1 + sizeof(sMapperSuffixOutput) ); /* MEM */ + strcpy( s, woc.szPname ); + strcat( s, sMapperSuffixOutput ); + } + else + { + s = (char *) PaHost_AllocateTrackedMemory( strlen( woc.szPname ) + 1 ); /* MEM */ + strcpy( s, woc.szPname ); + } + deviceInfo->name = s; + deviceInfo->maxOutputChannels = woc.wChannels; + /* Sometimes a device can return a rediculously large number of channels. + * This happened with an SBLive card on a Windows ME box. + * It also happens on Win XP! + */ + if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) ) + { +#if 1 + deviceInfo->maxOutputChannels = 2; +#else + /* If channel max is goofy, then query for max channels. PLB20020228 + * This doesn't seem to help. Disable code for now. Remove it later. + */ + ERR_RPT(("Pa_GetDeviceInfo: Num output channels reported as %d!", deviceInfo->maxOutputChannels )); + deviceInfo->maxOutputChannels = 0; + /* Attempt to find the correct maximum by querying the device. */ + for( i=2; i<16; i += 2 ) + { + WAVEFORMATEX wfx; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.cbSize = 0; /* ignored */ + wfx.nChannels = (WORD) i; + wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short); + wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short)); + if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR ) + { + deviceInfo->maxOutputChannels = i; + } + else + { + break; + } + } +#endif + ERR_RPT((" Changed to %d.\n", deviceInfo->maxOutputChannels )); + } + + /* Add a sample rate to the list if we can do stereo 16 bit at that rate + * based on the format flags. */ + if( woc.dwFormats & WAVE_FORMAT_1M16 ||woc.dwFormats & WAVE_FORMAT_1S16 ) + sampleRates[ deviceInfo->numSampleRates++ ] = 11025.; + if( woc.dwFormats & WAVE_FORMAT_2M16 ||woc.dwFormats & WAVE_FORMAT_2S16 ) + sampleRates[ deviceInfo->numSampleRates++ ] = 22050.; + if( woc.dwFormats & WAVE_FORMAT_4M16 ||woc.dwFormats & WAVE_FORMAT_4S16 ) + sampleRates[ deviceInfo->numSampleRates++ ] = 44100.; + + /* Add a sample rate to the list if we can do stereo 16 bit at that rate + * based on opening the device successfully. */ + for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ ) + { + WAVEFORMATEX wfx; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nSamplesPerSec = customSamplingRates[i]; + wfx.wBitsPerSample = 16; + wfx.cbSize = 0; /* ignored */ + wfx.nChannels = (WORD)deviceInfo->maxOutputChannels; + wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short); + wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short)); + if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR ) + { + sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i]; + } + } + } + sDevicePtrs[ id ] = deviceInfo; + return deviceInfo; + +error: + PaHost_FreeTrackedMemory( sampleRates ); /* MEM */ + PaHost_FreeTrackedMemory( deviceInfo ); /* MEM */ + + return NULL; +} +/************************************************************************* + * Returns recommended device ID. + * On the PC, the recommended device can be specified by the user by + * setting an environment variable. For example, to use device #1. + * + * set PA_RECOMMENDED_OUTPUT_DEVICE=1 + * + * The user should first determine the available device ID by using + * the supplied application "pa_devs". + */ +#define PA_ENV_BUF_SIZE (32) +#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE") +#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE") +static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName ) +{ + DWORD hresult; + char envbuf[PA_ENV_BUF_SIZE]; + PaDeviceID recommendedID = paNoDevice; + + /* Let user determine default device by setting environment variable. */ + hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + recommendedID = atoi( envbuf ); + } + return recommendedID; +} +/********************************************************************** + * Check for environment variable, else query devices and use result. + */ +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + PaDeviceID result; + + result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME ); + if( result == paNoDevice || result < 0 || result >= sNumInputDevices ) + { + result = sDefaultInputDeviceID; + } + return result; +} +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + PaDeviceID result; + + result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME ); + if( result == paNoDevice || result < sNumInputDevices || result >= sNumDevices ) + { + result = sDefaultOutputDeviceID; + } + return result; +} +/********************************************************************** + * Initialize Host dependant part of API. + */ +PaError PaHost_Init( void ) +{ + +#if PA_TRACK_MEMORY + PRINT(("PaHost_Init: sNumAllocations = %d\n", sNumAllocations )); +#endif + +#if PA_SIMULATE_UNDERFLOW + PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n")); +#endif + + + Pa_InitializeNumDevices(); + + return Pa_AllocateDevicePtrs(); +} + +/********************************************************************** + * Check WAVE buffers to see if they are done. + * Fill any available output buffers and use any available + * input buffers by calling user callback. + * + * This routine will loop until: + * user callback returns !=0 OR + * all output buffers are filled OR + * past->past_StopSoon is set OR + * an error occurs when calling WMME. + * + * Returns >0 when user requests a stop, <0 on error. + * + */ +static PaError Pa_TimeSlice( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + char *inBufPtr; + char *outBufPtr; + int gotInput = 0; + int gotOutput = 0; + int i; + int buffersProcessed = 0; + int done = 0; + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + + if( wmmeStreamData == NULL ) return paInternalError; + + stream->past_NumCallbacks += 1; +#if PA_TRACE_RUN + AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", stream->past_NumCallbacks ); +#endif + + /* JM20020118 - prevent hung thread when buffers underflow. */ + /* while( !done ) /* BAD */ + while( !done && !stream->past_StopSoon ) /* GOOD */ + { +#if PA_SIMULATE_UNDERFLOW + if(gUnderCallbackCounter++ == UNDER_SLEEP_AT) + { + Sleep(UNDER_SLEEP_FOR); + } +#endif + + /* If we are using output, then we need an empty output buffer. */ + gotOutput = 0; + outBufPtr = NULL; + if( stream->past_NumOutputChannels > 0 ) + { + if((wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ].dwFlags & WHDR_DONE) == 0) + { + break; /* If none empty then bail and try again later. */ + } + else + { + outBufPtr = wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ].lpData; + gotOutput = 1; + } + } + /* Use an input buffer if one is available. */ + gotInput = 0; + inBufPtr = NULL; + if( ( stream->past_NumInputChannels > 0 ) && + (wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ].dwFlags & WHDR_DONE) ) + { + inBufPtr = wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ].lpData; + gotInput = 1; +#if PA_TRACE_RUN + AddTraceMessage("Pa_TimeSlice: got input buffer at ", (int)inBufPtr ); + AddTraceMessage("Pa_TimeSlice: got input buffer # ", wmmeStreamData->currentInputBuffer ); +#endif + + } + /* If we can't do anything then bail out. */ + if( !gotInput && !gotOutput ) break; + buffersProcessed += 1; + /* Each Wave buffer contains multiple user buffers so do them all now. */ + /* Base Usage on time it took to process one host buffer. */ + Pa_StartUsageCalculation( stream ); + for( i=0; iuserBuffersPerHostBuffer; i++ ) + { + if( done ) + { + if( gotOutput ) + { + /* Clear remainder of wave buffer if we are waiting for stop. */ + AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i ); + memset( outBufPtr, 0, wmmeStreamData->bytesPerUserOutputBuffer ); + } + } + else + { + /* Convert 16 bit native data to user data and call user routine. */ + result = Pa_CallConvertInt16( stream, (short *) inBufPtr, (short *) outBufPtr ); + if( result != 0) done = 1; + } + if( gotInput ) inBufPtr += wmmeStreamData->bytesPerUserInputBuffer; + if( gotOutput) outBufPtr += wmmeStreamData->bytesPerUserOutputBuffer; + } + Pa_EndUsageCalculation( stream ); + /* Send WAVE buffer to Wave Device to be refilled. */ + if( gotInput ) + { + mmresult = waveInAddBuffer( wmmeStreamData->hWaveIn, + &wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ], + sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + sPaHostError = mmresult; + result = paHostError; + break; + } + wmmeStreamData->currentInputBuffer = (wmmeStreamData->currentInputBuffer+1 >= wmmeStreamData->numHostBuffers) ? + 0 : wmmeStreamData->currentInputBuffer+1; + } + /* Write WAVE buffer to Wave Device. */ + if( gotOutput ) + { +#if PA_TRACE_START_STOP + AddTraceMessage( "Pa_TimeSlice: writing buffer ", wmmeStreamData->currentOutputBuffer ); +#endif + mmresult = waveOutWrite( wmmeStreamData->hWaveOut, + &wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ], + sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + sPaHostError = mmresult; + result = paHostError; + break; + } + wmmeStreamData->currentOutputBuffer = (wmmeStreamData->currentOutputBuffer+1 >= wmmeStreamData->numHostBuffers) ? + 0 : wmmeStreamData->currentOutputBuffer+1; + } + + } + +#if PA_TRACE_RUN + AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed ); +#endif + return (result != 0) ? result : done; +} + +/*******************************************************************/ +static PaError PaHost_BackgroundManager( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + int i; + int numQueuedoutputBuffers = 0; + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + + /* Has someone asked us to abort by calling Pa_AbortStream()? */ + if( stream->past_StopNow ) + { + stream->past_IsActive = 0; /* Will cause thread to return. */ + } + /* Has someone asked us to stop by calling Pa_StopStream() + * OR has a user callback returned '1' to indicate finished. + */ + else if( stream->past_StopSoon ) + { + /* Poll buffer and when all have played then exit thread. */ + /* Count how many output buffers are queued. */ + numQueuedoutputBuffers = 0; + if( stream->past_NumOutputChannels > 0 ) + { + for( i=0; inumHostBuffers; i++ ) + { + if( !( wmmeStreamData->outputBuffers[ i ].dwFlags & WHDR_DONE) ) + { +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_BackgroundManager: waiting for buffer ", i ); +#endif + numQueuedoutputBuffers++; + } + } + } +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_BackgroundManager: numQueuedoutputBuffers ", numQueuedoutputBuffers ); +#endif + if( numQueuedoutputBuffers == 0 ) + { + stream->past_IsActive = 0; /* Will cause thread to return. */ + } + } + else + { + /* Process full input buffer and fill up empty output buffers. */ + if( (result = Pa_TimeSlice( stream )) != 0) + { + /* User callback has asked us to stop. */ +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_BackgroundManager: TimeSlice() returned ", result ); +#endif + stream->past_StopSoon = 1; /* Request that audio play out then stop. */ + result = paNoError; + } + } + + PaHost_UpdateStreamTime( wmmeStreamData ); + return result; +} + +#if PA_USE_TIMER_CALLBACK +/*******************************************************************/ +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + internalPortAudioStream *stream; + PaWMMEStreamData *wmmeStreamData; + PaError result; + + stream = (internalPortAudioStream *) dwUser; + if( stream == NULL ) return; + wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + if( wmmeStreamData == NULL ) return; + if( wmmeStreamData->ifInsideCallback ) + { + if( wmmeStreamData->timerID != 0 ) + { + timeKillEvent(wmmeStreamData->timerID); /* Stop callback timer. */ + wmmeStreamData->timerID = 0; + } + return; + } + wmmeStreamData->ifInsideCallback = 1; + /* Manage flags and audio processing. */ + result = PaHost_BackgroundManager( stream ); + if( result != paNoError ) + { + stream->past_IsActive = 0; + } + wmmeStreamData->ifInsideCallback = 0; +} +#else /* PA_USE_TIMER_CALLBACK */ +/*******************************************************************/ +static DWORD WINAPI WinMMPa_OutputThreadProc( void *pArg ) +{ + internalPortAudioStream *stream; + PaWMMEStreamData *wmmeStreamData; + HANDLE events[2]; + int numEvents = 0; + DWORD result = 0; + DWORD waitResult; + DWORD numTimeouts = 0; + DWORD timeOut; + stream = (internalPortAudioStream *) pArg; + wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; +#if PA_TRACE_START_STOP + AddTraceMessage( "WinMMPa_OutputThreadProc: timeoutPeriod", timeoutPeriod ); + AddTraceMessage( "WinMMPa_OutputThreadProc: past_NumUserBuffers", stream->past_NumUserBuffers ); +#endif + /* Calculate timeOut as half the time it would take to play all buffers. */ + timeOut = (DWORD) (500.0 * PaHost_GetTotalBufferFrames( stream ) / stream->past_SampleRate); + /* Get event(s) ready for wait. */ + events[numEvents++] = wmmeStreamData->bufferEvent; + if( wmmeStreamData->abortEventInited ) events[numEvents++] = wmmeStreamData->abortEvent; + /* Stay in this thread as long as we are "active". */ + while( stream->past_IsActive ) + { + /*******************************************************************/ + /******** WAIT here for an event from WMME or PA *******************/ + /*******************************************************************/ + waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeOut ); + /* Error? */ + if( waitResult == WAIT_FAILED ) + { + sPaHostError = GetLastError(); + result = paHostError; + stream->past_IsActive = 0; + } + /* Timeout? Don't stop. Just keep polling for DONE.*/ + else if( waitResult == WAIT_TIMEOUT ) + { +#if PA_TRACE_START_STOP + AddTraceMessage( "WinMMPa_OutputThreadProc: timed out ", numQueuedoutputBuffers ); +#endif + numTimeouts += 1; + } + /* Manage flags and audio processing. */ + result = PaHost_BackgroundManager( stream ); + if( result != paNoError ) + { + stream->past_IsActive = 0; + } + } + return result; +} +#endif + +/*******************************************************************/ +PaError PaHost_OpenInputStream( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + PaWMMEStreamData *wmmeStreamData; + int i; + int inputMmId; + int bytesPerInputFrame; + WAVEFORMATEX wfx; + const PaDeviceInfo *deviceInfo; + + wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", stream->past_InputDeviceID)); + deviceInfo = Pa_GetDeviceInfo( stream->past_InputDeviceID ); + if( deviceInfo == NULL ) return paInternalError; + + switch( deviceInfo->nativeSampleFormats ) + { + case paInt32: + case paFloat32: + bytesPerInputFrame = sizeof(float) * stream->past_NumInputChannels; + break; + default: + bytesPerInputFrame = sizeof(short) * stream->past_NumInputChannels; + break; + } + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = (WORD) stream->past_NumInputChannels; + wfx.nSamplesPerSec = (DWORD) stream->past_SampleRate; + wfx.nAvgBytesPerSec = (DWORD)(bytesPerInputFrame * stream->past_SampleRate); + wfx.nBlockAlign = (WORD)bytesPerInputFrame; + wfx.wBitsPerSample = (WORD)((bytesPerInputFrame/stream->past_NumInputChannels) * 8); + wfx.cbSize = 0; + inputMmId = PaDeviceIdToWinId( stream->past_InputDeviceID ); +#if PA_USE_TIMER_CALLBACK + mmresult = waveInOpen( &wmmeStreamData->hWaveIn, inputMmId, &wfx, + 0, 0, CALLBACK_NULL ); +#else + mmresult = waveInOpen( &wmmeStreamData->hWaveIn, inputMmId, &wfx, + (DWORD)wmmeStreamData->bufferEvent, (DWORD) stream, CALLBACK_EVENT ); +#endif + if( mmresult != MMSYSERR_NOERROR ) + { + ERR_RPT(("PortAudio: PaHost_OpenInputStream() failed!\n")); + result = paHostError; + sPaHostError = mmresult; + goto error; + } + /* Allocate an array to hold the buffer pointers. */ + wmmeStreamData->inputBuffers = (WAVEHDR *) PaHost_AllocateTrackedMemory( sizeof(WAVEHDR)*wmmeStreamData->numHostBuffers ); /* MEM */ + if( wmmeStreamData->inputBuffers == NULL ) + { + result = paInsufficientMemory; + goto error; + } + /* Allocate each buffer. */ + for( i=0; inumHostBuffers; i++ ) + { + wmmeStreamData->inputBuffers[i].lpData = (char *)PaHost_AllocateTrackedMemory( wmmeStreamData->bytesPerHostInputBuffer ); /* MEM */ + if( wmmeStreamData->inputBuffers[i].lpData == NULL ) + { + result = paInsufficientMemory; + goto error; + } + wmmeStreamData->inputBuffers[i].dwBufferLength = wmmeStreamData->bytesPerHostInputBuffer; + wmmeStreamData->inputBuffers[i].dwUser = i; + if( ( mmresult = waveInPrepareHeader( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR ) + { + result = paHostError; + sPaHostError = mmresult; + goto error; + } + } + return result; + +error: + return result; +} +/*******************************************************************/ +PaError PaHost_OpenOutputStream( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + PaWMMEStreamData *wmmeStreamData; + int i; + int outputMmID; + int bytesPerOutputFrame; + WAVEFORMATEX wfx; + const PaDeviceInfo *deviceInfo; + + wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", stream->past_OutputDeviceID)); + + deviceInfo = Pa_GetDeviceInfo( stream->past_OutputDeviceID ); + if( deviceInfo == NULL ) return paInternalError; + + switch( deviceInfo->nativeSampleFormats ) + { + case paInt32: + case paFloat32: + bytesPerOutputFrame = sizeof(float) * stream->past_NumOutputChannels; + break; + default: + bytesPerOutputFrame = sizeof(short) * stream->past_NumOutputChannels; + break; + } + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = (WORD) stream->past_NumOutputChannels; + wfx.nSamplesPerSec = (DWORD) stream->past_SampleRate; + wfx.nAvgBytesPerSec = (DWORD)(bytesPerOutputFrame * stream->past_SampleRate); + wfx.nBlockAlign = (WORD)bytesPerOutputFrame; + wfx.wBitsPerSample = (WORD)((bytesPerOutputFrame/stream->past_NumOutputChannels) * 8); + wfx.cbSize = 0; + outputMmID = PaDeviceIdToWinId( stream->past_OutputDeviceID ); +#if PA_USE_TIMER_CALLBACK + mmresult = waveOutOpen( &wmmeStreamData->hWaveOut, outputMmID, &wfx, + 0, 0, CALLBACK_NULL ); +#else + + wmmeStreamData->abortEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + if( wmmeStreamData->abortEvent == NULL ) + { + result = paHostError; + sPaHostError = GetLastError(); + goto error; + } + wmmeStreamData->abortEventInited = 1; + mmresult = waveOutOpen( &wmmeStreamData->hWaveOut, outputMmID, &wfx, + (DWORD)wmmeStreamData->bufferEvent, (DWORD) stream, CALLBACK_EVENT ); +#endif + if( mmresult != MMSYSERR_NOERROR ) + { + ERR_RPT(("PortAudio: PaHost_OpenOutputStream() failed!\n")); + result = paHostError; + sPaHostError = mmresult; + goto error; + } + /* Allocate an array to hold the buffer pointers. */ + wmmeStreamData->outputBuffers = (WAVEHDR *) PaHost_AllocateTrackedMemory( sizeof(WAVEHDR)*wmmeStreamData->numHostBuffers ); /* MEM */ + if( wmmeStreamData->outputBuffers == NULL ) + { + result = paInsufficientMemory; + goto error; + } + /* Allocate each buffer. */ + for( i=0; inumHostBuffers; i++ ) + { + wmmeStreamData->outputBuffers[i].lpData = (char *) PaHost_AllocateTrackedMemory( wmmeStreamData->bytesPerHostOutputBuffer ); /* MEM */ + if( wmmeStreamData->outputBuffers[i].lpData == NULL ) + { + result = paInsufficientMemory; + goto error; + } + wmmeStreamData->outputBuffers[i].dwBufferLength = wmmeStreamData->bytesPerHostOutputBuffer; + wmmeStreamData->outputBuffers[i].dwUser = i; + if( (mmresult = waveOutPrepareHeader( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR ) + { + result = paHostError; + sPaHostError = mmresult; + goto error; + } + } + return result; + +error: + return result; +} +/*******************************************************************/ +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *stream ) +{ + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + return wmmeStreamData->numHostBuffers * wmmeStreamData->framesPerHostBuffer; +} +/******************************************************************* + * Determine number of WAVE Buffers + * and how many User Buffers we can put into each WAVE buffer. + */ +static void PaHost_CalcNumHostBuffers( internalPortAudioStream *stream ) +{ + PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData; + unsigned int minNumBuffers; + int minframesPerHostBuffer; + int maxframesPerHostBuffer; + int minTotalFrames; + int userBuffersPerHostBuffer; + int framesPerHostBuffer; + int numHostBuffers; + + /* Calculate minimum and maximum sizes based on timing and sample rate. */ + minframesPerHostBuffer = (int) (PA_MIN_MSEC_PER_HOST_BUFFER * stream->past_SampleRate * 0.001); + minframesPerHostBuffer = (minframesPerHostBuffer + 7) & ~7; + DBUG(("PaHost_CalcNumHostBuffers: minframesPerHostBuffer = %d\n", minframesPerHostBuffer )); + maxframesPerHostBuffer = (int) (PA_MAX_MSEC_PER_HOST_BUFFER * stream->past_SampleRate * 0.001); + maxframesPerHostBuffer = (maxframesPerHostBuffer + 7) & ~7; + DBUG(("PaHost_CalcNumHostBuffers: maxframesPerHostBuffer = %d\n", maxframesPerHostBuffer )); + /* Determine number of user buffers based on minimum latency. */ + minNumBuffers = Pa_GetMinNumBuffers( stream->past_FramesPerUserBuffer, stream->past_SampleRate ); + stream->past_NumUserBuffers = ( minNumBuffers > stream->past_NumUserBuffers ) ? minNumBuffers : stream->past_NumUserBuffers; + DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", stream->past_NumUserBuffers )); + minTotalFrames = stream->past_NumUserBuffers * stream->past_FramesPerUserBuffer; + /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */ + if( (int) stream->past_FramesPerUserBuffer < minframesPerHostBuffer ) + { + userBuffersPerHostBuffer = + (minframesPerHostBuffer + stream->past_FramesPerUserBuffer - 1) / + stream->past_FramesPerUserBuffer; + } + else + { + userBuffersPerHostBuffer = 1; + } + framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer; + /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */ + numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; + /* Make sure we have anough WAVE buffers. */ + if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS) + { + numHostBuffers = PA_MIN_NUM_HOST_BUFFERS; + } + else if( (numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) && + ((int) stream->past_FramesPerUserBuffer < (maxframesPerHostBuffer/2) ) ) + { + /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */ + while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) + { + userBuffersPerHostBuffer += 1; + framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer; + numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; + /* If we have gone too far, back up one. */ + if( (framesPerHostBuffer > maxframesPerHostBuffer) || + (numHostBuffers < PA_MAX_NUM_HOST_BUFFERS) ) + { + userBuffersPerHostBuffer -= 1; + framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer; + numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; + break; + } + } + } + + wmmeStreamData->userBuffersPerHostBuffer = userBuffersPerHostBuffer; + wmmeStreamData->framesPerHostBuffer = framesPerHostBuffer; + wmmeStreamData->numHostBuffers = numHostBuffers; + DBUG(("PaHost_CalcNumHostBuffers: userBuffersPerHostBuffer = %d\n", wmmeStreamData->userBuffersPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: numHostBuffers = %d\n", wmmeStreamData->numHostBuffers )); + DBUG(("PaHost_CalcNumHostBuffers: framesPerHostBuffer = %d\n", wmmeStreamData->framesPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", stream->past_NumUserBuffers )); +} +/*******************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + PaWMMEStreamData *wmmeStreamData; + + result = PaHost_AllocateWMMEStreamData( stream ); + if( result != paNoError ) return result; + + wmmeStreamData = PaHost_GetWMMEStreamData( stream ); + + /* Figure out how user buffers fit into WAVE buffers. */ + PaHost_CalcNumHostBuffers( stream ); + { + int msecLatency = (int) ((PaHost_GetTotalBufferFrames(stream) * 1000) / stream->past_SampleRate); + DBUG(("PortAudio on WMME - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(stream), msecLatency )); + } + InitializeCriticalSection( &wmmeStreamData->streamLock ); + wmmeStreamData->streamLockInited = 1; + +#if (PA_USE_TIMER_CALLBACK == 0) + wmmeStreamData->bufferEventInited = 0; + wmmeStreamData->bufferEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + if( wmmeStreamData->bufferEvent == NULL ) + { + result = paHostError; + sPaHostError = GetLastError(); + goto error; + } + wmmeStreamData->bufferEventInited = 1; +#endif /* (PA_USE_TIMER_CALLBACK == 0) */ + /* ------------------ OUTPUT */ + wmmeStreamData->bytesPerUserOutputBuffer = stream->past_FramesPerUserBuffer * stream->past_NumOutputChannels * sizeof(short); + wmmeStreamData->bytesPerHostOutputBuffer = wmmeStreamData->userBuffersPerHostBuffer * wmmeStreamData->bytesPerUserOutputBuffer; + if( (stream->past_OutputDeviceID != paNoDevice) && (stream->past_NumOutputChannels > 0) ) + { + result = PaHost_OpenOutputStream( stream ); + if( result < 0 ) goto error; + } + /* ------------------ INPUT */ + wmmeStreamData->bytesPerUserInputBuffer = stream->past_FramesPerUserBuffer * stream->past_NumInputChannels * sizeof(short); + wmmeStreamData->bytesPerHostInputBuffer = wmmeStreamData->userBuffersPerHostBuffer * wmmeStreamData->bytesPerUserInputBuffer; + if( (stream->past_InputDeviceID != paNoDevice) && (stream->past_NumInputChannels > 0) ) + { + result = PaHost_OpenInputStream( stream ); + if( result < 0 ) goto error; + } + + Pa_InitializeCpuUsageScalar( stream ); + + return result; + +error: + PaHost_CloseStream( stream ); + return result; +} +/*************************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + int i; + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream ); + + if( wmmeStreamData == NULL ) return paInternalError; + + if( stream->past_OutputDeviceID != paNoDevice ) + { + if( (mmresult = waveOutPause( wmmeStreamData->hWaveOut )) != MMSYSERR_NOERROR ) + { + result = paHostError; + sPaHostError = mmresult; + goto error; + } + for( i=0; inumHostBuffers; i++ ) + { + ZeroMemory( wmmeStreamData->outputBuffers[i].lpData, wmmeStreamData->outputBuffers[i].dwBufferLength ); + mmresult = waveOutWrite( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paHostError; + sPaHostError = mmresult; + goto error; + } + stream->past_FrameCount += wmmeStreamData->framesPerHostBuffer; + } + wmmeStreamData->currentOutputBuffer = 0; + if( (mmresult = waveOutRestart( wmmeStreamData->hWaveOut )) != MMSYSERR_NOERROR ) + { + result = paHostError; + sPaHostError = mmresult; + goto error; + } + } + +error: + DBUG(("PaHost_StartOutput: wave returned mmresult = 0x%X.\n", mmresult)); + return result; +} +/*************************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *internalStream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + int i; + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream ); + + if( wmmeStreamData == NULL ) return paInternalError; + + if( internalStream->past_InputDeviceID != paNoDevice ) + { + for( i=0; inumHostBuffers; i++ ) + { + mmresult = waveInAddBuffer( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paHostError; + sPaHostError = mmresult; + goto error; + } + } + wmmeStreamData->currentInputBuffer = 0; + mmresult = waveInStart( wmmeStreamData->hWaveIn ); + DBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult)); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paHostError; + sPaHostError = mmresult; + goto error; + } + } + +error: + return result; +} +/*************************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *stream ) +{ + PaError result = paNoError; + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream ); +#if PA_USE_TIMER_CALLBACK + int resolution; + int bufsPerTimerCallback; + int msecPerBuffer; +#endif /* PA_USE_TIMER_CALLBACK */ + + if( wmmeStreamData == NULL ) return paInternalError; + + stream->past_StopSoon = 0; + stream->past_StopNow = 0; + stream->past_IsActive = 1; + wmmeStreamData->framesPlayed = 0.0; + wmmeStreamData->lastPosition = 0; +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result ); +#endif +#if PA_USE_TIMER_CALLBACK + /* Create timer that will wake us up so we can fill the DSound buffer. */ + bufsPerTimerCallback = wmmeStreamData->numHostBuffers/4; + if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1; + if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1; + msecPerBuffer = (1000 * bufsPerTimerCallback * + wmmeStreamData->userBuffersPerHostBuffer * + internalStream->past_FramesPerUserBuffer ) / (int) internalStream->past_SampleRate; + if( msecPerBuffer < 10 ) msecPerBuffer = 10; + else if( msecPerBuffer > 100 ) msecPerBuffer = 100; + resolution = msecPerBuffer/4; + wmmeStreamData->timerID = timeSetEvent( msecPerBuffer, resolution, + (LPTIMECALLBACK) Pa_TimerCallback, + (DWORD) stream, TIME_PERIODIC ); + if( wmmeStreamData->timerID == 0 ) + { + result = paHostError; + sPaHostError = GetLastError();; + goto error; + } +#else /* PA_USE_TIMER_CALLBACK */ + ResetEvent( wmmeStreamData->abortEvent ); + /* Create thread that waits for audio buffers to be ready for processing. */ + wmmeStreamData->engineThread = CreateThread( 0, 0, WinMMPa_OutputThreadProc, stream, 0, &wmmeStreamData->engineThreadID ); + if( wmmeStreamData->engineThread == NULL ) + { + result = paHostError; + sPaHostError = GetLastError();; + goto error; + } +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StartEngine: thread ", (int) wmmeStreamData->engineThread ); +#endif + /* I used to pass the thread which was failing. I now pass GetCurrentProcess(). + * This fix could improve latency for some applications. It could also result in CPU + * starvation if the callback did too much processing. + * I also added result checks, so we might see more failures at initialization. + * Thanks to Alberto di Bene for spotting this. + */ + if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */ + { + result = paHostError; + sPaHostError = GetLastError();; + goto error; + } + if( !SetThreadPriority( wmmeStreamData->engineThread, THREAD_PRIORITY_HIGHEST ) ) + { + result = paHostError; + sPaHostError = GetLastError();; + goto error; + } +#endif + +error: + return result; +} +/*************************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *internalStream, int abort ) +{ + int timeOut; + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream ); + + if( wmmeStreamData == NULL ) return paNoError; + + /* Tell background thread to stop generating more data and to let current data play out. */ + internalStream->past_StopSoon = 1; + /* If aborting, tell background thread to stop NOW! */ + if( abort ) internalStream->past_StopNow = 1; + + /* Calculate timeOut longer than longest time it could take to play all buffers. */ + timeOut = (DWORD) (1500.0 * PaHost_GetTotalBufferFrames( internalStream ) / internalStream->past_SampleRate); + if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC; + +#if PA_USE_TIMER_CALLBACK + if( (internalStream->past_OutputDeviceID != paNoDevice) && + internalStream->past_IsActive && + (wmmeStreamData->timerID != 0) ) + { + /* Wait for IsActive to drop. */ + while( (internalStream->past_IsActive) && (timeOut > 0) ) + { + Sleep(10); + timeOut -= 10; + } + timeKillEvent( wmmeStreamData->timerID ); /* Stop callback timer. */ + wmmeStreamData->timerID = 0; + } +#else /* PA_USE_TIMER_CALLBACK */ +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StopEngine: thread ", (int) wmmeStreamData->engineThread ); +#endif + if( (internalStream->past_OutputDeviceID != paNoDevice) && + (internalStream->past_IsActive) && + (wmmeStreamData->engineThread != NULL) ) + { + DWORD got; + /* Tell background thread to stop generating more data and to let current data play out. */ + DBUG(("PaHost_StopEngine: waiting for background thread.\n")); + got = WaitForSingleObject( wmmeStreamData->engineThread, timeOut ); + if( got == WAIT_TIMEOUT ) + { + ERR_RPT(("PaHost_StopEngine: timed out while waiting for background thread to finish.\n")); + return paTimedOut; + } + CloseHandle( wmmeStreamData->engineThread ); + wmmeStreamData->engineThread = NULL; + } +#endif /* PA_USE_TIMER_CALLBACK */ + + internalStream->past_IsActive = 0; + return paNoError; +} +/*************************************************************************/ +PaError PaHost_StopInput( internalPortAudioStream *stream, int abort ) +{ + MMRESULT mmresult; + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream ); + + if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return paNoError? */ + (void) abort; /* unused parameter */ + + if( wmmeStreamData->hWaveIn != NULL ) + { + mmresult = waveInReset( wmmeStreamData->hWaveIn ); + if( mmresult != MMSYSERR_NOERROR ) + { + sPaHostError = mmresult; + return paHostError; + } + } + return paNoError; +} +/*************************************************************************/ +PaError PaHost_StopOutput( internalPortAudioStream *internalStream, int abort ) +{ + MMRESULT mmresult; + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream ); + + if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return paNoError? */ + (void) abort; /* unused parameter */ + +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StopOutput: hWaveOut ", (int) wmmeStreamData->hWaveOut ); +#endif + if( wmmeStreamData->hWaveOut != NULL ) + { + mmresult = waveOutReset( wmmeStreamData->hWaveOut ); + if( mmresult != MMSYSERR_NOERROR ) + { + sPaHostError = mmresult; + return paHostError; + } + } + return paNoError; +} +/*******************************************************************/ +PaError PaHost_CloseStream( internalPortAudioStream *stream ) +{ + int i; + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream ); + + if( stream == NULL ) return paBadStreamPtr; + if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return no error? */ + +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_CloseStream: hWaveOut ", (int) wmmeStreamData->hWaveOut ); +#endif + /* Free data and device for output. */ + if( wmmeStreamData->hWaveOut ) + { + if( wmmeStreamData->outputBuffers ) + { + for( i=0; inumHostBuffers; i++ ) + { + waveOutUnprepareHeader( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) ); + PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers[i].lpData ); /* MEM */ + } + PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers ); /* MEM */ + } + waveOutClose( wmmeStreamData->hWaveOut ); + } + /* Free data and device for input. */ + if( wmmeStreamData->hWaveIn ) + { + if( wmmeStreamData->inputBuffers ) + { + for( i=0; inumHostBuffers; i++ ) + { + waveInUnprepareHeader( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) ); + PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers[i].lpData ); /* MEM */ + } + PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers ); /* MEM */ + } + waveInClose( wmmeStreamData->hWaveIn ); + } +#if (PA_USE_TIMER_CALLBACK == 0) + if( wmmeStreamData->abortEventInited ) CloseHandle( wmmeStreamData->abortEvent ); + if( wmmeStreamData->bufferEventInited ) CloseHandle( wmmeStreamData->bufferEvent ); +#endif + if( wmmeStreamData->streamLockInited ) + DeleteCriticalSection( &wmmeStreamData->streamLock ); + + PaHost_FreeWMMEStreamData( stream ); + + return paNoError; +} +/************************************************************************* + * Determine minimum number of buffers required for this host based + * on minimum latency. Latency can be optionally set by user by setting + * an environment variable. For example, to set latency to 200 msec, put: + * + * set PA_MIN_LATENCY_MSEC=200 + * + * in the AUTOEXEC.BAT file and reboot. + * If the environment variable is not set, then the latency will be determined + * based on the OS. Windows NT has higher latency than Win95. + */ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") +int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ) +{ + char envbuf[PA_ENV_BUF_SIZE]; + DWORD hresult; + int minLatencyMsec = 0; + double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate; + int minBuffers; + + /* Let user determine minimal latency by setting environment variable. */ + hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + minLatencyMsec = atoi( envbuf ); /* REVIEW: will we crash if the environment variable contains some nasty value? */ + } + else + { + /* Set minimal latency based on whether NT or other OS. + * NT has higher latency. + */ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof( osvi ); + GetVersionEx( &osvi ); + DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); + DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); + DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); + /* Check for NT */ + if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) + { + minLatencyMsec = PA_WIN_NT_LATENCY; + } + else if(osvi.dwMajorVersion >= 5) + { + minLatencyMsec = PA_WIN_WDM_LATENCY; + } + else + { + minLatencyMsec = PA_WIN_9X_LATENCY; + } +#if PA_USE_HIGH_LATENCY + PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); +#endif + + } + DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); + minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer)); + if( minBuffers < 2 ) minBuffers = 2; + return minBuffers; +} +/************************************************************************* + * Cleanup device info. + */ +PaError PaHost_Term( void ) +{ + int i; + + if( sNumDevices > 0 ) + { + if( sDevicePtrs != NULL ) + { + for( i=0; iname ); /* MEM */ + PaHost_FreeTrackedMemory( (void*)sDevicePtrs[i]->sampleRates ); /* MEM */ + PaHost_FreeTrackedMemory( sDevicePtrs[i] ); /* MEM */ + } + } + PaHost_FreeTrackedMemory( sDevicePtrs ); /* MEM */ + sDevicePtrs = NULL; + } + sNumDevices = 0; + } + +#if PA_TRACK_MEMORY + PRINT(("PaHost_Term: sNumAllocations = %d\n", sNumAllocations )); +#endif + + return paNoError; +} +/*************************************************************************/ +void Pa_Sleep( long msec ) +{ + Sleep( msec ); +} +/************************************************************************* +FIXME: the following memory allocation routines should not be declared here + * Allocate memory that can be accessed in real-time. + * This may need to be held in physical memory so that it is not + * paged to virtual memory. + * This call MUST be balanced with a call to PaHost_FreeFastMemory(). + * Memory will be set to zero. + */ +void *PaHost_AllocateFastMemory( long numBytes ) +{ + return PaHost_AllocateTrackedMemory( numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */ +} +/************************************************************************* + * Free memory that could be accessed in real-time. + * This call MUST be balanced with a call to PaHost_AllocateFastMemory(). + */ +void PaHost_FreeFastMemory( void *addr, long numBytes ) +{ + (void) numBytes; /* unused parameter */ + + PaHost_FreeTrackedMemory( addr ); /* MEM */ +} + +/************************************************************************* + * Track memory allocations to avoid leaks. + */ +static void *PaHost_AllocateTrackedMemory( long numBytes ) +{ + void *result = GlobalAlloc( GPTR, numBytes ); /* MEM */ + +#if PA_TRACK_MEMORY + if( result != NULL ) sNumAllocations += 1; +#endif + return result; +} + +static void PaHost_FreeTrackedMemory( void *addr ) +{ + if( addr != NULL ) + { + GlobalFree( addr ); /* MEM */ +#if PA_TRACK_MEMORY + sNumAllocations -= 1; +#endif + } +} + +/***********************************************************************/ +PaError PaHost_StreamActive( internalPortAudioStream *internalStream ) +{ + if( internalStream == NULL ) return paBadStreamPtr; + + return (PaError) internalStream->past_IsActive; +} +/************************************************************************* + * This must be called periodically because mmtime.u.sample + * is a DWORD and can wrap and lose sync after a few hours. + */ +static PaError PaHost_UpdateStreamTime( PaWMMEStreamData *wmmeStreamData ) +{ + MMRESULT mmresult; + MMTIME mmtime; + mmtime.wType = TIME_SAMPLES; + + if( wmmeStreamData->hWaveOut != NULL ) + { + mmresult = waveOutGetPosition( wmmeStreamData->hWaveOut, &mmtime, sizeof(mmtime) ); + } + else + { + mmresult = waveInGetPosition( wmmeStreamData->hWaveIn, &mmtime, sizeof(mmtime) ); + } + + if( mmresult != MMSYSERR_NOERROR ) + { + sPaHostError = mmresult; + return paHostError; + } + + /* This data has two variables and is shared by foreground and background. + * So we need to make it thread safe. */ + EnterCriticalSection( &wmmeStreamData->streamLock ); + wmmeStreamData->framesPlayed += ((long)mmtime.u.sample) - wmmeStreamData->lastPosition; + wmmeStreamData->lastPosition = (long)mmtime.u.sample; + LeaveCriticalSection( &wmmeStreamData->streamLock ); + + return paNoError; +} +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + internalPortAudioStream *internalStream = PaHost_GetStreamRepresentation( stream ); + PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream ); + + if( internalStream == NULL ) return paBadStreamPtr; + if( wmmeStreamData == NULL ) return paInternalError; + + PaHost_UpdateStreamTime( wmmeStreamData ); + return wmmeStreamData->framesPlayed; +} +/*************************************************************************/ + + + diff --git a/lib/portaudio/pablio/CVS/Entries b/lib/portaudio/pablio/CVS/Entries new file mode 100644 index 0000000..7488257 --- /dev/null +++ b/lib/portaudio/pablio/CVS/Entries @@ -0,0 +1,9 @@ +/pablio.c/1.3/Sun Mar 2 08:01:46 2003// +/pablio.h/1.3/Sun Mar 2 08:01:47 2003// +/ringbuffer.c/1.3/Sun Mar 2 08:01:47 2003// +/ringbuffer.h/1.3/Sun Mar 2 08:01:47 2003// +/test_rw.c/1.3/Sun Mar 2 08:01:47 2003// +/test_rw_echo.c/1.4/Sun Mar 2 08:01:47 2003// +/test_w_saw.c/1.3/Sun Mar 2 08:01:47 2003// +/test_w_saw8.c/1.3/Sun Mar 2 08:01:47 2003// +D diff --git a/lib/portaudio/pablio/CVS/Repository b/lib/portaudio/pablio/CVS/Repository new file mode 100644 index 0000000..35e4ba5 --- /dev/null +++ b/lib/portaudio/pablio/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/pablio diff --git a/lib/portaudio/pablio/CVS/Root b/lib/portaudio/pablio/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/pablio/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/pablio/pablio.c b/lib/portaudio/pablio/pablio.c new file mode 100644 index 0000000..1fee3b9 --- /dev/null +++ b/lib/portaudio/pablio/pablio.c @@ -0,0 +1,327 @@ +/* + * $Id: pablio.c,v 1.3 2003/03/02 08:01:46 dmazzoni Exp $ + * pablio.c + * Portable Audio Blocking Input/Output utility. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* History: + * PLB021214 - check for valid stream in CloseAudioStream() to prevent hang. + * add timeOutMSec to CloseAudioStream() to prevent hang. + */ +#include +#include +#include +#include "portaudio.h" +#include "ringbuffer.h" +#include "pablio.h" +#include + +/************************************************************************/ +/******** Constants *****************************************************/ +/************************************************************************/ + +#define FRAMES_PER_BUFFER (256) + +/************************************************************************/ +/******** Prototypes ****************************************************/ +/************************************************************************/ + +static int blockingIOCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ); +static PaError PABLIO_TermFIFO( RingBuffer *rbuf ); + +/************************************************************************/ +/******** Functions *****************************************************/ +/************************************************************************/ + +/* Called from PortAudio. + * Read and write data only if there is room in FIFOs. + */ +static int blockingIOCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + PABLIO_Stream *data = (PABLIO_Stream*)userData; + long numBytes = data->bytesPerFrame * framesPerBuffer; + (void) outTime; + + /* This may get called with NULL inputBuffer during initial setup. */ + if( inputBuffer != NULL ) + { + RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes ); + } + if( outputBuffer != NULL ) + { + int i; + int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes ); + /* Zero out remainder of buffer if we run out of data. */ + for( i=numRead; ibuffer ) free( rbuf->buffer ); + rbuf->buffer = NULL; + return paNoError; +} + +/************************************************************ + * Write data to ring buffer. + * Will not return until all the data has been written. + */ +long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) +{ + long bytesWritten; + char *p = (char *) data; + long numBytes = aStream->bytesPerFrame * numFrames; + while( numBytes > 0) + { + bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes ); + numBytes -= bytesWritten; + p += bytesWritten; + if( numBytes > 0) Pa_Sleep(10); + } + return numFrames; +} + +/************************************************************ + * Read data from ring buffer. + * Will not return until all the data has been read. + */ +long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) +{ + long bytesRead; + char *p = (char *) data; + long numBytes = aStream->bytesPerFrame * numFrames; + while( numBytes > 0) + { + bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes ); + numBytes -= bytesRead; + p += bytesRead; + if( numBytes > 0) Pa_Sleep(10); + } + return numFrames; +} + +/************************************************************ + * Return the number of frames that could be written to the stream without + * having to wait. + */ +long GetAudioStreamWriteable( PABLIO_Stream *aStream ) +{ + int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + return bytesEmpty / aStream->bytesPerFrame; +} + +/************************************************************ + * Return the number of frames that are available to be read from the + * stream without having to wait. + */ +long GetAudioStreamReadable( PABLIO_Stream *aStream ) +{ + int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO ); + return bytesFull / aStream->bytesPerFrame; +} + +/************************************************************/ +static unsigned long RoundUpToNextPowerOf2( unsigned long n ) +{ + long numBits = 0; + if( ((n-1) & n) == 0) return n; /* Already Power of two. */ + while( n > 0 ) + { + n= n>>1; + numBits++; + } + return (1<samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2; + aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame; + + /* Initialize PortAudio */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + /* Warning: numFrames must be larger than amount of data processed per interrupt + * inside PA to prevent glitches. Just to be safe, adjust size upwards. + */ + minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); + numFrames = minNumBuffers * FRAMES_PER_BUFFER; + /* The PortAudio callback runs in a high priority thread. But PABLIO + * runs in a normal foreground thread. So we may have much worse + * latency in PABLIO. So adjust latency to a safe level. + */ + { + const int safeLatencyMSec = 200; + int minLatencyMSec = (1000 * numFrames) / sampleRate; + if( minLatencyMSec < safeLatencyMSec ) + { + numFrames = (int) ((safeLatencyMSec * sampleRate) / 1000); + } + } + numFrames = RoundUpToNextPowerOf2( numFrames ); + + /* Initialize Ring Buffers */ + doRead = ((flags & PABLIO_READ) != 0); + doWrite = ((flags & PABLIO_WRITE) != 0); + if(doRead) + { + err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame ); + if( err != paNoError ) goto error; + } + if(doWrite) + { + long numBytes; + err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame ); + if( err != paNoError ) goto error; + /* Make Write FIFO appear full initially. */ + numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes ); + } + + /* Open a PortAudio stream that we will use to communicate with the underlying + * audio drivers. */ + err = Pa_OpenStream( + &aStream->stream, + (doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice), + (doRead ? aStream->samplesPerFrame : 0 ), + format, + NULL, + (doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice), + (doWrite ? aStream->samplesPerFrame : 0 ), + format, + NULL, + sampleRate, + FRAMES_PER_BUFFER, + minNumBuffers, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + blockingIOCallback, + aStream ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( aStream->stream ); + if( err != paNoError ) goto error; + + *rwblPtr = aStream; + return paNoError; + +error: + CloseAudioStream( aStream ); + *rwblPtr = NULL; + return err; +} + +/************************************************************/ +PaError CloseAudioStream( PABLIO_Stream *aStream ) +{ + PaError err = paNoError; + int bytesEmpty; + int byteSize = aStream->outFIFO.bufferSize; + + if( aStream->stream != NULL ) /* Make sure stream was opened. PLB021214 */ + { + /* If we are writing data, make sure we play everything written. */ + if( byteSize > 0 ) + { + int timeOutMSec = 2000; + bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + while( (bytesEmpty < byteSize) && (timeOutMSec > 0) ) + { + Pa_Sleep( 20 ); + timeOutMSec -= 20; + bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + } + } + err = Pa_StopStream( aStream->stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( aStream->stream ); + } + +error: + Pa_Terminate(); + PABLIO_TermFIFO( &aStream->inFIFO ); + PABLIO_TermFIFO( &aStream->outFIFO ); + free( aStream ); + return err; +} diff --git a/lib/portaudio/pablio/pablio.h b/lib/portaudio/pablio/pablio.h new file mode 100644 index 0000000..39b25e5 --- /dev/null +++ b/lib/portaudio/pablio/pablio.h @@ -0,0 +1,109 @@ +#ifndef _PABLIO_H +#define _PABLIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: pablio.h,v 1.3 2003/03/02 08:01:47 dmazzoni Exp $ + * PABLIO.h + * Portable Audio Blocking read/write utility. + * + * Author: Phil Burk, http://www.softsynth.com/portaudio/ + * + * Include file for PABLIO, the Portable Audio Blocking I/O Library. + * PABLIO is built on top of PortAudio, the Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "portaudio.h" +#include "ringbuffer.h" +#include + +typedef struct +{ + RingBuffer inFIFO; + RingBuffer outFIFO; + PortAudioStream *stream; + int bytesPerFrame; + int samplesPerFrame; +} +PABLIO_Stream; + +/* Values for flags for OpenAudioStream(). */ +#define PABLIO_READ (1<<0) +#define PABLIO_WRITE (1<<1) +#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE) +#define PABLIO_MONO (1<<2) +#define PABLIO_STEREO (1<<3) + +/************************************************************ + * Write data to ring buffer. + * Will not return until all the data has been written. + */ +long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); + +/************************************************************ + * Read data from ring buffer. + * Will not return until all the data has been read. + */ +long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); + +/************************************************************ + * Return the number of frames that could be written to the stream without + * having to wait. + */ +long GetAudioStreamWriteable( PABLIO_Stream *aStream ); + +/************************************************************ + * Return the number of frames that are available to be read from the + * stream without having to wait. + */ +long GetAudioStreamReadable( PABLIO_Stream *aStream ); + +/************************************************************ + * Opens a PortAudio stream with default characteristics. + * Allocates PABLIO_Stream structure. + * + * flags parameter can be an ORed combination of: + * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE, + * and either PABLIO_MONO or PABLIO_STEREO + */ +PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate, + PaSampleFormat format, long flags ); + +PaError CloseAudioStream( PABLIO_Stream *aStream ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _PABLIO_H */ diff --git a/lib/portaudio/pablio/ringbuffer.c b/lib/portaudio/pablio/ringbuffer.c new file mode 100644 index 0000000..287e0b3 --- /dev/null +++ b/lib/portaudio/pablio/ringbuffer.c @@ -0,0 +1,199 @@ +/* + * $Id: ringbuffer.c,v 1.3 2003/03/02 08:01:47 dmazzoni Exp $ + * ringbuffer.c + * Ring Buffer utility.. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "ringbuffer.h" +#include + +/*************************************************************************** + * Initialize FIFO. + * numBytes must be power of 2, returns -1 if not. + */ +long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ) +{ + if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */ + rbuf->bufferSize = numBytes; + rbuf->buffer = (char *)dataPtr; + RingBuffer_Flush( rbuf ); + rbuf->bigMask = (numBytes*2)-1; + rbuf->smallMask = (numBytes)-1; + return 0; +} +/*************************************************************************** +** Return number of bytes available for reading. */ +long RingBuffer_GetReadAvailable( RingBuffer *rbuf ) +{ + return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); +} +/*************************************************************************** +** Return number of bytes available for writing. */ +long RingBuffer_GetWriteAvailable( RingBuffer *rbuf ) +{ + return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf)); +} + +/*************************************************************************** +** Clear buffer. Should only be called when buffer is NOT being read. */ +void RingBuffer_Flush( RingBuffer *rbuf ) +{ + rbuf->writeIndex = rbuf->readIndex = 0; +} + +/*************************************************************************** +** Get address of region(s) to which we can write data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ) +{ + long index; + long available = RingBuffer_GetWriteAvailable( rbuf ); + if( numBytes > available ) numBytes = available; + /* Check to see if write is not contiguous. */ + index = rbuf->writeIndex & rbuf->smallMask; + if( (index + numBytes) > rbuf->bufferSize ) + { + /* Write data in two blocks that wrap the buffer. */ + long firstHalf = rbuf->bufferSize - index; + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = firstHalf; + *dataPtr2 = &rbuf->buffer[0]; + *sizePtr2 = numBytes - firstHalf; + } + else + { + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = numBytes; + *dataPtr2 = NULL; + *sizePtr2 = 0; + } + return numBytes; +} + + +/*************************************************************************** +*/ +long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes ) +{ + return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask; +} + +/*************************************************************************** +** Get address of region(s) from which we can read data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ) +{ + long index; + long available = RingBuffer_GetReadAvailable( rbuf ); + if( numBytes > available ) numBytes = available; + /* Check to see if read is not contiguous. */ + index = rbuf->readIndex & rbuf->smallMask; + if( (index + numBytes) > rbuf->bufferSize ) + { + /* Write data in two blocks that wrap the buffer. */ + long firstHalf = rbuf->bufferSize - index; + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = firstHalf; + *dataPtr2 = &rbuf->buffer[0]; + *sizePtr2 = numBytes - firstHalf; + } + else + { + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = numBytes; + *dataPtr2 = NULL; + *sizePtr2 = 0; + } + return numBytes; +} +/*************************************************************************** +*/ +long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ) +{ + return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask; +} + +/*************************************************************************** +** Return bytes written. */ +long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes ) +{ + long size1, size2, numWritten; + void *data1, *data2; + numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); + if( size2 > 0 ) + { + + memcpy( data1, data, size1 ); + data = ((char *)data) + size1; + memcpy( data2, data, size2 ); + } + else + { + memcpy( data1, data, size1 ); + } + RingBuffer_AdvanceWriteIndex( rbuf, numWritten ); + return numWritten; +} + +/*************************************************************************** +** Return bytes read. */ +long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes ) +{ + long size1, size2, numRead; + void *data1, *data2; + numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); + if( size2 > 0 ) + { + memcpy( data, data1, size1 ); + data = ((char *)data) + size1; + memcpy( data, data2, size2 ); + } + else + { + memcpy( data, data1, size1 ); + } + RingBuffer_AdvanceReadIndex( rbuf, numRead ); + return numRead; +} diff --git a/lib/portaudio/pablio/ringbuffer.h b/lib/portaudio/pablio/ringbuffer.h new file mode 100644 index 0000000..0ffb6e2 --- /dev/null +++ b/lib/portaudio/pablio/ringbuffer.h @@ -0,0 +1,101 @@ +#ifndef _RINGBUFFER_H +#define _RINGBUFFER_H +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: ringbuffer.h,v 1.3 2003/03/02 08:01:47 dmazzoni Exp $ + * ringbuffer.h + * Ring Buffer utility.. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program is distributed with the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "ringbuffer.h" +#include + +typedef struct +{ + long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */ + long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */ + long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */ + long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */ + long smallMask; /* Used for fitting indices to buffer. */ + char *buffer; +} +RingBuffer; +/* + * Initialize Ring Buffer. + * numBytes must be power of 2, returns -1 if not. + */ +long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ); + +/* Clear buffer. Should only be called when buffer is NOT being read. */ +void RingBuffer_Flush( RingBuffer *rbuf ); + +/* Return number of bytes available for writing. */ +long RingBuffer_GetWriteAvailable( RingBuffer *rbuf ); +/* Return number of bytes available for read. */ +long RingBuffer_GetReadAvailable( RingBuffer *rbuf ); +/* Return bytes written. */ +long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes ); +/* Return bytes read. */ +long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes ); + +/* Get address of region(s) to which we can write data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ); +long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes ); + +/* Get address of region(s) from which we can read data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ); + +long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _RINGBUFFER_H */ diff --git a/lib/portaudio/pablio/test_rw.c b/lib/portaudio/pablio/test_rw.c new file mode 100644 index 0000000..f3491d5 --- /dev/null +++ b/lib/portaudio/pablio/test_rw.c @@ -0,0 +1,99 @@ +/* + * $Id: test_rw.c,v 1.3 2003/03/02 08:01:47 dmazzoni Exp $ + * test_rw.c + * Read input from one stream and write it to another. + * + * Author: Phil Burk, http://www.softsynth.com/portaudio/ + * + * This program uses PABLIO, the Portable Audio Blocking I/O Library. + * PABLIO is built on top of PortAudio, the Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include "pablio.h" + +/* +** Note that many of the older ISA sound cards on PCs do NOT support +** full duplex audio (simultaneous record and playback). +** And some only support full duplex at lower sample rates. +*/ +#define SAMPLE_RATE (44100) +#define NUM_SECONDS (5) +#define SAMPLES_PER_FRAME (2) +#define FRAMES_PER_BLOCK (64) + +/* Select whether we will use floats or shorts. */ +#if 1 +#define SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#else +#define SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#endif + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i; + SAMPLE samples[SAMPLES_PER_FRAME * FRAMES_PER_BLOCK]; + PaError err; + PABLIO_Stream *aStream; + + printf("Full duplex sound test using PortAudio and RingBuffers\n"); + fflush(stdout); + + /* Open simplified blocking I/O layer on top of PortAudio. */ + err = OpenAudioStream( &aStream, SAMPLE_RATE, SAMPLE_TYPE, + (PABLIO_READ_WRITE | PABLIO_STEREO) ); + if( err != paNoError ) goto error; + + /* Process samples in the foreground. */ + for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK ) + { + /* Read one block of data into sample array from audio input. */ + ReadAudioStream( aStream, samples, FRAMES_PER_BLOCK ); + /* Write that same block of data to output. */ + WriteAudioStream( aStream, samples, FRAMES_PER_BLOCK ); + } + + CloseAudioStream( aStream ); + + printf("Full duplex sound test complete.\n" ); + fflush(stdout); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; +} diff --git a/lib/portaudio/pablio/test_rw_echo.c b/lib/portaudio/pablio/test_rw_echo.c new file mode 100644 index 0000000..42c595b --- /dev/null +++ b/lib/portaudio/pablio/test_rw_echo.c @@ -0,0 +1,123 @@ +/* + * $Id: test_rw_echo.c,v 1.4 2003/03/02 08:01:47 dmazzoni Exp $ + * test_rw_echo.c + * Echo delayed input to output. + * + * Author: Phil Burk, http://www.softsynth.com/portaudio/ + * + * This program uses PABLIO, the Portable Audio Blocking I/O Library. + * PABLIO is built on top of PortAudio, the Portable Audio Library. + * + * Note that if you need low latency, you should not use PABLIO. + * Use the PA_OpenStream callback technique which is lower level + * than PABLIO. + * + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include "pablio.h" +#include + +/* +** Note that many of the older ISA sound cards on PCs do NOT support +** full duplex audio (simultaneous record and playback). +** And some only support full duplex at lower sample rates. +*/ +#define SAMPLE_RATE (22050) +#define NUM_SECONDS (20) +#define SAMPLES_PER_FRAME (2) + +/* Select whether we will use floats or shorts. */ +#if 1 +#define SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#else +#define SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#endif + +#define NUM_ECHO_FRAMES (2*SAMPLE_RATE) +SAMPLE samples[NUM_ECHO_FRAMES][SAMPLES_PER_FRAME] = {0.0}; + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i; + PaError err; + PABLIO_Stream *aInStream; + PABLIO_Stream *aOutStream; + int index; + + printf("Full duplex sound test using PABLIO\n"); + fflush(stdout); + + /* Open simplified blocking I/O layer on top of PortAudio. */ + /* Open input first so it can start to fill buffers. */ + err = OpenAudioStream( &aInStream, SAMPLE_RATE, SAMPLE_TYPE, + (PABLIO_READ | PABLIO_STEREO) ); + if( err != paNoError ) goto error; + /* printf("opened input\n"); fflush(stdout); /**/ + + err = OpenAudioStream( &aOutStream, SAMPLE_RATE, SAMPLE_TYPE, + (PABLIO_WRITE | PABLIO_STEREO) ); + if( err != paNoError ) goto error; + /* printf("opened output\n"); fflush(stdout); /**/ + + /* Process samples in the foreground. */ + index = 0; + for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ ) + { + /* Write old frame of data to output. */ + /* samples[index][1] = (i&256) * (1.0f/256.0f); /* sawtooth */ + WriteAudioStream( aOutStream, &samples[index][0], 1 ); + + /* Read one frame of data into sample array for later output. */ + ReadAudioStream( aInStream, &samples[index][0], 1 ); + index += 1; + if( index >= NUM_ECHO_FRAMES ) index = 0; + + if( (i & 0xFFFF) == 0 ) printf("i = %d\n", i ); fflush(stdout); /**/ + } + + CloseAudioStream( aOutStream ); + CloseAudioStream( aInStream ); + + printf("R/W echo sound test complete.\n" ); + fflush(stdout); + return 0; + +error: + fprintf( stderr, "An error occured while using PortAudio\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; +} diff --git a/lib/portaudio/pablio/test_w_saw.c b/lib/portaudio/pablio/test_w_saw.c new file mode 100644 index 0000000..66748c4 --- /dev/null +++ b/lib/portaudio/pablio/test_w_saw.c @@ -0,0 +1,108 @@ +/* + * $Id: test_w_saw.c,v 1.3 2003/03/02 08:01:47 dmazzoni Exp $ + * test_w_saw.c + * Generate stereo sawtooth waveforms. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses PABLIO, the Portable Audio Blocking I/O Library. + * PABLIO is built on top of PortAudio, the Portable Audio Library. + * + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include "pablio.h" +#include + +#define SAMPLE_RATE (44100) +#define NUM_SECONDS (15) +#define SAMPLES_PER_FRAME (2) + +#define FREQUENCY (220.0f) +#define PHASE_INCREMENT (2.0f * FREQUENCY / SAMPLE_RATE) +#define FRAMES_PER_BLOCK (100) + +float samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME]; +float phases[SAMPLES_PER_FRAME]; + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i,j; + PaError err; + PABLIO_Stream *aOutStream; + + printf("Generate sawtooth waves using PABLIO.\n"); + fflush(stdout); + + /* Open simplified blocking I/O layer on top of PortAudio. */ + err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paFloat32, + (PABLIO_WRITE | PABLIO_STEREO) ); + if( err != paNoError ) goto error; + + /* Initialize oscillator phases. */ + phases[0] = 0.0; + phases[1] = 0.0; + + for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK ) + { + /* Generate sawtooth waveforms in a block for efficiency. */ + for( j=0; j 1.0f ) phases[0] -= 2.0f; + samples[j][0] = phases[0]; + + /* On the second channel, generate a sawtooth wave a fifth higher. */ + phases[1] += PHASE_INCREMENT * (3.0f / 2.0f); + if( phases[1] > 1.0f ) phases[1] -= 2.0f; + samples[j][1] = phases[1]; + } + + /* Write samples to output. */ + WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK ); + } + + CloseAudioStream( aOutStream ); + + printf("Sawtooth sound test complete.\n" ); + fflush(stdout); + return 0; + +error: + fprintf( stderr, "An error occured while using PABLIO\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; +} diff --git a/lib/portaudio/pablio/test_w_saw8.c b/lib/portaudio/pablio/test_w_saw8.c new file mode 100644 index 0000000..81c1d1a --- /dev/null +++ b/lib/portaudio/pablio/test_w_saw8.c @@ -0,0 +1,106 @@ +/* + * $Id: test_w_saw8.c,v 1.3 2003/03/02 08:01:47 dmazzoni Exp $ + * test_w_saw8.c + * Generate stereo 8 bit sawtooth waveforms. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses PABLIO, the Portable Audio Blocking I/O Library. + * PABLIO is built on top of PortAudio, the Portable Audio Library. + * + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include "pablio.h" +#include + +#define SAMPLE_RATE (22050) +#define NUM_SECONDS (6) +#define SAMPLES_PER_FRAME (2) + + +#define FRAMES_PER_BLOCK (100) + +unsigned char samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME]; +unsigned char phases[SAMPLES_PER_FRAME]; + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i,j; + PaError err; + PABLIO_Stream *aOutStream; + + printf("Generate unsigned 8 bit sawtooth waves using PABLIO.\n"); + fflush(stdout); + + /* Open simplified blocking I/O layer on top of PortAudio. */ + err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paUInt8, + (PABLIO_WRITE | PABLIO_STEREO) ); + if( err != paNoError ) goto error; + + /* Initialize oscillator phases to "ground" level for paUInt8. */ + phases[0] = 128; + phases[1] = 128; + + for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK ) + { + /* Generate sawtooth waveforms in a block for efficiency. */ + for( j=0; j + +# Microsoft Developer Studio Generated Build File, Format Version 6.00 + +# ** DO NOT EDIT ** + + + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + + + +CFG=PAStaticDS - Win32 Debug + +!MESSAGE This is not a valid makefile. To build this project using NMAKE, + +!MESSAGE use the Export Makefile command and run + +!MESSAGE + +!MESSAGE NMAKE /f "PAStaticDS.mak". + +!MESSAGE + +!MESSAGE You can specify a configuration when running NMAKE + +!MESSAGE by defining the macro CFG on the command line. For example: + +!MESSAGE + +!MESSAGE NMAKE /f "PAStaticDS.mak" CFG="PAStaticDS - Win32 Debug" + +!MESSAGE + +!MESSAGE Possible choices for configuration are: + +!MESSAGE + +!MESSAGE "PAStaticDS - Win32 Release" (based on "Win32 (x86) Static Library") + +!MESSAGE "PAStaticDS - Win32 Debug" (based on "Win32 (x86) Static Library") + +!MESSAGE + + + +# Begin Project + +# PROP AllowPerConfigDependencies 0 + +# PROP Scc_ProjName "" + +# PROP Scc_LocalPath "" + +CPP=cl.exe + +RSC=rc.exe + + + +!IF "$(CFG)" == "PAStaticDS - Win32 Release" + + + +# PROP BASE Use_MFC 0 + +# PROP BASE Use_Debug_Libraries 0 + +# PROP BASE Output_Dir "Release" + +# PROP BASE Intermediate_Dir "Release" + +# PROP BASE Target_Dir "" + +# PROP Use_MFC 0 + +# PROP Use_Debug_Libraries 0 + +# PROP Output_Dir "Release" + +# PROP Intermediate_Dir "Release" + +# PROP Target_Dir "" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c + +# ADD CPP /nologo /W3 /GX /O2 /I "../../pa_common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c + +# ADD BASE RSC /l 0x408 /d "NDEBUG" + +# ADD RSC /l 0x408 /d "NDEBUG" + +BSC32=bscmake.exe + +# ADD BASE BSC32 /nologo + +# ADD BSC32 /nologo + +LIB32=link.exe -lib + +# ADD BASE LIB32 /nologo + +# ADD LIB32 /nologo /out:"../Lib/PAStaticDS.lib" + + + +!ELSEIF "$(CFG)" == "PAStaticDS - Win32 Debug" + + + +# PROP BASE Use_MFC 0 + +# PROP BASE Use_Debug_Libraries 1 + +# PROP BASE Output_Dir "Debug" + +# PROP BASE Intermediate_Dir "Debug" + +# PROP BASE Target_Dir "" + +# PROP Use_MFC 0 + +# PROP Use_Debug_Libraries 1 + +# PROP Output_Dir "Debug" + +# PROP Intermediate_Dir "Debug" + +# PROP Target_Dir "" + +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c + +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../../pa_common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c + +# ADD BASE RSC /l 0x408 /d "_DEBUG" + +# ADD RSC /l 0x408 /d "_DEBUG" + +BSC32=bscmake.exe + +# ADD BASE BSC32 /nologo + +# ADD BSC32 /nologo + +LIB32=link.exe -lib + +# ADD BASE LIB32 /nologo + +# ADD LIB32 /nologo /out:"../Lib/PAStaticDSD.lib" + + + +!ENDIF + + + +# Begin Target + + + +# Name "PAStaticDS - Win32 Release" + +# Name "PAStaticDS - Win32 Debug" + +# Begin Group "Source Files" + + + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" + +# Begin Source File + + + +SOURCE=..\..\pa_win_ds\dsound_wrapper.c + +# End Source File + +# Begin Source File + + + +SOURCE=..\..\pa_win_ds\pa_dsound.c + +# End Source File + +# Begin Source File + + + +SOURCE=..\..\pa_common\pa_lib.c + +# End Source File + +# End Group + +# Begin Group "Header Files" + + + +# PROP Default_Filter "h;hpp;hxx;hm;inl" + +# Begin Source File + + + +SOURCE=..\..\pa_win_ds\dsound_wrapper.h + +# End Source File + +# Begin Source File + + + +SOURCE=..\..\pa_common\pa_host.h + +# End Source File + +# Begin Source File + + + +SOURCE=..\..\pa_common\portaudio.h + +# End Source File + +# End Group + +# End Target + +# End Project + diff --git a/lib/portaudio/winproj/PAStaticDS/PAStaticDS.mak b/lib/portaudio/winproj/PAStaticDS/PAStaticDS.mak new file mode 100644 index 0000000..c593548 --- /dev/null +++ b/lib/portaudio/winproj/PAStaticDS/PAStaticDS.mak @@ -0,0 +1,330 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on PAStaticDS.dsp + +!IF "$(CFG)" == "" + +CFG=PAStaticDS - Win32 Debug + +!MESSAGE No configuration specified. Defaulting to PAStaticDS - Win32 Debug. + +!ENDIF + + + +!IF "$(CFG)" != "PAStaticDS - Win32 Release" && "$(CFG)" != "PAStaticDS - Win32 Debug" + +!MESSAGE Invalid configuration "$(CFG)" specified. + +!MESSAGE You can specify a configuration when running NMAKE + +!MESSAGE by defining the macro CFG on the command line. For example: + +!MESSAGE + +!MESSAGE NMAKE /f "PAStaticDS.mak" CFG="PAStaticDS - Win32 Debug" + +!MESSAGE + +!MESSAGE Possible choices for configuration are: + +!MESSAGE + +!MESSAGE "PAStaticDS - Win32 Release" (based on "Win32 (x86) Static Library") + +!MESSAGE "PAStaticDS - Win32 Debug" (based on "Win32 (x86) Static Library") + +!MESSAGE + +!ERROR An invalid configuration is specified. + +!ENDIF + + + +!IF "$(OS)" == "Windows_NT" + +NULL= + +!ELSE + +NULL=nul + +!ENDIF + + + +CPP=cl.exe + +RSC=rc.exe + + + +!IF "$(CFG)" == "PAStaticDS - Win32 Release" + + + +OUTDIR=.\Release + +INTDIR=.\Release + + + +ALL : "..\Lib\PAStaticDS.lib" + + + + + +CLEAN : + + -@erase "$(INTDIR)\dsound_wrapper.obj" + + -@erase "$(INTDIR)\pa_dsound.obj" + + -@erase "$(INTDIR)\pa_lib.obj" + + -@erase "$(INTDIR)\vc60.idb" + + -@erase "..\Lib\PAStaticDS.lib" + + + +"$(OUTDIR)" : + + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + + + +CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "../../pa_common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fp"$(INTDIR)\PAStaticDS.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +BSC32=bscmake.exe + +BSC32_FLAGS=/nologo /o"$(OUTDIR)\PAStaticDS.bsc" + +BSC32_SBRS= \ + + + +LIB32=link.exe -lib + +LIB32_FLAGS=/nologo /out:"../Lib/PAStaticDS.lib" + +LIB32_OBJS= \ + + "$(INTDIR)\dsound_wrapper.obj" \ + + "$(INTDIR)\pa_dsound.obj" \ + + "$(INTDIR)\pa_lib.obj" + + + +"..\Lib\PAStaticDS.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + + $(LIB32) @<< + + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) + +<< + + + +!ELSEIF "$(CFG)" == "PAStaticDS - Win32 Debug" + + + +OUTDIR=.\Debug + +INTDIR=.\Debug + + + +ALL : "..\Lib\PAStaticDSD.lib" + + + + + +CLEAN : + + -@erase "$(INTDIR)\dsound_wrapper.obj" + + -@erase "$(INTDIR)\pa_dsound.obj" + + -@erase "$(INTDIR)\pa_lib.obj" + + -@erase "$(INTDIR)\vc60.idb" + + -@erase "$(INTDIR)\vc60.pdb" + + -@erase "..\Lib\PAStaticDSD.lib" + + + +"$(OUTDIR)" : + + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + + + +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od /I "../../pa_common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fp"$(INTDIR)\PAStaticDS.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +BSC32=bscmake.exe + +BSC32_FLAGS=/nologo /o"$(OUTDIR)\PAStaticDS.bsc" + +BSC32_SBRS= \ + + + +LIB32=link.exe -lib + +LIB32_FLAGS=/nologo /out:"../Lib/PAStaticDSD.lib" + +LIB32_OBJS= \ + + "$(INTDIR)\dsound_wrapper.obj" \ + + "$(INTDIR)\pa_dsound.obj" \ + + "$(INTDIR)\pa_lib.obj" + + + +"..\Lib\PAStaticDSD.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + + $(LIB32) @<< + + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) + +<< + + + +!ENDIF + + + +.c{$(INTDIR)}.obj:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cpp{$(INTDIR)}.obj:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cxx{$(INTDIR)}.obj:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.c{$(INTDIR)}.sbr:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cpp{$(INTDIR)}.sbr:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cxx{$(INTDIR)}.sbr:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" + +!IF EXISTS("PAStaticDS.dep") + +!INCLUDE "PAStaticDS.dep" + +!ELSE + +!MESSAGE Warning: cannot find "PAStaticDS.dep" + +!ENDIF + +!ENDIF + + + + + +!IF "$(CFG)" == "PAStaticDS - Win32 Release" || "$(CFG)" == "PAStaticDS - Win32 Debug" + +SOURCE=..\..\pa_win_ds\dsound_wrapper.c + + + +"$(INTDIR)\dsound_wrapper.obj" : $(SOURCE) "$(INTDIR)" + + $(CPP) $(CPP_PROJ) $(SOURCE) + + + + + +SOURCE=..\..\pa_win_ds\pa_dsound.c + + + +"$(INTDIR)\pa_dsound.obj" : $(SOURCE) "$(INTDIR)" + + $(CPP) $(CPP_PROJ) $(SOURCE) + + + + + +SOURCE=..\..\pa_common\pa_lib.c + + + +"$(INTDIR)\pa_lib.obj" : $(SOURCE) "$(INTDIR)" + + $(CPP) $(CPP_PROJ) $(SOURCE) + + + + + + + +!ENDIF + + + diff --git a/lib/portaudio/winproj/PAStaticWMME/CVS/Entries b/lib/portaudio/winproj/PAStaticWMME/CVS/Entries new file mode 100644 index 0000000..a0215b2 --- /dev/null +++ b/lib/portaudio/winproj/PAStaticWMME/CVS/Entries @@ -0,0 +1,4 @@ +/PAStaticWMME.dep/1.1/Mon Aug 27 06:25:16 2001// +/PAStaticWMME.dsp/1.2/Sun Nov 11 08:36:11 2001// +/PAStaticWMME.mak/1.1/Mon Aug 27 06:25:22 2001// +D diff --git a/lib/portaudio/winproj/PAStaticWMME/CVS/Repository b/lib/portaudio/winproj/PAStaticWMME/CVS/Repository new file mode 100644 index 0000000..01b2af3 --- /dev/null +++ b/lib/portaudio/winproj/PAStaticWMME/CVS/Repository @@ -0,0 +1 @@ +/cvsroot/audacity/lib-src/portaudio/winproj/PAStaticWMME diff --git a/lib/portaudio/winproj/PAStaticWMME/CVS/Root b/lib/portaudio/winproj/PAStaticWMME/CVS/Root new file mode 100644 index 0000000..ba5738e --- /dev/null +++ b/lib/portaudio/winproj/PAStaticWMME/CVS/Root @@ -0,0 +1 @@ +:ext:habes@cvs.sourceforge.net:/cvsroot/audacity diff --git a/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dep b/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dep new file mode 100644 index 0000000..5352f79 --- /dev/null +++ b/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dep @@ -0,0 +1,26 @@ +# Microsoft Developer Studio Generated Dependency File, included by PAStaticWMME.mak + + + +..\..\pa_common\pa_lib.c : \ + + "..\..\pa_common\pa_host.h"\ + + "..\..\pa_common\pa_trace.h"\ + + "..\..\pa_common\portaudio.h"\ + + + + + +..\..\pa_win_wmme\pa_win_wmme.c : \ + + "..\..\pa_common\pa_host.h"\ + + "..\..\pa_common\pa_trace.h"\ + + "..\..\pa_common\portaudio.h"\ + + + diff --git a/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dsp b/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dsp new file mode 100644 index 0000000..a70fa79 --- /dev/null +++ b/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.dsp @@ -0,0 +1,108 @@ +# Microsoft Developer Studio Project File - Name="PAStaticWMME" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=PAStaticWMME - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "PAStaticWMME.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "PAStaticWMME.mak" CFG="PAStaticWMME - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "PAStaticWMME - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "PAStaticWMME - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "PAStaticWMME - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../pa_common" /I "include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x408 /d "NDEBUG" +# ADD RSC /l 0x408 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"../Lib/PAStaticWMME.lib" + +!ELSEIF "$(CFG)" == "PAStaticWMME - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../pa_common" /I "include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x408 /d "_DEBUG" +# ADD RSC /l 0x408 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"../Lib/PAStaticWMMED.lib" + +!ENDIF + +# Begin Target + +# Name "PAStaticWMME - Win32 Release" +# Name "PAStaticWMME - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\pa_common\pa_lib.c +# End Source File +# Begin Source File + +SOURCE=..\..\pa_win_wmme\pa_win_wmme.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\pa_common\pa_host.h +# End Source File +# Begin Source File + +SOURCE=..\..\pa_common\portaudio.h +# End Source File +# End Group +# End Target +# End Project diff --git a/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.mak b/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.mak new file mode 100644 index 0000000..a08a486 --- /dev/null +++ b/lib/portaudio/winproj/PAStaticWMME/PAStaticWMME.mak @@ -0,0 +1,310 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on PAStaticWMME.dsp + +!IF "$(CFG)" == "" + +CFG=PAStaticWMME - Win32 Debug + +!MESSAGE No configuration specified. Defaulting to PAStaticWMME - Win32 Debug. + +!ENDIF + + + +!IF "$(CFG)" != "PAStaticWMME - Win32 Release" && "$(CFG)" != "PAStaticWMME - Win32 Debug" + +!MESSAGE Invalid configuration "$(CFG)" specified. + +!MESSAGE You can specify a configuration when running NMAKE + +!MESSAGE by defining the macro CFG on the command line. For example: + +!MESSAGE + +!MESSAGE NMAKE /f "PAStaticWMME.mak" CFG="PAStaticWMME - Win32 Debug" + +!MESSAGE + +!MESSAGE Possible choices for configuration are: + +!MESSAGE + +!MESSAGE "PAStaticWMME - Win32 Release" (based on "Win32 (x86) Static Library") + +!MESSAGE "PAStaticWMME - Win32 Debug" (based on "Win32 (x86) Static Library") + +!MESSAGE + +!ERROR An invalid configuration is specified. + +!ENDIF + + + +!IF "$(OS)" == "Windows_NT" + +NULL= + +!ELSE + +NULL=nul + +!ENDIF + + + +CPP=cl.exe + +RSC=rc.exe + + + +!IF "$(CFG)" == "PAStaticWMME - Win32 Release" + + + +OUTDIR=.\Release + +INTDIR=.\Release + + + +ALL : "..\Lib\PAStaticWMME.lib" + + + + + +CLEAN : + + -@erase "$(INTDIR)\pa_lib.obj" + + -@erase "$(INTDIR)\pa_win_wmme.obj" + + -@erase "$(INTDIR)\vc60.idb" + + -@erase "..\Lib\PAStaticWMME.lib" + + + +"$(OUTDIR)" : + + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + + + +CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "../../pa_common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fp"$(INTDIR)\PAStaticWMME.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +BSC32=bscmake.exe + +BSC32_FLAGS=/nologo /o"$(OUTDIR)\PAStaticWMME.bsc" + +BSC32_SBRS= \ + + + +LIB32=link.exe -lib + +LIB32_FLAGS=/nologo /out:"../Lib/PAStaticWMME.lib" + +LIB32_OBJS= \ + + "$(INTDIR)\pa_lib.obj" \ + + "$(INTDIR)\pa_win_wmme.obj" + + + +"..\Lib\PAStaticWMME.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + + $(LIB32) @<< + + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) + +<< + + + +!ELSEIF "$(CFG)" == "PAStaticWMME - Win32 Debug" + + + +OUTDIR=.\Debug + +INTDIR=.\Debug + + + +ALL : "..\Lib\PAStaticWMMED.lib" + + + + + +CLEAN : + + -@erase "$(INTDIR)\pa_lib.obj" + + -@erase "$(INTDIR)\pa_win_wmme.obj" + + -@erase "$(INTDIR)\vc60.idb" + + -@erase "$(INTDIR)\vc60.pdb" + + -@erase "..\Lib\PAStaticWMMED.lib" + + + +"$(OUTDIR)" : + + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + + + +CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od /I "../../pa_common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fp"$(INTDIR)\PAStaticWMME.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +BSC32=bscmake.exe + +BSC32_FLAGS=/nologo /o"$(OUTDIR)\PAStaticWMME.bsc" + +BSC32_SBRS= \ + + + +LIB32=link.exe -lib + +LIB32_FLAGS=/nologo /out:"../Lib/PAStaticWMMED.lib" + +LIB32_OBJS= \ + + "$(INTDIR)\pa_lib.obj" \ + + "$(INTDIR)\pa_win_wmme.obj" + + + +"..\Lib\PAStaticWMMED.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + + $(LIB32) @<< + + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) + +<< + + + +!ENDIF + + + +.c{$(INTDIR)}.obj:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cpp{$(INTDIR)}.obj:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cxx{$(INTDIR)}.obj:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.c{$(INTDIR)}.sbr:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cpp{$(INTDIR)}.sbr:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + +.cxx{$(INTDIR)}.sbr:: + + $(CPP) @<< + + $(CPP_PROJ) $< + +<< + + + + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" + +!IF EXISTS("PAStaticWMME.dep") + +!INCLUDE "PAStaticWMME.dep" + +!ELSE + +!MESSAGE Warning: cannot find "PAStaticWMME.dep" + +!ENDIF + +!ENDIF + + + + + +!IF "$(CFG)" == "PAStaticWMME - Win32 Release" || "$(CFG)" == "PAStaticWMME - Win32 Debug" + +SOURCE=..\..\pa_common\pa_lib.c + + + +"$(INTDIR)\pa_lib.obj" : $(SOURCE) "$(INTDIR)" + + $(CPP) $(CPP_PROJ) $(SOURCE) + + + + + +SOURCE=..\..\pa_win_wmme\pa_win_wmme.c + + + +"$(INTDIR)\pa_win_wmme.obj" : $(SOURCE) "$(INTDIR)" + + $(CPP) $(CPP_PROJ) $(SOURCE) + + + + + + + +!ENDIF + + + diff --git a/lib/portaudio/winproj/Readme.txt b/lib/portaudio/winproj/Readme.txt new file mode 100644 index 0000000..316fbf3 --- /dev/null +++ b/lib/portaudio/winproj/Readme.txt @@ -0,0 +1,6 @@ +Visual C++ 6.0 Workspace & makefiles for PortAudio (Static Libraries). + +2 configurations, one using DirectSound (DS) and one using WMME. + +Libraries are output to the Lib folder. + diff --git a/lib/portaudio/winproj/WinVC.dsw b/lib/portaudio/winproj/WinVC.dsw new file mode 100644 index 0000000..992a4a9 --- /dev/null +++ b/lib/portaudio/winproj/WinVC.dsw @@ -0,0 +1,82 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 + +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + + + +############################################################################### + + + +Project: "PAStaticDS"=.\PAStaticDS\PAStaticDS.dsp - Package Owner=<4> + + + +Package=<5> + +{{{ + +}}} + + + +Package=<4> + +{{{ + +}}} + + + +############################################################################### + + + +Project: "PAStaticWMME"=.\PAStaticWMME\PAStaticWMME.dsp - Package Owner=<4> + + + +Package=<5> + +{{{ + +}}} + + + +Package=<4> + +{{{ + +}}} + + + +############################################################################### + + + +Global: + + + +Package=<5> + +{{{ + +}}} + + + +Package=<3> + +{{{ + +}}} + + + +############################################################################### + + + diff --git a/lib/portaudio/winproj/WinVC.ncb b/lib/portaudio/winproj/WinVC.ncb new file mode 100644 index 0000000000000000000000000000000000000000..b7f2c9a0b8401ee145b3c224b917d8d0c34de71a GIT binary patch literal 82944 zcmeHw33Ob=neKmEt+p)r*z)eSjcG4{V{FR{i)|dN)waO0gch5Ch}>$mq_(AQrMqPt zKQhSJ$&i;!@FWaxlFSV2B!n}0@eqayWFTfjc#}-n9!Us!f$@+zFL6$C#=MycISGOH zeO33CZcDArWCK;*uCDE`?Y~ujEw}!^>JqVZDwFEWMykrHF1|R@mrix1qrH)iXg1m& z&BP-c)~;JuT)gnA9U>AGapW4QbVR0|-5q`ER`K}yZJ4XW%8<$DZ=nVsf28iKS4Nta8H#z z(kCyN@Dk~D`kbo^z3>@w#`${S`vsnRrmS-+oP`0;JzKu!e8c&lCVZ~k<2>s8he?0F zyd3yd;FBhNf&5e8>A-H~4wQ;4l5KcgH_Ed_GR_^&K9l}RS?sKF&Y1X{BqIx)lcs!I zq}u6l4w~?-(&g-TI*t2E`IK{~bGLDClV=0p5A0Cs2asR6Jnx)#jw3uMbXUlG@!SUY zTfoU&%;n6^_#=M>POc(8`Cdr0cs_H_fVFM_Qo?XV(n$wXu2z&O(Z*0+TEM#=aLSHx9CHPFflB!2#0!m<9q?0eS*ape1q9^G!~yA9<%XG7R@5s*4cmLh?lc&d1Vb1 zP1$HR5sR#e>`f#$ZiqC-d*TpE2(9Khk} zZAZ0-rc>RNQ~Z1&Om$=+mCb0WD1gB<4#v}&L@KGeb{Lf4LQ(_h*YCT$Z6i#oZ@^;? z%%jl7(4U2SHq08BGhsdsT>@Q!QH0^Ifl@Dq`6S#kVBQKh)1D6MKA{tJ{{&< zxaYyV47v!K3Is{cBK^5Azku+CFn+oV} zz;nX5r%NNAhfV%7Thm3oU?80-K zDbHMK!}F+d&y#(4Vx~Ovv3j~jo;B_T(uC(elU`Wr@XR#v7s~Z`eqzeMNUp>4fWgmV z>B4isgrA4?*dBQU;ZxN6T_RWVLHfuN^*diS;R%3N2SCpQp!ET)nF64}fyE18&VpVH z`)ru!L6=D2o5d1XxD57b&~u<8(C0&!LeGR=2)zh;9yDO_@&lmZf%H-p5L`7=qc!zT^KK%FtiVaW157erk&vh7~*Den0uF~gQyGp zKhPpaAG@0<}@^5$oi_o4H6{0;AKx3udJq2#Ivyf^#tUucaHP_3`kM@-)H%*q0 z?SYzYBnHzei-C6v20n*&JZl;VFBHf30k&t?&GBFD_qpy1a>TjCiD2rl-9e0ojq)hqo#}+1DxY+|?P6 zT;~kH{aw`Y8EC(G2JQgDN};{57N330a3AbveD+N!PbIXM=13;#mcKkhRifyF>%Len7Vmoj!wtP~!$~jH_ktxlKKuKfXnV5? zw43xp(GxeE^rO*1xEr7A2}Ms_x1lE)9iQ}M7PcBVpC}eb&Ho!we(esZ`T5-@yg;tS z({J2CHGln*fiG0^_ivl^XFTQyGYHicN+MqYCZA*`@1S{iL4B+2|k4_ z5$!IPCkkFD=m#>jyHqv?FAL69?f}X^OYV0bcBaw|`dN&6MS0`fQU&oF|Mz?DVKKneZm53hoMSQ21bYi~NgI9e9%M!=d|n*%A1V z=SKQbIf(i78kAqR-*(xK=kJU=rq+))8+V87#M5ltanx{!yo&t!eW(~~WzK(pVccD6 zy}1MR)8XA}{kQ`7)9!@q!}ECqzhAAF7ntw^YW;h)ara0E9=%rMT5bk3*JxPBq3s5i zaLvax8rN=I2VMca1$whw0h}EHUY>%jI{NOq>dvT&U3Kt21RKLVZ4kL6~Wm|ni{e10r9bqU@9zt3^4`X=5V z?VibWyAjUsVJr{bUS74RvzI5=G2VAE4?o`lZeD@@G4y{yzXeVBeE#*wmus2(VIGF| z@}bGH_}%ZmQO?$ABn+@$&cMbC4D{M-WG=Sa(HxYU<1xe0gyH7RAJ@p&5I(c79PWVn zKK^fXV>~X!`ndwnqu9dN?x3PqpEB-3MX%;zOJ0YE)P7XbxTmQ7uQdj~NUHD@8~0S4 z52%#g%Vm`fJ{?&xfQr}a&^-UOaG|p!qH0jNe z58!#pxaVR%dQhG<>CKY^cw(kJ^RYjCL_THQ3)KGXI^zz@VLaY?D2#pkqEK;Y@p)># zTpGrn|9yB|H{RFzawza%;8Gn9_fmPKV0Ewu<>&l5jJN+#;L(7$o(f~_*jM;Kp|_q2 z*@~g5G*8ESJj@!Gc0>Jr>41_b-Ew2fg)J7^6or z`01dx9t&eM3KecE^wwixjFpvzO@-ciER3;fPhoGNw;l^)EwC`OG~}(v!WcbI7yh!a z-r%cNE(%qJTsP`_wcLxxTaSe?TAh{u6mLBi##r}7dETtI!WeD;(Rtdex58LGKkIzo zthd5g9sgSHFzc-_R>MER4w|=~3S%|>Na6n}^wv{he0zJ?dCXf+p*+`OxBX6UJq3Ke zPrfTZGV7@@)-RlYa>; zDDc)lVf2PS#pA7i!f5@Uz;iR+C)Yb+^v)A_zK!_W-6em9=X?|1EqCDY)<0o<3%w0b z3Gl7q_sgH)xz4~JkdNc>)<dk2i{G<2M{vJ;p?&LbW1K(m8yLTB@mm+a zjqy7fzkTtW*$QZW6I%|=c?9PeoKtZA!0%x55Bdy{5{$BcB zcxSZJWI4Su=Ub04{9M*w#4AXeSvP$Yc-`n~rs>y!{DW0$KkiX9J}8dg-hLe29BQrq@s(7B=Vks@ z!885YYQF2b5zhJQU&);a|7&Q!Nm~0N{LVo0Jo+0jc@}*%e~&(kRTcx27z687-EYBK zlRf&Vdz;2{B;%|0I0rw#pU*E(UD>w@txfVDe(X_W#NI?rqI22H5|>0xF+} z42mbAKMMZZ`=LwEhkP&Sa_AOlt1JdA2Ht%bpxmzjhyd@c0Q)CYh58@zS6Nh96m&z# zs){{BMDnQtDJlvEj*3LUy5m;?eWoa){cLJy0pd zE2e&8#XT#XfCEWej&l^^Vaj)w|5@O~&RHvpR}4?(sVPTQxn4$@D1T-)uu}?twBM2% z7%APAkw~zj6u(;}a2!AnzIjlU>uV?z`TAQ>F7lOAooYQ7Y^{_~%kkU4sZuCq7Qy=i zkevmL4xJtt>?+uP805C|Y`a&Q(5;d%B3PRqr{9GLb(}pBYw(j^i&_ zIv~NwH~=RMGG~qMkNd#yN4`RIODMclsB-XP|L)b33Tt2fO22xKzx(`cjurzG!$1Py z}fb}3(%GIc3Om$GvyFPAcMInFGH8H1+G+b3YM4;}~KEqQt= z_n)%TKEU(tEy`=8j7H91C`*L*ZSjsR%4NJ8Cgm?uhR6=UQ~m>G;88XN<+84u4*+hg{ z_km;HJ!~p}zB~HM<;Rr#HNsEBs6bgxv=<>Rajen@4DU3cKV>wrUi6RLe9XK1O?`jX za$Qhw#3e2W-vGP5e@)4lQ21xu(1Yq3%xoJ>^Aysg>?8UwMF{Vmpq+AN`5v`BpxPGgoMTb;72}?$0}fqWo$NcN z-XUQckq+B}fN2x!iaDKkp-;os#%S z4`@dt< zsb~T1SHPclCeThvf$sf zWqS@~qYPR4Zvs5;L(wpfmQ4=-Imns!jL?4p{#}_^SRsVe{SEZzLPyDFhn;sjkoGbC zlSrR5j4+f!M4C+hKKQ$LFQPnXXXp@wnS#2IeiCLMV0ga^{kdKw4W=Em5f_6w`RO1ZuDo0+t=Y{^W@W0P3CyJuvN$R+TYMdc_1n|>QY5Mb~2+CZe{XC?v zv;) zu?%NHXE^rJej0Yli6hKHq{DHJc5?MnUMKAZNQYxI?epN`n8moyAuh*Y+E2mG`(y~S z82?=3(oRX1Tqn_9gmjdQec0JwC{K_6GXTZ0lj+=rbSM{({&V0@nccLPpuW6+hW7LE z&%0)5Uy6T@iL_q{7|Il-J&3$0uTH~M1+BSevM&~V-jME@E1=bVLh{_~C?+PU7RJU`m!Avekn z)czqQN00Uh{3)}P_F|Nu@{*90L@*Ajy9;11L3ucTV0lW>mN*9>d^zgH`33#&+I&nX zYn=YG;ZHeBX!dgUkB+%AuPL7~sO}pg%v?Z5KzL{`0wm{1Dt(Nzl<9<~B_~lw&aqVb zu%B{qMJZ$I-U5|Qfx6>?=@j6hJXYGLA_dAWqf&c6LOEw4%yRrw zmLct7z*Ei)rdQI3d^u-g8TKJA?<*qAdHCmD5wwSpFXb-MJ_|0+foNZWf6Aj*!U6x{o2;jib!^q&P7&Ig&-NxVnOtfYSl(&7A%{-r1nWhc^q z1^ju>6YUH6@Amf+*!$rZMm`$=Qw(zf%oQ--tK3@pb|u`+a4&?p5$2^ZuYwtZ8N`1n z{+GbiH4dRqVq8(~0Ll_T9LnkqVeI5q7~wctf{w$z3ho6k%V1s%vl8Zfn5$tffq6O1 zRWK`HZh*N1<|Qy|VXlL@9cBdPW|$YigcL;dogmT*Vf+bVPbdUh6GWLg8{<24fXg5> zsm>JGIBIf6$19ExBgR=8`o z683Gdv&}flht!6-aAaU9NZFYdOF;i3(5xWFSeA<^GA937st_a*2O)7W1Uv*GC7F4! zyc|_&F93}Tg5HPl-hz;O%=E0X82J5T0OZ;#ivf#)Ns56t|NYH>=UCOl&ub&zw>&IB z-(y_0rM024en(?PU1WRhu9~LSy4tG7`lkBr&5=E|O)V9>B2CRL)wT7lRrPgs^}Ac^ znyPE|Y~IvbS-ZOxmrZZnuol;h%iBpMmd?KIG* zV6Ls{M6yd+d*i*S^o`mY&34nW%v^!Kwi^;uar3plyjq|>Q1%XYR5aar*Vg>FPwbJD!%FRI-Z!^?mVVb-Xj$-;>p`hNt2slSpPG$^PEj zWM6-_symuY#(Og2de--6{k}sAol&^bHSqAF{S-d>lbJ+UGTsqkp(35cQ>G!FuI%sZ zL`ic)QGoV%y1r8fW(G@J747M1kH!u}R>fSK984uTBCGl{@pN@G8)a>;PW1G+)nvh= z?Z6oQwO>Unn>a{}!=37L;ks&*iEI__l+UuR45UW_tw6n;w&r-cH<8rMfUiz9h35F- zti)2uOg0khj;1545Kjd};^{Q1z+|fU_C1{I7vZ^vzDM^Bpnd2jGT6-cE<6_!&1otL z2Ks$mged>&G^(1eO?IXb%}XJYQm@f-4oy&p=1I|wHkf4YJIknMxVl{Q2wqwMo~#Xb)Qf}@#xSwlwkaz<475e-FtUq^*7a>^=Q=)ZBAey&0_2w z10xk6iP0|5xJ;rWZic#1dXpQWIhBoqqPj!lDDW*ABtJ$WYCCsbkXq;;PGF}1;LLTM3Xl?gk+jj>b_8g9LIYX4&;d2H@IJRuTNY)?A?un-}3A9Et zaH_B*rY%FfdZUMPiCS(91P>u%x#5^74AM^yorC28+w%2;^A zQlPCP)7p{2yX|N@l#cfG#nWqJvNhGdzdf3XtLbxp@<8%XQca+T0Adzh-LzNDll+}+ zXJbuM%dTdGG19+jctRMysE346{p(CgYMGB?HJW=&Ev^~yLE4DYwC%|`!pJQ zGw~RUqx(7w<)y2A%=BNa^R}ns{%YtEHrmm_C7J?I&CGo}nHu02L^KPs;H?EQ3%jvB zu3oh+9(%AG)3n!&TLJYBOf5WLe_H~=PI9jGStmZ^)}3V zv*}b%9xqPRx-+r1oL-5R;_IWM#8R&oMXRfGJGZW)KTj`%+D>RZoO={-)xrnqf*B|{yBN7pYOK+7VD_JEoK&4JDPIlDT)Dc*E!N1eY6YXm z#$6SSJ8Ig{>T7n?G>($IH~#5Zs-fb=s<9KDCx*p4o7eDRR5yo_{E7aKHbv{y->{j9 zj;_I6qNoVus?k&Qk4)~!bHVlN*WECBEO)_ay1`(xMSre!*JGns2#e_qW<*{mKwsgW zi|Jg7nCthVHaB$NmW=i?os%85A)Sh0IH+oFYHq}MkU3Hv&kXTMYeKTCVNbmpGuV%~ zw)b^Kl?vRbfZLgebz4>4A?tmB)7vYM!HA~DUV3UfiSrP>>h(_n)TV+SN(NETR+H=) z)?!=~)h0XQhgouDU6)7#hq@K(O`Ogl+b%8&eS@i2Cbe0VOR&7{$m*uZn#f*mBt{zJ zJ#kDG%oe6ENbgmi+a#o0wrbkMQ74B=ZP|jEL|b2VA{~$E?u&O@UE5fL_gmkxo8!2T zrfKM$(KH9TvSs^r^wvCa)Hqw;*_nxR+#MlFHLFLL&j;aADn(B60Vo0xPd6uG2XbaQ z!}210HH&+$y%l4<^xi7G)qM^B^u_j+!)f!!zobwvpI3vGJPKQQ*nJQhq z4O@YpkWIw)*45RF*v4}L$KT2i_4dZSDHiRVT4B1?fmfks+P%G*8?l4js~mkGTJeku z$keHcc@y?Y6YfgFzv+0v^*6YqjUt5l4SM7dyps`WmW*birf{%g<*2hf~bHs5)(LH4s*HcxEwav9v z6}#G+FqqWVkJfGnV{x$pN;fh_)INC4K{Q&~S+To%SIxL!$4pjNQ>Qy}=02z~Q`D2R z=5Bmc8m&HlFtu7Cnic6$9mb6pk0eNUr8k;LN>fE$0~&J|u)jN{-dRSNfY|}iuT{m+ z&CvZYl~P}yTrc1DB|l547nM^5cY;~nB>B8K6z2x0Di4KLo(lGR_~_2_-{f83SuefXOMZ;cc|joPx6vbeFtfgr-r-%yo2z5q(y#7zuJquE=Ag2%WeDaVyg?Kz4 zI}do7g>v(ZE6=Z!XNCMnUqU+M9pXK;JjY*%_Q5l*!|~%KkpkSPmiZ~=GRk{GZ8@^rPdvX&-Wl?+@s3E|SIjfGr+^RgnpeR8HQ4{z zS3agq9vGehUk-oD#wb7;$;-z3k%ubsOpyK>H$7}DoC_f5bfc{ZrNi1c~xpNi+7$!9{|5%M*VkA>&K$&W+c zp)=5D;ZN0ry!PRpeFm_P5n)3Vc^k+ZL_S{!_~#jF^6z+x=dmA0IPV0-fna$PI3hpI z%fL}5%1Ztx^7@fKkUV_6E7ViuY2w}2yce4?PexO`YkCXHOx}Z`DhXVXZ|W-uAa4uL zwLgL~{1|n+194tOInE%Q{08J}8LH+2rXTR+1)v%LE_ffeUy)CPd<-YhXUH2szA^Gg z@{GQx$bUjbz6>g$PAT%AkQa@2R8u_zd@S+dLHptiJYWh@PEU~^OfvpZvo%C5#OrO zF|b|*C*1o#Z+7qhgj|GJdl}A_bj7m??gy69-J9y@?}-n(Guc$y=S(DU0I4J1nejuH zjekzd<8!fdjN6U5KR&nmiaG9#eP3%Q65ahkHRN05#LDlcff=v)$o2M%FflB!2zfSy zCQ^*ytCESt9y;k)adpcp^Y(d%EiOg{(u<9JAihmuZ@+j9u#wZs*?b!E&BZ zVA;lY?6n?yZ1*k5=U&H9YLFh@iL9pdkxH&n%0HF9^o`IVK99Jk95M(ag z9LO$%b|*I|7iI}aAB11NfN+jxzlO#DBU9i{;*S9er5A}KoP*m%uHTh#Gq0n7!ytmY z$;rKCmBoO?z#s;O_WwDr??-%ROCsRv%wj@6)V#X z;CQ?Q9EX>IdvOyu6Ssi#aTPccm&j^a1Fpm}(6c$9IrBh27Jz0g1f5!pg~j<20UzHo z@V2f1FXM%vG%La5XqCmlI~N03R!JrL-L$j&13yPc*$R`jIsTe9Z->eLe;-Wt|9^nV z{{JnQ?EgQ2$^QRSnC$;=z-0fQ_W$;){=XO|`~PZ~?EhO~vj6Xb$^PF8ll{L3Cj0+Q zFxl@v=9+&2ll}g0T=O4bvj2a}HGcq;{r{(~`G#vw`w#8E7$*DwYMAfEJYcr%|G>H}%S)O#;BrFCd9R_UwpLDO>R@h>|U|^`+e-mIW76TRo?|KYyN1W$3_>EqF zZm!5zXDk07Sh4c|hc^u?|9?16hWHE&aT}+V|8G!nP8KWw|D4cfBUua>3?yK)^8Y8V z{Qt8b8yHjm|8t)l$SeQ95B@wKis>UxWaO9sf8y*wUitr}w-21N{C`2*-?;q$za2Pd z`Tr*o*OUK$7wkL}Pn_h)|A+rr^8W__!*lEW0M|!5zN0Z6o{D)l%KzVoI`WJ*>-dKz z|GxocnQuvQ0|6c%euJZrSo*ej{$^So%ye1<5 z|2e?_zUBX)LK%2&pY8J`?B^=~|1QM!nQ}E}Rf0lnd^8Zf)-jn}-0(MXS z{{Zadabh}eNB(~U(wT_-|8;=(u|BUoGKSQ3!y!eOB|EC!ZA(5FPh81fsdGv7LU+pYd z{=dmND%e_277R>;|A%}y?_~a;D{(8~ReAY;DsZo16}SVd!6m#McNmV$|F;!4_-&JN z!~eH)vNRN1Pm2MI0gC~P0gC~P0gC~P0gC~P0gC~P0gC~P0gC~P0gC~P0gC~Pfp-)J F{y#J`45ojx)3PTYn3e7-Qp=*%DiEbc23(Y|{pAbQ_982}t5T#&r=&LZ3jNLZ3mO zLn-JBNaB`oU53)o3Y3AekOr+nYmmg{aJ>uNgYwWiRDg7-2z>=foYxdbOS&>u5O-d{ zwWRK&#rD%tJTZ3P+y6E+sJ?vn=U-3%^4KTiwL_BVy@b0na5rgC&@hq&#*v1EMXMB4 z2Yd$*B+sslqgA|=OMK;F#pMcm*VQKa*N}6chS0ws)#UG^-Tnup8~I~FielDtxL2Xz zB@Lo~`8$=qCAHJWQ3I(TZpBCy^^%z` zXLI_}#>MeeGFMm(r{<>U)T75==hXQN;vm(hoK;r;EkBU}3G62V{@VZOh-pXv zZ$C{w@0A2dfCNZ@1S|sd|M)17011%5-X%c)&->ic|MSj{HA#R3I*ULbR{qY-UYDh< z^#3|*YB(AaAOR8}0TOs>1nB=gHPq~g1W14co;m^gf8LSP|MSj{HA#R3I*Y(=`>&JO zkzByOWZ4(pDve%T>W17%NY&!7@9{{OFxo}b^m_MT)^JT9ZB?u^J&9a%xRlFlhIr8Dd+ED zJ508G2Qw0=8P$Lzc2OizU(h#`%WJjfP9^oyJ)@Y;Xu&g;ZRBSrf@8sANx!RQ%(KS1 zYHLG|)>cv9>ZHNf?X_8LdBY}f#;W&2E=_%6x>#XOFqagtjAORBSECTfZI%_^S8WJD@ z68Jv}(EsyJlm4G~cC1MPB+yv|mY-BNejjtJxf=~)ZQiA^U_@IfuMO4;s&8ZIQ{nmQ zg1v@yj@Rl0?Z)C-Uc=j3j&bDTUjwiFWS=Zaa7OsA|5XXE(6vkYsY2bSlMk z1cd?<36KB@kigy|K>u%V%_r}M1W14coCN6qIcd1d)BkhFi_s)N0-a01KL60`2m>C@ zKfErTS?3Rian55}8TeVmsgoK`oD3?L=TB~*eJE!jwsRi~j{YO)a|I_*uHi?+Ih1bG z5fAtOWnKR|PMa*Nb@X0PCOEiD!#MvjfGWLEyZ3)by0G}${%?;uI8%P`0E;tyJJve- zR6iCNuA(rM5z}?Y5=W&56tXTTLjhBvcMW{UC zOe8=8BtQZ#2+;p^0gIs|KmsJtWd!K|dY>@*zuwugCJB&0XA$t>{V!srZEaOHO^5Tw zOg@{)>nje2PDbo|INnL~WZKN!xoBs`?93%QGj89aoZraxe>@S= zeja!w0TLhq66j6>^#8hZ-Z*L!AOR9+hXDP*b|~#0L;r7gF8r1RNTB-&^x|bHN}YlU zNdFJ7r;!P4vgufgdH$nNU?Kq$AORBCTLkF;?XCIb-H-qYkbsi_{XZuScX|4M?szep K1W2HB3H%G+FXH6@ literal 0 HcmV?d00001 diff --git a/lib/posh/.sconsign b/lib/posh/.sconsign new file mode 100644 index 0000000..387066f --- /dev/null +++ b/lib/posh/.sconsign @@ -0,0 +1,5 @@ +}q(Uposh.oq(cSCons.Sig +SConsignEntry +qoq}qUbsigqNsbUposh.cq(hoq}q (U timestampq +JòJ÷AUcsigq U ae041b30171cf19966a56aa96e84dbf5q ubUposh.hq (hoq}q(h +JòJ÷Ah U 344e05e117cefba48b55d94e48d861cbqubu. \ No newline at end of file diff --git a/lib/posh/posh.c b/lib/posh/posh.c new file mode 100644 index 0000000..a191528 --- /dev/null +++ b/lib/posh/posh.c @@ -0,0 +1,918 @@ +/* +LICENSE: + +Copyright (c) 2004, Brian Hook +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of this package'ss contributors contributors may not + be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/** + @file posh.c + @author Brian Hook + @date 2002 + @brief Portable Open Source Harness primary source file +*/ +#include "posh.h" + +#if !defined FORCE_DOXYGEN + +#if !defined POSH_NO_FLOAT +# define POSH_FLOAT_STRING "enabled" +#else +# define POSH_FLOAT_STRING "disabled" +#endif + +#if defined POSH_64BIT_INTEGER +# define POSH_64BIT_INTEGER_STRING "yes" +#else +# define POSH_64BIT_INTEGER_STRING "no" +#endif + +#if defined POSH_64BIT_POINTER +# define POSH_POINTER_STRING "64-bits" +#else +# define POSH_POINTER_STRING "32-bits" +#endif + +#if defined POSH_LITTLE_ENDIAN +# define IS_BIG_ENDIAN 0 + +# define NATIVE16 POSH_LittleU16 +# define NATIVE32 POSH_LittleU32 +# define NATIVE64 POSH_LittleU64 +# define FOREIGN16 POSH_BigU16 +# define FOREIGN32 POSH_BigU32 +# define FOREIGN64 POSH_BigU64 +#else +# define IS_BIG_ENDIAN 1 + +# define NATIVE16 POSH_BigU16 +# define NATIVE32 POSH_BigU32 +# define NATIVE64 POSH_BigU64 +# define FOREIGN16 POSH_LittleU16 +# define FOREIGN32 POSH_LittleU32 +# define FOREIGN64 POSH_LittleU64 +#endif /* POSH_LITTLE_ENDIAN */ + +static +int +s_testBigEndian( void ) +{ + union + { + posh_byte_t c[ 4 ]; + posh_u32_t i; + } u; + + u.i= 1; + + if ( u.c[ 0 ] == 1 ) + { + return 0; + } + return 1; +} + +static +const char * +s_testSerialization( void ) +{ + posh_byte_t serbuf[ 8 ]; + posh_u16_t tmp16; + posh_u32_t tmp32; + + /* 16-bit serialization */ + POSH_WriteU16ToLittle( serbuf, 0xABCD ); + if ( ( tmp16 = POSH_ReadU16FromLittle( serbuf ) ) != 0xABCD ) + { + return "*ERROR: failed little-endian 16-bit serialization test"; + } + + POSH_WriteU16ToBig( serbuf, 0xABCD ); + if ( ( tmp16 = POSH_ReadU16FromBig( serbuf ) ) != 0xABCD ) + { + return "*ERROR: failed big-endian 16-bit serialization test"; + } + + /* 32-bit serialization */ + POSH_WriteU32ToLittle( serbuf, 0xABCD1234L ); + if ( ( tmp32 = POSH_ReadU32FromLittle( serbuf ) ) != 0xABCD1234 ) + { + return "*ERROR: failed little-endian 32-bit serialization test"; + } + + POSH_WriteU32ToBig( serbuf, 0xABCD1234L ); + if ( ( tmp32 = POSH_ReadU32FromBig( serbuf ) ) != 0xABCD1234 ) + { + return "*ERROR: failed big-endian 32-bit serialization test"; + } + +#if defined POSH_64BIT_INTEGER + { +#define REF64 POSH_U64(0xFEDCBA9876543210) + + posh_u64_t tmp64; + + POSH_WriteU64ToLittle( serbuf, REF64 ); + + if ( ( tmp64 = POSH_ReadU64FromLittle( serbuf ) ) != REF64 ) + { + return "*ERROR: failed little-endian 64-bit serialization test"; + } + + POSH_WriteU64ToBig( serbuf, REF64 ); + + if ( ( tmp64 = POSH_ReadU64FromBig( serbuf ) ) != REF64 ) + { + return "*ERROR: failed big-endian 64-bit serialization test"; + } + } +#endif + + return 0; +} + +#if !defined POSH_NO_FLOAT +static +const char * +s_testFloatingPoint( void ) +{ + float fRef = 10.0f/30.0f; + double dRef = 10.0/30.0; + posh_byte_t dbuf[ 8 ]; + float fTmp; + double dTmp; + + fTmp = POSH_FloatFromLittleBits( POSH_LittleFloatBits( fRef ) ); + + if ( fTmp != fRef ) + { + return "*ERROR: POSH little endian floating point conversion failed. Please report this to poshlib@poshlib.org!\n"; + } + + fTmp = POSH_FloatFromBigBits( POSH_BigFloatBits( fRef ) ); + if ( fTmp != fRef ) + { + return "*ERROR: POSH big endian floating point conversion failed. Please report this to poshlib@poshlib.org!\n"; + } + + POSH_DoubleBits( dRef, dbuf ); + + dTmp = POSH_DoubleFromBits( dbuf ); + + if ( dTmp != dRef ) + { + return "*ERROR: POSH double precision floating point serialization failed. Please report this to poshlib@poshlib.org!\n"; + } + + return 0; +} +#endif /* !defined POSH_NO_FLOAT */ + +static +const char * +s_testEndianess( void ) +{ + /* check endianess */ + if ( s_testBigEndian() != IS_BIG_ENDIAN ) + { + return "*ERROR: POSH compile time endianess does not match run-time endianess verification. Please report this to poshlib@poshlib.org!\n"; + } + + /* make sure our endian swap routines work */ + if ( ( NATIVE32( 0x11223344L ) != 0x11223344L ) || + ( FOREIGN32( 0x11223344L ) != 0x44332211L ) || + ( NATIVE16( 0x1234 ) != 0x1234 ) || + ( FOREIGN16( 0x1234 ) != 0x3412 ) ) + { + return "*ERROR: POSH endianess macro selection failed. Please report this to poshlib@poshlib.org!\n"; + } + + /* test serialization routines */ + + return 0; +} +#endif /* !defined FORCE_DOXYGEN */ + +/** + Returns a string describing this platform's basic attributes. + + POSH_GetArchString() reports on an architecture's statically determined + attributes. In addition, it will perform run-time verification checks + to make sure the various platform specific functions work. If an error + occurs, please contact me at poshlib@poshlib.org so we can try to resolve + what the specific failure case is. + @returns a string describing this platform on success, or a string in the + form "*ERROR: [text]" on failure. You can simply check to see if + the first character returned is '*' to verify an error condition. +*/ +const char * +POSH_GetArchString( void ) +{ + const char *err; + const char *s = "OS:.............."POSH_OS_STRING"\n" + "CPU:............."POSH_CPU_STRING"\n" + "endian:.........."POSH_ENDIAN_STRING"\n" + "ptr size:........"POSH_POINTER_STRING"\n" + "64-bit ints......"POSH_64BIT_INTEGER_STRING"\n" + "floating point..."POSH_FLOAT_STRING"\n" + "compiler........."POSH_COMPILER_STRING"\n"; + + /* test endianess */ + err = s_testEndianess(); + + if ( err != 0 ) + { + return err; + } + + /* test serialization */ + err = s_testSerialization(); + + if ( err != 0 ) + { + return err; + } + +#if !defined POSH_NO_FLOAT + /* check that our floating point support is correct */ + err = s_testFloatingPoint(); + + if ( err != 0 ) + { + return err; + } + +#endif + + return s; +} + +/* ---------------------------------------------------------------------------*/ +/* BYTE SWAPPING SUPPORT */ +/* ---------------------------------------------------------------------------*/ +/** + * Byte swaps a 16-bit unsigned value + * + @param v [in] unsigned 16-bit input value to swap + @returns a byte swapped version of v + */ +posh_u16_t +POSH_SwapU16( posh_u16_t v ) +{ + posh_u16_t swapped; + + swapped = v << 8; + swapped |= v >> 8; + + return swapped; +} + +/** + * Byte swaps a 16-bit signed value + * + @param v [in] signed 16-bit input value to swap + @returns a byte swapped version of v + @remarks This just calls back to the unsigned version, since byte swapping + is independent of sign. However, we still provide this function to + avoid signed/unsigned mismatch compiler warnings. + */ +posh_i16_t +POSH_SwapI16( posh_i16_t v ) +{ + return ( posh_i16_t ) POSH_SwapU16( v ); +} + +/** + * Byte swaps a 32-bit unsigned value + * + @param v [in] unsigned 32-bit input value to swap + @returns a byte swapped version of v + */ +posh_u32_t +POSH_SwapU32( posh_u32_t v ) +{ + posh_u32_t swapped; + + swapped = ( v & 0xFF ) << 24; + swapped |= ( v & 0xFF00 ) << 8; + swapped |= ( v >> 8 ) & 0xFF00; + swapped |= ( v >> 24 ); + + return swapped; +} + +/** + * Byte swaps a 32-bit signed value + * + @param v [in] signed 32-bit input value to swap + @returns a byte swapped version of v + @remarks This just calls back to the unsigned version, since byte swapping + is independent of sign. However, we still provide this function to + avoid signed/unsigned mismatch compiler warnings. + */ +posh_i32_t +POSH_SwapI32( posh_i32_t v ) +{ + return ( posh_i32_t ) POSH_SwapU32( ( posh_u32_t ) v ); +} + +#if defined POSH_64BIT_INTEGER +/** + * Byte swaps a 64-bit unsigned value + + @param v [in] a 64-bit input value to swap + @ingroup SixtyFourBit + @returns a byte swapped version of v +*/ +posh_u64_t +POSH_SwapU64( posh_u64_t v ) +{ + posh_byte_t tmp; + union { + posh_byte_t bytes[ 8 ]; + posh_u64_t u64; + } u; + + u.u64 = v; + + tmp = u.bytes[ 0 ]; u.bytes[ 0 ] = u.bytes[ 7 ]; u.bytes[ 7 ] = tmp; + tmp = u.bytes[ 1 ]; u.bytes[ 1 ] = u.bytes[ 6 ]; u.bytes[ 6 ] = tmp; + tmp = u.bytes[ 2 ]; u.bytes[ 2 ] = u.bytes[ 5 ]; u.bytes[ 5 ] = tmp; + tmp = u.bytes[ 3 ]; u.bytes[ 3 ] = u.bytes[ 4 ]; u.bytes[ 4 ] = tmp; + + return u.u64; +} + +/** + * Byte swaps a 64-bit signed value + + @param v [in] a 64-bit input value to swap + @ingroup SixtyFourBit + @returns a byte swapped version of v +*/ +posh_i64_t +POSH_SwapI64( posh_i64_t v ) +{ + return ( posh_i64_t ) POSH_SwapU64( ( posh_u64_t ) v ); +} + +#endif /* defined POSH_64BIT_INTEGER */ + +/* ---------------------------------------------------------------------------*/ +/* IN-MEMORY SERIALIZATION */ +/* ---------------------------------------------------------------------------*/ + +/** + * Writes an unsigned 16-bit value to a little endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian unsigned 16-bit value + @returns a pointer to the location two bytes after dst + @remarks does no validation of the inputs +*/ +posh_u16_t * +POSH_WriteU16ToLittle( void *dst, posh_u16_t value ) +{ + posh_u16_t *p16 = ( posh_u16_t * ) dst; + + *p16 = POSH_LittleU16(value); + + return p16 + 1; +} + +/** + * Writes a signed 16-bit value to a little endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian signed 16-bit value + @returns a pointer to the location two bytes after dst + @remarks does no validation of the inputs. This simply calls + POSH_WriteU16ToLittle() with appropriate casting. +*/ +posh_i16_t * +POSH_WriteI16ToLittle( void *dst, posh_i16_t value ) +{ + return ( posh_i16_t * ) POSH_WriteU16ToLittle( dst, ( posh_u16_t ) value ); +} + +/** + * Writes an unsigned 32-bit value to a little endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian signed 32-bit value + @returns a pointer to the location four bytes after dst + @remarks does no validation of the inputs. +*/ +posh_u32_t * +POSH_WriteU32ToLittle( void *dst, posh_u32_t value ) +{ + posh_u32_t *p32 = ( posh_u32_t * ) dst; + + *p32 = POSH_LittleU32(value); + + return p32 + 1; +} + +/** + * Writes a signed 32-bit value to a little endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian signed 32-bit value + @returns a pointer to the location four bytes after dst + @remarks does no validation of the inputs. This simply calls + POSH_WriteU32ToLittle() with appropriate casting. +*/ +posh_i32_t * +POSH_WriteI32ToLittle( void *dst, posh_i32_t value ) +{ + return ( posh_i32_t * ) POSH_WriteU32ToLittle( dst, ( posh_u32_t ) value ); +} + +/** + * Writes an unsigned 16-bit value to a big endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian unsigned 16-bit value + @returns a pointer to the location two bytes after dst + @remarks does no validation of the inputs +*/ +posh_u16_t * +POSH_WriteU16ToBig( void *dst, posh_u16_t value ) +{ + posh_u16_t *p16 = ( posh_u16_t * ) dst; + + *p16 = POSH_BigU16(value); + + return p16 + 1; +} + +/** + * Writes a signed 16-bit value to a big endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian signed 16-bit value + @returns a pointer to the location two bytes after dst + @remarks does no validation of the inputs. This simply calls + POSH_WriteU16ToLittle() with appropriate casting. +*/ +posh_i16_t * +POSH_WriteI16ToBig( void *dst, posh_i16_t value ) +{ + return ( posh_i16_t * ) POSH_WriteU16ToBig( dst, ( posh_u16_t ) value ); +} + +/** + * Writes an unsigned 32-bit value to a big endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian unsigned 32-bit value + @returns a pointer to the location four bytes after dst + @remarks does no validation of the inputs. +*/ +posh_u32_t * +POSH_WriteU32ToBig( void *dst, posh_u32_t value ) +{ + posh_u32_t *p32 = ( posh_u32_t * ) dst; + + *p32 = POSH_BigU32(value); + + return p32 + 1; +} + +/** + * Writes a signed 32-bit value to a big endian buffer + + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian signed 32-bit value + @returns a pointer to the location four bytes after dst + @remarks does no validation of the inputs. This simply calls + POSH_WriteU32ToBig() with appropriate casting. +*/ +posh_i32_t * +POSH_WriteI32ToBig( void *dst, posh_i32_t value ) +{ + return ( posh_i32_t * ) POSH_WriteU32ToBig( dst, ( posh_u32_t ) value ); +} + +#if defined POSH_64BIT_INTEGER +/** + * Writes an unsigned 64-bit value to a little-endian buffer + + @ingroup SixtyFourBit + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian unsigned 64-bit value + @returns a pointer to the location eight bytes after dst + @remarks does no validation of the inputs. +*/ +posh_u64_t * +POSH_WriteU64ToLittle( void *dst, posh_u64_t value ) +{ + posh_u64_t *p64 = ( posh_u64_t * ) dst; + + *p64 = POSH_LittleU64(value); + + return p64 + 1; +} + +/** + * Writes a signed 64-bit value to a little-endian buffer + + @ingroup SixtyFourBit + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian unsigned 64-bit value + @returns a pointer to the location eight bytes after dst + @remarks does no validation of the inputs. +*/ +posh_i64_t * +POSH_WriteI64ToLittle( void *dst, posh_i64_t value ) +{ + return ( posh_i64_t * ) POSH_WriteU64ToLittle( dst, ( posh_u64_t ) value ); +} + +/** + * Writes an unsigned 64-bit value to a big-endian buffer + + @ingroup SixtyFourBit + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian unsigned 64-bit value + @returns a pointer to the location eight bytes after dst + @remarks does no validation of the inputs. +*/ +posh_u64_t * +POSH_WriteU64ToBig( void *dst, posh_u64_t value ) +{ + posh_u64_t *p64 = ( posh_u64_t * ) dst; + + *p64 = POSH_BigU64(value); + + return p64 + 8; +} + +/** + * Writes a signed 64-bit value to a big-endian buffer + + @ingroup SixtyFourBit + @param dst [out] pointer to the destination buffer, may not be NULL + @param value [in] host-endian signed 64-bit value + @returns a pointer to the location eight bytes after dst + @remarks does no validation of the inputs. +*/ +posh_i64_t * +POSH_WriteI64ToBig( void *dst, posh_i64_t value ) +{ + return ( posh_i64_t * ) POSH_WriteU64ToBig( dst, ( posh_u64_t ) value ); +} + +#endif /* POSH_64BIT_INTEGER */ + +/* ---------------------------------------------------------------------------*/ +/* IN-MEMORY DESERIALIZATION */ +/* ---------------------------------------------------------------------------*/ + +/** + * Reads an unsigned 16-bit value from a little-endian buffer + @param src [in] source buffer + @returns host-endian unsigned 16-bit value +*/ +posh_u16_t +POSH_ReadU16FromLittle( const void *src ) +{ + return POSH_LittleU16( (*(const posh_u16_t*)src) ); +} + +/** + * Reads a signed 16-bit value from a little-endian buffer + @param src [in] source buffer + @returns host-endian signed 16-bit value +*/ +posh_i16_t +POSH_ReadI16FromLittle( const void *src ) +{ + return POSH_LittleI16( (*(const posh_i16_t*)src) ); +} + +/** + * Reads an unsigned 32-bit value from a little-endian buffer + @param src [in] source buffer + @returns host-endian unsigned 32-bit value +*/ +posh_u32_t +POSH_ReadU32FromLittle( const void *src ) +{ + return POSH_LittleU32( (*(const posh_u32_t*)src) ); +} + +/** + * Reads a signed 32-bit value from a little-endian buffer + @param src [in] source buffer + @returns host-endian signed 32-bit value +*/ +posh_i32_t +POSH_ReadI32FromLittle( const void *src ) +{ + return POSH_LittleI32( (*(const posh_i32_t*)src) ); +} + + +/** + * Reads an unsigned 16-bit value from a big-endian buffer + @param src [in] source buffer + @returns host-endian unsigned 16-bit value +*/ +posh_u16_t +POSH_ReadU16FromBig( const void *src ) +{ + return POSH_BigU16( (*(const posh_u16_t*)src) ); +} + +/** + * Reads a signed 16-bit value from a big-endian buffer + @param src [in] source buffer + @returns host-endian signed 16-bit value +*/ +posh_i16_t +POSH_ReadI16FromBig( const void *src ) +{ + return POSH_BigI16( (*(const posh_i16_t*)src)); +} + +/** + * Reads an unsigned 32-bit value from a big-endian buffer + @param src [in] source buffer + @returns host-endian unsigned 32-bit value +*/ +posh_u32_t +POSH_ReadU32FromBig( const void *src ) +{ + return POSH_BigU32( (*(const posh_u32_t*)src) ); +} + +/** + * Reads a signed 32-bit value from a big-endian buffer + @param src [in] source buffer + @returns host-endian signed 32-bit value +*/ +posh_i32_t +POSH_ReadI32FromBig( const void *src ) +{ + return POSH_BigI32( (*(const posh_i32_t*)src ) ); +} + +#if defined POSH_64BIT_INTEGER + +/** + * Reads an unsigned 64-bit value from a little-endian buffer + @param src [in] source buffer + @returns host-endian unsigned 32-bit value +*/ +posh_u64_t +POSH_ReadU64FromLittle( const void *src ) +{ + return POSH_LittleU64( (*(const posh_u64_t*)src) ); +} + +/** + * Reads a signed 64-bit value from a little-endian buffer + @param src [in] source buffer + @returns host-endian signed 32-bit value +*/ +posh_i64_t +POSH_ReadI64FromLittle( const void *src ) +{ + return POSH_LittleI64( (*(const posh_i64_t*)src) ); +} + +/** + * Reads an unsigned 64-bit value from a big-endian buffer + @param src [in] source buffer + @returns host-endian unsigned 32-bit value +*/ +posh_u64_t +POSH_ReadU64FromBig( const void *src ) +{ + return POSH_BigU64( (*(const posh_u64_t*)src) ); +} + +/** + * Reads an signed 64-bit value from a big-endian buffer + @param src [in] source buffer + @returns host-endian signed 32-bit value +*/ +posh_i64_t +POSH_ReadI64FromBig( const void *src ) +{ + return POSH_BigI64( (*(const posh_i64_t*)src) ); +} + +#endif /* POSH_64BIT_INTEGER */ + +/* ---------------------------------------------------------------------------*/ +/* FLOATING POINT SUPPORT */ +/* ---------------------------------------------------------------------------*/ + +#if !defined POSH_NO_FLOAT + +/** @ingroup FloatingPoint + @param[in] f floating point value + @returns a little-endian bit representation of f + */ +posh_u32_t +POSH_LittleFloatBits( float f ) +{ + union + { + float f32; + posh_u32_t u32; + } u; + + u.f32 = f; + + return POSH_LittleU32( u.u32 ); +} + +/** + * Extracts raw big-endian bits from a 32-bit floating point value + * + @ingroup FloatingPoint + @param f [in] floating point value + @returns a big-endian bit representation of f + */ +posh_u32_t +POSH_BigFloatBits( float f ) +{ + union + { + float f32; + posh_u32_t u32; + } u; + + u.f32 = f; + + return POSH_BigU32( u.u32 ); +} + +/** + * Extracts raw, little-endian bit representation from a 64-bit double. + * + @param d [in] 64-bit double precision value + @param dst [out] 8-byte storage buffer + @ingroup FloatingPoint + @returns the raw bits used to represent the value 'd', in the form dst[0]=LSB + */ +void +POSH_DoubleBits( double d, posh_byte_t dst[ 8 ] ) +{ + union + { + double d64; + posh_byte_t bytes[ 8 ]; + } u; + + u.d64 = d; + +#if defined POSH_LITTLE_ENDIAN + dst[ 0 ] = u.bytes[ 0 ]; + dst[ 1 ] = u.bytes[ 1 ]; + dst[ 2 ] = u.bytes[ 2 ]; + dst[ 3 ] = u.bytes[ 3 ]; + dst[ 4 ] = u.bytes[ 4 ]; + dst[ 5 ] = u.bytes[ 5 ]; + dst[ 6 ] = u.bytes[ 6 ]; + dst[ 7 ] = u.bytes[ 7 ]; +#else + dst[ 0 ] = u.bytes[ 7 ]; + dst[ 1 ] = u.bytes[ 6 ]; + dst[ 2 ] = u.bytes[ 5 ]; + dst[ 3 ] = u.bytes[ 4 ]; + dst[ 4 ] = u.bytes[ 3 ]; + dst[ 5 ] = u.bytes[ 2 ]; + dst[ 6 ] = u.bytes[ 1 ]; + dst[ 7 ] = u.bytes[ 0 ]; +#endif +} + +/** + * Creates a double-precision, 64-bit floating point value from a set of raw, + * little-endian bits + + @ingroup FloatingPoint + @param src [in] little-endian byte representation of 64-bit double precision + floating point value + @returns double precision floating point representation of the raw bits + @remarks No error checking is performed, so there are no guarantees that the + result is a valid number, nor is there any check to ensure that src is + non-NULL. BE CAREFUL USING THIS. + */ +double +POSH_DoubleFromBits( const posh_byte_t src[ 8 ] ) +{ + union + { + double d64; + posh_byte_t bytes[ 8 ]; + } u; + +#if defined POSH_LITTLE_ENDIAN + u.bytes[ 0 ] = src[ 0 ]; + u.bytes[ 1 ] = src[ 1 ]; + u.bytes[ 2 ] = src[ 2 ]; + u.bytes[ 3 ] = src[ 3 ]; + u.bytes[ 4 ] = src[ 4 ]; + u.bytes[ 5 ] = src[ 5 ]; + u.bytes[ 6 ] = src[ 6 ]; + u.bytes[ 7 ] = src[ 7 ]; +#else + u.bytes[ 0 ] = src[ 7 ]; + u.bytes[ 1 ] = src[ 6 ]; + u.bytes[ 2 ] = src[ 5 ]; + u.bytes[ 3 ] = src[ 4 ]; + u.bytes[ 4 ] = src[ 3 ]; + u.bytes[ 5 ] = src[ 2 ]; + u.bytes[ 6 ] = src[ 1 ]; + u.bytes[ 7 ] = src[ 0 ]; +#endif + + return u.d64; +} + +/** + * Creates a floating point number from little endian bits + * + @ingroup FloatingPoint + @param bits [in] raw floating point bits in little-endian form + @returns a floating point number based on the given bit representation + @remarks No error checking is performed, so there are no guarantees that the + result is a valid number. BE CAREFUL USING THIS. + */ +float +POSH_FloatFromLittleBits( posh_u32_t bits ) +{ + union + { + float f32; + posh_u32_t u32; + } u; + + u.u32 = bits; +#if defined POSH_BIG_ENDIAN + u.u32 = POSH_SwapU32( u.u32 ); +#endif + + return u.f32; +} + +/** + * Creates a floating point number from big-endian bits + * + @ingroup FloatingPoint + @param bits [in] raw floating point bits in big-endian form + @returns a floating point number based on the given bit representation + @remarks No error checking is performed, so there are no guarantees that the + result is a valid number. BE CAREFUL USING THIS. + */ +float +POSH_FloatFromBigBits( posh_u32_t bits ) +{ + union + { + float f32; + posh_u32_t u32; + } u; + + u.u32 = bits; +#if defined POSH_LITTLE_ENDIAN + u.u32 = POSH_SwapU32( u.u32 ); +#endif + + return u.f32; +} + +#endif /* !defined POSH_NO_FLOAT */ diff --git a/lib/posh/posh.h b/lib/posh/posh.h new file mode 100644 index 0000000..0b71f11 --- /dev/null +++ b/lib/posh/posh.h @@ -0,0 +1,998 @@ +/** +@file posh.h +@author Brian Hook + +Header file for POSH, the Portable Open Source Harness project. + +NOTE: Unlike most header files, this one is designed to be included +multiple times, which is why it does not have the @#ifndef/@#define +preamble. + +POSH relies on environment specified preprocessor symbols in order +to infer as much as possible about the target OS/architecture and +the host compiler capabilities. + +NOTE: POSH is simple and focused. It attempts to provide basic +functionality and information, but it does NOT attempt to emulate +missing functionality. I am also not willing to make POSH dirty +and hackish to support truly ancient and/or outmoded and/or bizarre +technologies such as non-ANSI compilers, systems with non-IEEE +floating point formats, segmented 16-bit operating systems, etc. + +Please refer to the accompanying HTML documentation or visit +http://www.poshlib.org for more information on how to use POSH. + +LICENSE: + +Copyright (c) 2004, Brian Hook +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of this package'ss contributors contributors may not + be used to endorse or promote products derived from this + software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ +/* +I have yet to find an authoritative reference on preprocessor +symbols, but so far this is what I've gleaned: + +GNU GCC/G++: + - __GNUC__: GNU C version + - __GNUG__: GNU C++ compiler + - __sun__ : on Sun platforms + - __svr4__: on Solaris and other SysV R4 platforms + - __mips__: on MIPS processor platforms + - __sparc_v9__: on Sparc 64-bit CPUs + - __sparcv9: 64-bit Solaris + - __MIPSEL__: mips processor, compiled for little endian + - __MIPSEB__: mips processor, compiled for big endian + - _R5900: MIPS/Sony/Toshiba R5900 (PS2) + - mc68000: 68K + - m68000: 68K + - m68k: 68K + - __palmos__: PalmOS + +Intel C/C++ Compiler: + - __ECC : compiler version, IA64 only + - __EDG__ + - __ELF__ + - __GXX_ABI_VERSION + - __i386 : IA-32 only + - __i386__ : IA-32 only + - i386 : IA-32 only + - __ia64 : IA-64 only + - __ia64__ : IA-64 only + - ia64 : IA-64 only + - __ICC : IA-32 only + - __INTEL_COMPILER : IA-32 or IA-64, newer versions only + +Apple's C/C++ Compiler for OS X: + - __APPLE_CC__ + - __APPLE__ + - __BIG_ENDIAN__ + - __APPLE__ + - __ppc__ + - __MACH__ + +DJGPP: + - __MSDOS__ + - __unix__ + - __unix + - __GNUC__ + - __GO32 + - DJGPP + - __i386, __i386, i386 + +Cray's C compiler: + - _ADDR64: if 64-bit pointers + - _UNICOS: + - __unix: + +SGI's CC compiler predefines the following (and more) with -ansi: + - __sgi + - __unix + - __host_mips + - _SYSTYPE_SVR4 + - __mips + - _MIPSEB + - anyone know if there is a predefined symbol for the compiler?! + +MinGW: + - as GnuC but also defines _WIN32, __WIN32, WIN32, _X86_, __i386, __i386__, and several others + - __MINGW32__ + +Cygwin: + - as Gnu C, but also + - __unix__ + - __CYGWIN32__ + +Microsoft Visual Studio predefines the following: + - _MSC_VER + - _WIN32: on Win32 + - _M_IX6 (on x86 systems) + - _M_ALPHA (on DEC AXP systems) + - _SH3: WinCE, Hitachi SH-3 + - _MIPS: WinCE, MIPS + - _ARM: WinCE, ARM + +Sun's C Compiler: + - sun and _sun + - unix and _unix + - sparc and _sparc (SPARC systems only) + - i386 and _i386 (x86 systems only) + - __SVR4 (Solaris only) + - __sparcv9: 64-bit solaris + - __SUNPRO_C + - _LP64: defined in 64-bit LP64 mode, but only if is included + +Borland C/C++ predefines the following: + - __BORLANDC__: + +DEC/Compaq C/C++ on Alpha: + - __alpha + - __arch64__ + - __unix__ (on Tru64 Unix) + - __osf__ + - __DECC + - __DECCXX (C++ compilation) + - __DECC_VER + - __DECCXX_VER + +IBM's AIX compiler: + - __64BIT__ if 64-bit mode + - _AIX + - __IBMC__: C compiler version + - __IBMCPP__: C++ compiler version + - _LONG_LONG: compiler allows long long + +Watcom: + - __WATCOMC__ + - __DOS__ : if targeting DOS + - __386__ : if 32-bit support + - __WIN32__ : if targetin 32-bit Windows + +HP-UX C/C++ Compiler: + - __hpux + - __unix + - __hppa (on PA-RISC) + - __LP64__: if compiled in 64-bit mode + +Metrowerks: + - __MWERKS__ + - __powerpc__ + - _powerc + - __MC68K__ + - macintosh when compiling for MacOS + - __INTEL__ for x86 targets + - __POWERPC__ + +*/ + +#ifndef HAVE_POSH_H +#define HAVE_POSH_H + +/* +** ---------------------------------------------------------------------------- +** Include optionally +** ---------------------------------------------------------------------------- +*/ +#ifdef POSH_USE_LIMITS_H +# include +#endif + +/* +** ---------------------------------------------------------------------------- +** Determine compilation environment +** ---------------------------------------------------------------------------- +*/ +#if defined __ECC || defined __ICC || defined __INTEL_COMPILER +# define POSH_COMPILER_STRING "Intel C/C++" +# define POSH_COMPILER_INTEL 1 +#endif + +#if ( defined __host_mips || defined __sgi ) && !defined __GNUC__ +# define POSH_COMPILER_STRING "MIPSpro C/C++" +# define POSH_COMPILER_MIPSPRO 1 +#endif + +#if defined __hpux && !defined __GNUC__ +# define POSH_COMPILER_STRING "HP-UX CC" +# define POSH_COMPILER_HPCC 1 +#endif + +#if defined __GNUC__ +# define POSH_COMPILER_STRING "Gnu GCC" +# define POSH_COMPILER_GCC 1 +#endif + +#if defined __APPLE_CC__ + /* we don't define the compiler string here, let it be GNU */ +# define POSH_COMPILER_APPLECC 1 +#endif + +#if defined __IBMC__ || defined __IBMCPP__ +# define POSH_COMPILER_STRING "IBM C/C++" +# define POSH_COMPILER_IBM 1 +#endif + +#if defined _MSC_VER +# define POSH_COMPILER_STRING "Microsoft Visual C++" +# define POSH_COMPILER_MSVC 1 +#endif + +#if defined __SUNPRO_C +# define POSH_COMPILER_STRING "Sun Pro" +# define POSH_COMPILER_SUN 1 +#endif + +#if defined __BORLANDC__ +# define POSH_COMPILER_STRING "Borland C/C++" +# define POSH_COMPILER_BORLAND 1 +#endif + +#if defined __MWERKS__ +# define POSH_COMPILER_STRING "MetroWerks CodeWarrior" +# define POSH_COMPILER_METROWERKS 1 +#endif + +#if defined __DECC || defined __DECCXX +# define POSH_COMPILER_STRING "Compaq/DEC C/C++" +# define POSH_COMPILER_DEC 1 +#endif + +#if defined __WATCOMC__ +# define POSH_COMPILER_STRING "Watcom C/C++" +# define POSH_COMPILER_WATCOM 1 +#endif + +#if !defined POSH_COMPILER_STRING +# define POSH_COMPILER_STRING "Unknown compiler" +#endif + +/* +** ---------------------------------------------------------------------------- +** Determine target operating system +** ---------------------------------------------------------------------------- +*/ +#if defined linux || defined __linux__ +# define POSH_OS_LINUX 1 +# define POSH_OS_STRING "Linux" +#endif + +#if defined __CYGWIN32__ +# define POSH_OS_CYGWIN32 1 +# define POSH_OS_STRING "Cygwin" +#endif + +#if defined GEKKO +# define POSH_OS_GAMECUBE +# define __powerpc__ +# define POSH_OS_STRING "GameCube" +#endif + +#if defined __MINGW32__ +# define POSH_OS_MINGW 1 +# define POSH_OS_STRING "MinGW" +#endif + +#if defined GO32 && defined DJGPP && defined __MSDOS__ +# define POSH_OS_GO32 1 +# define POSH_OS_STRING "GO32/MS-DOS" +#endif + +/* NOTE: make sure you use /bt=DOS if compiling for 32-bit DOS, + otherwise Watcom assumes host=target */ +#if defined __WATCOMC__ && defined __386__ && defined __DOS__ +# define POSH_OS_DOS32 1 +# define POSH_OS_STRING "DOS/32-bit" +#endif + +#if defined _UNICOS +# define POSH_OS_UNICOS 1 +# define POSH_OS_STRING "UNICOS" +#endif + +#if ( defined __MWERKS__ && defined __powerc && !defined macintosh ) || defined __APPLE_CC__ || defined macosx +# define POSH_OS_OSX 1 +# define POSH_OS_STRING "MacOS X" +#endif + +#if defined __sun__ || defined sun || defined __sun || defined __solaris__ +# if defined __SVR4 || defined __svr4__ || defined __solaris__ +# define POSH_OS_STRING "Solaris" +# define POSH_OS_SOLARIS 1 +# endif +# if !defined POSH_OS_STRING +# define POSH_OS_STRING "SunOS" +# define POSH_OS_SUNOS 1 +# endif +#endif + +#if defined __sgi__ || defined sgi || defined __sgi +# define POSH_OS_IRIX 1 +# define POSH_OS_STRING "Irix" +#endif + +#if defined __hpux__ || defined __hpux +# define POSH_OS_HPUX 1 +# define POSH_OS_STRING "HP-UX" +#endif + +#if defined _AIX +# define POSH_OS_AIX 1 +# define POSH_OS_STRING "AIX" +#endif + +#if ( defined __alpha && defined __osf__ ) +# define POSH_OS_TRU64 1 +# define POSH_OS_STRING "Tru64" +#endif + +#if defined __BEOS__ || defined __beos__ +# define POSH_OS_BEOS 1 +# define POSH_OS_STRING "BeOS" +#endif + +#if defined amiga || defined amigados || defined AMIGA || defined _AMIGA +# define POSH_OS_AMIGA 1 +# define POSH_OS_STRING "Amiga" +#endif + +#if defined __unix__ +# define POSH_OS_UNIX 1 +# if !defined POSH_OS_STRING +# define POSH_OS_STRING "Unix-like(generic)" +# endif +#endif + +#if defined _WIN32_WCE +# define POSH_OS_WINCE 1 +# define POSH_OS_STRING "Windows CE" +#endif + +#if defined _XBOX +# define POSH_OS_XBOX 1 +# define POSH_OS_STRING "XBOX" +#endif + +#if defined _WIN32 || defined WIN32 || defined __NT__ || defined __WIN32__ +# define POSH_OS_WIN32 1 +# if !defined POSH_OS_XBOX +# if defined _WIN64 +# define POSH_OS_WIN64 1 +# define POSH_OS_STRING "Win64" +# else +# if !defined POSH_OS_STRING +# define POSH_OS_STRING "Win32" +# endif +# endif +# endif +#endif + +#if defined __palmos__ +# define POSH_OS_PALM 1 +# define POSH_OS_STRING "PalmOS" +#endif + +#if defined THINK_C || defined macintosh +# define POSH_OS_MACOS 1 +# define POSH_OS_STRING "MacOS" +#endif + +/* +** ----------------------------------------------------------------------------- +** Determine target CPU +** ----------------------------------------------------------------------------- +*/ + +#if defined GEKKO +# define POSH_CPU_PPC750 1 +# define POSH_CPU_STRING "IBM PowerPC 750 (NGC)" +#endif + +#if defined mc68000 || defined m68k || defined __MC68K__ || defined m68000 +# define POSH_CPU_68K 1 +# define POSH_CPU_STRING "MC68000" +#endif + +#if defined __PPC__ || defined __POWERPC__ || defined powerpc || defined _POWER || defined __ppc__ || defined __powerpc__ +# define POSH_CPU_PPC 1 +# if !defined POSH_CPU_STRING +# if defined __powerpc64__ +# define POSH_CPU_STRING "PowerPC64" +# else +# define POSH_CPU_STRING "PowerPC" +# endif +# endif +#endif + +#if defined _CRAYT3E || defined _CRAYMPP +# define POSH_CPU_CRAYT3E 1 /* target processor is a DEC Alpha 21164 used in a Cray T3E*/ +# define POSH_CPU_STRING "Cray T3E (Alpha 21164)" +#endif + +#if defined CRAY || defined _CRAY && !defined _CRAYT3E +# error Non-AXP Cray systems not supported +#endif + +#if defined _SH3 +# define POSH_CPU_SH3 1 +# define POSH_CPU_STRING "Hitachi SH-3" +#endif + +#if defined __sh4__ || defined __SH4__ +# define POSH_CPU_SH3 1 +# define POSH_CPU_SH4 1 +# define POSH_CPU_STRING "Hitachi SH-4" +#endif + +#if defined __sparc__ || defined __sparc +# if defined __arch64__ || defined __sparcv9 || defined __sparc_v9__ +# define POSH_CPU_SPARC64 1 +# define POSH_CPU_STRING "Sparc/64" +# else +# define POSH_CPU_STRING "Sparc/32" +# endif +# define POSH_CPU_SPARC 1 +#endif + +#if defined ARM || defined __arm__ || defined _ARM +# define POSH_CPU_STRONGARM 1 +# define POSH_CPU_STRING "ARM" +#endif + +#if defined mips || defined __mips__ || defined __MIPS__ || defined _MIPS +# define POSH_CPU_MIPS 1 +# if defined _R5900 +# define POSH_CPU_STRING "MIPS R5900 (PS2)" +# else +# define POSH_CPU_STRING "MIPS" +# endif +#endif + +#if defined __ia64 || defined _M_IA64 || defined __ia64__ +# define POSH_CPU_IA64 1 +# define POSH_CPU_STRING "IA64" +#endif + +#if defined __X86__ || defined __i386__ || defined i386 || defined _M_IX86 || defined __386__ || defined __x86_64__ +# define POSH_CPU_X86 1 +# if defined __x86_64__ +# define POSH_CPU_X86_64 1 +# endif +# if defined POSH_CPU_X86_64 +# define POSH_CPU_STRING "AMD x86-64" +# else +# define POSH_CPU_STRING "Intel 386+" +# endif +#endif + +#if defined __alpha || defined alpha || defined _M_ALPHA || defined __alpha__ +# define POSH_CPU_AXP 1 +# define POSH_CPU_STRING "AXP" +#endif + +#if defined __hppa || defined hppa +# define POSH_CPU_HPPA 1 +# define POSH_CPU_STRING "PA-RISC" +#endif + +#if !defined POSH_CPU_STRING +# error POSH cannot determine target CPU +# define POSH_CPU_STRING "Unknown" /* this is here for Doxygen's benefit */ +#endif + +/* +** ----------------------------------------------------------------------------- +** Attempt to autodetect building for embedded on Sony PS2 +** ----------------------------------------------------------------------------- +*/ +#if !defined POSH_OS_STRING +# if !defined FORCE_DOXYGEN +# define POSH_OS_EMBEDDED 1 +# endif +# if defined _R5900 +# define POSH_OS_STRING "Sony PS2(embedded)" +# else +# define POSH_OS_STRING "Embedded/Unknown" +# endif +#endif + +/* +** --------------------------------------------------------------------------- +** Handle cdecl, stdcall, fastcall, etc. +** --------------------------------------------------------------------------- +*/ +#if defined POSH_CPU_X86 && !defined POSH_CPU_X86_64 +# if defined __GNUC__ +# define POSH_CDECL __attribute__((cdecl)) +# define POSH_STDCALL __attribute__((stdcall)) +# define POSH_FASTCALL __attribute__((fastcall)) +# elif ( defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ || defined __MWERKS__ ) +# define POSH_CDECL __cdecl +# define POSH_STDCALL __stdcall +# define POSH_FASTCALL __fastcall +# endif +#else +# define POSH_CDECL +# define POSH_STDCALL +# define POSH_FASTCALL +#endif + +/* +** --------------------------------------------------------------------------- +** Define POSH_IMPORTEXPORT signature based on POSH_DLL and POSH_BUILDING_LIB +** --------------------------------------------------------------------------- +*/ + +/* +** We undefine this so that multiple inclusions will work +*/ +#if defined POSH_IMPORTEXPORT +# undef POSH_IMPORTEXPORT +#endif + +#if defined POSH_DLL +# if defined POSH_OS_WIN32 +# if defined _MSC_VER +# if ( _MSC_VER >= 800 ) +# if defined POSH_BUILDING_LIB +# define POSH_IMPORTEXPORT __declspec( dllexport ) +# else +# define POSH_IMPORTEXPORT __declspec( dllimport ) +# endif +# else +# if defined POSH_BUILDING_LIB +# define POSH_IMPORTEXPORT __export +# else +# define POSH_IMPORTEXPORT +# endif +# endif +# endif /* defined _MSC_VER */ +# if defined __BORLANDC__ +# if ( __BORLANDC__ >= 0x500 ) +# if defined POSH_BUILDING_LIB +# define POSH_IMPORTEXPORT __declspec( dllexport ) +# else +# define POSH_IMPORTEXPORT __declspec( dllimport ) +# endif +# else +# if defined POSH_BUILDING_LIB +# define POSH_IMPORTEXPORT __export +# else +# define POSH_IMPORTEXPORT +# endif +# endif +# endif /* defined __BORLANDC__ */ + /* for all other compilers, we're just making a blanket assumption */ +# if defined __GNUC__ || defined __WATCOMC__ || defined __MWERKS__ +# if defined POSH_BUILDING_LIB +# define POSH_IMPORTEXPORT __declspec( dllexport ) +# else +# define POSH_IMPORTEXPORT __declspec( dllimport ) +# endif +# endif /* all other compilers */ +# if !defined POSH_IMPORTEXPORT +# error Building DLLs not supported on this compiler (poshlib@poshlib.org if you know how) +# endif +# endif /* defined POSH_OS_WIN32 */ +#endif + +/* On pretty much everything else, we can thankfully just ignore this */ +#if !defined POSH_IMPORTEXPORT +# define POSH_IMPORTEXPORT +#endif + +#if defined FORCE_DOXYGEN +# define POSH_DLL +# define POSH_BUILDING_LIB +# undef POSH_DLL +# undef POSH_BUILDING_LIB +#endif + +/* +** ---------------------------------------------------------------------------- +** (Re)define POSH_PUBLIC_API export signature +** ---------------------------------------------------------------------------- +*/ +#ifdef POSH_PUBLIC_API +# undef POSH_PUBLIC_API +#endif + +#if ( ( defined _MSC_VER ) && ( _MSC_VER < 800 ) ) || ( defined __BORLANDC__ && ( __BORLANDC__ < 0x500 ) ) +# define POSH_PUBLIC_API(rtype) extern rtype POSH_IMPORTEXPORT +#else +# define POSH_PUBLIC_API(rtype) extern POSH_IMPORTEXPORT rtype +#endif + +/* +** ---------------------------------------------------------------------------- +** Try to infer endianess. Basically we just go through the CPUs we know are +** little endian, and assume anything that isn't one of those is big endian. +** As a sanity check, we also do this with operating systems we know are +** little endian, such as Windows. Some processors are bi-endian, such as +** the MIPS series, so we have to be careful about those. +** ---------------------------------------------------------------------------- +*/ +#if defined POSH_CPU_X86 || defined POSH_CPU_AXP || defined POSH_CPU_STRONGARM || defined POSH_OS_WIN32 || defined POSH_OS_WINCE || defined __MIPSEL__ +# define POSH_ENDIAN_STRING "little" +# define POSH_LITTLE_ENDIAN 1 +#else +# define POSH_ENDIAN_STRING "big" +# define POSH_BIG_ENDIAN 1 +#endif + +#if defined FORCE_DOXYGEN +# define POSH_LITTLE_ENDIAN +#endif + +/* +** ---------------------------------------------------------------------------- +** Cross-platform compile time assertion macro +** ---------------------------------------------------------------------------- +*/ +#define POSH_COMPILE_TIME_ASSERT(name, x) typedef int _POSH_dummy_ ## name[(x) ? 1 : -1 ] + +/* +** ---------------------------------------------------------------------------- +** 64-bit Integer +** +** We don't require 64-bit support, nor do we emulate its functionality, we +** simply export it if it's available. Since we can't count on +** for 64-bit support, we ignore the POSH_USE_LIMITS_H directive. +** ---------------------------------------------------------------------------- +*/ +#if defined ( __LP64__ ) || defined ( __powerpc64__ ) || defined POSH_CPU_SPARC64 +# define POSH_64BIT_INTEGER 1 +typedef long posh_i64_t; +typedef unsigned long posh_u64_t; +# define POSH_I64( x ) ((posh_i64_t)x) +# define POSH_U64( x ) ((posh_u64_t)x) +# define POSH_I64_PRINTF_PREFIX "l" +#elif defined _MSC_VER || defined __BORLANDC__ || defined __WATCOMC__ || ( defined __alpha && defined __DECC ) +# define POSH_64BIT_INTEGER 1 +typedef __int64 posh_i64_t; +typedef unsigned __int64 posh_u64_t; +# define POSH_I64( x ) ((posh_i64_t)x) +# define POSH_U64( x ) ((posh_u64_t)x) +# define POSH_I64_PRINTF_PREFIX "I64" +#elif defined __GNUC__ || defined __MWERKS__ || defined __SUNPRO_C || defined __SUNPRO_CC || defined __APPLE_CC__ || defined POSH_OS_IRIX || defined _LONG_LONG || defined _CRAYC +# define POSH_64BIT_INTEGER 1 +typedef long long posh_i64_t; +typedef unsigned long long posh_u64_t; +# define POSH_U64( x ) ((posh_u64_t)(x##LL)) +# define POSH_I64( x ) ((posh_i64_t)(x##LL)) +# define POSH_I64_PRINTF_PREFIX "ll" +#endif + +/* hack */ +#ifdef __MINGW32__ +#undef POSH_I64 +#undef POSH_U64 +#undef POSH_I64_PRINTF_PREFIX +#define POSH_I64( x ) ((posh_i64_t)x) +#define POSH_U64( x ) ((posh_u64_t)x) +#define POSH_I64_PRINTF_PREFIX "I64" +#endif + +#ifdef FORCE_DOXYGEN +typedef long long posh_i64_t; +typedef unsigned long posh_u64_t; +# define POSH_64BIT_INTEGER +# define POSH_I64_PRINTF_PREFIX +# define POSH_I64(x) +# define POSH_U64(x) +#endif + +/** Minimum value for a 64-bit signed integer */ +#define POSH_I64_MIN POSH_I64(0x8000000000000000) +/** Maximum value for a 64-bit signed integer */ +#define POSH_I64_MAX POSH_I64(0x7FFFFFFFFFFFFFFF) +/** Minimum value for a 64-bit unsigned integer */ +#define POSH_U64_MIN POSH_U64(0) +/** Maximum value for a 64-bit unsigned integer */ +#define POSH_U64_MAX POSH_U64(0xFFFFFFFFFFFFFFFF) + +/* ---------------------------------------------------------------------------- +** Basic Sized Types +** +** These types are expected to be EXACTLY sized so you can use them for +** serialization. +** ---------------------------------------------------------------------------- +*/ +#define POSH_FALSE 0 +#define POSH_TRUE 1 + +typedef int posh_bool_t; +typedef unsigned char posh_byte_t; + +/* NOTE: These assume that CHAR_BIT is 8!! */ +typedef unsigned char posh_u8_t; +typedef signed char posh_i8_t; + +#if defined POSH_USE_LIMITS_H +# if CHAR_BITS > 8 +# error This machine uses 9-bit characters. This is a warning, you can comment this out now. +# endif /* CHAR_BITS > 8 */ + +/* 16-bit */ +# if ( USHRT_MAX == 65535 ) + typedef unsigned short posh_u16_t; + typedef short posh_i16_t; +# else + /* Yes, in theory there could still be a 16-bit character type and shorts are + 32-bits in size...if you find such an architecture, let me know =P */ +# error No 16-bit type found +# endif + +/* 32-bit */ +# if ( INT_MAX == 2147483647 ) + typedef unsigned posh_u32_t; + typedef int posh_i32_t; +# elif ( LONG_MAX == 2147483647 ) + typedef unsigned long posh_u32_t; + typedef long posh_i32_t; +# else + error No 32-bit type found +# endif + +#else /* POSH_USE_LIMITS_H */ + + typedef unsigned short posh_u16_t; + typedef short posh_i16_t; + +# if !defined POSH_OS_PALM + typedef unsigned posh_u32_t; + typedef int posh_i32_t; +# else + typedef unsigned long posh_u32_t; + typedef long posh_i32_t; +# endif +#endif + +/** Minimum value for a byte */ +#define POSH_BYTE_MIN 0 +/** Maximum value for an 8-bit unsigned value */ +#define POSH_BYTE_MAX 255 +/** Minimum value for a byte */ +#define POSH_I16_MIN ( ( posh_i16_t ) 0x8000 ) +/** Maximum value for a 16-bit signed value */ +#define POSH_I16_MAX ( ( posh_i16_t ) 0x7FFF ) +/** Minimum value for a 16-bit unsigned value */ +#define POSH_U16_MIN 0 +/** Maximum value for a 16-bit unsigned value */ +#define POSH_U16_MAX ( ( posh_u16_t ) 0xFFFF ) +/** Minimum value for a 32-bit signed value */ +#define POSH_I32_MIN ( ( posh_i32_t ) 0x80000000 ) +/** Maximum value for a 32-bit signed value */ +#define POSH_I32_MAX ( ( posh_i32_t ) 0x7FFFFFFF ) +/** Minimum value for a 32-bit unsigned value */ +#define POSH_U32_MIN 0 +/** Maximum value for a 32-bit unsigned value */ +#define POSH_U32_MAX ( ( posh_u32_t ) 0xFFFFFFFF ) + +/* +** ---------------------------------------------------------------------------- +** Sanity checks on expected sizes +** ---------------------------------------------------------------------------- +*/ +#if !defined FORCE_DOXYGEN + +POSH_COMPILE_TIME_ASSERT(posh_byte_t, sizeof(posh_byte_t) == 1); +POSH_COMPILE_TIME_ASSERT(posh_u8_t, sizeof(posh_u8_t) == 1); +POSH_COMPILE_TIME_ASSERT(posh_i8_t, sizeof(posh_i8_t) == 1); +POSH_COMPILE_TIME_ASSERT(posh_u16_t, sizeof(posh_u16_t) == 2); +POSH_COMPILE_TIME_ASSERT(posh_i16_t, sizeof(posh_i16_t) == 2); +POSH_COMPILE_TIME_ASSERT(posh_u32_t, sizeof(posh_u32_t) == 4); +POSH_COMPILE_TIME_ASSERT(posh_i32_t, sizeof(posh_i32_t) == 4); + +#if !defined POSH_NO_FLOAT + POSH_COMPILE_TIME_ASSERT(posh_testfloat_t, sizeof(float)==4 ); + POSH_COMPILE_TIME_ASSERT(posh_testdouble_t, sizeof(double)==8); +#endif + +#if defined POSH_64BIT_INTEGER + POSH_COMPILE_TIME_ASSERT(posh_u64_t, sizeof(posh_u64_t) == 8); + POSH_COMPILE_TIME_ASSERT(posh_i64_t, sizeof(posh_i64_t) == 8); +#endif + +#endif + +/* +** ---------------------------------------------------------------------------- +** 64-bit pointer support +** ---------------------------------------------------------------------------- +*/ +#if defined POSH_CPU_AXP && ( defined POSH_OS_TRU64 || defined POSH_OS_LINUX ) +# define POSH_64BIT_POINTER 1 +#endif + +#if defined POSH_CPU_X86_64 && defined POSH_OS_LINUX +# define POSH_64BIT_POINTER 1 +#endif + +#if defined POSH_CPU_SPARC64 || defined POSH_OS_WIN64 || defined __64BIT__ || defined __LP64 || defined _LP64 || defined __LP64__ || defined _ADDR64 || defined _CRAYC +# define POSH_64BIT_POINTER 1 +#endif + +#if defined POSH_64BIT_POINTER + POSH_COMPILE_TIME_ASSERT( posh_64bit_pointer, sizeof( void * ) == 8 ); +#elif !defined FORCE_DOXYGEN +/* if this assertion is hit then you're on a system that either has 64-bit + addressing and we didn't catch it, or you're on a system with 16-bit + pointers. In the latter case, POSH doesn't actually care, we're just + triggering this assertion to make sure you're aware of the situation, + so feel free to delete it. + + If this assertion is triggered on a known 32 or 64-bit platform, + please let us know (poshlib@poshlib.org) */ + POSH_COMPILE_TIME_ASSERT( posh_32bit_pointer, sizeof( void * ) == 4 ); +#endif + +#if defined FORCE_DOXYGEN +# define POSH_64BIT_POINTER +#endif + +/* +** ---------------------------------------------------------------------------- +** POSH Utility Functions +** +** These are optional POSH utility functions that are not required if you don't +** need anything except static checking of your host and target environment. +** +** These functions are NOT wrapped with POSH_PUBLIC_API because I didn't want +** to enforce their export if your own library is only using them internally. +** ---------------------------------------------------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +const char *POSH_GetArchString( void ); + +#if !defined POSH_NO_FLOAT + +posh_u32_t POSH_LittleFloatBits( float f ); +posh_u32_t POSH_BigFloatBits( float f ); +float POSH_FloatFromLittleBits( posh_u32_t bits ); +float POSH_FloatFromBigBits( posh_u32_t bits ); + +void POSH_DoubleBits( double d, posh_byte_t dst[ 8 ] ); +double POSH_DoubleFromBits( const posh_byte_t src[ 8 ] ); + +/* unimplemented +float *POSH_WriteFloatToLittle( void *dst, float f ); +float *POSH_WriteFloatToBig( void *dst, float f ); +float POSH_ReadFloatFromLittle( const void *src ); +float POSH_ReadFloatFromBig( const void *src ); + +double *POSH_WriteDoubleToLittle( void *dst, double d ); +double *POSH_WriteDoubleToBig( void *dst, double d ); +double POSH_ReadDoubleFromLittle( const void *src ); +double POSH_ReadDoubleFromBig( const void *src ); +*/ +#endif /* !defined POSH_NO_FLOAT */ + +#if defined FORCE_DOXYGEN +# define POSH_NO_FLOAT +# undef POSH_NO_FLOAT +#endif + +extern posh_u16_t POSH_SwapU16( posh_u16_t u ); +extern posh_i16_t POSH_SwapI16( posh_i16_t u ); +extern posh_u32_t POSH_SwapU32( posh_u32_t u ); +extern posh_i32_t POSH_SwapI32( posh_i32_t u ); + +#if defined POSH_64BIT_INTEGER + +extern posh_u64_t POSH_SwapU64( posh_u64_t u ); +extern posh_i64_t POSH_SwapI64( posh_i64_t u ); + +#endif /*POSH_64BIT_INTEGER */ + +extern posh_u16_t *POSH_WriteU16ToLittle( void *dst, posh_u16_t value ); +extern posh_i16_t *POSH_WriteI16ToLittle( void *dst, posh_i16_t value ); +extern posh_u32_t *POSH_WriteU32ToLittle( void *dst, posh_u32_t value ); +extern posh_i32_t *POSH_WriteI32ToLittle( void *dst, posh_i32_t value ); + +extern posh_u16_t *POSH_WriteU16ToBig( void *dst, posh_u16_t value ); +extern posh_i16_t *POSH_WriteI16ToBig( void *dst, posh_i16_t value ); +extern posh_u32_t *POSH_WriteU32ToBig( void *dst, posh_u32_t value ); +extern posh_i32_t *POSH_WriteI32ToBig( void *dst, posh_i32_t value ); + +extern posh_u16_t POSH_ReadU16FromLittle( const void *src ); +extern posh_i16_t POSH_ReadI16FromLittle( const void *src ); +extern posh_u32_t POSH_ReadU32FromLittle( const void *src ); +extern posh_i32_t POSH_ReadI32FromLittle( const void *src ); + +extern posh_u16_t POSH_ReadU16FromBig( const void *src ); +extern posh_i16_t POSH_ReadI16FromBig( const void *src ); +extern posh_u32_t POSH_ReadU32FromBig( const void *src ); +extern posh_i32_t POSH_ReadI32FromBig( const void *src ); + +#if defined POSH_64BIT_INTEGER +extern posh_u64_t *POSH_WriteU64ToLittle( void *dst, posh_u64_t value ); +extern posh_i64_t *POSH_WriteI64ToLittle( void *dst, posh_i64_t value ); +extern posh_u64_t *POSH_WriteU64ToBig( void *dst, posh_u64_t value ); +extern posh_i64_t *POSH_WriteI64ToBig( void *dst, posh_i64_t value ); + +extern posh_u64_t POSH_ReadU64FromLittle( const void *src ); +extern posh_i64_t POSH_ReadI64FromLittle( const void *src ); +extern posh_u64_t POSH_ReadU64FromBig( const void *src ); +extern posh_i64_t POSH_ReadI64FromBig( const void *src ); +#endif /* POSH_64BIT_INTEGER */ + +#if defined POSH_LITTLE_ENDIAN + +# define POSH_LittleU16(x) (x) +# define POSH_LittleU32(x) (x) +# define POSH_LittleI16(x) (x) +# define POSH_LittleI32(x) (x) +# if defined POSH_64BIT_INTEGER +# define POSH_LittleU64(x) (x) +# define POSH_LittleI64(x) (x) +# endif /* defined POSH_64BIT_INTEGER */ + +# define POSH_BigU16(x) POSH_SwapU16(x) +# define POSH_BigU32(x) POSH_SwapU32(x) +# define POSH_BigI16(x) POSH_SwapI16(x) +# define POSH_BigI32(x) POSH_SwapI32(x) +# if defined POSH_64BIT_INTEGER +# define POSH_BigU64(x) POSH_SwapU64(x) +# define POSH_BigI64(x) POSH_SwapI64(x) +# endif /* defined POSH_64BIT_INTEGER */ + +#else + +# define POSH_BigU16(x) (x) +# define POSH_BigU32(x) (x) +# define POSH_BigI16(x) (x) +# define POSH_BigI32(x) (x) + +# if defined POSH_64BIT_INTEGER +# define POSH_BigU64(x) (x) +# define POSH_BigI64(x) (x) +# endif /* POSH_64BIT_INTEGER */ + +# define POSH_LittleU16(x) POSH_SwapU16(x) +# define POSH_LittleU32(x) POSH_SwapU32(x) +# define POSH_LittleI16(x) POSH_SwapI16(x) +# define POSH_LittleI32(x) POSH_SwapI32(x) + +# if defined POSH_64BIT_INTEGER +# define POSH_LittleU64(x) POSH_SwapU64(x) +# define POSH_LittleI64(x) POSH_SwapI64(x) +# endif /* POSH_64BIT_INTEGER */ + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_POSH_H */ diff --git a/scons/scons-LICENSE b/scons/scons-LICENSE new file mode 100644 index 0000000..552bf12 --- /dev/null +++ b/scons/scons-LICENSE @@ -0,0 +1,25 @@ + Copyright and license for SCons - a software construction tool + + This copyright and license do not apply to any other software + with which this software may have been included. + +Copyright (c) 2001, 2002, 2003, 2004 Steven Knight + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/scons/scons-README b/scons/scons-README new file mode 100644 index 0000000..3ed25b9 --- /dev/null +++ b/scons/scons-README @@ -0,0 +1,204 @@ +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight + + SCons - a software construction tool + +This is the scons-README file for a version of SCons packaged for local +execution--that is, execution out of a specific local directory, without +having to install SCons as a system-wide utility. + +You are likely reading this file in one of the following two situations: + + 1) You have unpacked an scons-local-{version} package and are + examining the contents. + + In this case, you are presumably interested in using this + package to include a local copy of SCons with some other + software that you package, so that you can use SCons to build + your software without forcing all of your users to have it fully + installed. Instructions for this can be found below. + + If you are not looking to use SCons in this way, then please + use either the scons-{version} package to install SCons on your + system, or the scons-src-{version} package if you want the full + source to SCons, including its packaging code and underlying + tests and testing infrastructure. + + 2) This file was included in some other software package so that + the package could be built using SCons. + + In this case, follow the instructions provided with the + rest of the software package for how to use SCons to build + and/or install the software. The file containing build and + installation instructions will typically be named README or + INSTALL. + +LATEST VERSION +============== + +Before going further, you can check for the latest version of the +scons-local package, or any SCons package, at the SCons download page: + + http://www.scons.org/download.html + + +EXECUTION REQUIREMENTS +====================== + +Running SCons requires Python version 1.5.2 or later. There should be +no other dependencies or requirements to run SCons. + +The default SCons configuration assumes use of the Microsoft Visual C++ +compiler suite on WIN32 systems, and assumes a C compiler named 'cc', +a C++ compiler named 'c++', and a Fortran compiler named 'g77' (such +as found in the GNU C compiler suite) on any other type of system. +You may, of course, override these default values by appropriate +configuration of Environment construction variables. + + +INSTALLATION +============ + +Installation of this package should be as simple as unpacking the +archive (either .tar.gz or .zip) in any directory (top-level or a +subdirectory) within the software package with which you want to ship +SCons. + +Once you have installed this package, you should write an SConstruct +file at the top level of your source tree to build your software as you +see fit. + +Then modify the build/install instructions for your package to instruct +your users to execute SCons as follows (if you installed this package in +your top-level directory): + + $ python scons.py + +Or (if, for example, you installed this package in a subdirectory named +"scons"): + + $ python scons/scons.py + +That should be all you have to do. (If it isn't that simple, please let +us know!) + + +CONTENTS OF THIS PACKAGE +======================== + +This scons-local package consists of the following: + +scons-LICENSE + A copy of the copyright and terms under which SCons is + distributed (the Open Source Initiative-approved MIT license). + + A disclaimer has been added to the beginning to make clear that + this license applies only to SCons, and not to any separate + software you've written with which you're planning to package + SCons. + +scons-README + What you're looking at right now. + +scons-local-{version}/ + The SCons build engine. This is structured as a Python + library. + +scons.py + The SCons script itself. The script sets up the Python + sys.path variable to use the build engine found in the + scons-local-{version}/ directory in preference to any other + SCons build engine installed on your system. + + +DOCUMENTATION +============= + +Because this package is intended to be included with other software by +experienced users, we have not included any SCons documentation in this +package (other than this scons-README file you're reading right now). + +If, however, you need documentation about SCons, then consult any of the +following from the corresponding scons-{version} or scons-src-{version} +package: + + The RELEASE.txt file (src/RELEASE.txt file in the + scons-src-{version} package), which contains notes about this + specific release, including known problems. + + The CHANGES.txt file (src/CHANGES.txt file in the + scons-src-{version} package), which contains a list of changes + since the previous release. + + The scons.1 man page (doc/man/scons.1 in the scons-src-{version} + package), which contains a section of small examples for getting + started using SCons. + +Additional documentation for SCons is available at: + + http://www.scons.org/doc.html + + +LICENSING +========= + +SCons is distributed under the MIT license, a full copy of which is +available in the scons-LICENSE file in this package. The MIT license is +an approved Open Source license, which means: + + This software is OSI Certified Open Source Software. OSI + Certified is a certification mark of the Open Source Initiative. + +More information about OSI certifications and Open Source software is +available at: + + http://www.opensource.org/ + + +REPORTING BUGS +============== + +You can report bugs either by following the "Tracker - Bugs" link +on the SCons project page: + + http://sourceforge.net/projects/scons/ + +or by sending mail to the SCons developers mailing list: + + scons-devel@lists.sourceforge.net + + +MAILING LISTS +============= + +A mailing list for users of SCons is available. You may send questions +or comments to the list at: + + scons-users@lists.sourceforge.net + +You may subscribe to the scons-users mailing list at: + + http://lists.sourceforge.net/lists/listinfo/scons-users + + +FOR MORE INFORMATION +==================== + +Check the SCons web site at: + + http://www.scons.org/ + + +AUTHOR INFO +=========== + +Steven Knight +knight at baldmt dot com +http://www.baldmt.com/~knight/ + +With plenty of help from the SCons Development team: + Chad Austin + Charles Crain + Steve Leblanc + Anthony Roach + Terrel Shumway + diff --git a/scons/scons-local-0.95/SCons/Action.py b/scons/scons-local-0.95/SCons/Action.py new file mode 100644 index 0000000..c34c2e8 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Action.py @@ -0,0 +1,447 @@ +"""engine.SCons.Action + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Action.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import re +import string +import sys + +from SCons.Debug import logInstanceCreation +import SCons.Errors +import SCons.Util + +class _Null: + pass + +_null = _Null + +print_actions = 1; +execute_actions = 1; + +default_ENV = None + +def rfile(n): + try: + return n.rfile() + except AttributeError: + return n + +def _actionAppend(act1, act2): + # This function knows how to slap two actions together. + # Mainly, it handles ListActions by concatenating into + # a single ListAction. + a1 = Action(act1) + a2 = Action(act2) + if a1 is None or a2 is None: + raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2)) + if isinstance(a1, ListAction): + if isinstance(a2, ListAction): + return ListAction(a1.list + a2.list) + else: + return ListAction(a1.list + [ a2 ]) + else: + if isinstance(a2, ListAction): + return ListAction([ a1 ] + a2.list) + else: + return ListAction([ a1, a2 ]) + +class CommandGenerator: + """ + Wraps a command generator function so the Action() factory + function can tell a generator function from a function action. + """ + def __init__(self, generator): + self.generator = generator + + def __add__(self, other): + return _actionAppend(self, other) + + def __radd__(self, other): + return _actionAppend(other, self) + +def _do_create_action(act, strfunction=_null, varlist=[]): + """This is the actual "implementation" for the + Action factory method, below. This handles the + fact that passing lists to Action() itself has + different semantics than passing lists as elements + of lists. + + The former will create a ListAction, the latter + will create a CommandAction by converting the inner + list elements to strings.""" + + if isinstance(act, ActionBase): + return act + elif SCons.Util.is_List(act): + return CommandAction(act) + elif isinstance(act, CommandGenerator): + return CommandGeneratorAction(act.generator) + elif callable(act): + return FunctionAction(act, strfunction=strfunction, varlist=varlist) + elif SCons.Util.is_String(act): + var=SCons.Util.get_environment_var(act) + if var: + # This looks like a string that is purely an Environment + # variable reference, like "$FOO" or "${FOO}". We do + # something special here...we lazily evaluate the contents + # of that Environment variable, so a user could but something + # like a function or a CommandGenerator in that variable + # instead of a string. + return CommandGeneratorAction(LazyCmdGenerator(var)) + listCmds = map(lambda x: CommandAction(x), + string.split(str(act), '\n')) + if len(listCmds) == 1: + return listCmds[0] + else: + return ListAction(listCmds) + else: + return None + +def Action(act, strfunction=_null, varlist=[]): + """A factory for action objects.""" + if SCons.Util.is_List(act): + acts = map(lambda x, s=strfunction, v=varlist: + _do_create_action(x, s, v), + act) + acts = filter(lambda x: not x is None, acts) + if len(acts) == 1: + return acts[0] + else: + return ListAction(acts) + else: + return _do_create_action(act, strfunction=strfunction, varlist=varlist) + +class ActionBase: + """Base class for actions that create output objects.""" + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + + def show(self, string): + if print_actions: + sys.stdout.write(string + '\n') + + def get_actions(self): + return [self] + + def __add__(self, other): + return _actionAppend(self, other) + + def __radd__(self, other): + return _actionAppend(other, self) + +def _string_from_cmd_list(cmd_list): + """Takes a list of command line arguments and returns a pretty + representation for printing.""" + cl = [] + for arg in map(str, cmd_list): + if ' ' in arg or '\t' in arg: + arg = '"' + arg + '"' + cl.append(arg) + return string.join(cl) + +class CommandAction(ActionBase): + """Class for command-execution actions.""" + def __init__(self, cmd): + # Cmd list can actually be a list or a single item...basically + # anything that we could pass in as the first arg to + # Environment.subst_list(). + if __debug__: logInstanceCreation(self) + self.cmd_list = cmd + + def strfunction(self, target, source, env): + cmd_list = env.subst_list(self.cmd_list, 0, target, source) + return map(_string_from_cmd_list, cmd_list) + + def __call__(self, target, source, env): + """Execute a command action. + + This will handle lists of commands as well as individual commands, + because construction variable substitution may turn a single + "command" into a list. This means that this class can actually + handle lists of commands, even though that's not how we use it + externally. + """ + import SCons.Util + + escape = env.get('ESCAPE', lambda x: x) + + if env.has_key('SHELL'): + shell = env['SHELL'] + else: + raise SCons.Errors.UserError('Missing SHELL construction variable.') + + # for SConf support (by now): check, if we want to pipe the command + # output to somewhere else + if env.has_key('PIPE_BUILD'): + pipe_build = 1 + if env.has_key('PSPAWN'): + pspawn = env['PSPAWN'] + else: + raise SCons.Errors.UserError('Missing PSPAWN construction variable.') + if env.has_key('PSTDOUT'): + pstdout = env['PSTDOUT'] + else: + raise SCons.Errors.UserError('Missing PSTDOUT construction variable.') + if env.has_key('PSTDERR'): + pstderr = env['PSTDERR'] + else: + raise SCons.Errors.UserError('Missing PSTDOUT construction variable.') + else: + pipe_build = 0 + if env.has_key('SPAWN'): + spawn = env['SPAWN'] + else: + raise SCons.Errors.UserError('Missing SPAWN construction variable.') + + cmd_list = env.subst_list(self.cmd_list, 0, target, source) + for cmd_line in cmd_list: + if len(cmd_line): + if print_actions: + self.show(_string_from_cmd_list(cmd_line)) + if execute_actions: + try: + ENV = env['ENV'] + except KeyError: + global default_ENV + if not default_ENV: + import SCons.Environment + default_ENV = SCons.Environment.Environment()['ENV'] + ENV = default_ENV + + # ensure that the ENV values are all strings: + for key, value in ENV.items(): + if SCons.Util.is_List(value): + # If the value is a list, then we assume + # it is a path list, because that's a pretty + # common list like value to stick in an environment + # variable: + ENV[key] = string.join(map(str, value), os.pathsep) + elif not SCons.Util.is_String(value): + # If it isn't a string or a list, then + # we just coerce it to a string, which + # is proper way to handle Dir and File instances + # and will produce something reasonable for + # just about everything else: + ENV[key] = str(value) + + # Escape the command line for the command + # interpreter we are using + cmd_line = SCons.Util.escape_list(cmd_line, escape) + if pipe_build: + ret = pspawn( shell, escape, cmd_line[0], cmd_line, + ENV, pstdout, pstderr ) + else: + ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV) + if ret: + return ret + return 0 + + def get_raw_contents(self, target, source, env, dict=None): + """Return the complete contents of this action's command line. + """ + cmd = self.cmd_list + if SCons.Util.is_List(cmd): + cmd = string.join(map(str, cmd)) + else: + cmd = str(cmd) + return env.subst(cmd, SCons.Util.SUBST_RAW, target, source, dict) + + def get_contents(self, target, source, env, dict=None): + """Return the signature contents of this action's command line. + + This strips $(-$) and everything in between the string, + since those parts don't affect signatures. + """ + cmd = self.cmd_list + if SCons.Util.is_List(cmd): + cmd = string.join(map(str, cmd)) + else: + cmd = str(cmd) + return env.subst(cmd, SCons.Util.SUBST_SIG, target, source, dict) + +class CommandGeneratorAction(ActionBase): + """Class for command-generator actions.""" + def __init__(self, generator): + if __debug__: logInstanceCreation(self) + self.generator = generator + + def __generate(self, target, source, env, for_signature): + # ensure that target is a list, to make it easier to write + # generator functions: + if not SCons.Util.is_List(target): + target = [target] + + ret = self.generator(target=target, source=source, env=env, for_signature=for_signature) + gen_cmd = Action(ret) + if not gen_cmd: + raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) + return gen_cmd + + def strfunction(self, target, source, env): + if not SCons.Util.is_List(source): + source = [source] + rsources = map(rfile, source) + act = self.__generate(target, source, env, 0) + return act.strfunction(target, rsources, env) + + def __call__(self, target, source, env): + if not SCons.Util.is_List(source): + source = [source] + rsources = map(rfile, source) + act = self.__generate(target, source, env, 0) + return act(target, rsources, env) + + def get_contents(self, target, source, env, dict=None): + """Return the signature contents of this action's command line. + + This strips $(-$) and everything in between the string, + since those parts don't affect signatures. + """ + return self.__generate(target, source, env, 1).get_contents(target, source, env, dict=None) + +class LazyCmdGenerator: + """This is a simple callable class that acts as a command generator. + It holds on to a key into an Environment dictionary, then waits + until execution time to see what type it is, then tries to + create an Action out of it.""" + def __init__(self, var): + if __debug__: logInstanceCreation(self) + self.var = SCons.Util.to_String(var) + + def strfunction(self, target, source, env): + try: + return env[self.var] + except KeyError: + # The variable reference substitutes to nothing. + return '' + + def __call__(self, target, source, env, for_signature): + try: + return env[self.var] + except KeyError: + # The variable reference substitutes to nothing. + return '' + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + +class FunctionAction(ActionBase): + """Class for Python function actions.""" + + def __init__(self, execfunction, strfunction=_null, varlist=[]): + if __debug__: logInstanceCreation(self) + self.execfunction = execfunction + if strfunction is _null: + def strfunction(target, source, env, execfunction=execfunction): + def quote(s): + return '"' + str(s) + '"' + def array(a, q=quote): + return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']' + try: + name = execfunction.__name__ + except AttributeError: + try: + name = execfunction.__class__.__name__ + except AttributeError: + name = "unknown_python_function" + tstr = len(target) == 1 and quote(target[0]) or array(target) + sstr = len(source) == 1 and quote(source[0]) or array(source) + return "%s(%s, %s)" % (name, tstr, sstr) + self.strfunction = strfunction + self.varlist = varlist + + def __call__(self, target, source, env): + r = 0 + if not SCons.Util.is_List(target): + target = [target] + if not SCons.Util.is_List(source): + source = [source] + if print_actions and self.strfunction: + s = self.strfunction(target, source, env) + if s: + self.show(s) + if execute_actions: + rsources = map(rfile, source) + r = self.execfunction(target=target, source=rsources, env=env) + return r + + def get_contents(self, target, source, env, dict=None): + """Return the signature contents of this callable action. + + By providing direct access to the code object of the + function, Python makes this extremely easy. Hooray! + """ + try: + # "self.execfunction" is a function. + code = self.execfunction.func_code.co_code + except AttributeError: + # "self.execfunction" is a callable object. + code = self.execfunction.__call__.im_func.func_code.co_code + return str(code) + env.subst(string.join(map(lambda v: '${'+v+'}', + self.varlist))) + +class ListAction(ActionBase): + """Class for lists of other actions.""" + def __init__(self, list): + if __debug__: logInstanceCreation(self) + self.list = map(lambda x: Action(x), list) + + def get_actions(self): + return self.list + + def strfunction(self, target, source, env): + s = [] + for l in self.list: + if l.strfunction: + x = l.strfunction(target, source, env) + if not SCons.Util.is_List(x): + x = [x] + s.extend(x) + return string.join(s, "\n") + + def __call__(self, target, source, env): + for l in self.list: + r = l(target, source, env) + if r: + return r + return 0 + + def get_contents(self, target, source, env, dict=None): + """Return the signature contents of this action list. + + Simple concatenation of the signatures of the elements. + """ + dict = SCons.Util.subst_dict(target, source) + return string.join(map(lambda x, t=target, s=source, e=env, d=dict: + x.get_contents(t, s, e, d), + self.list), + "") diff --git a/scons/scons-local-0.95/SCons/Builder.py b/scons/scons-local-0.95/SCons/Builder.py new file mode 100644 index 0000000..8324723 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Builder.py @@ -0,0 +1,606 @@ +"""SCons.Builder + +Builder object subsystem. + +A Builder object is a callable that encapsulates information about how +to execute actions to create a Node (file) from other Nodes (files), and +how to create those dependencies for tracking. + +The main entry point here is the Builder() factory method. This +provides a procedural interface that creates the right underlying +Builder object based on the keyword arguments supplied and the types of +the arguments. + +The goal is for this external interface to be simple enough that the +vast majority of users can create new Builders as necessary to support +building new types of files in their configurations, without having to +dive any deeper into this subsystem. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Builder.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path +import UserDict + +import SCons.Action +from SCons.Debug import logInstanceCreation +from SCons.Errors import InternalError, UserError +import SCons.Executor +import SCons.Node.FS +import SCons.Util +import SCons.Warnings + +class _Null: + pass + +_null = _Null + +class DictCmdGenerator(SCons.Util.Selector): + """This is a callable class that can be used as a + command generator function. It holds on to a dictionary + mapping file suffixes to Actions. It uses that dictionary + to return the proper action based on the file suffix of + the source file.""" + + def src_suffixes(self): + return self.keys() + + def add_action(self, suffix, action): + """Add a suffix-action pair to the mapping. + """ + self[suffix] = action + + def __call__(self, target, source, env, for_signature): + ext = None + for src in map(str, source): + my_ext = SCons.Util.splitext(src)[1] + if ext and my_ext != ext: + raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext)) + ext = my_ext + + if not ext: + raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source)))) + + try: + ret = SCons.Util.Selector.__call__(self, env, source) + except KeyError, e: + raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2])) + if ret is None: + raise UserError("While building `%s': Don't know how to build a file with suffix `%s'." % (repr(map(str, target)), ext)) + return ret + +class CallableSelector(SCons.Util.Selector): + """A callable dictionary that will, in turn, call the value it + finds if it can.""" + def __call__(self, env, source): + value = SCons.Util.Selector.__call__(self, env, source) + if callable(value): + value = value(env, source) + return value + +class DictEmitter(SCons.Util.Selector): + """A callable dictionary that maps file suffixes to emitters. + When called, it finds the right emitter in its dictionary for the + suffix of the first source file, and calls that emitter to get the + right lists of targets and sources to return. If there's no emitter + for the suffix in its dictionary, the original target and source are + returned. + """ + def __call__(self, target, source, env): + emitter = SCons.Util.Selector.__call__(self, env, source) + if emitter: + target, source = emitter(target, source, env) + return (target, source) + +def Builder(**kw): + """A factory for builder objects.""" + composite = None + if kw.has_key('generator'): + if kw.has_key('action'): + raise UserError, "You must not specify both an action and a generator." + kw['action'] = SCons.Action.CommandGenerator(kw['generator']) + del kw['generator'] + elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']): + composite = DictCmdGenerator(kw['action']) + kw['action'] = SCons.Action.CommandGenerator(composite) + kw['src_suffix'] = composite.src_suffixes() + + if kw.has_key('emitter'): + emitter = kw['emitter'] + if SCons.Util.is_String(emitter): + # This allows users to pass in an Environment + # variable reference (like "$FOO") as an emitter. + # We will look in that Environment variable for + # a callable to use as the actual emitter. + var = SCons.Util.get_environment_var(emitter) + if not var: + raise UserError, "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter + kw['emitter'] = EmitterProxy(var) + elif SCons.Util.is_Dict(emitter): + kw['emitter'] = DictEmitter(emitter) + + if kw.has_key('src_builder'): + ret = apply(MultiStepBuilder, (), kw) + else: + ret = apply(BuilderBase, (), kw) + + if not composite is None: + ret = CompositeBuilder(ret, composite) + + return ret + +def _init_nodes(builder, env, overrides, tlist, slist): + """Initialize lists of target and source nodes with all of + the proper Builder information. + """ + + # First, figure out if there are any errors in the way the targets + # were specified. + for t in tlist: + if t.side_effect: + raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) + if t.has_builder(): + if t.env != env: + t_contents = t.builder.action.get_contents(tlist, slist, t.env) + contents = t.builder.action.get_contents(tlist, slist, env) + + if t_contents == contents: + SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, + "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), t.builder.action.strfunction(tlist, slist, t.env))) + + else: + raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t) + + elif t.overrides != overrides: + raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t) + + elif builder.scanner and t.target_scanner and builder.scanner != t.target_scanner: + raise UserError, "Two different scanners were specified for the same target: %s"%str(t) + + if builder.multi: + if t.builder != builder: + if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder: + raise UserError, "Two different target sets have a target in common: %s"%str(t) + else: + raise UserError, "Two different builders (%s and %s) were specified for the same target: %s"%(t.builder.get_name(env), builder.get_name(env), str(t)) + elif t.sources != slist: + raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t) + + # The targets are fine, so find or make the appropriate Executor to + # build this particular list of targets from this particular list of + # sources. + executor = None + if builder.multi: + try: + executor = tlist[0].get_executor(create = 0) + except AttributeError: + pass + else: + executor.add_sources(slist) + if executor is None: + executor = SCons.Executor.Executor(builder, + env, + overrides, + tlist, + slist) + + # Now set up the relevant information in the target Nodes themselves. + for t in tlist: + t.overrides = overrides + t.cwd = SCons.Node.FS.default_fs.getcwd() + t.builder_set(builder) + t.env_set(env) + t.add_source(slist) + t.set_executor(executor) + if builder.scanner: + t.target_scanner = builder.scanner + if not t.source_scanner: + t.source_scanner = env.get_scanner(t.scanner_key()) + + # Last, add scanners from the Environment to the source Nodes. + for s in slist: + if not s.source_scanner: + s.source_scanner = env.get_scanner(s.scanner_key()) + +class EmitterProxy: + """This is a callable class that can act as a + Builder emitter. It holds on to a string that + is a key into an Environment dictionary, and will + look there at actual build time to see if it holds + a callable. If so, we will call that as the actual + emitter.""" + def __init__(self, var): + self.var = SCons.Util.to_String(var) + + def __call__(self, target, source, env): + emitter = self.var + + # Recursively substitute the variable. + # We can't use env.subst() because it deals only + # in strings. Maybe we should change that? + while SCons.Util.is_String(emitter) and \ + env.has_key(emitter): + emitter = env[emitter] + if not callable(emitter): + return (target, source) + + return emitter(target, source, env) + + def __cmp__(self, other): + return cmp(self.var, other.var) + +class BuilderBase: + """Base class for Builders, objects that create output + nodes (files) from input nodes (files). + """ + + def __init__(self, action = None, + prefix = '', + suffix = '', + src_suffix = '', + node_factory = SCons.Node.FS.default_fs.File, + target_factory = None, + source_factory = None, + scanner = None, + emitter = None, + multi = 0, + env = None, + **overrides): + if __debug__: logInstanceCreation(self, 'BuilderBase') + self.action = SCons.Action.Action(action) + self.multi = multi + if SCons.Util.is_Dict(prefix): + prefix = CallableSelector(prefix) + self.prefix = prefix + if SCons.Util.is_Dict(suffix): + suffix = CallableSelector(suffix) + self.suffix = suffix + self.env = env + if overrides.has_key('overrides'): + SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, + "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ + "\tspecify the items as keyword arguments to the Builder() call instead.") + overrides.update(overrides['overrides']) + del overrides['overrides'] + self.overrides = overrides + + self.set_src_suffix(src_suffix) + + self.target_factory = target_factory or node_factory + self.source_factory = source_factory or node_factory + self.scanner = scanner + + self.emitter = emitter + + def __nonzero__(self): + raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead" + + def get_name(self, env): + """Attempts to get the name of the Builder. + + Look at the BUILDERS variable of env, expecting it to be a + dictionary containing this Builder, and return the key of the + dictionary.""" + + try: + index = env['BUILDERS'].values().index(self) + return env['BUILDERS'].keys()[index] + except (AttributeError, KeyError, ValueError): + return str(self.__class__) + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + + def splitext(self, path): + return SCons.Util.splitext(path) + + def _create_nodes(self, env, overrides, target = None, source = None): + """Create and return lists of target and source nodes. + """ + def _adjustixes(files, pre, suf): + if not files: + return [] + result = [] + if not SCons.Util.is_List(files): + files = [files] + + for f in files: + if SCons.Util.is_String(f): + f = SCons.Util.adjustixes(f, pre, suf) + result.append(f) + return result + + env = env.Override(overrides) + + src_suf = self.get_src_suffix(env) + + source = _adjustixes(source, None, src_suf) + slist = env.arg2nodes(source, self.source_factory) + + pre = self.get_prefix(env, slist) + suf = self.get_suffix(env, slist) + + if target is None: + try: + t_from_s = slist[0].target_from_source + except AttributeError: + raise UserError("Do not know how to create a target from source `%s'" % slist[0]) + tlist = [ t_from_s(pre, suf, self.splitext) ] + else: + target = _adjustixes(target, pre, suf) + tlist = env.arg2nodes(target, self.target_factory) + + if self.emitter: + # The emitter is going to do str(node), but because we're + # being called *from* a builder invocation, the new targets + # don't yet have a builder set on them and will look like + # source files. Fool the emitter's str() calls by setting + # up a temporary builder on the new targets. + new_targets = [] + for t in tlist: + if not t.is_derived(): + t.builder = self + new_targets.append(t) + + target, source = self.emitter(target=tlist, source=slist, env=env) + + # Now delete the temporary builders that we attached to any + # new targets, so that _init_nodes() doesn't do weird stuff + # to them because it thinks they already have builders. + for t in new_targets: + if t.builder is self: + # Only delete the temporary builder if the emitter + # didn't change it on us. + t.builder = None + + # Have to call arg2nodes yet again, since it is legal for + # emitters to spit out strings as well as Node instances. + slist = env.arg2nodes(source, self.source_factory) + tlist = env.arg2nodes(target, self.target_factory) + + return tlist, slist + + def __call__(self, env, target = None, source = _null, **overrides): + if source is _null: + source = target + target = None + tlist, slist = self._create_nodes(env, overrides, target, source) + + if len(tlist) == 1: + _init_nodes(self, env, overrides, tlist, slist) + tlist = tlist[0] + else: + _init_nodes(ListBuilder(self, env, tlist), env, overrides, tlist, slist) + + return tlist + + def adjust_suffix(self, suff): + if suff and not suff[0] in [ '.', '_', '$' ]: + return '.' + suff + return suff + + def get_prefix(self, env, sources=[]): + prefix = self.prefix + if callable(prefix): + prefix = prefix(env, sources) + return env.subst(prefix) + + def get_suffix(self, env, sources=[]): + suffix = self.suffix + if callable(suffix): + suffix = suffix(env, sources) + else: + suffix = self.adjust_suffix(suffix) + return env.subst(suffix) + + def src_suffixes(self, env): + return map(lambda x, s=self, e=env: e.subst(s.adjust_suffix(x)), + self.src_suffix) + + def set_src_suffix(self, src_suffix): + if not src_suffix: + src_suffix = [] + elif not SCons.Util.is_List(src_suffix): + src_suffix = [ src_suffix ] + self.src_suffix = src_suffix + + def get_src_suffix(self, env): + """Get the first src_suffix in the list of src_suffixes.""" + ret = self.src_suffixes(env) + if not ret: + return '' + return ret[0] + + def targets(self, node): + """Return the list of targets for this builder instance. + + For most normal builders, this is just the supplied node. + """ + return [ node ] + + def add_emitter(self, suffix, emitter): + """Add a suffix-emitter mapping to this Builder. + + This assumes that emitter has been initialized with an + appropriate dictionary type, and will throw a TypeError if + not, so the caller is responsible for knowing that this is an + appropriate method to call for the Builder in question. + """ + self.emitter[suffix] = emitter + +class ListBuilder(SCons.Util.Proxy): + """A Proxy to support building an array of targets (for example, + foo.o and foo.h from foo.y) from a single Action execution. + """ + + def __init__(self, builder, env, tlist): + if __debug__: logInstanceCreation(self) + SCons.Util.Proxy.__init__(self, builder) + self.builder = builder + self.scanner = builder.scanner + self.env = env + self.tlist = tlist + self.multi = builder.multi + + def targets(self, node): + """Return the list of targets for this builder instance. + """ + return self.tlist + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + + def get_name(self, env): + """Attempts to get the name of the Builder.""" + + return "ListBuilder(%s)" % self.builder.get_name(env) + +class MultiStepBuilder(BuilderBase): + """This is a builder subclass that can build targets in + multiple steps. The src_builder parameter to the constructor + accepts a builder that is called to build sources supplied to + this builder. The targets of that first build then become + the sources of this builder. + + If this builder has a src_suffix supplied, then the src_builder + builder is NOT invoked if the suffix of a source file matches + src_suffix. + """ + def __init__(self, src_builder, + action = None, + prefix = '', + suffix = '', + src_suffix = '', + node_factory = SCons.Node.FS.default_fs.File, + target_factory = None, + source_factory = None, + scanner=None, + emitter=None): + if __debug__: logInstanceCreation(self) + BuilderBase.__init__(self, action, prefix, suffix, src_suffix, + node_factory, target_factory, source_factory, + scanner, emitter) + if not SCons.Util.is_List(src_builder): + src_builder = [ src_builder ] + self.src_builder = src_builder + self.sdict = {} + self.cached_src_suffixes = {} # source suffixes keyed on id(env) + + def __call__(self, env, target = None, source = _null, **kw): + if source is _null: + source = target + target = None + + slist = env.arg2nodes(source, self.source_factory) + final_sources = [] + + try: + sdict = self.sdict[id(env)] + except KeyError: + sdict = {} + self.sdict[id(env)] = sdict + for bld in self.src_builder: + if SCons.Util.is_String(bld): + try: + bld = env['BUILDERS'][bld] + except KeyError: + continue + for suf in bld.src_suffixes(env): + sdict[suf] = bld + + src_suffixes = self.src_suffixes(env) + + for snode in slist: + base, ext = self.splitext(str(snode)) + if sdict.has_key(ext): + tgt = apply(sdict[ext], (env, None, snode), kw) + # Only supply the builder with sources it is capable + # of building. + if SCons.Util.is_List(tgt): + tgt = filter(lambda x, self=self, suf=src_suffixes: + self.splitext(SCons.Util.to_String(x))[1] in suf, + tgt) + if not SCons.Util.is_List(tgt): + final_sources.append(tgt) + else: + final_sources.extend(tgt) + else: + final_sources.append(snode) + + return apply(BuilderBase.__call__, + (self, env, target, final_sources), kw) + + def get_src_builders(self, env): + """Return all the src_builders for this Builder. + + This is essentially a recursive descent of the src_builder "tree." + """ + ret = [] + for bld in self.src_builder: + if SCons.Util.is_String(bld): + # All Environments should have a BUILDERS + # variable, so no need to check for it. + try: + bld = env['BUILDERS'][bld] + except KeyError: + continue + ret.append(bld) + return ret + + def src_suffixes(self, env): + """Return a list of the src_suffix attributes for all + src_builders of this Builder. + """ + try: + return self.cached_src_suffixes[id(env)] + except KeyError: + suffixes = BuilderBase.src_suffixes(self, env) + for builder in self.get_src_builders(env): + suffixes.extend(builder.src_suffixes(env)) + self.cached_src_suffixes[id(env)] = suffixes + return suffixes + +class CompositeBuilder(SCons.Util.Proxy): + """A Builder Proxy whose main purpose is to always have + a DictCmdGenerator as its action, and to provide access + to the DictCmdGenerator's add_action() method. + """ + + def __init__(self, builder, cmdgen): + if __debug__: logInstanceCreation(self) + SCons.Util.Proxy.__init__(self, builder) + + # cmdgen should always be an instance of DictCmdGenerator. + self.cmdgen = cmdgen + self.builder = builder + + def add_action(self, suffix, action): + self.cmdgen.add_action(suffix, action) + self.set_src_suffix(self.cmdgen.src_suffixes()) + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) diff --git a/scons/scons-local-0.95/SCons/Conftest.py b/scons/scons-local-0.95/SCons/Conftest.py new file mode 100644 index 0000000..a6bedf5 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Conftest.py @@ -0,0 +1,483 @@ +"""SCons.Conftest + +Autoconf-like configuration support; low level implementation of tests. +""" + +# +# Copyright (c) 2003 Stichting NLnet Labs +# Copyright (c) 2001, 2002, 2003 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +# +# The purpose of this module is to define how a check is to be performed. +# Use one of the Check...() functions below. +# + +# +# A context class is used that defines functions for carrying out the tests, +# logging and messages. The following methods and members must be present: +# +# context.Display(msg) Function called to print messages that are normally +# displayed for the user. Newlines are explicitly used. +# The text should also be written to the logfile! +# +# context.Log(msg) Function called to write to a log file. +# +# context.BuildProg(text, ext) +# Function called to build a program, using "ext" for the +# file extention. Must return an empty string for +# success, an error message for failure. +# For reliable test results building should be done just +# like an actual program would be build, using the same +# command and arguments (including configure results so +# far). +# +# context.CompileProg(text, ext) +# Function called to compile a program, using "ext" for +# the file extention. Must return an empty string for +# success, an error message for failure. +# For reliable test results compiling should be done just +# like an actual source file would be compiled, using the +# same command and arguments (including configure results +# so far). +# +# context.AppendLIBS(lib_name_list) +# Append "lib_name_list" to the value of LIBS. +# "lib_namelist" is a list of strings. +# Return the value of LIBS before changing it (any type +# can be used, it is passed to SetLIBS() later. +# +# context.SetLIBS(value) +# Set LIBS to "value". The type of "value" is what +# AppendLIBS() returned. +# Return the value of LIBS before changing it (any type +# can be used, it is passed to SetLIBS() later. +# +# context.headerfilename +# Name of file to append configure results to, usually +# "confdefs.h". +# The file must not exist or be empty when starting. +# Empty or None to skip this (some tests will not work!). +# +# context.vardict Dictionary holding variables used for the tests and +# stores results from the tests, used for the build +# commands. +# Normally contains "CC", "LIBS", "CPPFLAGS", etc. +# +# context.havedict Dictionary holding results from the tests that are to +# be used inside a program. +# Names often start with "HAVE_". These are zero +# (feature not present) or one (feature present). Other +# variables may have any value, e.g., "PERLVERSION" can +# be a number and "SYSTEMNAME" a string. +# + +import string +from types import IntType + +# +# PUBLIC FUNCTIONS +# + +# Generic remarks: +# - When a language is specified which is not supported the test fails. The +# message is a bit different, because not all the arguments for the normal +# message are available yet (chicken-egg problem). + + +def CheckBuilder(context, text = None, language = None): + """ + Configure check to see if the compiler works. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + "text" may be used to specify the code to be build. + Returns an empty string for success, an error message for failure. + """ + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("%s\n" % msg) + return msg + + if not text: + text = """ + int main() { + return 0; + }\n\n""" + + context.Display("Checking if building a %s file works... " % lang) + ret = context.BuildProg(text, suffix) + _YesNoResult(context, ret, None, text) + return ret + + +def CheckFunc(context, function_name, header = None, language = None): + """ + Configure check for a function "function_name". + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Optional "header" can be defined to define a function prototype, include a + header file or anything else that comes before main(). + Sets HAVE_function_name in context.havedict according to the result. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + Returns an empty string for success, an error message for failure. + """ + + # Remarks from autoconf: + # - Don't include because on OSF/1 3.0 it includes + # which includes which contains a prototype for select. + # Similarly for bzero. + # - assert.h is included to define __stub macros and hopefully few + # prototypes, which can conflict with char $1(); below. + # - Override any gcc2 internal prototype to avoid an error. + # - We use char for the function declaration because int might match the + # return type of a gcc2 builtin and then its argument prototype would + # still apply. + # - The GNU C library defines this for functions which it implements to + # always fail with ENOSYS. Some functions are actually named something + # starting with __ and the normal name is an alias. + + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + if not header: + header = """ + #ifdef __cplusplus + extern "C" + #endif + char %s();""" % function_name + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for %s(): %s\n" % (function_name, msg)) + return msg + + text = """ + %(include)s + #include + %(hdr)s + + int main() { + #if defined (__stub_%(name)s) || defined (__stub___%(name)s) + fail fail fail + #else + %(name)s(); + #endif + + return 0; + }\n\n""" % { 'name': function_name, + 'include': includetext, + 'hdr': header } + + context.Display("Checking for %s function %s()... " % (lang, function_name)) + ret = context.BuildProg(text, suffix) + _YesNoResult(context, ret, "HAVE_" + function_name, text) + return ret + + +def CheckHeader(context, header_name, header = None, language = None, + include_quotes = None): + """ + Configure check for a C or C++ header file "header_name". + Optional "header" can be defined to do something before including the + header file (unusual, supported for consistency). + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Sets HAVE_header_name in context.havedict according to the result. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS and $CPPFLAGS are set correctly. + Returns an empty string for success, an error message for failure. + """ + # Why compile the program instead of just running the preprocessor? + # It is possible that the header file exists, but actually using it may + # fail (e.g., because it depends on other header files). Thus this test is + # more strict. It may require using the "header" argument. + # + # Use <> by default, because the check is normally used for system header + # files. SCons passes '""' to overrule this. + + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"\n' % context.headerfilename + else: + includetext = '' + if not header: + header = "" + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for header file %s: %s\n" + % (header_name, msg)) + return msg + + if not include_quotes: + include_quotes = "<>" + + text = "%s%s\n#include %s%s%s\n\n" % (includetext, header, + include_quotes[0], header_name, include_quotes[1]) + + context.Display("Checking for %s header file %s... " % (lang, header_name)) + ret = context.CompileProg(text, suffix) + _YesNoResult(context, ret, "HAVE_" + header_name, text) + return ret + + +def CheckType(context, type_name, fallback = None, + header = None, language = None): + """ + Configure check for a C or C++ type "type_name". + Optional "header" can be defined to include a header file. + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Sets HAVE_type_name in context.havedict according to the result. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + Returns an empty string for success, an error message for failure. + """ + + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + if not header: + header = "" + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) + return msg + + # Remarks from autoconf about this test: + # - Grepping for the type in include files is not reliable (grep isn't + # portable anyway). + # - Using "TYPE my_var;" doesn't work for const qualified types in C++. + # Adding an initializer is not valid for some C++ classes. + # - Using the type as parameter to a function either fails for K&$ C or for + # C++. + # - Using "TYPE *my_var;" is valid in C for some types that are not + # declared (struct something). + # - Using "sizeof(TYPE)" is valid when TYPE is actually a variable. + # - Using the previous two together works reliably. + text = """ + %(include)s + %(header)s + + int main() { + if ((%(name)s *) 0) + return 0; + if (sizeof (%(name)s)) + return 0; + }\n\n""" % { 'include': includetext, + 'header': header, + 'name': type_name } + + context.Display("Checking for %s type %s... " % (lang, type_name)) + ret = context.BuildProg(text, suffix) + _YesNoResult(context, ret, "HAVE_" + type_name, text) + if ret and fallback and context.headerfilename: + f = open(context.headerfilename, "a") + f.write("typedef %s %s;\n" % (fallback, type_name)) + f.close() + + return ret + + +def CheckLib(context, lib_name, func_name, header = None, + extra_libs = None, call = None, language = None, autoadd = 1): + """ + Configure check for a C or C++ library "lib_name". + Tests if "func_name" or "call" exists in the library. Note: if it exists + in another library the test succeeds anyway! + Optional "header" can be defined to include a header file. If not given a + default prototype for "func_name" is added. + Optional "extra_libs" is a list of library names to be added after + "lib_name" in the build command. To be used for libraries that "lib_name" + depends on. + Optional "call" replaces the call to "func_name" in the test code. It must + consist of complete C statements, including a trailing ";". + There must either be a "func_name" or a "call" argument (or both). + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + Returns an empty string for success, an error message for failure. + """ + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + if not header: + header = "" + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for library %s: %s\n" % (lib_name, msg)) + return msg + + text = """ + %s + %s """ % (includetext, header) + + # Add a function declaration if needed. + if func_name and func_name != "main" and not header: + text = text + """ + #ifdef __cplusplus + extern "C" + #endif + char %s();""" % func_name + + # The actual test code. + if not call: + call = "%s();" % func_name + text = text + """ + int + main() { + %s + return 0; + } + \n\n""" % call + + i = string.find(call, "\n") + if i > 0: + calltext = call[:i] + ".." + elif call[-1] == ';': + calltext = call[:-1] + else: + calltext = call + + context.Display("Checking for %s in %s library %s... " + % (calltext, lang, lib_name)) + if lib_name: + l = [ lib_name ] + if extra_libs: + l.extend(extra_libs) + oldLIBS = context.AppendLIBS(l) + sym = "HAVE_LIB" + lib_name + else: + oldLIBS = -1 + sym = None + + ret = context.BuildProg(text, suffix) + + _YesNoResult(context, ret, sym, text) + if oldLIBS != -1 and (ret or not autoadd): + context.SetLIBS(oldLIBS) + + return ret + + +# +# END OF PUBLIC FUNCTIONS +# + +def _YesNoResult(context, ret, key, text): + """ + Handle the result of a test with a "yes" or "no" result. + "ret" is the return value: empty if OK, error message when not. + "key" is the name of the symbol to be defined (HAVE_foo). + "text" is the source code of the program used for testing. + """ + if key: + _Have(context, key, not ret) + if ret: + context.Display("no\n") + _LogFailed(context, text, ret) + else: + context.Display("yes\n") + + +def _Have(context, key, have): + """ + Store result of a test in context.havedict and context.headerfilename. + "key" is a "HAVE_abc" name. It is turned into all CAPITALS and ":./" are + replaced by an underscore. + The value of "have" can be: + 1 - Feature is defined, add "#define key". + 0 - Feature is not defined, add "/* #undef key */". + Adding "undef" is what autoconf does. Not useful for the + compiler, but it shows that the test was done. + number - Feature is defined to this number "#define key have". + Doesn't work for 0 or 1, use a string then. + string - Feature is defined to this string "#define key have". + Give "have" as is should appear in the header file, include quotes + when desired and escape special characters! + """ + key_up = string.upper(key) + key_up = string.replace(key_up, ':', '_') + key_up = string.replace(key_up, '.', '_') + key_up = string.replace(key_up, '/', '_') + key_up = string.replace(key_up, ' ', '_') + context.havedict[key_up] = have + if context.headerfilename: + f = open(context.headerfilename, "a") + if have == 1: + f.write("#define %s\n" % key_up) + elif have == 0: + f.write("/* #undef %s */\n" % key_up) + elif type(have) == IntType: + f.write("#define %s %d\n" % (key_up, have)) + else: + f.write("#define %s %s\n" % (key_up, str(have))) + f.close() + + +def _LogFailed(context, text, msg): + """ + Write to the log about a failed program. + Add line numbers, so that error messages can be understood. + """ + context.Log("Failed program was:\n") + lines = string.split(text, '\n') + if len(lines) and lines[-1] == '': + lines = lines[:-1] # remove trailing empty line + n = 1 + for line in lines: + context.Log("%d: %s\n" % (n, line)) + n = n + 1 + context.Log("Error message: %s\n" % msg) + + +def _lang2suffix(lang): + """ + Convert a language name to a suffix. + When "lang" is empty or None C is assumed. + Returns a tuple (lang, suffix, None) when it works. + For an unrecognized language returns (None, None, msg). + Where: + lang = the unified language name + suffix = the suffix, including the leading dot + msg = an error message + """ + if not lang or lang in ["C", "c"]: + return ("C", ".c", None) + if lang in ["c++", "C++", "cpp", "CXX", "cxx"]: + return ("C++", ".cpp", None) + + return None, None, "Unsupported language: %s" % lang + + +# vim: set sw=4 et sts=4 tw=79 fo+=l: diff --git a/scons/scons-local-0.95/SCons/Debug.py b/scons/scons-local-0.95/SCons/Debug.py new file mode 100644 index 0000000..2c92953 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Debug.py @@ -0,0 +1,102 @@ +"""SCons.Debug + +Code for debugging SCons internal things. Not everything here is +guaranteed to work all the way back to Python 1.5.2, and shouldn't be +needed by most users. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Debug.py 0.95.D001 2004/03/08 07:28:28 knight" + + +# Recipe 14.10 from the Python Cookbook. +import string +import sys +try: + import weakref +except ImportError: + def logInstanceCreation(instance, name=None): + pass +else: + def logInstanceCreation(instance, name=None): + if name is None: + name = instance.__class__.__name__ + if not tracked_classes.has_key(name): + tracked_classes[name] = [] + tracked_classes[name].append(weakref.ref(instance)) + + + +tracked_classes = {} + +def string_to_classes(s): + if s == '*': + c = tracked_classes.keys() + c.sort() + return c + else: + return string.split(s) + +def countLoggedInstances(classes, file=sys.stdout): + for classname in string_to_classes(classes): + file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) + +def listLoggedInstances(classes, file=sys.stdout): + for classname in string_to_classes(classes): + file.write('\n%s:\n' % classname) + for ref in tracked_classes[classname]: + obj = ref() + if obj is not None: + file.write(' %s\n' % repr(obj)) + +def dumpLoggedInstances(classes, file=sys.stdout): + for classname in string_to_classes(classes): + file.write('\n%s:\n' % classname) + for ref in tracked_classes[classname]: + obj = ref() + if obj is not None: + file.write(' %s:\n' % obj) + for key, value in obj.__dict__.items(): + file.write(' %20s : %s\n' % (key, value)) + + + +if sys.platform[:5] == "linux": + # Linux doesn't actually support memory usage stats from getrusage(). + def memory(): + mstr = open('/proc/self/stat').read() + mstr = string.split(mstr)[22] + return int(mstr) +else: + try: + import resource + except ImportError: + def memory(): + return 0 + else: + def memory(): + res = resource.getrusage(resource.RUSAGE_SELF) + return res[4] diff --git a/scons/scons-local-0.95/SCons/Defaults.py b/scons/scons-local-0.95/SCons/Defaults.py new file mode 100644 index 0000000..aef1405 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Defaults.py @@ -0,0 +1,271 @@ +"""SCons.Defaults + +Builders and other things for the local site. Here's where we'll +duplicate the functionality of autoconf until we move it into the +installation procedure or use something like qmconf. + +The code that reads the registry to find MSVC components was borrowed +from distutils.msvccompiler. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Defaults.py 0.95.D001 2004/03/08 07:28:28 knight" + + + +import os +import os.path +import shutil +import stat +import string +import types + +import SCons.Action +import SCons.Builder +import SCons.Environment +import SCons.Scanner.C +import SCons.Scanner.D +import SCons.Scanner.Fortran +import SCons.Scanner.Prog +import SCons.Sig + +# A placeholder for a default Environment (for fetching source files +# from source code management systems and the like). This must be +# initialized later, after the top-level directory is set by the calling +# interface. +_default_env = None + +# Lazily instantiate the default environment so the overhead of creating +# it doesn't apply when it's not needed. +def DefaultEnvironment(*args, **kw): + global _default_env + if not _default_env: + _default_env = apply(SCons.Environment.Environment, args, kw) + _default_env._build_signature = 1 + _default_env._calc_module = SCons.Sig.default_module + return _default_env + +# Emitters for setting the shared attribute on object files, +# and an action for checking that all of the source files +# going into a shared library are, in fact, shared. +def StaticObjectEmitter(target, source, env): + for tgt in target: + tgt.attributes.shared = None + return (target, source) + +def SharedObjectEmitter(target, source, env): + for tgt in target: + tgt.attributes.shared = 1 + return (target, source) + +def SharedFlagChecker(source, target, env): + same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') + if same == '0' or same == '' or same == 'False': + for src in source: + try: + shared = src.attributes.shared + except AttributeError: + shared = None + if not shared: + raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0]) + +SharedCheck = SCons.Action.Action(SharedFlagChecker, None) + +# Scanners and actions for common language(s). +CScan = SCons.Scanner.C.CScan() +DScan = SCons.Scanner.D.DScan() + +FortranScan = SCons.Scanner.Fortran.FortranScan() + +CAction = SCons.Action.Action("$CCCOM") +DAction = SCons.Action.Action("$DCOM") +ShCAction = SCons.Action.Action("$SHCCCOM") +CXXAction = SCons.Action.Action("$CXXCOM") +ShCXXAction = SCons.Action.Action("$SHCXXCOM") + +F77Action = SCons.Action.Action("$F77COM") +ShF77Action = SCons.Action.Action("$SHF77COM") +F77PPAction = SCons.Action.Action("$F77PPCOM") +ShF77PPAction = SCons.Action.Action("$SHF77PPCOM") + +ASAction = SCons.Action.Action("$ASCOM") +ASPPAction = SCons.Action.Action("$ASPPCOM") + +ProgScan = SCons.Scanner.Prog.ProgScan() + +def DVI(): + """Common function to generate a DVI file Builder.""" + return SCons.Builder.Builder(action = {}, + # The suffix is not configurable via a + # construction variable like $DVISUFFIX + # because the output file name is + # hard-coded within TeX. + suffix = '.dvi') + +def PDF(): + """A function for generating the PDF Builder.""" + return SCons.Builder.Builder(action = { }, + prefix = '$PDFPREFIX', + suffix = '$PDFSUFFIX') + +def copyFunc(dest, source, env): + """Install a source file into a destination by copying it (and its + permission/mode bits).""" + shutil.copy2(source, dest) + st = os.stat(source) + os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + return 0 + +def _concat(prefix, list, suffix, env, f=lambda x: x): + """Creates a new list from 'list' by first interpolating each + element in the list using the 'env' dictionary and then calling f + on the list, and finally concatenating 'prefix' and 'suffix' onto + each element of the list. A trailing space on 'prefix' or leading + space on 'suffix' will cause them to be put into seperate list + elements rather than being concatenated.""" + + if not list: + return list + + list = f(env.subst_path(list)) + + ret = [] + + # ensure that prefix and suffix are strings + prefix = str(env.subst(prefix, SCons.Util.SUBST_RAW)) + suffix = str(env.subst(suffix, SCons.Util.SUBST_RAW)) + + for x in list: + x = str(x) + + if prefix: + if prefix[-1] == ' ': + ret.append(prefix[:-1]) + elif x[:len(prefix)] != prefix: + x = prefix + x + + ret.append(x) + + if suffix: + if suffix[0] == ' ': + ret.append(suffix[1:]) + elif x[-len(suffix):] != suffix: + ret[-1] = ret[-1]+suffix + + return ret + +def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None): + """This is a wrapper around _concat() that checks for the existence + of prefixes or suffixes on list elements and strips them where it + finds them. This is used by tools (like the GNU linker) that need + to turn something like 'libfoo.a' into '-lfoo'.""" + + if not callable(c): + if callable(env["_concat"]): + c = env["_concat"] + else: + c = _concat + def f(list, sp=stripprefix, ss=stripsuffix): + ret = [] + for l in list: + if not SCons.Util.is_String(l): + l = str(l) + if l[:len(sp)] == sp: + l = l[len(sp):] + if l[-len(ss):] == ss: + l = l[:-len(ss)] + ret.append(l) + return ret + return c(prefix, list, suffix, env, f) + +def _defines(prefix, defs, suffix, env, c=_concat): + """A wrapper around _concat that turns a list or string + into a list of C preprocessor command-line definitions. + """ + if SCons.Util.is_List(defs): + l = [] + for d in defs: + if SCons.Util.is_List(d) or type(d) is types.TupleType: + l.append(str(d[0]) + '=' + str(d[1])) + else: + l.append(str(d)) + elif SCons.Util.is_Dict(defs): + # The items in a dictionary are stored in random order, but + # if the order of the command-line options changes from + # invocation to invocation, then the signature of the command + # line will change and we'll get random unnecessary rebuilds. + # Consequently, we have to sort the keys to ensure a + # consistent order... + l = [] + keys = defs.keys() + keys.sort() + for k in keys: + v = defs[k] + if v is None: + l.append(str(k)) + else: + l.append(str(k) + '=' + str(v)) + else: + l = [str(defs)] + return c(prefix, l, suffix, env) + +class NullCmdGenerator: + """This is a callable class that can be used in place of other + command generators if you don't want them to do anything. + + The __call__ method for this class simply returns the thing + you instantiated it with. + + Example usage: + env["DO_NOTHING"] = NullCmdGenerator + env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" + """ + + def __init__(self, cmd): + self.cmd = cmd + + def __call__(self, target, source, env, for_signature=None): + return self.cmd + +ConstructionEnvironment = { + 'BUILDERS' : {}, + 'SCANNERS' : [CScan, FortranScan, DScan], + 'PDFPREFIX' : '', + 'PDFSUFFIX' : '.pdf', + 'PSPREFIX' : '', + 'PSSUFFIX' : '.ps', + 'ENV' : {}, + 'INSTALL' : copyFunc, + '_concat' : _concat, + '_defines' : _defines, + '_stripixes' : _stripixes, + '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', + '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs)} $)', + '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)', + '_F77INCFLAGS' : '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs)} $)', + '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', + 'TEMPFILE' : NullCmdGenerator + } diff --git a/scons/scons-local-0.95/SCons/Environment.py b/scons/scons-local-0.95/SCons/Environment.py new file mode 100644 index 0000000..7c3b6ed --- /dev/null +++ b/scons/scons-local-0.95/SCons/Environment.py @@ -0,0 +1,1109 @@ +"""SCons.Environment + +Base class for construction Environments. These are +the primary objects used to communicate dependency and +construction information to the build engine. + +Keyword arguments supplied when the construction Environment +is created are construction variables used to initialize the +Environment +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Environment.py 0.95.D001 2004/03/08 07:28:28 knight" + + +import copy +import os +import os.path +import string +import re +import shutil +from UserDict import UserDict + +import SCons.Action +import SCons.Builder +from SCons.Debug import logInstanceCreation +import SCons.Defaults +import SCons.Errors +import SCons.Node +import SCons.Node.Alias +import SCons.Node.FS +import SCons.Node.Python +import SCons.Platform +import SCons.Sig +import SCons.Sig.MD5 +import SCons.Sig.TimeStamp +import SCons.Tool +import SCons.Util +import SCons.Warnings + +class _Null: + pass + +_null = _Null + +CleanTargets = {} +CalculatorArgs = {} + +# Pull UserError into the global name space for the benefit of +# Environment().SourceSignatures(), which has some import statements +# which seem to mess up its ability to reference SCons directly. +UserError = SCons.Errors.UserError + +def installFunc(target, source, env): + """Install a source file into a target using the function specified + as the INSTALL construction variable.""" + try: + install = env['INSTALL'] + except KeyError: + raise SCons.Errors.UserError('Missing INSTALL construction variable.') + return install(target[0].path, source[0].path, env) + +def installString(target, source, env): + return 'Install file: "%s" as "%s"' % (source[0], target[0]) + +installAction = SCons.Action.Action(installFunc, installString) + +InstallBuilder = SCons.Builder.Builder(action=installAction) + +def alias_builder(env, target, source): + pass + +AliasBuilder = SCons.Builder.Builder(action = alias_builder, + target_factory = SCons.Node.Alias.default_ans.Alias, + source_factory = SCons.Node.FS.default_fs.Entry, + multi = 1) + +def our_deepcopy(x): + """deepcopy lists and dictionaries, and just copy the reference + for everything else.""" + if SCons.Util.is_Dict(x): + copy = {} + for key in x.keys(): + copy[key] = our_deepcopy(x[key]) + elif SCons.Util.is_List(x): + copy = map(our_deepcopy, x) + else: + copy = x + return copy + +def apply_tools(env, tools, toolpath): + if tools: + for tool in tools: + if SCons.Util.is_String(tool): + env.Tool(tool, toolpath) + else: + tool(env) + +class BuilderWrapper: + """Wrapper class that associates an environment with a Builder at + instantiation.""" + def __init__(self, env, builder): + self.env = env + self.builder = builder + + def __call__(self, *args, **kw): + return apply(self.builder, (self.env,) + args, kw) + + # This allows a Builder to be executed directly + # through the Environment to which it's attached. + # In practice, we shouldn't need this, because + # builders actually get executed through a Node. + # But we do have a unit test for this, and can't + # yet rule out that it would be useful in the + # future, so leave it for now. + def execute(self, **kw): + kw['env'] = self.env + apply(self.builder.execute, (), kw) + +class BuilderDict(UserDict): + """This is a dictionary-like class used by an Environment to hold + the Builders. We need to do this because every time someone changes + the Builders in the Environment's BUILDERS dictionary, we must + update the Environment's attributes.""" + def __init__(self, dict, env): + # Set self.env before calling the superclass initialization, + # because it will end up calling our other methods, which will + # need to point the values in this dictionary to self.env. + self.env = env + UserDict.__init__(self, dict) + + def __setitem__(self, item, val): + UserDict.__setitem__(self, item, val) + try: + self.setenvattr(item, val) + except AttributeError: + # Have to catch this because sometimes __setitem__ gets + # called out of __init__, when we don't have an env + # attribute yet, nor do we want one! + pass + + def setenvattr(self, item, val): + """Set the corresponding environment attribute for this Builder. + + If the value is already a BuilderWrapper, we pull the builder + out of it and make another one, so that making a copy of an + existing BuilderDict is guaranteed separate wrappers for each + Builder + Environment pair.""" + try: + builder = val.builder + except AttributeError: + builder = val + setattr(self.env, item, BuilderWrapper(self.env, builder)) + + def __delitem__(self, item): + UserDict.__delitem__(self, item) + delattr(self.env, item) + + def update(self, dict): + for i, v in dict.items(): + self.__setitem__(i, v) + +class Base: + """Base class for construction Environments. These are + the primary objects used to communicate dependency and + construction information to the build engine. + + Keyword arguments supplied when the construction Environment + is created are construction variables used to initialize the + Environment. + """ + + ####################################################################### + # This is THE class for interacting with the SCons build engine, + # and it contains a lot of stuff, so we're going to try to keep this + # a little organized by grouping the methods. + ####################################################################### + + ####################################################################### + # Methods that make an Environment act like a dictionary. These have + # the expected standard names for Python mapping objects. Note that + # we don't actually make an Environment a subclass of UserDict for + # performance reasons. Note also that we only supply methods for + # dictionary functionality that we actually need and use. + ####################################################################### + + def __init__(self, + platform=None, + tools=None, + toolpath=[], + options=None, + **kw): + if __debug__: logInstanceCreation(self) + self.fs = SCons.Node.FS.default_fs + self.ans = SCons.Node.Alias.default_ans + self.lookup_list = SCons.Node.arg2nodes_lookups + self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment) + + self._dict['__env__'] = self + self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) + + if platform is None: + platform = self._dict.get('PLATFORM', None) + if platform is None: + platform = SCons.Platform.Platform() + if SCons.Util.is_String(platform): + platform = SCons.Platform.Platform(platform) + self._dict['PLATFORM'] = str(platform) + platform(self) + + # Apply the passed-in variables before calling the tools, + # because they may use some of them: + apply(self.Replace, (), kw) + + # Update the environment with the customizable options + # before calling the tools, since they may use some of the options: + if options: + options.Update(self) + + if tools is None: + tools = self._dict.get('TOOLS', None) + if tools is None: + tools = ['default'] + apply_tools(self, tools, toolpath) + + # Reapply the passed in variables after calling the tools, + # since they should overide anything set by the tools: + apply(self.Replace, (), kw) + + # Update the environment with the customizable options + # after calling the tools, since they should override anything + # set by the tools: + if options: + options.Update(self) + + def __cmp__(self, other): + return cmp(self._dict, other._dict) + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + if key in ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']: + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, + "Ignoring attempt to set reserved variable `%s'" % key) + elif key == 'BUILDERS': + try: + bd = self._dict[key] + for k in bd.keys(): + del bd[k] + except KeyError: + self._dict[key] = BuilderDict(kwbd, self) + self._dict[key].update(value) + else: + if not SCons.Util.is_valid_construction_var(key): + raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key + self._dict[key] = value + + def __delitem__(self, key): + del self._dict[key] + + def items(self): + "Emulates the items() method of dictionaries.""" + return self._dict.items() + + def has_key(self, key): + return self._dict.has_key(key) + + def get(self, key, default=None): + "Emulates the get() method of dictionaries.""" + return self._dict.get(key, default) + + ####################################################################### + # Utility methods that are primarily for internal use by SCons. + # These begin with lower-case letters. Note that the subst() method + # is actually already out of the closet and used by people. + ####################################################################### + + def arg2nodes(self, args, node_factory=_null, lookup_list=_null): + if node_factory is _null: + node_factory = self.fs.File + if lookup_list is _null: + lookup_list = self.lookup_list + + if not args: + return [] + + if not SCons.Util.is_List(args): + args = [args] + + nodes = [] + for v in args: + if SCons.Util.is_String(v): + n = None + for l in lookup_list: + n = l(v) + if not n is None: + break + if not n is None: + if SCons.Util.is_String(n): + n = self.subst(n, raw=1) + if node_factory: + n = node_factory(n) + nodes.append(n) + elif node_factory: + v = self.subst(v, raw=1) + nodes.append(node_factory(v)) + else: + nodes.append(v) + + return nodes + + def get_calculator(self): + try: + return self._calculator + except AttributeError: + try: + module = self._calc_module + c = apply(SCons.Sig.Calculator, (module,), CalculatorArgs) + except AttributeError: + # Note that we're calling get_calculator() here, so the + # DefaultEnvironment() must have a _calc_module attribute + # to avoid infinite recursion. + c = SCons.Defaults.DefaultEnvironment().get_calculator() + self._calculator = c + return c + + def get_builder(self, name): + """Fetch the builder with the specified name from the environment. + """ + try: + return self._dict['BUILDERS'][name] + except KeyError: + return None + + def get_scanner(self, skey): + """Find the appropriate scanner given a key (usually a file suffix). + Does a linear search. Could be sped up by creating a dictionary if + this proves too slow. + """ + if self._dict['SCANNERS']: + for scanner in self._dict['SCANNERS']: + if skey in scanner.skeys: + return scanner + return None + + def subst(self, string, raw=0, target=None, source=None, dict=None, conv=None): + """Recursively interpolates construction variables from the + Environment into the specified string, returning the expanded + result. Construction variables are specified by a $ prefix + in the string and begin with an initial underscore or + alphabetic character followed by any number of underscores + or alphanumeric characters. The construction variable names + may be surrounded by curly braces to separate the name from + trailing characters. + """ + return SCons.Util.scons_subst(string, self, raw, target, source, dict, conv) + + def subst_kw(self, kw, raw=0, target=None, source=None, dict=None): + nkw = {} + for k, v in kw.items(): + k = self.subst(k, raw, target, source, dict) + if SCons.Util.is_String(v): + v = self.subst(v, raw, target, source, dict) + nkw[k] = v + return nkw + + def subst_list(self, string, raw=0, target=None, source=None, dict=None, conv=None): + """Calls through to SCons.Util.scons_subst_list(). See + the documentation for that function.""" + return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv) + + + def subst_path(self, path): + """Substitute a path list, turning EntryProxies into Nodes + and leaving Nodes (and other objects) as-is.""" + + if not SCons.Util.is_List(path): + path = [path] + + def s(obj): + """This is the "string conversion" routine that we have our + substitutions use to return Nodes, not strings. This relies + on the fact that an EntryProxy object has a get() method that + returns the underlying Node that it wraps, which is a bit of + architectural dependence that we might need to break or modify + in the future in response to additional requirements.""" + try: + get = obj.get + except AttributeError: + pass + else: + obj = get() + return obj + + r = [] + for p in path: + if SCons.Util.is_String(p): + p = self.subst(p, conv=s) + if SCons.Util.is_List(p): + p = p[0] + else: + p = s(p) + r.append(p) + return r + + def _update(self, dict): + """Update an environment's values directly, bypassing the normal + checks that occur when users try to set items. + """ + self._dict.update(dict) + + def use_build_signature(self): + try: + return self._build_signature + except AttributeError: + b = SCons.Defaults.DefaultEnvironment()._build_signature + self._build_signature = b + return b + + ####################################################################### + # Public methods for manipulating an Environment. These begin with + # upper-case letters. The essential characteristic of methods in + # this section is that they do *not* have corresponding same-named + # global functions. For example, a stand-alone Append() function + # makes no sense, because Append() is all about appending values to + # an Environment's construction variables. + ####################################################################### + + def Append(self, **kw): + """Append values to existing construction variables + in an Environment. + """ + kw = our_deepcopy(kw) + for key in kw.keys(): + if not self._dict.has_key(key): + self._dict[key] = kw[key] + elif SCons.Util.is_List(self._dict[key]) and not \ + SCons.Util.is_List(kw[key]): + if kw[key]: + self._dict[key] = self._dict[key] + [ kw[key] ] + #self._dict[key] = map(None, self._dict[key] + [ kw[key] ]) + elif SCons.Util.is_List(kw[key]) and not \ + SCons.Util.is_List(self._dict[key]): + if self._dict[key]: + self._dict[key] = [ self._dict[key] ] + kw[key] + else: + self._dict[key] = kw[key] + #self._dict[key] = map(None, self._dict[key] + [ kw[key] ]) + elif SCons.Util.is_Dict(self._dict[key]) and \ + SCons.Util.is_Dict(kw[key]): + self._dict[key].update(kw[key]) + else: + self._dict[key] = self._dict[key] + kw[key] + + def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): + """Append path elements to the path 'name' in the 'ENV' + dictionary for this environment. Will only add any particular + path once, and will normpath and normcase all paths to help + assure this. This can also handle the case where the env + variable is a list instead of a string. + """ + + orig = '' + if self._dict.has_key(envname) and self._dict[envname].has_key(name): + orig = self._dict[envname][name] + + nv = SCons.Util.AppendPath(orig, newpath, sep) + + if not self._dict.has_key(envname): + self._dict[envname] = {} + + self._dict[envname][name] = nv + + def AppendUnique(self, **kw): + """Append values to existing construction variables + in an Environment, if they're not already there. + """ + kw = our_deepcopy(kw) + for key, val in kw.items(): + if not self._dict.has_key(key): + self._dict[key] = val + elif SCons.Util.is_Dict(self._dict[key]) and \ + SCons.Util.is_Dict(val): + self._dict[key].update(val) + elif SCons.Util.is_List(val): + dk = self._dict[key] + if not SCons.Util.is_List(dk): + dk = [dk] + val = filter(lambda x, dk=dk: x not in dk, val) + self._dict[key] = dk + val + else: + dk = self._dict[key] + if SCons.Util.is_List(dk): + if not val in dk: + self._dict[key] = dk + val + else: + self._dict[key] = self._dict[key] + val + + def Copy(self, tools=None, toolpath=[], **kw): + """Return a copy of a construction Environment. The + copy is like a Python "deep copy"--that is, independent + copies are made recursively of each objects--except that + a reference is copied when an object is not deep-copyable + (like a function). There are no references to any mutable + objects in the original Environment. + """ + clone = copy.copy(self) + clone._dict = our_deepcopy(self._dict) + clone['__env__'] = clone + try: + cbd = clone._dict['BUILDERS'] + clone._dict['BUILDERS'] = BuilderDict(cbd, clone) + except KeyError: + pass + + apply_tools(clone, tools, toolpath) + + # Apply passed-in variables after the new tools. + new = {} + for key, value in kw.items(): + new[key] = SCons.Util.scons_subst_once(value, self, key) + apply(clone.Replace, (), new) + return clone + + def Detect(self, progs): + """Return the first available program in progs. + """ + if not SCons.Util.is_List(progs): + progs = [ progs ] + for prog in progs: + path = self.WhereIs(prog) + if path: return prog + return None + + def Dictionary(self, *args): + if not args: + return self._dict + dlist = map(lambda x, s=self: s._dict[x], args) + if len(dlist) == 1: + dlist = dlist[0] + return dlist + + def FindIxes(self, paths, prefix, suffix): + """ + Search a list of paths for something that matches the prefix and suffix. + + paths - the list of paths or nodes. + prefix - construction variable for the prefix. + suffix - construction variable for the suffix. + """ + + suffix = self.subst('$'+suffix) + prefix = self.subst('$'+prefix) + + for path in paths: + dir,name = os.path.split(str(path)) + if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: + return path + + def Override(self, overrides): + """ + Produce a modified environment whose variables + are overriden by the overrides dictionaries. + + overrides - a dictionary that will override + the variables of this environment. + + This function is much more efficient than Copy() + or creating a new Environment because it doesn't do + a deep copy of the dictionary, and doesn't do a copy + at all if there are no overrides. + """ + + if overrides: + env = copy.copy(self) + env._dict = copy.copy(self._dict) + env['__env__'] = env + new = {} + for key, value in overrides.items(): + new[key] = SCons.Util.scons_subst_once(value, self, key) + env._dict.update(new) + return env + else: + return self + + def ParseConfig(self, command, function=None): + """ + Use the specified function to parse the output of the command + in order to modify the current environment. The 'command' + can be a string or a list of strings representing a command and + it's arguments. 'Function' is an optional argument that takes + the environment and the output of the command. If no function is + specified, the output will be treated as the output of a typical + 'X-config' command (i.e. gtk-config) and used to set the CPPPATH, + LIBPATH, LIBS, and CCFLAGS variables. + """ + + # the default parse function + def parse_conf(env, output): + dict = { + 'CPPPATH' : [], + 'LIBPATH' : [], + 'LIBS' : [], + 'CCFLAGS' : [], + } + static_libs = [] + + params = string.split(output) + for arg in params: + switch = arg[0:1] + opt = arg[1:2] + if switch == '-': + if opt == 'L': + dict['LIBPATH'].append(arg[2:]) + elif opt == 'l': + dict['LIBS'].append(arg[2:]) + elif opt == 'I': + dict['CPPPATH'].append(arg[2:]) + else: + dict['CCFLAGS'].append(arg) + else: + static_libs.append(arg) + apply(env.Append, (), dict) + return static_libs + + if function is None: + function = parse_conf + if type(command) is type([]): + command = string.join(command) + command = self.subst(command) + return function(self, os.popen(command).read()) + + def Platform(self, platform): + platform = self.subst(platform) + return SCons.Platform.Platform(platform)(self) + + def Prepend(self, **kw): + """Prepend values to existing construction variables + in an Environment. + """ + kw = our_deepcopy(kw) + for key in kw.keys(): + if not self._dict.has_key(key): + self._dict[key] = kw[key] + elif SCons.Util.is_List(self._dict[key]) and not \ + SCons.Util.is_List(kw[key]): + self._dict[key] = [ kw[key] ] + self._dict[key] + elif SCons.Util.is_List(kw[key]) and not \ + SCons.Util.is_List(self._dict[key]): + self._dict[key] = kw[key] + [ self._dict[key] ] + elif SCons.Util.is_Dict(self._dict[key]) and \ + SCons.Util.is_Dict(kw[key]): + self._dict[key].update(kw[key]) + else: + self._dict[key] = kw[key] + self._dict[key] + + def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): + """Prepend path elements to the path 'name' in the 'ENV' + dictionary for this environment. Will only add any particular + path once, and will normpath and normcase all paths to help + assure this. This can also handle the case where the env + variable is a list instead of a string. + """ + + orig = '' + if self._dict.has_key(envname) and self._dict[envname].has_key(name): + orig = self._dict[envname][name] + + nv = SCons.Util.PrependPath(orig, newpath, sep) + + if not self._dict.has_key(envname): + self._dict[envname] = {} + + self._dict[envname][name] = nv + + def PrependUnique(self, **kw): + """Append values to existing construction variables + in an Environment, if they're not already there. + """ + kw = our_deepcopy(kw) + for key, val in kw.items(): + if not self._dict.has_key(key): + self._dict[key] = val + elif SCons.Util.is_Dict(self._dict[key]) and \ + SCons.Util.is_Dict(val): + self._dict[key].update(val) + elif SCons.Util.is_List(val): + dk = self._dict[key] + if not SCons.Util.is_List(dk): + dk = [dk] + val = filter(lambda x, dk=dk: x not in dk, val) + self._dict[key] = val + dk + else: + dk = self._dict[key] + if SCons.Util.is_List(dk): + if not val in dk: + self._dict[key] = val + dk + else: + self._dict[key] = val + dk + + def Replace(self, **kw): + """Replace existing construction variables in an Environment + with new construction variables and/or values. + """ + try: + kwbd = our_deepcopy(kw['BUILDERS']) + del kw['BUILDERS'] + self.__setitem__('BUILDERS', kwbd) + except KeyError: + pass + self._dict.update(our_deepcopy(kw)) + + def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix): + """ + Replace old_prefix with new_prefix and old_suffix with new_suffix. + + env - Environment used to interpolate variables. + path - the path that will be modified. + old_prefix - construction variable for the old prefix. + old_suffix - construction variable for the old suffix. + new_prefix - construction variable for the new prefix. + new_suffix - construction variable for the new suffix. + """ + old_prefix = self.subst('$'+old_prefix) + old_suffix = self.subst('$'+old_suffix) + + new_prefix = self.subst('$'+new_prefix) + new_suffix = self.subst('$'+new_suffix) + + dir,name = os.path.split(str(path)) + if name[:len(old_prefix)] == old_prefix: + name = name[len(old_prefix):] + if name[-len(old_suffix):] == old_suffix: + name = name[:-len(old_suffix)] + return os.path.join(dir, new_prefix+name+new_suffix) + + def Tool(self, tool, toolpath=[]): + tool = self.subst(tool) + return SCons.Tool.Tool(tool, map(self.subst, toolpath))(self) + + def WhereIs(self, prog, path=None, pathext=None): + """Find prog in the path. + """ + if path is None: + try: + path = self['ENV']['PATH'] + except KeyError: + pass + elif SCons.Util.is_String(path): + path = self.subst(path) + if pathext is None: + try: + pathext = self['ENV']['PATHEXT'] + except KeyError: + pass + elif SCons.Util.is_String(pathext): + pathext = self.subst(pathext) + path = SCons.Util.WhereIs(prog, path, pathext) + if path: return path + return None + + ####################################################################### + # Public methods for doing real "SCons stuff" (manipulating + # dependencies, setting attributes on targets, etc.). These begin + # with upper-case letters. The essential characteristic of methods + # in this section is that they all *should* have corresponding + # same-named global functions. + ####################################################################### + + def Action(self, *args, **kw): + nargs = self.subst(args) + nkw = self.subst_kw(kw) + return apply(SCons.Action.Action, nargs, nkw) + + def AddPreAction(self, files, action): + nodes = self.arg2nodes(files, self.fs.Entry) + action = SCons.Action.Action(action) + for n in nodes: + n.add_pre_action(action) + return nodes + + def AddPostAction(self, files, action): + nodes = self.arg2nodes(files, self.fs.Entry) + action = SCons.Action.Action(action) + for n in nodes: + n.add_post_action(action) + return nodes + + def Alias(self, target, *source, **kw): + if not SCons.Util.is_List(target): + target = [target] + tlist = [] + for t in target: + if not isinstance(t, SCons.Node.Alias.Alias): + t = self.arg2nodes(self.subst(t), self.ans.Alias)[0] + tlist.append(t) + try: + s = kw['source'] + except KeyError: + try: + s = source[0] + except IndexError: + s = None + if s: + if not SCons.Util.is_List(s): + s = [s] + s = filter(None, s) + s = self.arg2nodes(s, self.fs.Entry) + for t in tlist: + AliasBuilder(self, t, s) + if len(tlist) == 1: + tlist = tlist[0] + return tlist + + def AlwaysBuild(self, *targets): + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.File)) + + for t in tlist: + t.set_always_build() + + if len(tlist) == 1: + tlist = tlist[0] + return tlist + + def BuildDir(self, build_dir, src_dir, duplicate=1): + build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0] + src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] + self.fs.BuildDir(build_dir, src_dir, duplicate) + + def Builder(self, **kw): + nkw = self.subst_kw(kw) + return apply(SCons.Builder.Builder, [], nkw) + + def CacheDir(self, path): + self.fs.CacheDir(self.subst(path)) + + def Clean(self, target, files): + global CleanTargets + + if not isinstance(target, SCons.Node.Node): + target = self.subst(target) + target = self.fs.Entry(target, create=1) + + if not SCons.Util.is_List(files): + files = [files] + + nodes = [] + for f in files: + if isinstance(f, SCons.Node.Node): + nodes.append(f) + else: + nodes.extend(self.arg2nodes(f, self.fs.Entry)) + + try: + CleanTargets[target].extend(nodes) + except KeyError: + CleanTargets[target] = nodes + + def Configure(self, *args, **kw): + nargs = [self] + if args: + nargs = nargs + self.subst_list(args)[0] + nkw = self.subst_kw(kw) + try: + nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) + except KeyError: + pass + return apply(SCons.SConf.SConf, nargs, nkw) + + def Command(self, target, source, action): + """Builds the supplied target files from the supplied + source files using the supplied action. Action may + be any type that the Builder constructor will accept + for an action.""" + bld = SCons.Builder.Builder(action=action, + source_factory=self.fs.Entry) + return bld(self, target, source) + + def Depends(self, target, dependency): + """Explicity specify that 'target's depend on 'dependency'.""" + tlist = self.arg2nodes(target, self.fs.Entry) + dlist = self.arg2nodes(dependency, self.fs.Entry) + for t in tlist: + t.add_dependency(dlist) + + if len(tlist) == 1: + tlist = tlist[0] + return tlist + + def Dir(self, name, *args, **kw): + """ + """ + return apply(self.fs.Dir, (self.subst(name),) + args, kw) + + def Environment(self, **kw): + return apply(SCons.Environment.Environment, [], self.subst_kw(kw)) + + def File(self, name, *args, **kw): + """ + """ + return apply(self.fs.File, (self.subst(name),) + args, kw) + + def FindFile(self, file, dirs): + file = self.subst(file) + nodes = self.arg2nodes(dirs, self.fs.Dir) + return SCons.Node.FS.find_file(file, nodes, self.fs.File) + + def GetBuildPath(self, files): + ret = map(str, self.arg2nodes(files, self.fs.Entry)) + if len(ret) == 1: + return ret[0] + return ret + + def Ignore(self, target, dependency): + """Ignore a dependency.""" + tlist = self.arg2nodes(target, self.fs.Entry) + dlist = self.arg2nodes(dependency, self.fs.Entry) + for t in tlist: + t.add_ignore(dlist) + + if len(tlist) == 1: + tlist = tlist[0] + return tlist + + def Install(self, dir, source): + """Install specified files in the given directory.""" + try: + dnodes = self.arg2nodes(dir, self.fs.Dir) + except TypeError: + raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir) + try: + sources = self.arg2nodes(source, self.fs.File) + except TypeError: + if SCons.Util.is_List(source): + raise SCons.Errors.UserError, "Source `%s' of Install() contains one or more non-files. Install() source must be one or more files." % repr(map(str, source)) + else: + raise SCons.Errors.UserError, "Source `%s' of Install() is not a file. Install() source must be one or more files." % str(source) + tgt = [] + for dnode in dnodes: + for src in sources: + target = self.fs.File(src.name, dnode) + tgt.append(InstallBuilder(self, target, src)) + if len(tgt) == 1: + tgt = tgt[0] + return tgt + + def InstallAs(self, target, source): + """Install sources as targets.""" + sources = self.arg2nodes(source, self.fs.File) + targets = self.arg2nodes(target, self.fs.File) + ret = [] + for src, tgt in map(lambda x, y: (x, y), sources, targets): + ret.append(InstallBuilder(self, tgt, src)) + if len(ret) == 1: + ret = ret[0] + return ret + + def Literal(self, string): + return SCons.Util.Literal(string) + + def Local(self, *targets): + ret = [] + for targ in targets: + if isinstance(targ, SCons.Node.Node): + targ.set_local() + ret.append(targ) + else: + for t in self.arg2nodes(targ, self.fs.Entry): + t.set_local() + ret.append(t) + return ret + + def Precious(self, *targets): + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + + for t in tlist: + t.set_precious() + + if len(tlist) == 1: + tlist = tlist[0] + return tlist + + def Repository(self, *dirs, **kw): + dirs = self.arg2nodes(list(dirs), self.fs.Dir) + apply(self.fs.Repository, dirs, kw) + + def Scanner(self, *args, **kw): + nargs = [] + for arg in args: + if SCons.Util.is_String(arg): + arg = self.subst(arg) + nargs.append(arg) + nkw = self.subst_kw(kw) + return apply(SCons.Scanner.Base, nargs, nkw) + + def SConsignFile(self, name=".sconsign.dbm", dbm_module=None): + name = self.subst(name) + if not os.path.isabs(name): + name = os.path.join(str(self.fs.SConstruct_dir), name) + SCons.Sig.SConsignFile(name, dbm_module) + + def SideEffect(self, side_effect, target): + """Tell scons that side_effects are built as side + effects of building targets.""" + side_effects = self.arg2nodes(side_effect, self.fs.Entry) + targets = self.arg2nodes(target, self.fs.Entry) + + for side_effect in side_effects: + # A builder of 1 means the node is supposed to appear + # buildable without actually having a builder, so we allow + # it to be a side effect as well. + if side_effect.has_builder() and side_effect.builder != 1: + raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect) + side_effect.add_source(targets) + side_effect.side_effect = 1 + self.Precious(side_effect) + for target in targets: + target.side_effects.append(side_effect) + if len(side_effects) == 1: + return side_effects[0] + else: + return side_effects + + def SourceCode(self, entry, builder): + """Arrange for a source code builder for (part of) a tree.""" + entries = self.arg2nodes(entry, self.fs.Entry) + for entry in entries: + entry.set_src_builder(builder) + if len(entries) == 1: + return entries[0] + return entries + + def SourceSignatures(self, type): + type = self.subst(type) + if type == 'MD5': + import SCons.Sig.MD5 + self._calc_module = SCons.Sig.MD5 + elif type == 'timestamp': + import SCons.Sig.TimeStamp + self._calc_module = SCons.Sig.TimeStamp + else: + raise UserError, "Unknown source signature type '%s'"%type + + def Split(self, arg): + """This function converts a string or list into a list of strings + or Nodes. This makes things easier for users by allowing files to + be specified as a white-space separated list to be split. + The input rules are: + - A single string containing names separated by spaces. These will be + split apart at the spaces. + - A single Node instance + - A list containing either strings or Node instances. Any strings + in the list are not split at spaces. + In all cases, the function returns a list of Nodes and strings.""" + if SCons.Util.is_List(arg): + return map(self.subst, arg) + elif SCons.Util.is_String(arg): + return string.split(self.subst(arg)) + else: + return [self.subst(arg)] + + def TargetSignatures(self, type): + type = self.subst(type) + if type == 'build': + self._build_signature = 1 + elif type == 'content': + self._build_signature = 0 + else: + raise SCons.Errors.UserError, "Unknown target signature type '%s'"%type + + def Value(self, value): + """ + """ + return SCons.Node.Python.Value(value) + +# The entry point that will be used by the external world +# to refer to a construction environment. This allows the wrapper +# interface to extend a construction environment for its own purposes +# by subclassing SCons.Environment.Base and then assigning the +# class to SCons.Environment.Environment. + +Environment = Base diff --git a/scons/scons-local-0.95/SCons/Errors.py b/scons/scons-local-0.95/SCons/Errors.py new file mode 100644 index 0000000..e6349fe --- /dev/null +++ b/scons/scons-local-0.95/SCons/Errors.py @@ -0,0 +1,70 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +"""SCons.Errors + +This file contains the exception classes used to handle internal +and user errors in SCons. + +""" + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Errors.py 0.95.D001 2004/03/08 07:28:28 knight" + + + +class BuildError(Exception): + def __init__(self, node=None, errstr="Unknown error", *args): + self.node = node + self.errstr = errstr + self.args = args + +class InternalError(Exception): + def __init__(self, args=None): + self.args = args + +class UserError(Exception): + def __init__(self, args=None): + self.args = args + +class StopError(Exception): + def __init__(self, args=None): + self.args = args + +class ExplicitExit(Exception): + def __init__(self, node=None, status=None, *args): + self.node = node + self.status = status + self.args = args + +class ConfigureDryRunError(UserError): + """Raised when a file needs to be updated during a Configure process, + but the user requested a dry-run""" + def __init__(self,file): + UserError.__init__(self,"Cannot update configure test (%s) within a dry-run." % str(file)) + +class TaskmasterException(Exception): + def __init__(self, type, value, traceback, *args): + self.type = type + self.value = value + self.traceback = traceback + self.args = args diff --git a/scons/scons-local-0.95/SCons/Executor.py b/scons/scons-local-0.95/SCons/Executor.py new file mode 100644 index 0000000..c5ca4c6 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Executor.py @@ -0,0 +1,171 @@ +"""SCons.Executor + +A module for executing actions with specific lists of target and source +Nodes. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Executor.py 0.95.D001 2004/03/08 07:28:28 knight" + + +from SCons.Debug import logInstanceCreation +import SCons.Util + + +class Executor: + """A class for controlling instances of executing an action. + + This largely exists to hold a single association of a builder, + environment, environment overrides, targets and sources for later + processing as needed. + """ + + def __init__(self, builder, env, overrides, targets, sources): + if __debug__: logInstanceCreation(self) + self.builder = builder + self.env = env + self.overrides = overrides + self.targets = targets + self.sources = sources[:] + + def get_build_env(self): + """Fetch or create the appropriate build Environment + for this Executor. + """ + try: + return self.build_env + except AttributeError: + if self.env is None: + # There was no Environment specifically associated with + # this set of targets (which kind of implies that it + # is--or they are--source files, but who knows...). + # So use the environment associated with the Builder + # itself. + env = self.builder.env + else: + # The normal case: use the Environment that was + # used to specify how these targets will be built. + env = self.env + + # Create the build environment instance with appropriate + # overrides. These get evaluated against the current + # environment's construction variables so that users can + # add to existing values by referencing the variable in + # the expansion. + overrides = {} + overrides.update(self.builder.overrides) + overrides.update(self.overrides) + try: + generate_build_dict = self.targets[0].generate_build_dict + except AttributeError: + pass + else: + overrides.update(generate_build_dict()) + self.build_env = env.Override(overrides) + + # Now update the build environment with the things that we + # don't want expanded against the current construction + # variables. + self.build_env._update(SCons.Util.subst_dict(self.targets, + self.sources)) + return self.build_env + + def get_action_list(self, target): + """Fetch or create the appropriate action list (for this target). + + There is an architectural mistake here: we cache the action list + for the Executor and re-use it regardless of which target is + being asked for. In practice, this doesn't seem to be a problem + because executing the action list will update all of the targets + involved, so only one target's pre- and post-actions will win, + anyway. This is probably a bug we should fix... + """ + try: + al = self.action_list + except AttributeError: + al = self.builder.action.get_actions() + self.action_list = al + # XXX shouldn't reach into node attributes like this + return target.pre_actions + al + target.post_actions + + def __call__(self, target, func): + """Actually execute the action list.""" + action_list = self.get_action_list(target) + if not action_list: + return + env = self.get_build_env() + for action in action_list: + func(action, self.targets, self.sources, env) + + def cleanup(self): + try: + del self.build_env + except AttributeError: + pass + + def add_sources(self, sources): + """Add source files to this Executor's list. This is necessary + for "multi" Builders that can be called repeatedly to build up + a source file list for a given target.""" + slist = filter(lambda x, s=self.sources: x not in s, sources) + self.sources.extend(slist) + + def get_raw_contents(self): + """Fetch the raw signature contents. This, along with + get_contents(), is the real reason this class exists, so we can + compute this once and cache it regardless of how many target or + source Nodes there are. + """ + try: + return self.raw_contents + except AttributeError: + action = self.builder.action + self.raw_contents = action.get_raw_contents(self.targets, + self.sources, + self.get_build_env()) + return self.raw_contents + + def get_contents(self): + """Fetch the signature contents. This, along with + get_raw_contents(), is the real reason this class exists, so we + can compute this once and cache it regardless of how many target + or source Nodes there are. + """ + try: + return self.contents + except AttributeError: + action = self.builder.action + self.contents = action.get_contents(self.targets, + self.sources, + self.get_build_env()) + return self.contents + + def get_timestamp(self): + """Fetch a time stamp for this Executor. We don't have one, of + course (only files do), but this is the interface used by the + timestamp module. + """ + return 0 diff --git a/scons/scons-local-0.95/SCons/Job.py b/scons/scons-local-0.95/SCons/Job.py new file mode 100644 index 0000000..e5dc1c7 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Job.py @@ -0,0 +1,257 @@ +"""SCons.Job + +This module defines the Serial and Parallel classes that execute tasks to +complete a build. The Jobs class provides a higher level interface to start, +stop, and wait on jobs. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Job.py 0.95.D001 2004/03/08 07:28:28 knight" + +class Jobs: + """An instance of this class initializes N jobs, and provides + methods for starting, stopping, and waiting on all N jobs. + """ + + def __init__(self, num, taskmaster): + """ + create 'num' jobs using the given taskmaster. + + If 'num' is 1 or less, then a serial job will be used, + otherwise a parallel job with 'num' worker threads will + be used. + + The 'num_jobs' attribute will be set to the actual number of jobs + allocated. If more than one job is requested but the Parallel + class can't do it, it gets reset to 1. Wrapping interfaces that + care should check the value of 'num_jobs' after initialization. + """ + + self.job = None + if num > 1: + try: + self.job = Parallel(taskmaster, num) + self.num_jobs = num + except NameError: + pass + if self.job is None: + self.job = Serial(taskmaster) + self.num_jobs = 1 + + def run(self): + """run the job""" + try: + self.job.start() + except KeyboardInterrupt: + # mask any further keyboard interrupts so that scons + # can shutdown cleanly: + # (this only masks the keyboard interrupt for Python, + # child processes can still get the keyboard interrupt) + import signal + signal.signal(signal.SIGINT, signal.SIG_IGN) + raise + +class Serial: + """This class is used to execute tasks in series, and is more efficient + than Parallel, but is only appropriate for non-parallel builds. Only + one instance of this class should be in existence at a time. + + This class is not thread safe. + """ + + def __init__(self, taskmaster): + """Create a new serial job given a taskmaster. + + The taskmaster's next_task() method should return the next task + that needs to be executed, or None if there are no more tasks. The + taskmaster's executed() method will be called for each task when it + is successfully executed or failed() will be called if it failed to + execute (e.g. execute() raised an exception). The taskmaster's + is_blocked() method will not be called. """ + + self.taskmaster = taskmaster + + def start(self): + """Start the job. This will begin pulling tasks from the taskmaster + and executing them, and return when there are no more tasks. If a task + fails to execute (i.e. execute() raises an exception), then the job will + stop.""" + + while 1: + task = self.taskmaster.next_task() + + if task is None: + break + + try: + task.prepare() + task.execute() + except KeyboardInterrupt: + raise + except: + # Let the failed() callback function arrange for the + # build to stop if that's appropriate. + task.failed() + else: + task.executed() + + task.postprocess() + + +# Trap import failure so that everything in the Job module but the +# Parallel class (and its dependent classes) will work if the interpreter +# doesn't support threads. +try: + import Queue + import threading +except ImportError: + pass +else: + class Worker(threading.Thread): + """A worker thread waits on a task to be posted to its request queue, + dequeues the task, executes it, and posts a tuple including the task + and a boolean indicating whether the task executed successfully. """ + + def __init__(self, requestQueue, resultsQueue): + threading.Thread.__init__(self) + self.setDaemon(1) + self.requestQueue = requestQueue + self.resultsQueue = resultsQueue + self.start() + + def run(self): + while 1: + task = self.requestQueue.get() + + try: + task.execute() + except KeyboardInterrupt: + # be explicit here for test/interrupts.py + ok = False + except: + ok = 0 + else: + ok = 1 + + self.resultsQueue.put((task, ok)) + + class ThreadPool: + """This class is responsible for spawning and managing worker threads.""" + + def __init__(self, num): + """Create the request and reply queues, and 'num' worker threads.""" + self.requestQueue = Queue.Queue(0) + self.resultsQueue = Queue.Queue(0) + + # Create worker threads + for i in range(num): + worker = Worker(self.requestQueue, self.resultsQueue) + + def put(self, obj): + """Put task into request queue.""" + self.requestQueue.put(obj) + + def get(self, block = 1): + """Remove and return a result tuple from the results queue.""" + return self.resultsQueue.get(block) + + def get_nowait(self): + """Remove and result a result tuple from the results queue + without blocking.""" + return self.get(0) + + class Parallel: + """This class is used to execute tasks in parallel, and is somewhat + less efficient than Serial, but is appropriate for parallel builds. + + This class is thread safe. + """ + + def __init__(self, taskmaster, num): + """Create a new parallel job given a taskmaster. + + The taskmaster's next_task() method should return the next task + that needs to be executed, or None if there are no more tasks. The + taskmaster's executed() method will be called for each task when it + is successfully executed or failed() will be called if the task + failed to execute (i.e. execute() raised an exception). The + taskmaster's is_blocked() method should return true iff there are + more tasks, but they can't be executed until one or more other + tasks have been executed. next_task() will be called iff + is_blocked() returned false. + + Note: calls to taskmaster are serialized, but calls to execute() on + distinct tasks are not serialized, because that is the whole point + of parallel jobs: they can execute multiple tasks + simultaneously. """ + + self.taskmaster = taskmaster + self.tp = ThreadPool(num) + + self.jobs = 0 + self.maxjobs = num + + def start(self): + """Start the job. This will begin pulling tasks from the + taskmaster and executing them, and return when there are no + more tasks. If a task fails to execute (i.e. execute() raises + an exception), then the job will stop.""" + + while 1: + if self.jobs < self.maxjobs: + task = self.taskmaster.next_task() + if task is None: + break + + # prepare task for execution + try: + task.prepare() + except KeyboardInterrupt: + raise + except: + # Let the failed() callback function arrange for the + # build to stop if that's appropriate. + task.failed() + + # dispatch task + self.tp.put(task) + self.jobs = self.jobs + 1 + + while 1: + try: + task, ok = self.tp.get_nowait() + except Queue.Empty: + if not (self.jobs is self.maxjobs or self.taskmaster.is_blocked()): + break + task, ok = self.tp.get() + + self.jobs = self.jobs - 1 + if ok: + task.executed() + else: + task.failed() + + task.postprocess() diff --git a/scons/scons-local-0.95/SCons/Node/Alias.py b/scons/scons-local-0.95/SCons/Node/Alias.py new file mode 100644 index 0000000..2b73c78 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Node/Alias.py @@ -0,0 +1,101 @@ + +"""scons.Node.Alias + +Alias nodes. + +This creates a hash of global Aliases (dummy targets). + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Node/Alias.py 0.95.D001 2004/03/08 07:28:28 knight" + +import UserDict + +import SCons.Errors +import SCons.Node +import SCons.Util + +class AliasNameSpace(UserDict.UserDict): + def Alias(self, name): + if self.has_key(name): + raise SCons.Errors.UserError + self[name] = SCons.Node.Alias.Alias(name) + return self[name] + + def lookup(self, name): + try: + return self[name] + except KeyError: + return None + +class Alias(SCons.Node.Node): + def __init__(self, name): + SCons.Node.Node.__init__(self) + self.name = name + + def __str__(self): + return self.name + + def build(self): + """A "builder" for aliases.""" + pass + + def current(self, calc): + """If all of our children were up-to-date, then this + Alias was up-to-date, too.""" + # Allow the children to calculate their signatures. + calc.bsig(self) + state = 0 + for kid in self.children(None): + s = kid.get_state() + if s and (not state or s > state): + state = s + if state == 0 or state == SCons.Node.up_to_date: + return 1 + else: + return 0 + + def sconsign(self): + """An Alias is not recorded in .sconsign files""" + pass + + def is_under(self, dir): + # Make Alias nodes get built regardless of + # what directory scons was run from. Alias nodes + # are outside the filesystem: + return 1 + + def get_contents(self): + """The contents of an alias is the concatenation + of all the contents of its sources""" + contents = "" + for kid in self.children(None): + contents = contents + kid.get_contents() + return contents + +default_ans = AliasNameSpace() + +SCons.Node.arg2nodes_lookups.append(default_ans.lookup) diff --git a/scons/scons-local-0.95/SCons/Node/FS.py b/scons/scons-local-0.95/SCons/Node/FS.py new file mode 100644 index 0000000..b33e4c9 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Node/FS.py @@ -0,0 +1,1706 @@ +"""scons.Node.FS + +File system nodes. + +These Nodes represent the canonical external objects that people think +of when they think of building software: files and directories. + +This initializes a "default_fs" Node with an FS at the current directory +for its own purposes, and for use by scripts or modules looking for the +canonical default. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Node/FS.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import shutil +import stat +import string +import sys +import cStringIO + +import SCons.Action +from SCons.Debug import logInstanceCreation +import SCons.Errors +import SCons.Node +import SCons.Sig.MD5 +import SCons.Util +import SCons.Warnings + +# +# SCons.Action objects for interacting with the outside world. +# +# The Node.FS methods in this module should use these actions to +# create and/or remove files and directories; they should *not* use +# os.{link,symlink,unlink,mkdir}(), etc., directly. +# +# Using these SCons.Action objects ensures that descriptions of these +# external activities are properly displayed, that the displays are +# suppressed when the -s (silent) option is used, and (most importantly) +# the actions are disabled when the the -n option is used, in which case +# there should be *no* changes to the external file system(s)... +# + +def _copy_func(src, dest): + shutil.copy2(src, dest) + st=os.stat(src) + os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + +Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', + 'hard-copy', 'soft-copy', 'copy'] + +Link_Funcs = [] # contains the callables of the specified duplication style + +def set_duplicate(duplicate): + # Fill in the Link_Funcs list according to the argument + # (discarding those not available on the platform). + + # Set up the dictionary that maps the argument names to the + # underlying implementations. We do this inside this function, + # not in the top-level module code, so that we can remap os.link + # and os.symlink for testing purposes. + try: + _hardlink_func = os.link + except AttributeError: + _hardlink_func = None + + try: + _softlink_func = os.symlink + except AttributeError: + _softlink_func = None + + link_dict = { + 'hard' : _hardlink_func, + 'soft' : _softlink_func, + 'copy' : _copy_func + } + + if not duplicate in Valid_Duplicates: + raise SCons.Errors.InternalError, ("The argument of set_duplicate " + "should be in Valid_Duplicates") + global Link_Funcs + Link_Funcs = [] + for func in string.split(duplicate,'-'): + if link_dict[func]: + Link_Funcs.append(link_dict[func]) + +def LinkFunc(target, source, env): + # Relative paths cause problems with symbolic links, so + # we use absolute paths, which may be a problem for people + # who want to move their soft-linked src-trees around. Those + # people should use the 'hard-copy' mode, softlinks cannot be + # used for that; at least I have no idea how ... + src = source[0].abspath + dest = target[0].abspath + dir, file = os.path.split(dest) + if dir and not os.path.isdir(dir): + os.makedirs(dir) + if not Link_Funcs: + # Set a default order of link functions. + set_duplicate('hard-soft-copy') + # Now link the files with the previously specified order. + for func in Link_Funcs: + try: + func(src,dest) + break + except OSError: + if func == Link_Funcs[-1]: + # exception of the last link method (copy) are fatal + raise + else: + pass + return 0 + +Link = SCons.Action.Action(LinkFunc, None) +def LocalString(target, source, env): + return 'Local copy of %s from %s' % (target[0], source[0]) + +LocalCopy = SCons.Action.Action(LinkFunc, LocalString) + +def UnlinkFunc(target, source, env): + t = target[0] + t.fs.unlink(t.path) + return 0 + +Unlink = SCons.Action.Action(UnlinkFunc, None) + +def MkdirFunc(target, source, env): + t = target[0] + t.fs.mkdir(t.path) + return 0 + +Mkdir = SCons.Action.Action(MkdirFunc, None) + +def CacheRetrieveFunc(target, source, env): + t = target[0] + fs = t.fs + cachedir, cachefile = t.cachepath() + if fs.exists(cachefile): + fs.copy2(cachefile, t.path) + st = fs.stat(cachefile) + fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + return 0 + return 1 + +def CacheRetrieveString(target, source, env): + t = target[0] + cachedir, cachefile = t.cachepath() + if t.fs.exists(cachefile): + return "Retrieved `%s' from cache" % t.path + return None + +CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) + +CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) + +def CachePushFunc(target, source, env): + t = target[0] + fs = t.fs + cachedir, cachefile = t.cachepath() + if fs.exists(cachefile): + # Don't bother copying it if it's already there. + return + + if not fs.isdir(cachedir): + fs.mkdir(cachedir) + + tempfile = cachefile+'.tmp' + try: + fs.copy2(t.path, tempfile) + fs.rename(tempfile, cachefile) + st = fs.stat(t.path) + fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + except OSError: + # It's possible someone else tried writing the file at the same + # time we did. Print a warning but don't stop the build, since + # it doesn't affect the correctness of the build. + SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, + "Unable to copy %s to cache. Cache file is %s" + % (str(target), cachefile)) + return + +CachePush = SCons.Action.Action(CachePushFunc, None) + +class _Null: + pass + +_null = _Null() + +DefaultSCCSBuilder = None +DefaultRCSBuilder = None + +def get_DefaultSCCSBuilder(): + global DefaultSCCSBuilder + if DefaultSCCSBuilder is None: + import SCons.Builder + import SCons.Defaults + DefaultSCCSBuilder = SCons.Builder.Builder(action = '$SCCSCOM', + env = SCons.Defaults.DefaultEnvironment()) + return DefaultSCCSBuilder + +def get_DefaultRCSBuilder(): + global DefaultRCSBuilder + if DefaultRCSBuilder is None: + import SCons.Builder + import SCons.Defaults + DefaultRCSBuilder = SCons.Builder.Builder(action = '$RCS_COCOM', + env = SCons.Defaults.DefaultEnvironment()) + return DefaultRCSBuilder + +# +class ParentOfRoot: + """ + An instance of this class is used as the parent of the root of a + filesystem (POSIX) or drive (Win32). This isn't actually a node, + but it looks enough like one so that we don't have to have + special purpose code everywhere to deal with dir being None. + This class is an instance of the Null object pattern. + """ + def __init__(self): + self.abspath = '' + self.path = '' + self.abspath_ = '' + self.path_ = '' + self.name='' + self.duplicate=0 + self.srcdir=None + self.build_dirs=[] + + def is_under(self, dir): + return 0 + + def up(self): + return None + + def getRepositories(self): + return [] + + def get_dir(self): + return None + + def recurse_get_path(self, dir, path_elems): + return path_elems + + def src_builder(self): + return _null + +# Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. +_is_cygwin = sys.platform == "cygwin" +if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: + def _my_normcase(x): + return x +else: + def _my_normcase(x): + return string.upper(x) + +class EntryProxy(SCons.Util.Proxy): + def __get_abspath(self): + entry = self.get() + return SCons.Util.SpecialAttrWrapper(entry.get_abspath(), + entry.name + "_abspath") + + def __get_filebase(self): + name = self.get().name + return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(name)[0], + name + "_filebase") + + def __get_suffix(self): + name = self.get().name + return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(name)[1], + name + "_suffix") + + def __get_file(self): + name = self.get().name + return SCons.Util.SpecialAttrWrapper(name, name + "_file") + + def __get_base_path(self): + """Return the file's directory and file name, with the + suffix stripped.""" + entry = self.get() + return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0], + entry.name + "_base") + + def __get_posix_path(self): + """Return the path with / as the path separator, regardless + of platform.""" + if os.sep == '/': + return self + else: + entry = self.get() + return SCons.Util.SpecialAttrWrapper(string.replace(entry.get_path(), + os.sep, '/'), + entry.name + "_posix") + + def __get_srcnode(self): + return EntryProxy(self.get().srcnode()) + + def __get_srcdir(self): + """Returns the directory containing the source node linked to this + node via BuildDir(), or the directory of this node if not linked.""" + return EntryProxy(self.get().srcnode().dir) + + def __get_rsrcnode(self): + return EntryProxy(self.get().srcnode().rfile()) + + def __get_rsrcdir(self): + """Returns the directory containing the source node linked to this + node via BuildDir(), or the directory of this node if not linked.""" + return EntryProxy(self.get().srcnode().rfile().dir) + + def __get_dir(self): + return EntryProxy(self.get().dir) + + dictSpecialAttrs = { "base" : __get_base_path, + "posix" : __get_posix_path, + "srcpath" : __get_srcnode, + "srcdir" : __get_srcdir, + "dir" : __get_dir, + "abspath" : __get_abspath, + "filebase" : __get_filebase, + "suffix" : __get_suffix, + "file" : __get_file, + "rsrcpath" : __get_rsrcnode, + "rsrcdir" : __get_rsrcdir, + } + + def __getattr__(self, name): + # This is how we implement the "special" attributes + # such as base, posix, srcdir, etc. + try: + return self.dictSpecialAttrs[name](self) + except KeyError: + try: + attr = SCons.Util.Proxy.__getattr__(self, name) + except AttributeError: + entry = self.get() + classname = string.split(str(entry.__class__), '.')[-1] + raise AttributeError, "%s instance '%s' has no attribute '%s'" % (classname, entry.name, name) + return attr + +class Base(SCons.Node.Node): + """A generic class for file system entries. This class is for + when we don't know yet whether the entry being looked up is a file + or a directory. Instances of this class can morph into either + Dir or File objects by a later, more precise lookup. + + Note: this class does not define __cmp__ and __hash__ for + efficiency reasons. SCons does a lot of comparing of + Node.FS.{Base,Entry,File,Dir} objects, so those operations must be + as fast as possible, which means we want to use Python's built-in + object identity comparisons. + """ + + def __init__(self, name, directory, fs): + """Initialize a generic Node.FS.Base object. + + Call the superclass initialization, take care of setting up + our relative and absolute paths, identify our parent + directory, and indicate that this node should use + signatures.""" + if __debug__: logInstanceCreation(self, 'Node.FS.Base') + SCons.Node.Node.__init__(self) + + self.name = name + self.fs = fs + self.relpath = {} + + assert directory, "A directory must be provided" + + self.abspath = directory.abspath_ + name + if directory.path == '.': + self.path = name + else: + self.path = directory.path_ + name + + self.path_ = self.path + self.abspath_ = self.abspath + self.dir = directory + self.cwd = None # will hold the SConscript directory for target nodes + self.duplicate = directory.duplicate + + def clear(self): + """Completely clear a Node.FS.Base object of all its cached + state (so that it can be re-evaluated by interfaces that do + continuous integration builds). + """ + SCons.Node.Node.clear(self) + try: + delattr(self, '_exists') + except AttributeError: + pass + try: + delattr(self, '_rexists') + except AttributeError: + pass + + def get_dir(self): + return self.dir + + def __str__(self): + """A Node.FS.Base object's string representation is its path + name.""" + if self.duplicate or self.is_derived(): + return self.get_path() + return self.srcnode().get_path() + + def exists(self): + try: + return self._exists + except AttributeError: + self._exists = self.fs.exists_or_islink(self.abspath) + return self._exists + + def rexists(self): + try: + return self._rexists + except AttributeError: + self._rexists = self.rfile().exists() + return self._rexists + + def get_parents(self): + parents = SCons.Node.Node.get_parents(self) + if self.dir and not isinstance(self.dir, ParentOfRoot): + parents.append(self.dir) + return parents + + def current(self, calc): + """If the underlying path doesn't exist, we know the node is + not current without even checking the signature, so return 0. + Otherwise, return None to indicate that signature calculation + should proceed as normal to find out if the node is current.""" + bsig = calc.bsig(self) + if not self.exists(): + return 0 + return calc.current(self, bsig) + + def is_under(self, dir): + if self is dir: + return 1 + else: + return self.dir.is_under(dir) + + def set_local(self): + self._local = 1 + + def srcnode(self): + """If this node is in a build path, return the node + corresponding to its source file. Otherwise, return + ourself.""" + try: + return self._srcnode + except AttributeError: + dir=self.dir + name=self.name + while dir: + if dir.srcdir: + self._srcnode = self.fs.Entry(name, dir.srcdir, + klass=self.__class__) + return self._srcnode + name = dir.name + os.sep + name + dir=dir.get_dir() + self._srcnode = self + return self._srcnode + + def recurse_get_path(self, dir, path_elems): + """Recursively build a path relative to a supplied directory + node.""" + if self != dir: + path_elems.append(self.name) + path_elems = self.dir.recurse_get_path(dir, path_elems) + return path_elems + + def get_path(self, dir=None): + """Return path relative to the current working directory of the + Node.FS.Base object that owns us.""" + if not dir: + dir = self.fs.getcwd() + try: + return self.relpath[dir] + except KeyError: + if self == dir: + # Special case, return "." as the path + ret = '.' + else: + path_elems = self.recurse_get_path(dir, []) + path_elems.reverse() + ret = string.join(path_elems, os.sep) + self.relpath[dir] = ret + return ret + + def set_src_builder(self, builder): + """Set the source code builder for this node.""" + self.sbuilder = builder + + def src_builder(self): + """Fetch the source code builder for this node. + + If there isn't one, we cache the source code builder specified + for the directory (which in turn will cache the value from its + parent directory, and so on up to the file system root). + """ + try: + scb = self.sbuilder + except AttributeError: + scb = self.dir.src_builder() + self.sbuilder = scb + return scb + + def get_abspath(self): + """Get the absolute path of the file.""" + return self.abspath + + def for_signature(self): + # Return just our name. Even an absolute path would not work, + # because that can change thanks to symlinks or remapped network + # paths. + return self.name + + def get_subst_proxy(self): + try: + return self._proxy + except AttributeError: + ret = EntryProxy(self) + self._proxy = ret + return ret + +class Entry(Base): + """This is the class for generic Node.FS entries--that is, things + that could be a File or a Dir, but we're just not sure yet. + Consequently, the methods in this class really exist just to + transform their associated object into the right class when the + time comes, and then call the same-named method in the transformed + class.""" + + def rfile(self): + """We're a generic Entry, but the caller is actually looking for + a File at this point, so morph into one.""" + self.__class__ = File + self._morph() + self.clear() + return File.rfile(self) + + def get_found_includes(self, env, scanner, target): + """If we're looking for included files, it's because this Entry + is really supposed to be a File itself.""" + node = self.rfile() + return node.get_found_includes(env, scanner, target) + + def scanner_key(self): + return SCons.Util.splitext(self.name)[1] + + def get_contents(self): + """Fetch the contents of the entry. + + Since this should return the real contents from the file + system, we check to see into what sort of subclass we should + morph this Entry.""" + if self.fs.isfile(self.abspath): + self.__class__ = File + self._morph() + return File.get_contents(self) + if self.fs.isdir(self.abspath): + self.__class__ = Dir + self._morph() + return Dir.get_contents(self) + raise AttributeError + + def exists(self): + """Return if the Entry exists. Check the file system to see + what we should turn into first. Assume a file if there's no + directory.""" + if self.fs.isdir(self.abspath): + self.__class__ = Dir + self._morph() + return Dir.exists(self) + else: + self.__class__ = File + self._morph() + self.clear() + return File.exists(self) + + def calc_signature(self, calc): + """Return the Entry's calculated signature. Check the file + system to see what we should turn into first. Assume a file if + there's no directory.""" + if self.fs.isdir(self.abspath): + self.__class__ = Dir + self._morph() + return Dir.calc_signature(self, calc) + else: + self.__class__ = File + self._morph() + self.clear() + return File.calc_signature(self, calc) + +# This is for later so we can differentiate between Entry the class and Entry +# the method of the FS class. +_classEntry = Entry + + +class LocalFS: + # This class implements an abstraction layer for operations involving + # a local file system. Essentially, this wraps any function in + # the os, os.path or shutil modules that we use to actually go do + # anything with or to the local file system. + # + # Note that there's a very good chance we'll refactor this part of + # the architecture in some way as we really implement the interface(s) + # for remote file system Nodes. For example, the right architecture + # might be to have this be a subclass instead of a base class. + # Nevertheless, we're using this as a first step in that direction. + # + # We're not using chdir() yet because the calling subclass method + # needs to use os.chdir() directly to avoid recursion. Will we + # really need this one? + #def chdir(self, path): + # return os.chdir(path) + def chmod(self, path, mode): + return os.chmod(path, mode) + def copy2(self, src, dst): + return shutil.copy2(src, dst) + def exists(self, path): + return os.path.exists(path) + def getmtime(self, path): + return os.path.getmtime(path) + def isdir(self, path): + return os.path.isdir(path) + def isfile(self, path): + return os.path.isfile(path) + def link(self, src, dst): + return os.link(src, dst) + def listdir(self, path): + return os.listdir(path) + def makedirs(self, path): + return os.makedirs(path) + def mkdir(self, path): + return os.mkdir(path) + def rename(self, old, new): + return os.rename(old, new) + def stat(self, path): + return os.stat(path) + def symlink(self, src, dst): + return os.symlink(src, dst) + def open(self, path): + return open(path) + def unlink(self, path): + return os.unlink(path) + + if hasattr(os, 'symlink'): + def exists_or_islink(self, path): + return os.path.exists(path) or os.path.islink(path) + else: + exists_or_islink = exists + +#class RemoteFS: +# # Skeleton for the obvious methods we might need from the +# # abstraction layer for a remote filesystem. +# def upload(self, local_src, remote_dst): +# pass +# def download(self, remote_src, local_dst): +# pass + + +class FS(LocalFS): + def __init__(self, path = None): + """Initialize the Node.FS subsystem. + + The supplied path is the top of the source tree, where we + expect to find the top-level build file. If no path is + supplied, the current directory is the default. + + The path argument must be a valid absolute path. + """ + if __debug__: logInstanceCreation(self) + self.Top = None + if path == None: + self.pathTop = os.getcwd() + else: + self.pathTop = path + self.Root = {} + self.SConstruct_dir = None + self.CachePath = None + self.cache_force = None + self.cache_show = None + + def set_toplevel_dir(self, path): + assert not self.Top, "You can only set the top-level path on an FS object that has not had its File, Dir, or Entry methods called yet." + self.pathTop = path + + def set_SConstruct_dir(self, dir): + self.SConstruct_dir = dir + + def __setTopLevelDir(self): + if not self.Top: + self.Top = self.__doLookup(Dir, os.path.normpath(self.pathTop)) + self.Top.path = '.' + self.Top.path_ = '.' + os.sep + self._cwd = self.Top + + def getcwd(self): + self.__setTopLevelDir() + return self._cwd + + def __checkClass(self, node, klass): + if klass == Entry: + return node + if node.__class__ == Entry: + node.__class__ = klass + node._morph() + return node + if not isinstance(node, klass): + raise TypeError, "Tried to lookup %s '%s' as a %s." % \ + (node.__class__.__name__, node.path, klass.__name__) + return node + + def __doLookup(self, fsclass, name, directory = None, create = 1): + """This method differs from the File and Dir factory methods in + one important way: the meaning of the directory parameter. + In this method, if directory is None or not supplied, the supplied + name is expected to be an absolute path. If you try to look up a + relative path with directory=None, then an AssertionError will be + raised.""" + + if not name: + # This is a stupid hack to compensate for the fact + # that the POSIX and Win32 versions of os.path.normpath() + # behave differently. In particular, in POSIX: + # os.path.normpath('./') == '.' + # in Win32 + # os.path.normpath('./') == '' + # os.path.normpath('.\\') == '' + # + # This is a definite bug in the Python library, but we have + # to live with it. + name = '.' + path_comp = string.split(name, os.sep) + drive, path_first = os.path.splitdrive(path_comp[0]) + if not path_first: + # Absolute path + drive = _my_normcase(drive) + try: + directory = self.Root[drive] + except KeyError: + if not create: + raise SCons.Errors.UserError + dir = Dir(drive, ParentOfRoot(), self) + dir.path = dir.path + os.sep + dir.abspath = dir.abspath + os.sep + self.Root[drive] = dir + directory = dir + path_comp = path_comp[1:] + else: + path_comp = [ path_first, ] + path_comp[1:] + + if not path_comp: + path_comp = [''] + + # Lookup the directory + for path_name in path_comp[:-1]: + path_norm = _my_normcase(path_name) + try: + directory = self.__checkClass(directory.entries[path_norm], + Dir) + except KeyError: + if not create: + raise SCons.Errors.UserError + + # look at the actual filesystem and make sure there isn't + # a file already there + path = directory.path_ + path_name + if self.isfile(path): + raise TypeError, \ + "File %s found where directory expected." % path + + dir_temp = Dir(path_name, directory, self) + directory.entries[path_norm] = dir_temp + directory.add_wkid(dir_temp) + directory = dir_temp + file_name = _my_normcase(path_comp[-1]) + try: + ret = self.__checkClass(directory.entries[file_name], fsclass) + except KeyError: + if not create: + raise SCons.Errors.UserError + + # make sure we don't create File nodes when there is actually + # a directory at that path on the disk, and vice versa + path = directory.path_ + path_comp[-1] + if fsclass == File: + if self.isdir(path): + raise TypeError, \ + "Directory %s found where file expected." % path + elif fsclass == Dir: + if self.isfile(path): + raise TypeError, \ + "File %s found where directory expected." % path + + ret = fsclass(path_comp[-1], directory, self) + directory.entries[file_name] = ret + directory.add_wkid(ret) + return ret + + def __transformPath(self, name, directory): + """Take care of setting up the correct top-level directory, + usually in preparation for a call to doLookup(). + + If the path name is prepended with a '#', then it is unconditionally + interpreted as relative to the top-level directory of this FS. + + If directory is None, and name is a relative path, + then the same applies. + """ + self.__setTopLevelDir() + if name and name[0] == '#': + directory = self.Top + name = name[1:] + if name and (name[0] == os.sep or name[0] == '/'): + # Correct such that '#/foo' is equivalent + # to '#foo'. + name = name[1:] + name = os.path.join('.', os.path.normpath(name)) + elif not directory: + directory = self._cwd + return (os.path.normpath(name), directory) + + def chdir(self, dir, change_os_dir=0): + """Change the current working directory for lookups. + If change_os_dir is true, we will also change the "real" cwd + to match. + """ + self.__setTopLevelDir() + curr=self._cwd + try: + if not dir is None: + self._cwd = dir + if change_os_dir: + os.chdir(dir.abspath) + except OSError: + self._cwd = curr + raise + + def Entry(self, name, directory = None, create = 1, klass=None): + """Lookup or create a generic Entry node with the specified name. + If the name is a relative path (begins with ./, ../, or a file + name), then it is looked up relative to the supplied directory + node, or to the top level directory of the FS (supplied at + construction time) if no directory is supplied. + """ + + if not klass: + klass = Entry + + if isinstance(name, Base): + return self.__checkClass(name, klass) + else: + if directory and not isinstance(directory, Dir): + directory = self.Dir(directory) + name, directory = self.__transformPath(name, directory) + return self.__doLookup(klass, name, directory, create) + + def File(self, name, directory = None, create = 1): + """Lookup or create a File node with the specified name. If + the name is a relative path (begins with ./, ../, or a file name), + then it is looked up relative to the supplied directory node, + or to the top level directory of the FS (supplied at construction + time) if no directory is supplied. + + This method will raise TypeError if a directory is found at the + specified path. + """ + + return self.Entry(name, directory, create, File) + + def Dir(self, name, directory = None, create = 1): + """Lookup or create a Dir node with the specified name. If + the name is a relative path (begins with ./, ../, or a file name), + then it is looked up relative to the supplied directory node, + or to the top level directory of the FS (supplied at construction + time) if no directory is supplied. + + This method will raise TypeError if a normal file is found at the + specified path. + """ + + return self.Entry(name, directory, create, Dir) + + def BuildDir(self, build_dir, src_dir, duplicate=1): + """Link the supplied build directory to the source directory + for purposes of building files.""" + + self.__setTopLevelDir() + if not isinstance(src_dir, SCons.Node.Node): + src_dir = self.Dir(src_dir) + if not isinstance(build_dir, SCons.Node.Node): + build_dir = self.Dir(build_dir) + if not src_dir.is_under(self.Top): + raise SCons.Errors.UserError, "Source directory must be under top of build tree." + if src_dir.is_under(build_dir): + raise SCons.Errors.UserError, "Source directory cannot be under build directory." + if build_dir.srcdir: + if build_dir.srcdir == src_dir: + return # We already did this. + raise SCons.Errors.UserError, "'%s' already has a source directory: '%s'."%(build_dir, build_dir.srcdir) + build_dir.link(src_dir, duplicate) + + def Repository(self, *dirs): + """Specify Repository directories to search.""" + for d in dirs: + if not isinstance(d, SCons.Node.Node): + d = self.Dir(d) + self.__setTopLevelDir() + self.Top.addRepository(d) + + def Rsearch(self, path, clazz=_classEntry, cwd=None): + """Search for something in a Repository. Returns the first + one found in the list, or None if there isn't one.""" + if isinstance(path, SCons.Node.Node): + return path + else: + name, d = self.__transformPath(path, cwd) + n = self.__doLookup(clazz, name, d) + if n.exists(): + return n + if isinstance(n, Dir): + # If n is a Directory that has Repositories directly + # attached to it, then any of those is a valid Repository + # path. Return the first one that exists. + reps = filter(lambda x: x.exists(), n.getRepositories()) + if len(reps): + return reps[0] + d = n.get_dir() + name = n.name + # Search repositories of all directories that this file is under. + while d: + for rep in d.getRepositories(): + try: + rnode = self.__doLookup(clazz, name, rep) + # Only find the node if it exists and it is not + # a derived file. If for some reason, we are + # explicitly building a file IN a Repository, we + # don't want it to show up in the build tree. + # This is usually the case with BuildDir(). + # We only want to find pre-existing files. + if rnode.exists() and \ + (isinstance(rnode, Dir) or not rnode.is_derived()): + return rnode + except TypeError: + pass # Wrong type of node. + # Prepend directory name + name = d.name + os.sep + name + # Go up one directory + d = d.get_dir() + return None + + def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None): + """Search for a list of somethings in the Repository list.""" + ret = [] + if SCons.Util.is_String(pathlist): + pathlist = string.split(pathlist, os.pathsep) + if not SCons.Util.is_List(pathlist): + pathlist = [pathlist] + for path in pathlist: + if isinstance(path, SCons.Node.Node): + ret.append(path) + else: + name, d = self.__transformPath(path, cwd) + n = self.__doLookup(clazz, name, d) + if not must_exist or n.exists(): + ret.append(n) + if isinstance(n, Dir): + # If this node is a directory, then any repositories + # attached to this node can be repository paths. + ret.extend(filter(lambda x, me=must_exist, clazz=clazz: isinstance(x, clazz) and (not me or x.exists()), + n.getRepositories())) + + d = n.get_dir() + name = n.name + # Search repositories of all directories that this file + # is under. + while d: + for rep in d.getRepositories(): + try: + rnode = self.__doLookup(clazz, name, rep) + # Only find the node if it exists (or + # must_exist is zero) and it is not a + # derived file. If for some reason, we + # are explicitly building a file IN a + # Repository, we don't want it to show up in + # the build tree. This is usually the case + # with BuildDir(). We only want to find + # pre-existing files. + if (not must_exist or rnode.exists()) and \ + (not rnode.is_derived() or isinstance(rnode, Dir)): + ret.append(rnode) + except TypeError: + pass # Wrong type of node. + # Prepend directory name + name = d.name + os.sep + name + # Go up one directory + d = d.get_dir() + return ret + + def CacheDir(self, path): + self.CachePath = path + + def build_dir_target_climb(self, dir, tail): + """Create targets in corresponding build directories + + Climb the directory tree, and look up path names + relative to any linked build directories we find. + """ + targets = [] + message = None + while dir: + for bd in dir.build_dirs: + p = apply(os.path.join, [bd.path] + tail) + targets.append(self.Entry(p)) + tail = [dir.name] + tail + dir = dir.up() + if targets: + message = "building associated BuildDir targets: %s" % string.join(map(str, targets)) + return targets, message + +class DummyExecutor: + """Dummy executor class returned by Dir nodes to bamboozle SCons + into thinking we are an actual derived node, where our sources are + our directory entries.""" + def cleanup(self): + pass + def get_raw_contents(self): + return '' + def get_contents(self): + return '' + def get_timestamp(self): + return 0 + +class Dir(Base): + """A class for directories in a file system. + """ + + def __init__(self, name, directory, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.Dir') + Base.__init__(self, name, directory, fs) + self._morph() + + def _morph(self): + """Turn a file system node (either a freshly initialized + directory object or a separate Entry object) into a + proper directory object. + + Modify our paths to add the trailing slash that indicates + a directory. Set up this directory's entries and hook it + into the file system tree. Specify that directories (this + node) don't use signatures for currency calculation.""" + + self.path_ = self.path + os.sep + self.abspath_ = self.abspath + os.sep + self.repositories = [] + self.srcdir = None + self.source_scanner = None + + self.entries = {} + self.entries['.'] = self + self.entries['..'] = self.dir + self.cwd = self + self.builder = 1 + self.searched = 0 + self._sconsign = None + self.build_dirs = [] + + def __clearRepositoryCache(self, duplicate=None): + """Called when we change the repository(ies) for a directory. + This clears any cached information that is invalidated by changing + the repository.""" + + for node in self.entries.values(): + if node != self.dir: + if node != self and isinstance(node, Dir): + node.__clearRepositoryCache(duplicate) + else: + try: + del node._srcreps + except AttributeError: + pass + try: + del node._rfile + except AttributeError: + pass + try: + del node._rexists + except AttributeError: + pass + try: + del node._exists + except AttributeError: + pass + try: + del node._srcnode + except AttributeError: + pass + if duplicate != None: + node.duplicate=duplicate + + def __resetDuplicate(self, node): + if node != self: + node.duplicate = node.get_dir().duplicate + + def Entry(self, name): + """Create an entry node named 'name' relative to this directory.""" + return self.fs.Entry(name, self) + + def Dir(self, name): + """Create a directory node named 'name' relative to this directory.""" + return self.fs.Dir(name, self) + + def File(self, name): + """Create a file node named 'name' relative to this directory.""" + return self.fs.File(name, self) + + def link(self, srcdir, duplicate): + """Set this directory as the build directory for the + supplied source directory.""" + self.srcdir = srcdir + self.duplicate = duplicate + self.__clearRepositoryCache(duplicate) + srcdir.build_dirs.append(self) + + def getRepositories(self): + """Returns a list of repositories for this directory.""" + if self.srcdir and not self.duplicate: + try: + return self._srcreps + except AttributeError: + self._srcreps = self.fs.Rsearchall(self.srcdir.path, + clazz=Dir, + must_exist=0, + cwd=self.fs.Top) \ + + self.repositories + return self._srcreps + return self.repositories + + def addRepository(self, dir): + if not dir in self.repositories and dir != self: + self.repositories.append(dir) + self.__clearRepositoryCache() + + def up(self): + return self.entries['..'] + + def root(self): + if not self.entries['..']: + return self + else: + return self.entries['..'].root() + + def children(self, scan=1): + return filter(lambda x, i=self.ignore: x not in i, + self.all_children(scan)) + + def all_children(self, scan=1): + # Before we traverse our children, make sure we have created Nodes + # for any files that this directory contains. We need to do this + # so any change in a file in this directory will cause it to + # be out of date. + if not self.searched: + try: + for filename in self.fs.listdir(self.abspath): + if filename != '.sconsign': + self.Entry(filename) + except OSError: + # Directory does not exist. No big deal + pass + self.searched = 1 + keys = filter(lambda k: k != '.' and k != '..', self.entries.keys()) + kids = map(lambda x, s=self: s.entries[x], keys) + def c(one, two): + if one.abspath < two.abspath: + return -1 + if one.abspath > two.abspath: + return 1 + return 0 + kids.sort(c) + return kids + SCons.Node.Node.all_children(self, 0) + + def get_actions(self): + """A null "builder" for directories.""" + return [] + + def build(self): + """A null "builder" for directories.""" + pass + + def alter_targets(self): + """Return any corresponding targets in a build directory. + """ + return self.fs.build_dir_target_climb(self, []) + + def scanner_key(self): + """A directory does not get scanned.""" + return None + + def set_bsig(self, bsig): + """A directory has no signature.""" + bsig = None + + def set_csig(self, csig): + """A directory has no signature.""" + csig = None + + def get_contents(self): + """Return aggregate contents of all our children.""" + contents = cStringIO.StringIO() + for kid in self.children(None): + contents.write(kid.get_contents()) + return contents.getvalue() + + def prepare(self): + pass + + def current(self, calc): + """If all of our children were up-to-date, then this + directory was up-to-date, too.""" + state = 0 + for kid in self.children(None): + s = kid.get_state() + if s and (not state or s > state): + state = s + import SCons.Node + if state == 0 or state == SCons.Node.up_to_date: + return 1 + else: + return 0 + + def rdir(self): + try: + return self._rdir + except AttributeError: + self._rdir = self + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top) + if n: + self._rdir = n + return self._rdir + + def sconsign(self): + """Return the .sconsign file info for this directory, + creating it first if necessary.""" + if not self._sconsign: + import SCons.Sig + self._sconsign = SCons.Sig.SConsignForDirectory(self) + return self._sconsign + + def srcnode(self): + """Dir has a special need for srcnode()...if we + have a srcdir attribute set, then that *is* our srcnode.""" + if self.srcdir: + return self.srcdir + return Base.srcnode(self) + + def get_executor(self, create=1): + """Fetch the action executor for this node. Create one if + there isn't already one, and requested to do so.""" + try: + executor = self.executor + except AttributeError: + executor = DummyExecutor() + self.executor = executor + return executor + + def get_timestamp(self): + """Return the latest timestamp from among our children""" + stamp = 0 + for kid in self.children(None): + if kid.get_timestamp() > stamp: + stamp = kid.get_timestamp() + return stamp + +class File(Base): + """A class for files in a file system. + """ + def __init__(self, name, directory, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.File') + Base.__init__(self, name, directory, fs) + self._morph() + + def Entry(self, name): + """Create an entry node named 'name' relative to + the SConscript directory of this file.""" + return self.fs.Entry(name, self.cwd) + + def Dir(self, name): + """Create a directory node named 'name' relative to + the SConscript directory of this file.""" + return self.fs.Dir(name, self.cwd) + + def File(self, name): + """Create a file node named 'name' relative to + the SConscript directory of this file.""" + return self.fs.File(name, self.cwd) + + def RDirs(self, pathlist): + """Search for a list of directories in the Repository list.""" + return self.fs.Rsearchall(pathlist, clazz=Dir, must_exist=0, + cwd=self.cwd) + + def generate_build_dict(self): + """Return an appropriate dictionary of values for building + this File.""" + return {'Dir' : self.Dir, + 'File' : self.File, + 'RDirs' : self.RDirs} + + def _morph(self): + """Turn a file system node into a File object.""" + self.scanner_paths = {} + self.found_includes = {} + if not hasattr(self, '_local'): + self._local = 0 + + def root(self): + return self.dir.root() + + def scanner_key(self): + return SCons.Util.splitext(self.name)[1] + + def get_contents(self): + if not self.rexists(): + return '' + return open(self.rfile().abspath, "rb").read() + + def get_timestamp(self): + if self.rexists(): + return self.fs.getmtime(self.rfile().abspath) + else: + return 0 + + def store_csig(self): + self.dir.sconsign().set_csig(self.name, self.get_csig()) + + def store_bsig(self): + self.dir.sconsign().set_bsig(self.name, self.get_bsig()) + + def store_implicit(self): + self.dir.sconsign().set_implicit(self.name, self.implicit) + + def store_timestamp(self): + self.dir.sconsign().set_timestamp(self.name, self.get_timestamp()) + + def get_prevsiginfo(self): + """Fetch the previous signature information from the + .sconsign entry.""" + return self.dir.sconsign().get(self.name) + + def get_stored_implicit(self): + return self.dir.sconsign().get_implicit(self.name) + + def get_found_includes(self, env, scanner, target): + """Return the included implicit dependencies in this file. + Cache results so we only scan the file once regardless of + how many times this information is requested.""" + if not scanner: + return [] + + try: + path = target.scanner_paths[scanner] + except AttributeError: + # The target had no scanner_paths attribute, which means + # it's an Alias or some other node that's not actually a + # file. In that case, back off and use the path for this + # node itself. + try: + path = self.scanner_paths[scanner] + except KeyError: + path = scanner.path(env, self.cwd) + self.scanner_paths[scanner] = path + except KeyError: + path = scanner.path(env, target.cwd) + target.scanner_paths[scanner] = path + + try: + includes = self.found_includes[path] + except KeyError: + includes = scanner(self, env, path) + self.found_includes[path] = includes + + return includes + + def _createDir(self): + # ensure that the directories for this node are + # created. + + listDirs = [] + parent=self.dir + while parent: + if parent.exists(): + break + listDirs.append(parent) + p = parent.up() + if isinstance(p, ParentOfRoot): + raise SCons.Errors.StopError, parent.path + parent = p + listDirs.reverse() + for dirnode in listDirs: + try: + Mkdir(dirnode, None, None) + # The Mkdir() action may or may not have actually + # created the directory, depending on whether the -n + # option was used or not. Delete the _exists and + # _rexists attributes so they can be reevaluated. + try: + delattr(dirnode, '_exists') + except AttributeError: + pass + try: + delattr(dirnode, '_rexists') + except AttributeError: + pass + except OSError: + pass + + def retrieve_from_cache(self): + """Try to retrieve the node's content from a cache + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Returns true iff the node was successfully retrieved. + """ + b = self.is_derived() + if not b and not self.has_src_builder(): + return None + if b and self.fs.CachePath: + if self.fs.cache_show: + if CacheRetrieveSilent(self, None, None) == 0: + def do_print(action, targets, sources, env, s=self): + if action.strfunction: + al = action.strfunction(targets, s.sources, env) + if not SCons.Util.is_List(al): + al = [al] + for a in al: + action.show(a) + self._for_each_action(do_print) + return 1 + elif CacheRetrieve(self, None, None) == 0: + return 1 + return None + + def built(self): + """Called just after this node is sucessfully built.""" + # Push this file out to cache before the superclass Node.built() + # method has a chance to clear the build signature, which it + # will do if this file has a source scanner. + if self.fs.CachePath and self.fs.exists(self.path): + CachePush(self, None, None) + SCons.Node.Node.built(self) + self.found_includes = {} + try: + delattr(self, '_exists') + except AttributeError: + pass + try: + delattr(self, '_rexists') + except AttributeError: + pass + + def visited(self): + if self.fs.CachePath and self.fs.cache_force and self.fs.exists(self.path): + CachePush(self, None, None) + + def has_src_builder(self): + """Return whether this Node has a source builder or not. + + If this Node doesn't have an explicit source code builder, this + is where we figure out, on the fly, if there's a transparent + source code builder for it. + + Note that if we found a source builder, we also set the + self.builder attribute, so that all of the methods that actually + *build* this file don't have to do anything different. + """ + try: + scb = self.sbuilder + except AttributeError: + if self.rexists(): + scb = None + else: + scb = self.dir.src_builder() + if scb is _null: + scb = None + dir = self.dir.path + sccspath = os.path.join('SCCS', 's.' + self.name) + if dir != '.': + sccspath = os.path.join(dir, sccspath) + if self.fs.exists(sccspath): + scb = get_DefaultSCCSBuilder() + else: + rcspath = os.path.join('RCS', self.name + ',v') + if dir != '.': + rcspath = os.path.join(dir, rcspath) + if os.path.exists(rcspath): + scb = get_DefaultRCSBuilder() + self.builder = scb + self.sbuilder = scb + return not scb is None + + def alter_targets(self): + """Return any corresponding targets in a build directory. + """ + if self.is_derived(): + return [], None + return self.fs.build_dir_target_climb(self.dir, [self.name]) + + def is_pseudo_derived(self): + return self.has_src_builder() + + def prepare(self): + """Prepare for this file to be created.""" + SCons.Node.Node.prepare(self) + + if self.get_state() != SCons.Node.up_to_date: + if self.exists(): + if self.is_derived() and not self.precious: + try: + Unlink(self, None, None) + except OSError, e: + raise SCons.Errors.BuildError(node = self, + errstr = e.strerror) + try: + delattr(self, '_exists') + except AttributeError: + pass + else: + try: + self._createDir() + except SCons.Errors.StopError, drive: + desc = "No drive `%s' for target `%s'." % (drive, self) + raise SCons.Errors.StopError, desc + + def remove(self): + """Remove this file.""" + if self.fs.exists_or_islink(self.path): + self.fs.unlink(self.path) + return 1 + return None + + def exists(self): + # Duplicate from source path if we are set up to do this. + if self.duplicate and not self.is_derived() and not self.linked: + src=self.srcnode().rfile() + if src.exists() and src.abspath != self.abspath: + self._createDir() + try: + Unlink(self, None, None) + except OSError: + pass + try: + Link(self, src, None) + except IOError, e: + desc = "Cannot duplicate `%s' in `%s': %s." % (src, self.dir, e.strerror) + raise SCons.Errors.StopError, desc + self.linked = 1 + # The Link() action may or may not have actually + # created the file, depending on whether the -n + # option was used or not. Delete the _exists and + # _rexists attributes so they can be reevaluated. + try: + delattr(self, '_exists') + except AttributeError: + pass + try: + delattr(self, '_rexists') + except AttributeError: + pass + return Base.exists(self) + + def current(self, calc): + bsig = calc.bsig(self) + if not self.exists(): + # The file doesn't exist locally... + r = self.rfile() + if r != self: + # ...but there is one in a Repository... + if calc.current(r, bsig): + # ...and it's even up-to-date... + if self._local: + # ...and they'd like a local copy. + LocalCopy(self, r, None) + self.set_bsig(bsig) + self.store_bsig() + return 1 + self._rfile = self + return None + else: + return calc.current(self, bsig) + + def rfile(self): + try: + return self._rfile + except AttributeError: + self._rfile = self + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=File, + cwd=self.fs.Top) + if n: + self._rfile = n + return self._rfile + + def rstr(self): + return str(self.rfile()) + + def cachepath(self): + if self.fs.CachePath: + bsig = self.get_bsig() + if bsig is None: + raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path + # Add the path to the cache signature, because multiple + # targets built by the same action will all have the same + # build signature, and we have to differentiate them somehow. + cache_sig = SCons.Sig.MD5.collect([bsig, self.path]) + subdir = string.upper(cache_sig[0]) + dir = os.path.join(self.fs.CachePath, subdir) + return dir, os.path.join(dir, cache_sig) + return None, None + + def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): + return self.dir.File(prefix + splitext(self.name)[0] + suffix) + + +default_fs = FS() + + +def find_file(filename, paths, node_factory = default_fs.File): + """ + find_file(str, [Dir()]) -> [nodes] + + filename - a filename to find + paths - a list of directory path *nodes* to search in + + returns - the node created from the found file. + + Find a node corresponding to either a derived file or a file + that exists already. + + Only the first file found is returned, and none is returned + if no file is found. + """ + retval = None + for dir in paths: + try: + node = node_factory(filename, dir) + # Return true if the node exists or is a derived node. + if node.is_derived() or \ + node.is_pseudo_derived() or \ + (isinstance(node, SCons.Node.FS.Base) and node.exists()): + retval = node + break + except TypeError: + # If we find a directory instead of a file, we don't care + pass + + return retval + +def find_files(filenames, paths, node_factory = default_fs.File): + """ + find_files([str], [Dir()]) -> [nodes] + + filenames - a list of filenames to find + paths - a list of directory path *nodes* to search in + + returns - the nodes created from the found files. + + Finds nodes corresponding to either derived files or files + that exist already. + + Only the first file found is returned for each filename, + and any files that aren't found are ignored. + """ + nodes = map(lambda x, paths=paths, node_factory=node_factory: + find_file(x, paths, node_factory), + filenames) + return filter(lambda x: x != None, nodes) diff --git a/scons/scons-local-0.95/SCons/Node/Python.py b/scons/scons-local-0.95/SCons/Node/Python.py new file mode 100644 index 0000000..c358fd4 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Node/Python.py @@ -0,0 +1,81 @@ +"""scons.Node.Python + +Python nodes. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Node/Python.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Node +import time + +class Value(SCons.Node.Node): + """A class for Python variables, typically passed on the command line + or generated by a script, but not from a file or some other source. + """ + def __init__(self, value): + SCons.Node.Node.__init__(self) + self.value = value + self.timestamp = time.time() + + def __str__(self): + return repr(self.value) + + def build(self): + """A "builder" for Values.""" + pass + + def current(self, calc): + """If all of our children were up-to-date, then this + Value was up-to-date, too.""" + # Allow the children to calculate their signatures. + calc.bsig(self) + state = 0 + for kid in self.children(None): + s = kid.get_state() + if s and (not state or s > state): + state = s + if state == 0 or state == SCons.Node.up_to_date: + return 1 + else: + return 0 + + def is_under(self, dir): + # Make Value nodes get built regardless of + # what directory scons was run from. Value nodes + # are outside the filesystem: + return 1 + + def get_contents(self): + """The contents of a Value are the concatenation + of all the contents of its sources with the node's value itself.""" + contents = str(self.value) + for kid in self.children(None): + contents = contents + kid.get_contents() + return contents + + def get_timestamp(self): + return self.timestamp diff --git a/scons/scons-local-0.95/SCons/Node/__init__.py b/scons/scons-local-0.95/SCons/Node/__init__.py new file mode 100644 index 0000000..9bf34bf --- /dev/null +++ b/scons/scons-local-0.95/SCons/Node/__init__.py @@ -0,0 +1,877 @@ +"""SCons.Node + +The Node package for the SCons software construction utility. + +This is, in many ways, the heart of SCons. + +A Node is where we encapsulate all of the dependency information about +any thing that SCons can build, or about any thing which SCons can use +to build some other thing. The canonical "thing," of course, is a file, +but a Node can also represent something remote (like a web page) or +something completely abstract (like an Alias). + +Each specific type of "thing" is specifically represented by a subclass +of the Node base class: Node.FS.File for files, Node.Alias for aliases, +etc. Dependency information is kept here in the base class, and +information specific to files/aliases/etc. is in the subclass. The +goal, if we've done this correctly, is that any type of "thing" should +be able to depend on any other type of "thing." + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Node/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + + + +import copy + +from SCons.Debug import logInstanceCreation +import SCons.Sig +import SCons.Util + +# Node states +# +# These are in "priority" order, so that the maximum value for any +# child/dependency of a node represents the state of that node if +# it has no builder of its own. The canonical example is a file +# system directory, which is only up to date if all of its children +# were up to date. +pending = 1 +executing = 2 +up_to_date = 3 +executed = 4 +failed = 5 +stack = 6 # nodes that are in the current Taskmaster execution stack + +# controls whether implicit depedencies are cached: +implicit_cache = 0 + +# controls whether implicit dep changes are ignored: +implicit_deps_unchanged = 0 + +# controls whether the cached implicit deps are ignored: +implicit_deps_changed = 0 + +# A variable that can be set to an interface-specific function be called +# to annotate a Node with information about its creation. +def do_nothing(node): pass + +Annotate = do_nothing + +class Node: + """The base Node class, for entities that we know how to + build, or use to build other Nodes. + """ + + class Attrs: + pass + + def __init__(self): + if __debug__: logInstanceCreation(self, 'Node') + # Note that we no longer explicitly initialize a self.builder + # attribute to None here. That's because the self.builder + # attribute may be created on-the-fly later by a subclass (the + # canonical example being a builder to fetch a file from a + # source code system like CVS or Subversion). + + # Each list of children that we maintain is accompanied by a + # dictionary used to look up quickly whether a node is already + # present in the list. Empirical tests showed that it was + # fastest to maintain them as side-by-side Node attributes in + # this way, instead of wrapping up each list+dictionary pair in + # a class. (Of course, we could always still do that in the + # future if we had a good reason to...). + self.sources = [] # source files used to build node + self.sources_dict = {} + self.depends = [] # explicit dependencies (from Depends) + self.depends_dict = {} + self.ignore = [] # dependencies to ignore + self.ignore_dict = {} + self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) + self.parents = {} + self.wkids = None # Kids yet to walk, when it's an array + self.target_scanner = None # explicit scanner from this node's Builder + self.source_scanner = None # source scanner + + self.env = None + self.state = None + self.precious = None + self.always_build = None + self.found_includes = {} + self.includes = None + self.overrides = {} # construction variable overrides for building this node + self.attributes = self.Attrs() # Generic place to stick information about the Node. + self.side_effect = 0 # true iff this node is a side effect + self.side_effects = [] # the side effects of building this target + self.pre_actions = [] + self.post_actions = [] + self.linked = 0 # is this node linked to the build directory? + + # Let the interface in which the build engine is embedded + # annotate this Node with its own info (like a description of + # what line in what file created the node, for example). + Annotate(self) + + def generate_build_dict(self): + """Return an appropriate dictionary of values for building + this Node.""" + return {} + + def get_build_env(self): + """Fetch the appropriate Environment to build this node.""" + executor = self.get_executor() + return executor.get_build_env() + + def set_executor(self, executor): + """Set the action executor for this node.""" + self.executor = executor + + def get_executor(self, create=1): + """Fetch the action executor for this node. Create one if + there isn't already one, and requested to do so.""" + try: + executor = self.executor + except AttributeError: + if not create: + raise + import SCons.Executor + executor = SCons.Executor.Executor(self.builder, + self.builder.env, + {}, + [self], + self.sources) + self.executor = executor + return executor + + def _for_each_action(self, func): + """Call a function for each action required to build a node. + + The purpose here is to have one place for the logic that + collects and executes all of the actions for a node's builder, + even though multiple sections of code elsewhere need this logic + to do different things.""" + if not self.has_builder(): + return + executor = self.get_executor() + executor(self, func) + + def retrieve_from_cache(self): + """Try to retrieve the node's content from a cache + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Returns true iff the node was successfully retrieved. + """ + return 0 + + def build(self): + """Actually build the node. + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + """ + def do_action(action, targets, sources, env, s=self): + stat = action(targets, sources, env) + if stat: + raise SCons.Errors.BuildError(node = s, + errstr = "Error %d" % stat) + self._for_each_action(do_action) + + def built(self): + """Called just after this node is sucessfully built.""" + self.store_bsig() + + # Clear out the implicit dependency caches: + # XXX this really should somehow be made more general and put + # under the control of the scanners. + if self.source_scanner: + self.found_includes = {} + self.includes = None + + def get_parents(node, parent): return node.get_parents() + def clear_cache(node, parent): + node.implicit = None + node.del_bsig() + w = Walker(self, get_parents, ignore_cycle, clear_cache) + while w.next(): pass + + # clear out the content signature, since the contents of this + # node were presumably just changed: + self.del_csig() + + def postprocess(self): + """Clean up anything we don't need to hang onto after we've + been built.""" + try: + executor = self.get_executor(create=None) + except AttributeError: + pass + else: + executor.cleanup() + + def clear(self): + """Completely clear a Node of all its cached state (so that it + can be re-evaluated by interfaces that do continuous integration + builds). + """ + self.set_state(None) + self.del_bsig() + self.del_csig() + try: + delattr(self, '_calculated_sig') + except AttributeError: + pass + try: + delattr(self, '_tempbsig') + except AttributeError: + pass + self.includes = None + self.found_includes = {} + self.implicit = None + + def visited(self): + """Called just after this node has been visited + without requiring a build..""" + pass + + def depends_on(self, nodes): + """Does this node depend on any of 'nodes'?""" + for node in nodes: + if node in self.children(): + return 1 + + return 0 + + def builder_set(self, builder): + self.builder = builder + + def has_builder(self): + """Return whether this Node has a builder or not. + + In Boolean tests, this turns out to be a *lot* more efficient + than simply examining the builder attribute directly ("if + node.builder: ..."). When the builder attribute is examined + directly, it ends up calling __getattr__ for both the __len__ + and __nonzero__ attributes on instances of our Builder Proxy + class(es), generating a bazillion extra calls and slowing + things down immensely. + """ + try: + b = self.builder + except AttributeError: + # There was no explicit builder for this Node, so initialize + # the self.builder attribute to None now. + self.builder = None + b = self.builder + return not b is None + + def is_derived(self): + """ + Returns true iff this node is derived (i.e. built). + + This should return true only for nodes whose path should be in + the build directory when duplicate=0 and should contribute their build + signatures when they are used as source files to other derived files. For + example: source with source builders are not derived in this sense, + and hence should not return true. + """ + return self.has_builder() or self.side_effect + + def is_pseudo_derived(self): + """ + Returns true iff this node is built, but should use a source path + when duplicate=0 and should contribute a content signature (i.e. + source signature) when used as a source for other derived files. + """ + return 0 + + def alter_targets(self): + """Return a list of alternate targets for this Node. + """ + return [], None + + def get_found_includes(self, env, scanner, target): + """Return the scanned include lines (implicit dependencies) + found in this node. + + The default is no implicit dependencies. We expect this method + to be overridden by any subclass that can be scanned for + implicit dependencies. + """ + return [] + + def get_implicit_deps(self, env, scanner, target): + """Return a list of implicit dependencies for this node. + + This method exists to handle recursive invocation of the scanner + on the implicit dependencies returned by the scanner, if the + scanner's recursive flag says that we should. + """ + if not scanner: + return [] + + try: + recurse = scanner.recursive + except AttributeError: + recurse = None + + nodes = [self] + seen = {} + seen[self] = 1 + deps = [] + while nodes: + n = nodes.pop(0) + d = filter(lambda x, seen=seen: not seen.has_key(x), + n.get_found_includes(env, scanner, target)) + if d: + deps.extend(d) + for n in d: + seen[n] = 1 + if recurse: + nodes.extend(d) + + return deps + + # cache used to make implicit_factory fast. + implicit_factory_cache = {} + + def implicit_factory(self, path): + """ + Turn a cache implicit dependency path into a node. + This is called so many times that doing caching + here is a significant perforamnce boost. + """ + try: + return self.implicit_factory_cache[path] + except KeyError: + n = self.builder.source_factory(path) + self.implicit_factory_cache[path] = n + return n + + def scan(self): + """Scan this node's dependents for implicit dependencies.""" + # Don't bother scanning non-derived files, because we don't + # care what their dependencies are. + # Don't scan again, if we already have scanned. + if not self.implicit is None: + return + self.implicit = [] + self.implicit_dict = {} + self._children_reset() + if not self.has_builder(): + return + + if implicit_cache and not implicit_deps_changed: + implicit = self.get_stored_implicit() + if implicit is not None: + implicit = map(self.implicit_factory, implicit) + self._add_child(self.implicit, self.implicit_dict, implicit) + calc = SCons.Sig.default_calc + if implicit_deps_unchanged or calc.current(self, calc.bsig(self)): + return + else: + # one of this node's sources has changed, so + # we need to recalculate the implicit deps, + # and the bsig: + self.implicit = [] + self.implicit_dict = {} + self._children_reset() + self.del_bsig() + + build_env = self.get_build_env() + + for child in self.children(scan=0): + scanner = child.source_scanner + if scanner: + self._add_child(self.implicit, + self.implicit_dict, + child.get_implicit_deps(build_env, + scanner, + self)) + + # scan this node itself for implicit dependencies + self._add_child(self.implicit, + self.implicit_dict, + self.get_implicit_deps(build_env, + self.target_scanner, + self)) + + if implicit_cache: + self.store_implicit() + + def scanner_key(self): + return None + + def env_set(self, env, safe=0): + if safe and self.env: + return + self.env = env + + def calculator(self): + import SCons.Defaults + + env = self.env or SCons.Defaults.DefaultEnvironment() + return env.get_calculator() + + def calc_signature(self, calc): + """ + Select and calculate the appropriate build signature for a node. + + self - the node + calc - the signature calculation module + returns - the signature + """ + try: + return self._calculated_sig + except AttributeError: + if self.is_derived(): + import SCons.Defaults + + env = self.env or SCons.Defaults.DefaultEnvironment() + if env.use_build_signature(): + sig = self.rfile().calc_bsig(calc, self) + else: + sig = self.rfile().calc_csig(calc, self) + elif not self.rexists(): + sig = None + else: + sig = self.rfile().calc_csig(calc, self) + self._calculated_sig = sig + return sig + + def calc_bsig(self, calc, cache=None): + """Return the node's build signature, calculating it first + if necessary. + + Note that we don't save it in the "real" build signature + attribute if we have to calculate it here; the "real" build + signature only gets updated after a file is actually built. + """ + if cache is None: cache = self + try: + return cache.bsig + except AttributeError: + try: + return cache._tempbsig + except AttributeError: + cache._tempbsig = calc.bsig(self, cache) + return cache._tempbsig + + def get_bsig(self): + """Get the node's build signature (based on the signatures + of its dependency files and build information).""" + try: + return self.bsig + except AttributeError: + return None + + def set_bsig(self, bsig): + """Set the node's build signature (based on the signatures + of its dependency files and build information).""" + self.bsig = bsig + try: + delattr(self, '_tempbsig') + except AttributeError: + pass + + def store_bsig(self): + """Make the build signature permanent (that is, store it in the + .sconsign file or equivalent).""" + pass + + def del_bsig(self): + """Delete the bsig from this node.""" + try: + delattr(self, 'bsig') + except AttributeError: + pass + + def get_csig(self): + """Get the signature of the node's content.""" + try: + return self.csig + except AttributeError: + return None + + def calc_csig(self, calc, cache=None): + """Return the node's content signature, calculating it first + if necessary. + """ + if cache is None: cache = self + try: + return cache.csig + except AttributeError: + cache.csig = calc.csig(self, cache) + return cache.csig + + def set_csig(self, csig): + """Set the signature of the node's content.""" + self.csig = csig + + def store_csig(self): + """Make the content signature permanent (that is, store it in the + .sconsign file or equivalent).""" + pass + + def del_csig(self): + """Delete the csig from this node.""" + try: + delattr(self, 'csig') + except AttributeError: + pass + + def get_prevsiginfo(self): + """Fetch the previous signature information from the + .sconsign entry.""" + return SCons.Sig._SConsign.null_siginfo + + def get_timestamp(self): + return 0 + + def store_timestamp(self): + """Make the timestamp permanent (that is, store it in the + .sconsign file or equivalent).""" + pass + + def store_implicit(self): + """Make the implicit deps permanent (that is, store them in the + .sconsign file or equivalent).""" + pass + + def get_stored_implicit(self): + """Fetch the stored implicit dependencies""" + return None + + def set_precious(self, precious = 1): + """Set the Node's precious value.""" + self.precious = precious + + def set_always_build(self, always_build = 1): + """Set the Node's always_build value.""" + self.always_build = always_build + + def exists(self): + """Does this node exists?""" + # All node exist by default: + return 1 + + def rexists(self): + """Does this node exist locally or in a repositiory?""" + # There are no repositories by default: + return self.exists() + + def prepare(self): + """Prepare for this Node to be created. + The default implemenation checks that all children either exist + or are derived. + """ + def missing(node): + return not node.is_derived() and \ + not node.is_pseudo_derived() and \ + not node.linked and \ + not node.rexists() + missing_sources = filter(missing, self.children()) + if missing_sources: + desc = "Source `%s' not found, needed by target `%s'." % (missing_sources[0], self) + raise SCons.Errors.StopError, desc + + def remove(self): + """Remove this Node: no-op by default.""" + return None + + def add_dependency(self, depend): + """Adds dependencies.""" + try: + self._add_child(self.depends, self.depends_dict, depend) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = map(str, e) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def add_ignore(self, depend): + """Adds dependencies to ignore.""" + try: + self._add_child(self.ignore, self.ignore_dict, depend) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = map(str, e) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def add_source(self, source): + """Adds sources.""" + try: + self._add_child(self.sources, self.sources_dict, source) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = map(str, e) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def _add_child(self, collection, dict, child): + """Adds 'child' to 'collection', first checking 'dict' to see + if it's already present.""" + if type(child) is not type([]): + child = [child] + for c in child: + if not isinstance(c, Node): + raise TypeError, c + added = None + for c in child: + if not dict.has_key(c): + collection.append(c) + dict[c] = 1 + added = 1 + c.parents[self] = 1 + if added: + self._children_reset() + + def add_wkid(self, wkid): + """Add a node to the list of kids waiting to be evaluated""" + if self.wkids != None: + self.wkids.append(wkid) + + def _children_reset(self): + try: + delattr(self, '_children') + except AttributeError: + pass + + def children(self, scan=1): + """Return a list of the node's direct children, minus those + that are ignored by this node.""" + if scan: + self.scan() + try: + return self._children + except AttributeError: + c = filter(lambda x, i=self.ignore: x not in i, + self.all_children(scan=0)) + self._children = c + return c + + def all_children(self, scan=1): + """Return a list of all the node's direct children.""" + # The return list may contain duplicate Nodes, especially in + # source trees where there are a lot of repeated #includes + # of a tangle of .h files. Profiling shows, however, that + # eliminating the duplicates with a brute-force approach that + # preserves the order (that is, something like: + # + # u = [] + # for n in list: + # if n not in u: + # u.append(n)" + # + # takes more cycles than just letting the underlying methods + # hand back cached values if a Node's information is requested + # multiple times. (Other methods of removing duplicates, like + # using dictionary keys, lose the order, and the only ordered + # dictionary patterns I found all ended up using "not in" + # internally anyway...) + if scan: + self.scan() + if self.implicit is None: + return self.sources + self.depends + else: + return self.sources + self.depends + self.implicit + + def get_parents(self): + return self.parents.keys() + + def set_state(self, state): + self.state = state + + def get_state(self): + return self.state + + def current(self, calc=None): + return None + + def rfile(self): + return self + + def rstr(self): + return str(self) + + def is_literal(self): + """Always pass the string representation of a Node to + the command interpreter literally.""" + return 1 + + def add_pre_action(self, act): + """Adds an Action performed on this Node only before + building it.""" + self.pre_actions.append(act) + + def add_post_action(self, act): + """Adds and Action performed on this Node only after + building it.""" + self.post_actions.append(act) + + def render_include_tree(self): + """ + Return a text representation, suitable for displaying to the + user, of the include tree for the sources of this node. + """ + if self.is_derived() and self.env: + env = self.get_build_env() + for s in self.sources: + def f(node, env=env, scanner=s.source_scanner, target=self): + return node.get_found_includes(env, scanner, target) + return SCons.Util.render_tree(s, f, 1) + else: + return None + + def get_abspath(self): + """ + Return an absolute path to the Node. This will return simply + str(Node) by default, but for Node types that have a concept of + relative path, this might return something different. + """ + return str(self) + + def for_signature(self): + """ + Return a string representation of the Node that will always + be the same for this particular Node, no matter what. This + is by contrast to the __str__() method, which might, for + instance, return a relative path for a file Node. The purpose + of this method is to generate a value to be used in signature + calculation for the command line used to build a target, and + we use this method instead of str() to avoid unnecessary + rebuilds. This method does not need to return something that + would actually work in a command line; it can return any kind of + nonsense, so long as it does not change. + """ + return str(self) + + def get_string(self, for_signature): + """This is a convenience function designed primarily to be + used in command generators (i.e., CommandGeneratorActions or + Environment variables that are callable), which are called + with a for_signature argument that is nonzero if the command + generator is being called to generate a signature for the + command line, which determines if we should rebuild or not. + + Such command generators shoud use this method in preference + to str(Node) when converting a Node to a string, passing + in the for_signature parameter, such that we will call + Node.for_signature() or str(Node) properly, depending on whether + we are calculating a signature or actually constructing a + command line.""" + if for_signature: + return self.for_signature() + return str(self) + + def get_subst_proxy(self): + """ + This method is expected to return an object that will function + exactly like this Node, except that it implements any additional + special features that we would like to be in effect for + Environment variable substitution. The principle use is that + some Nodes would like to implement a __getattr__() method, + but putting that in the Node type itself has a tendency to kill + performance. We instead put it in a proxy and return it from + this method. It is legal for this method to return self + if no new functionality is needed for Environment substitution. + """ + return self + + +def get_children(node, parent): return node.children() +def ignore_cycle(node, stack): pass +def do_nothing(node, parent): pass + +class Walker: + """An iterator for walking a Node tree. + + This is depth-first, children are visited before the parent. + The Walker object can be initialized with any node, and + returns the next node on the descent with each next() call. + 'kids_func' is an optional function that will be called to + get the children of a node instead of calling 'children'. + 'cycle_func' is an optional function that will be called + when a cycle is detected. + + This class does not get caught in node cycles caused, for example, + by C header file include loops. + """ + def __init__(self, node, kids_func=get_children, + cycle_func=ignore_cycle, + eval_func=do_nothing): + self.kids_func = kids_func + self.cycle_func = cycle_func + self.eval_func = eval_func + node.wkids = copy.copy(kids_func(node, None)) + self.stack = [node] + self.history = {} # used to efficiently detect and avoid cycles + self.history[node] = None + + def next(self): + """Return the next node for this walk of the tree. + + This function is intentionally iterative, not recursive, + to sidestep any issues of stack size limitations. + """ + + while self.stack: + if self.stack[-1].wkids: + node = self.stack[-1].wkids.pop(0) + if not self.stack[-1].wkids: + self.stack[-1].wkids = None + if self.history.has_key(node): + self.cycle_func(node, self.stack) + else: + node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) + self.stack.append(node) + self.history[node] = None + else: + node = self.stack.pop() + del self.history[node] + if node: + if self.stack: + parent = self.stack[-1] + else: + parent = None + self.eval_func(node, parent) + return node + + def is_done(self): + return not self.stack + + +arg2nodes_lookups = [] diff --git a/scons/scons-local-0.95/SCons/Optik/__init__.py b/scons/scons-local-0.95/SCons/Optik/__init__.py new file mode 100644 index 0000000..b103210 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Optik/__init__.py @@ -0,0 +1,32 @@ +"""optik + +A powerful, extensible, and easy-to-use command-line parser for Python. + +By Greg Ward + +See http://optik.sourceforge.net/ +""" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Optik/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + +# Original Optik revision this is based on: +__Optik_revision__ = "__init__.py,v 1.11 2002/04/11 19:17:34 gward Exp" + +__version__ = "1.3" + + +# Re-import these for convenience +from SCons.Optik.option import Option +from SCons.Optik.option_parser import \ + OptionParser, SUPPRESS_HELP, SUPPRESS_USAGE +from SCons.Optik.errors import OptionValueError + + +# Some day, there might be many Option classes. As of Optik 1.3, the +# preferred way to instantiate Options is indirectly, via make_option(), +# which will become a factory function when there are many Option +# classes. +make_option = Option diff --git a/scons/scons-local-0.95/SCons/Optik/errors.py b/scons/scons-local-0.95/SCons/Optik/errors.py new file mode 100644 index 0000000..8ff70d7 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Optik/errors.py @@ -0,0 +1,55 @@ +"""optik.errors + +Exception classes used by Optik. +""" + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Optik/errors.py 0.95.D001 2004/03/08 07:28:28 knight" + +# Original Optik revision this is based on: +__Optik_revision__ = "errors.py,v 1.5 2002/02/13 23:29:47 gward Exp" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +# created 2001/10/17 GPW (from optik.py) + + +class OptikError (Exception): + def __init__ (self, msg): + self.msg = msg + + def __str__ (self): + return self.msg + + +class OptionError (OptikError): + """ + Raised if an Option instance is created with invalid or + inconsistent arguments. + """ + + def __init__ (self, msg, option): + self.msg = msg + self.option_id = str(option) + + def __str__ (self): + if self.option_id: + return "option %s: %s" % (self.option_id, self.msg) + else: + return self.msg + +class OptionConflictError (OptionError): + """ + Raised if conflicting options are added to an OptionParser. + """ + +class OptionValueError (OptikError): + """ + Raised if an invalid option value is encountered on the command + line. + """ + +class BadOptionError (OptikError): + """ + Raised if an invalid or ambiguous option is seen on the command-line. + """ diff --git a/scons/scons-local-0.95/SCons/Optik/option.py b/scons/scons-local-0.95/SCons/Optik/option.py new file mode 100644 index 0000000..cd1c231 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Optik/option.py @@ -0,0 +1,388 @@ +"""optik.option + +Defines the Option class and some standard value-checking functions. +""" + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Optik/option.py 0.95.D001 2004/03/08 07:28:28 knight" + +# Original Optik revision this is based on: +__Optik_revision__ = "option.py,v 1.19.2.1 2002/07/23 01:51:14 gward Exp" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +# created 2001/10/17, GPW (from optik.py) + +import sys +import string +from types import TupleType, ListType, DictType +from SCons.Optik.errors import OptionError, OptionValueError + +_builtin_cvt = { "int" : (int, "integer"), + "long" : (long, "long integer"), + "float" : (float, "floating-point"), + "complex" : (complex, "complex") } + +def check_builtin (option, opt, value): + (cvt, what) = _builtin_cvt[option.type] + try: + return cvt(value) + except ValueError: + raise OptionValueError( + #"%s: invalid %s argument %s" % (opt, what, repr(value))) + "option %s: invalid %s value: %s" % (opt, what, repr(value))) + +def check_choice(option, opt, value): + if value in option.choices: + return value + else: + choices = string.join(map(repr, option.choices),", ") + raise OptionValueError( + "option %s: invalid choice: %s (choose from %s)" + % (opt, repr(value), choices)) + +# Not supplying a default is different from a default of None, +# so we need an explicit "not supplied" value. +NO_DEFAULT = "NO"+"DEFAULT" + + +class Option: + """ + Instance attributes: + _short_opts : [string] + _long_opts : [string] + + action : string + type : string + dest : string + default : any + nargs : int + const : any + choices : [string] + callback : function + callback_args : (any*) + callback_kwargs : { string : any } + help : string + metavar : string + """ + + # The list of instance attributes that may be set through + # keyword args to the constructor. + ATTRS = ['action', + 'type', + 'dest', + 'default', + 'nargs', + 'const', + 'choices', + 'callback', + 'callback_args', + 'callback_kwargs', + 'help', + 'metavar'] + + # The set of actions allowed by option parsers. Explicitly listed + # here so the constructor can validate its arguments. + ACTIONS = ("store", + "store_const", + "store_true", + "store_false", + "append", + "count", + "callback", + "help", + "version") + + # The set of actions that involve storing a value somewhere; + # also listed just for constructor argument validation. (If + # the action is one of these, there must be a destination.) + STORE_ACTIONS = ("store", + "store_const", + "store_true", + "store_false", + "append", + "count") + + # The set of actions for which it makes sense to supply a value + # type, ie. where we expect an argument to this option. + TYPED_ACTIONS = ("store", + "append", + "callback") + + # The set of known types for option parsers. Again, listed here for + # constructor argument validation. + TYPES = ("string", "int", "long", "float", "complex", "choice") + + # Dictionary of argument checking functions, which convert and + # validate option arguments according to the option type. + # + # Signature of checking functions is: + # check(option : Option, opt : string, value : string) -> any + # where + # option is the Option instance calling the checker + # opt is the actual option seen on the command-line + # (eg. "-a", "--file") + # value is the option argument seen on the command-line + # + # The return value should be in the appropriate Python type + # for option.type -- eg. an integer if option.type == "int". + # + # If no checker is defined for a type, arguments will be + # unchecked and remain strings. + TYPE_CHECKER = { "int" : check_builtin, + "long" : check_builtin, + "float" : check_builtin, + "complex" : check_builtin, + "choice" : check_choice, + } + + + # CHECK_METHODS is a list of unbound method objects; they are called + # by the constructor, in order, after all attributes are + # initialized. The list is created and filled in later, after all + # the methods are actually defined. (I just put it here because I + # like to define and document all class attributes in the same + # place.) Subclasses that add another _check_*() method should + # define their own CHECK_METHODS list that adds their check method + # to those from this class. + CHECK_METHODS = None + + + # -- Constructor/initialization methods ---------------------------- + + def __init__ (self, *opts, **attrs): + # Set _short_opts, _long_opts attrs from 'opts' tuple + opts = self._check_opt_strings(opts) + self._set_opt_strings(opts) + + # Set all other attrs (action, type, etc.) from 'attrs' dict + self._set_attrs(attrs) + + # Check all the attributes we just set. There are lots of + # complicated interdependencies, but luckily they can be farmed + # out to the _check_*() methods listed in CHECK_METHODS -- which + # could be handy for subclasses! The one thing these all share + # is that they raise OptionError if they discover a problem. + for checker in self.CHECK_METHODS: + checker(self) + + def _check_opt_strings (self, opts): + # Filter out None because early versions of Optik had exactly + # one short option and one long option, either of which + # could be None. + opts = filter(None, opts) + if not opts: + raise OptionError("at least one option string must be supplied", + self) + return opts + + def _set_opt_strings (self, opts): + self._short_opts = [] + self._long_opts = [] + for opt in opts: + if len(opt) < 2: + raise OptionError( + "invalid option string %s: " + "must be at least two characters long" % (`opt`,), self) + elif len(opt) == 2: + if not (opt[0] == "-" and opt[1] != "-"): + raise OptionError( + "invalid short option string %s: " + "must be of the form -x, (x any non-dash char)" % (`opt`,), + self) + self._short_opts.append(opt) + else: + if not (opt[0:2] == "--" and opt[2] != "-"): + raise OptionError( + "invalid long option string %s: " + "must start with --, followed by non-dash" % (`opt`,), + self) + self._long_opts.append(opt) + + def _set_attrs (self, attrs): + for attr in self.ATTRS: + if attrs.has_key(attr): + setattr(self, attr, attrs[attr]) + del attrs[attr] + else: + if attr == 'default': + setattr(self, attr, NO_DEFAULT) + else: + setattr(self, attr, None) + if attrs: + raise OptionError( + "invalid keyword arguments: %s" % string.join(attrs.keys(),", "), + self) + + + # -- Constructor validation methods -------------------------------- + + def _check_action (self): + if self.action is None: + self.action = "store" + elif self.action not in self.ACTIONS: + raise OptionError("invalid action: %s" % (`self.action`,), self) + + def _check_type (self): + if self.type is None: + # XXX should factor out another class attr here: list of + # actions that *require* a type + if self.action in ("store", "append"): + if self.choices is not None: + # The "choices" attribute implies "choice" type. + self.type = "choice" + else: + # No type given? "string" is the most sensible default. + self.type = "string" + else: + if self.type not in self.TYPES: + raise OptionError("invalid option type: %s" % (`self.type`,), self) + if self.action not in self.TYPED_ACTIONS: + raise OptionError( + "must not supply a type for action %s" % (`self.action`,), self) + + def _check_choice(self): + if self.type == "choice": + if self.choices is None: + raise OptionError( + "must supply a list of choices for type 'choice'", self) + elif type(self.choices) not in (TupleType, ListType): + raise OptionError( + "choices must be a list of strings ('%s' supplied)" + % string.split(str(type(self.choices)),"'")[1], self) + elif self.choices is not None: + raise OptionError( + "must not supply choices for type %s" % (repr(self.type),), self) + + def _check_dest (self): + if self.action in self.STORE_ACTIONS and self.dest is None: + # No destination given, and we need one for this action. + # Glean a destination from the first long option string, + # or from the first short option string if no long options. + if self._long_opts: + # eg. "--foo-bar" -> "foo_bar" + self.dest = string.replace(self._long_opts[0][2:],'-', '_') + else: + self.dest = self._short_opts[0][1] + + def _check_const (self): + if self.action != "store_const" and self.const is not None: + raise OptionError( + "'const' must not be supplied for action %s" % (repr(self.action),), + self) + + def _check_nargs (self): + if self.action in self.TYPED_ACTIONS: + if self.nargs is None: + self.nargs = 1 + elif self.nargs is not None: + raise OptionError( + "'nargs' must not be supplied for action %s" % (repr(self.action),), + self) + + def _check_callback (self): + if self.action == "callback": + if not callable(self.callback): + raise OptionError( + "callback not callable: %s" % (repr(self.callback),), self) + if (self.callback_args is not None and + type(self.callback_args) is not TupleType): + raise OptionError( + "callback_args, if supplied, must be a tuple: not %s" + % (repr(self.callback_args),), self) + if (self.callback_kwargs is not None and + type(self.callback_kwargs) is not DictType): + raise OptionError( + "callback_kwargs, if supplied, must be a dict: not %s" + % (repr(self.callback_kwargs),), self) + else: + if self.callback is not None: + raise OptionError( + "callback supplied (%s) for non-callback option" + % (repr(self.callback),), self) + if self.callback_args is not None: + raise OptionError( + "callback_args supplied for non-callback option", self) + if self.callback_kwargs is not None: + raise OptionError( + "callback_kwargs supplied for non-callback option", self) + + + CHECK_METHODS = [_check_action, + _check_type, + _check_choice, + _check_dest, + _check_const, + _check_nargs, + _check_callback] + + + # -- Miscellaneous methods ----------------------------------------- + + def __str__ (self): + if self._short_opts or self._long_opts: + return string.join(self._short_opts + self._long_opts,"/") + else: + raise RuntimeError, "short_opts and long_opts both empty!" + + def takes_value (self): + return self.type is not None + + + # -- Processing methods -------------------------------------------- + + def check_value (self, opt, value): + checker = self.TYPE_CHECKER.get(self.type) + if checker is None: + return value + else: + return checker(self, opt, value) + + def process (self, opt, value, values, parser): + + # First, convert the value(s) to the right type. Howl if any + # value(s) are bogus. + if value is not None: + if self.nargs == 1: + value = self.check_value(opt, value) + else: + def cv(v,check=self.check_value,o=opt): + return check(o,v) + + value = tuple(map(cv,value)) + + # And then take whatever action is expected of us. + # This is a separate method to make life easier for + # subclasses to add new actions. + return self.take_action( + self.action, self.dest, opt, value, values, parser) + + def take_action (self, action, dest, opt, value, values, parser): + if action == "store": + setattr(values, dest, value) + elif action == "store_const": + setattr(values, dest, self.const) + elif action == "store_true": + setattr(values, dest, 1) + elif action == "store_false": + setattr(values, dest, 0) + elif action == "append": + values.ensure_value(dest, []).append(value) + elif action == "count": + setattr(values, dest, values.ensure_value(dest, 0) + 1) + elif action == "callback": + args = self.callback_args or () + kwargs = self.callback_kwargs or {} + apply( self.callback, (self, opt, value, parser,)+ args, kwargs) + elif action == "help": + parser.print_help() + sys.exit(0) + elif action == "version": + parser.print_version() + sys.exit(0) + else: + raise RuntimeError, "unknown action %s" % (repr(self.action),) + + return 1 + +# class Option diff --git a/scons/scons-local-0.95/SCons/Optik/option_parser.py b/scons/scons-local-0.95/SCons/Optik/option_parser.py new file mode 100644 index 0000000..08a19c9 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Optik/option_parser.py @@ -0,0 +1,730 @@ +"""optik.option_parser + +Provides the OptionParser and Values classes. +""" + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Optik/option_parser.py 0.95.D001 2004/03/08 07:28:28 knight" + +# Original Optik revision this is based on: +__Optik_revision__ = "option_parser.py,v 1.38.2.1 2002/07/23 01:51:14 gward Exp" + +# Copyright (c) 2001 Gregory P. Ward. All rights reserved. +# See the README.txt distributed with Optik for licensing terms. + +# created 2001/10/17, GPW (from optik.py) + +import sys, os +import string +import types +from SCons.Optik.option import Option, NO_DEFAULT +from SCons.Optik.errors import OptionConflictError, OptionValueError, BadOptionError + +def get_prog_name (): + return os.path.basename(sys.argv[0]) + + +SUPPRESS_HELP = "SUPPRESS"+"HELP" +SUPPRESS_USAGE = "SUPPRESS"+"USAGE" + +class Values: + + def __init__ (self, defaults=None): + if defaults: + for (attr, val) in defaults.items(): + setattr(self, attr, val) + + + def _update_careful (self, dict): + """ + Update the option values from an arbitrary dictionary, but only + use keys from dict that already have a corresponding attribute + in self. Any keys in dict without a corresponding attribute + are silently ignored. + """ + for attr in dir(self): + if dict.has_key(attr): + dval = dict[attr] + if dval is not None: + setattr(self, attr, dval) + + def _update_loose (self, dict): + """ + Update the option values from an arbitrary dictionary, + using all keys from the dictionary regardless of whether + they have a corresponding attribute in self or not. + """ + self.__dict__.update(dict) + + def _update (self, dict, mode): + if mode == "careful": + self._update_careful(dict) + elif mode == "loose": + self._update_loose(dict) + else: + raise ValueError, "invalid update mode: %s" % (repr(mode),) + + def read_module (self, modname, mode="careful"): + __import__(modname) + mod = sys.modules[modname] + self._update(vars(mod), mode) + + def read_file (self, filename, mode="careful"): + vars = {} + execfile(filename, vars) + self._update(vars, mode) + + def ensure_value (self, attr, value): + if not hasattr(self, attr) or getattr(self, attr) is None: + setattr(self, attr, value) + return getattr(self, attr) + + +class OptionParser: + """ + Class attributes: + standard_option_list : [Option] + list of standard options that will be accepted by all instances + of this parser class (intended to be overridden by subclasses). + + Instance attributes: + usage : string + a usage string for your program. Before it is displayed + to the user, "%prog" will be expanded to the name of + your program (os.path.basename(sys.argv[0])). + option_list : [Option] + the list of all options accepted on the command-line of + this program + _short_opt : { string : Option } + dictionary mapping short option strings, eg. "-f" or "-X", + to the Option instances that implement them. If an Option + has multiple short option strings, it will appears in this + dictionary multiple times. + _long_opt : { string : Option } + dictionary mapping long option strings, eg. "--file" or + "--exclude", to the Option instances that implement them. + Again, a given Option can occur multiple times in this + dictionary. + defaults : { string : any } + dictionary mapping option destination names to default + values for each destination. + + allow_interspersed_args : boolean = true + if true, positional arguments may be interspersed with options. + Assuming -a and -b each take a single argument, the command-line + -ablah foo bar -bboo baz + will be interpreted the same as + -ablah -bboo -- foo bar baz + If this flag were false, that command line would be interpreted as + -ablah -- foo bar -bboo baz + -- ie. we stop processing options as soon as we see the first + non-option argument. (This is the tradition followed by + Python's getopt module, Perl's Getopt::Std, and other argument- + parsing libraries, but it is generally annoying to users.) + + rargs : [string] + the argument list currently being parsed. Only set when + parse_args() is active, and continually trimmed down as + we consume arguments. Mainly there for the benefit of + callback options. + largs : [string] + the list of leftover arguments that we have skipped while + parsing options. If allow_interspersed_args is false, this + list is always empty. + values : Values + the set of option values currently being accumulated. Only + set when parse_args() is active. Also mainly for callbacks. + + Because of the 'rargs', 'largs', and 'values' attributes, + OptionParser is not thread-safe. If, for some perverse reason, you + need to parse command-line arguments simultaneously in different + threads, use different OptionParser instances. + + """ + + standard_option_list = [] + + + def __init__ (self, + usage=None, + option_list=None, + option_class=Option, + version=None, + conflict_handler="error"): + self.set_usage(usage) + self.option_class = option_class + self.version = version + self.set_conflict_handler(conflict_handler) + self.allow_interspersed_args = 1 + + # Create the various lists and dicts that constitute the + # "option list". See class docstring for details about + # each attribute. + self._create_option_list() + + # Populate the option list; initial sources are the + # standard_option_list class attribute, the 'option_list' + # argument, and the STD_VERSION_OPTION global (if 'version' + # supplied). + self._populate_option_list(option_list) + + self._init_parsing_state() + + # -- Private methods ----------------------------------------------- + # (used by the constructor) + + def _create_option_list (self): + self.option_list = [] + self._short_opt = {} # single letter -> Option instance + self._long_opt = {} # long option -> Option instance + self.defaults = {} # maps option dest -> default value + + def _populate_option_list (self, option_list): + if self.standard_option_list: + self.add_options(self.standard_option_list) + if option_list: + self.add_options(option_list) + + def _init_parsing_state (self): + # These are set in parse_args() for the convenience of callbacks. + self.rargs = None + self.largs = None + self.values = None + + + # -- Simple modifier methods --------------------------------------- + + def set_usage (self, usage): + if usage is None: + self.usage = "usage: %prog [options]" + elif usage is SUPPRESS_USAGE: + self.usage = None + else: + self.usage = usage + + def enable_interspersed_args (self): + self.allow_interspersed_args = 1 + + def disable_interspersed_args (self): + self.allow_interspersed_args = 0 + + def set_conflict_handler (self, handler): + if handler not in ("ignore", "error", "resolve"): + raise ValueError, "invalid conflict_resolution value %s" % (repr(handler),) + self.conflict_handler = handler + + def set_default (self, dest, value): + self.defaults[dest] = value + + def set_defaults (self, **kwargs): + self.defaults.update(kwargs) + + def get_default_values(self): + return Values(self.defaults) + + + # -- Option-adding methods ----------------------------------------- + + def _check_conflict (self, option): + conflict_opts = [] + for opt in option._short_opts: + if self._short_opt.has_key(opt): + conflict_opts.append((opt, self._short_opt[opt])) + for opt in option._long_opts: + if self._long_opt.has_key(opt): + conflict_opts.append((opt, self._long_opt[opt])) + + if conflict_opts: + handler = self.conflict_handler + if handler == "ignore": # behaviour for Optik 1.0, 1.1 + pass + elif handler == "error": # new in 1.2 + raise OptionConflictError( + "conflicting option string(s): %s" + % string.join( map( lambda x: x[0], conflict_opts),", "), + option) + elif handler == "resolve": # new in 1.2 + for (opt, c_option) in conflict_opts: + if len(opt)>2 and opt[:2]=="--": + c_option._long_opts.remove(opt) + del self._long_opt[opt] + else: + c_option._short_opts.remove(opt) + del self._short_opt[opt] + if not (c_option._short_opts or c_option._long_opts): + self.option_list.remove(c_option) + + + def add_option (self, *args, **kwargs): + """add_option(Option) + add_option(opt_str, ..., kwarg=val, ...) + """ + if type(args[0]) is types.StringType: + option = apply(self.option_class,args, kwargs) + elif len(args) == 1 and not kwargs: + option = args[0] + if not isinstance(option, Option): + raise TypeError, "not an Option instance: %s" % (repr(option),) + else: + raise TypeError, "invalid arguments" + + self._check_conflict(option) + + self.option_list.append(option) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + if option.dest is not None: # option has a dest, we need a default + if option.default is not NO_DEFAULT: + self.defaults[option.dest] = option.default + elif not self.defaults.has_key(option.dest): + self.defaults[option.dest] = None + + def add_options (self, option_list): + for option in option_list: + self.add_option(option) + + + # -- Option query/removal methods ---------------------------------- + + def get_option (self, opt_str): + return (self._short_opt.get(opt_str) or + self._long_opt.get(opt_str)) + + def has_option (self, opt_str): + return (self._short_opt.has_key(opt_str) or + self._long_opt.has_key(opt_str)) + + + def remove_option (self, opt_str): + option = self._short_opt.get(opt_str) + if option is None: + option = self._long_opt.get(opt_str) + if option is None: + raise ValueError("no such option %s" % (repr(opt_str),)) + + for opt in option._short_opts: + del self._short_opt[opt] + for opt in option._long_opts: + del self._long_opt[opt] + self.option_list.remove(option) + + + # -- Option-parsing methods ---------------------------------------- + + def _get_args (self, args): + if args is None: + return sys.argv[1:] + else: + return args[:] # don't modify caller's list + + def parse_args (self, args=None, values=None): + """ + parse_args(args : [string] = sys.argv[1:], + values : Values = None) + -> (values : Values, args : [string]) + + Parse the command-line options found in 'args' (default: + sys.argv[1:]). Any errors result in a call to 'error()', which + by default prints the usage message to stderr and calls + sys.exit() with an error message. On success returns a pair + (values, args) where 'values' is an Values instance (with all + your option values) and 'args' is the list of arguments left + over after parsing options. + """ + rargs = self._get_args(args) + if values is None: + values = self.get_default_values() + + # Store the halves of the argument list as attributes for the + # convenience of callbacks: + # rargs + # the rest of the command-line (the "r" stands for + # "remaining" or "right-hand") + # largs + # the leftover arguments -- ie. what's left after removing + # options and their arguments (the "l" stands for "leftover" + # or "left-hand") + self.rargs = rargs + self.largs = largs = [] + self.values = values + + try: + stop = self._process_args(largs, rargs, values) + except (BadOptionError, OptionValueError), err: + self.error(err.msg) + + args = largs + rargs + return self.check_values(values, args) + + def check_values (self, values, args): + """ + check_values(values : Values, args : [string]) + -> (values : Values, args : [string]) + + Check that the supplied option values and leftover arguments are + valid. Returns the option values and leftover arguments + (possibly adjusted, possibly completely new -- whatever you + like). Default implementation just returns the passed-in + values; subclasses may override as desired. + """ + return (values, args) + + def _process_args (self, largs, rargs, values): + """_process_args(largs : [string], + rargs : [string], + values : Values) + + Process command-line arguments and populate 'values', consuming + options and arguments from 'rargs'. If 'allow_interspersed_args' is + false, stop at the first non-option argument. If true, accumulate any + interspersed non-option arguments in 'largs'. + """ + while rargs: + arg = rargs[0] + # We handle bare "--" explicitly, and bare "-" is handled by the + # standard arg handler since the short arg case ensures that the + # len of the opt string is greater than 1. + if arg == "--": + del rargs[0] + return + elif arg[0:2] == "--": + # process a single long option (possibly with value(s)) + self._process_long_opt(rargs, values) + elif arg[:1] == "-" and len(arg) > 1: + # process a cluster of short options (possibly with + # value(s) for the last one only) + self._process_short_opts(rargs, values) + elif self.allow_interspersed_args: + largs.append(arg) + del rargs[0] + else: + return # stop now, leave this arg in rargs + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt (self, opt): + """_match_long_opt(opt : string) -> string + + Determine which long option string 'opt' matches, ie. which one + it is an unambiguous abbrevation for. Raises BadOptionError if + 'opt' doesn't unambiguously match any long option string. + """ + return _match_abbrev(opt, self._long_opt) + + def _process_long_opt (self, rargs, values): + arg = rargs.pop(0) + + # Value explicitly attached to arg? Pretend it's the next + # argument. + if "=" in arg: + (opt, next_arg) = string.split(arg,"=", 1) + rargs.insert(0, next_arg) + had_explicit_value = 1 + else: + opt = arg + had_explicit_value = 0 + + opt = self._match_long_opt(opt) + option = self._long_opt[opt] + if option.takes_value(): + nargs = option.nargs + if len(rargs) < nargs: + if nargs == 1: + self.error("%s option requires a value" % opt) + else: + self.error("%s option requires %d values" + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + elif had_explicit_value: + self.error("%s option does not take a value" % opt) + + else: + value = None + + option.process(opt, value, values, self) + + def _process_short_opts (self, rargs, values): + arg = rargs.pop(0) + stop = 0 + i = 1 + for ch in arg[1:]: + opt = "-" + ch + option = self._short_opt.get(opt) + i = i+1 # we have consumed a character + + if not option: + self.error("no such option: %s" % opt) + if option.takes_value(): + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + rargs.insert(0, arg[i:]) + stop = 1 + + nargs = option.nargs + if len(rargs) < nargs: + if nargs == 1: + self.error("%s option requires a value" % opt) + else: + self.error("%s option requires %s values" + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + else: # option doesn't take a value + value = None + + option.process(opt, value, values, self) + + if stop: + break + + + # -- Output/error methods ------------------------------------------ + + def error (self, msg): + """error(msg : string) + + Print a usage message incorporating 'msg' to stderr and exit. + If you override this in a subclass, it should not return -- it + should either exit or raise an exception. + """ + self.print_usage(sys.stderr) + sys.stderr.write("\nSCons error: %s\n" % msg) + sys.exit(2) + + def print_usage (self, file=None): + """print_usage(file : file = stdout) + + Print the usage message for the current program (self.usage) to + 'file' (default stdout). Any occurence of the string "%prog" in + self.usage is replaced with the name of the current program + (basename of sys.argv[0]). Does nothing if self.usage is empty + or not defined. + """ + if file is None: + file = sys.stdout + if self.usage: + usage = string.replace(self.usage,"%prog", get_prog_name()) + file.write(usage + "\n") + + def print_version (self, file=None): + """print_version(file : file = stdout) + + Print the version message for this program (self.version) to + 'file' (default stdout). As with print_usage(), any occurence + of "%prog" in self.version is replaced by the current program's + name. Does nothing if self.version is empty or undefined. + """ + if file is None: + file = sys.stdout + if self.version: + version = string.replace(self.version,"%prog", get_prog_name()) + file.write(version+"\n") + + def print_help (self, file=None): + """print_help(file : file = stdout) + + Print an extended help message, listing all options and any + help text provided with them, to 'file' (default stdout). + """ + # SCons: don't import wrap_text from distutils, use the + # copy we've included below, so we can avoid being dependent + # on having the right version of distutils installed. + #from distutils.fancy_getopt import wrap_text + + if file is None: + file = sys.stdout + + self.print_usage(file) + + # The help for each option consists of two parts: + # * the opt strings and metavars + # eg. ("-x", or "-fFILENAME, --file=FILENAME") + # * the user-supplied help string + # eg. ("turn on expert mode", "read data from FILENAME") + # + # If possible, we write both of these on the same line: + # -x turn on expert mode + # + # But if the opt string list is too long, we put the help + # string on a second line, indented to the same column it would + # start in if it fit on the first line. + # -fFILENAME, --file=FILENAME + # read data from FILENAME + + file.write("Options:\n") + width = 78 # assume 80 cols for now + + option_help = [] # list of (string, string) tuples + lengths = [] + + for option in self.option_list: + takes_value = option.takes_value() + if takes_value: + metavar = option.metavar or string.upper(option.dest) + + opts = [] # list of "-a" or "--foo=FILE" strings + if option.help is SUPPRESS_HELP: + continue + + if takes_value: + for sopt in option._short_opts: + opts.append(sopt + ' ' + metavar) + for lopt in option._long_opts: + opts.append(lopt + "=" + metavar) + else: + for opt in option._short_opts + option._long_opts: + opts.append(opt) + + opts = string.join(opts,", ") + option_help.append((opts, option.help)) + lengths.append(len(opts)) + + max_opts = min(max(lengths), 26) + + for (opts, help) in option_help: + # how much to indent lines 2 .. N of help text + indent_rest = 2 + max_opts + 2 + help_width = width - indent_rest + + if len(opts) > max_opts: + opts = " " + opts + "\n" + indent_first = indent_rest + else: # start help on same line as opts + opts = " %-*s " % (max_opts, opts) + indent_first = 0 + + file.write(opts) + + if help: + help_lines = wrap_text(help, help_width) + file.write( "%*s%s\n" % (indent_first, "", help_lines[0])) + for line in help_lines[1:]: + file.write(" %*s%s\n" % (indent_rest, "", line)) + elif opts[-1] != "\n": + file.write("\n") + +# class OptionParser + + +def _match_abbrev (s, wordmap): + """_match_abbrev(s : string, wordmap : {string : Option}) -> string + + Return the string key in 'wordmap' for which 's' is an unambiguous + abbreviation. If 's' is found to be ambiguous or doesn't match any of + 'words', raise BadOptionError. + """ + # Is there an exact match? + if wordmap.has_key(s): + return s + else: + # Isolate all words with s as a prefix. + possibilities = [] + ls = len(s) + for word in wordmap.keys(): + if len(word)>=ls and word[:ls]==s: + possibilities.append(word) + # No exact match, so there had better be just one possibility. + if len(possibilities) == 1: + return possibilities[0] + elif not possibilities: + raise BadOptionError("no such option: %s" % s) + else: + # More than one possible completion: ambiguous prefix. + raise BadOptionError("ambiguous option: %s (%s?)" + % (s, string.join(possibilities,", "))) + +# SCons: Include a snarfed copy of wrap_text(), so we're not dependent +# on the right version of distutils being installed. +import re + +WS_TRANS = string.maketrans(string.whitespace, ' ' * len(string.whitespace)) + +def wrap_text (text, width): + """wrap_text(text : string, width : int) -> [string] + + Split 'text' into multiple lines of no more than 'width' characters + each, and return the list of strings that results. + """ + + if text is None: + return [] + if len(text) <= width: + return [text] + + text = string.expandtabs(text) + text = string.translate(text, WS_TRANS) + chunks = re.split(r'( +|-+)', text) + chunks = filter(None, chunks) # ' - ' results in empty strings + lines = [] + + while chunks: + + cur_line = [] # list of chunks (to-be-joined) + cur_len = 0 # length of current line + + while chunks: + l = len(chunks[0]) + if cur_len + l <= width: # can squeeze (at least) this chunk in + cur_line.append(chunks[0]) + del chunks[0] + cur_len = cur_len + l + else: # this line is full + # drop last chunk if all space + if cur_line and cur_line[-1][0] == ' ': + del cur_line[-1] + break + + if chunks: # any chunks left to process? + + # if the current line is still empty, then we had a single + # chunk that's too big too fit on a line -- so we break + # down and break it up at the line width + if cur_len == 0: + cur_line.append(chunks[0][0:width]) + chunks[0] = chunks[0][width:] + + # all-whitespace chunks at the end of a line can be discarded + # (and we know from the re.split above that if a chunk has + # *any* whitespace, it is *all* whitespace) + if chunks[0][0] == ' ': + del chunks[0] + + # and store this line in the list-of-all-lines -- as a single + # string, of course! + lines.append(string.join(cur_line, '')) + + # while chunks + + return lines + +# wrap_text () diff --git a/scons/scons-local-0.95/SCons/Options/BoolOption.py b/scons/scons-local-0.95/SCons/Options/BoolOption.py new file mode 100644 index 0000000..644cb7d --- /dev/null +++ b/scons/scons-local-0.95/SCons/Options/BoolOption.py @@ -0,0 +1,88 @@ +"""engine.SCons.Options.BoolOption + +This file defines the option type for SCons implementing true/false values. + +Usage example: + + opts = Options() + opts.Add(BoolOption('embedded', 'build for an embedded system', 0)) + ... + if env['embedded'] == 1: + ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Options/BoolOption.py 0.95.D001 2004/03/08 07:28:28 knight" + +__all__ = ('BoolOption', 'True', 'False') + +import string + +import SCons.Errors + +__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) +__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') + +# we need this since SCons should work version indepentant +True, False = 1, 0 + + +def _text2bool(val): + """ + Converts strings to True/False depending on the 'truth' expressed by + the string. If the string can't be converted, the original value + will be returned. + + See '__true_strings' and '__false_strings' for values considered + 'true' or 'false respectivly. + + This is usable as 'converter' for SCons' Options. + """ + lval = string.lower(val) + if lval in __true_strings: return True + if lval in __false_strings: return False + raise ValueError("Invalid value for boolean option: %s" % val) + + +def _validator(key, val, env): + """ + Validates the given value to be either '0' or '1'. + + This is usable as 'validator' for SCons' Options. + """ + if not env[key] in (True, False): + raise SCons.Errors.UserError( + 'Invalid value for boolean option %s: %s' % (key, env[key])) + + +def BoolOption(key, help, default): + """ + The input parameters describe a boolen option, thus they are + returned with the correct converter and validator appended. The + 'help' text will by appended by '(yes|no) to show the valid + valued. The result is usable for input to opts.Add(). + """ + return (key, '%s (yes|no)' % help, default, + _validator, _text2bool) diff --git a/scons/scons-local-0.95/SCons/Options/EnumOption.py b/scons/scons-local-0.95/SCons/Options/EnumOption.py new file mode 100644 index 0000000..132bd6b --- /dev/null +++ b/scons/scons-local-0.95/SCons/Options/EnumOption.py @@ -0,0 +1,101 @@ +"""engine.SCons.Options.EnumOption + +This file defines the option type for SCons allowing only specified +input-values. + +Usage example: + + opts = Options() + opts.Add(EnumOption('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=2)) + ... + if env['debug'] == 'full': + ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Options/EnumOption.py 0.95.D001 2004/03/08 07:28:28 knight" + +__all__ = ('EnumOption',) + +import string + +import SCons.Errors + +def _validator(key, val, env, vals): + if not val in vals: + raise SCons.Errors.UserError( + 'Invalid value for option %s: %s' % (key, val)) + + +def EnumOption(key, help, default, allowed_values, map={}, ignorecase=0): + """ + The input parameters describe a option with only certain values + allowed. They are returned with an appropriate converter and + validator appended. The result is usable for input to + Options.Add(). + + 'key' and 'default' are the values to be passed on to Options.Add(). + + 'help' will be appended by the allowed values automatically + + 'allowed_values' is a list of strings, which are allowed as values + for this option. + + The 'map'-dictionary may be used for converting the input value + into canonical values (eg. for aliases). + + 'ignorecase' defines the behaviour of the validator: + + If ignorecase == 0, the validator/converter are case-sensitive. + If ignorecase == 1, the validator/converter are case-insensitive. + If ignorecase == 2, the validator/converter is case-insensitive and + the converted value will always be lower-case. + + The 'validator' tests whether the value is in the list of allowed + values. The 'converter' converts input values according to the + given 'map'-dictionary (unmapped input values are returned + unchanged). + """ + help = '%s (%s)' % (help, string.join(allowed_values, '|')) + # define validator + if ignorecase >= 1: + validator = lambda key, val, env, vals=allowed_values: \ + _validator(key, string.lower(val), env, vals) + else: + validator = lambda key, val, env, vals=allowed_values: \ + _validator(key, val, env, vals) + # define converter + if ignorecase == 2: + converter = lambda val, map=map: \ + string.lower(map.get(string.lower(val), val)) + elif ignorecase == 1: + converter = lambda val, map=map: \ + map.get(string.lower(val), val) + else: + converter = lambda val, map=map: \ + map.get(val, val) + return (key, help, default, validator, converter) diff --git a/scons/scons-local-0.95/SCons/Options/ListOption.py b/scons/scons-local-0.95/SCons/Options/ListOption.py new file mode 100644 index 0000000..994487e --- /dev/null +++ b/scons/scons-local-0.95/SCons/Options/ListOption.py @@ -0,0 +1,131 @@ +"""engine.SCons.Options.ListOption + +This file defines the option type for SCons implementing 'lists'. + +A 'list' option may either be 'all', 'none' or a list of names +separated by comma. After the option has been processed, the option +value holds either the named list elements, all list elemens or no +list elements at all. + +Usage example: + + list_of_libs = Split('x11 gl qt ical') + + opts = Options() + opts.Add(ListOption('shared', + 'libraries to build as shared libraries', + 'all', + elems = list_of_libs)) + ... + for lib in list_of_libs: + if lib in env['shared']: + env.SharedObject(...) + else: + env.Object(...) +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Options/ListOption.py 0.95.D001 2004/03/08 07:28:28 knight" + +# Know Bug: This should behave like a Set-Type, but does not really, +# since elements can occur twice. + +__all__ = ('ListOption',) + +import string +import UserList + + +class _ListOption(UserList.UserList): + def __init__(self, allowedElems, initlist): + UserList.UserList.__init__(self, filter(None, initlist)) + self.allowedElems = allowedElems[:] + self.allowedElems.sort() + + def __cmp__(self, other): + raise NotImplementedError + def __eq__(self, other): + raise NotImplementedError + def __ge__(self, other): + raise NotImplementedError + def __gt__(self, other): + raise NotImplementedError + def __le__(self, other): + raise NotImplementedError + def __lt__(self, other): + raise NotImplementedError + def __str__(self): + if len(self) == 0: + return 'none' + self.data.sort() + if self.data == self.allowedElems: + return 'all' + else: + return string.join(self, ',') + #def __repr__(self): + # todo: implement this + +def _converter(val, allowedElems): + """ + """ + if val == 'none': + val = [] + elif val == 'all': + val = allowedElems + else: + val = filter(None, string.split(val, ',')) + notAllowed = [] + for v in val: + if not v in allowedElems: + notAllowed.append(v) + if notAllowed: + raise ValueError("Invalid value(s) for option: %s" % + string.join(notAllowed, ',')) + return _ListOption(allowedElems, val) + + +## def _validator(key, val, env): +## """ +## """ +## # todo: write validater for pgk list +## return 1 + + +def ListOption(key, help, default, names): + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validater appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (separated by space). + """ + names_str = 'allowed names: %s' % string.join(names, ' ') + help = string.join( + (help, '(all|none|comma-separated list of names)', names_str), + '\n ') + return (key, help, default, + None, #_validator, + lambda val, elems=names: _converter(val, elems)) diff --git a/scons/scons-local-0.95/SCons/Options/PackageOption.py b/scons/scons-local-0.95/SCons/Options/PackageOption.py new file mode 100644 index 0000000..b0e62c0 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Options/PackageOption.py @@ -0,0 +1,106 @@ +"""engine.SCons.Options.PackageOption + +This file defines the option type for SCons implementing 'package +activation'. + +To be used whenever a 'package' may be enabled/disabled and the +package path may be specified. + +Usage example: + + Examples: + x11=no (disables X11 support) + x11=yes (will search for the package installation dir) + x11=/usr/local/X11 (will check this path for existance) + + To replace autoconf's --with-xxx=yyy + + opts = Options() + opts.Add(PackageOption('x11', + 'use X11 installed here (yes = search some places', + 'yes')) + ... + if env['x11'] == True: + dir = ... search X11 in some standard places ... + env['x11'] = dir + if env['x11']: + ... build with x11 ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Options/PackageOption.py 0.95.D001 2004/03/08 07:28:28 knight" + +__all__ = ('PackageOption', 'True', 'False') + +import string + +from BoolOption import True, False +import SCons.Errors + +__enable_strings = (str(True), 'yes', 'true', 'on', 'enable', 'search') +__disable_strings = (str(False), 'no', 'false', 'off', 'disable') + +def _converter(val): + """ + """ + lval = string.lower(val) + if lval in __enable_strings: return True + if lval in __disable_strings: return False + #raise ValueError("Invalid value for boolean option: %s" % val) + return val + + +def _validator(key, val, env, searchfunc): + # NB: searchfunc is currenty undocumented and unsupported + """ + """ + # todo: write validator, check for path + import os + if env[key] == False: + pass + elif env[key] == True: + if searchfunc: + env[key] = searchfunc(key, val) + elif not os.path.exists(val): + raise SCons.Errors.UserError( + 'Path does not exist for option %s: %s' % (key, val)) + + +def PackageOption(key, help, default, searchfunc=None): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (seperated by space). + """ + help = string.join( + (help, '( yes | no | /path/to/%s )' % key), + '\n ') + return (key, help, default, + lambda k, v, e, f=searchfunc: _validator(k,v,e,f), + _converter) diff --git a/scons/scons-local-0.95/SCons/Options/PathOption.py b/scons/scons-local-0.95/SCons/Options/PathOption.py new file mode 100644 index 0000000..241d345 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Options/PathOption.py @@ -0,0 +1,85 @@ +"""engine.SCons.Options.PathOption + +This file defines an option type for SCons implementing 'package +activation'. + +To be used whenever a 'package' may be enabled/disabled and the +package path may be specified. + +Usage example: + + Examples: + x11=no (disables X11 support) + x11=yes (will search for the package installation dir) + x11=/usr/local/X11 (will check this path for existance) + + To replace autoconf's --with-xxx=yyy + + opts = Options() + + opts = Options() + opts.Add(PathOption('qtdir', + 'where the root of Qt is installed', + qtdir)) + opts.Add(PathOption('qt_includes', + 'where the Qt includes are installed', + '$qtdir/includes')) + opts.Add(PathOption('qt_libraries', + 'where the Qt library is installed', + '$qtdir/lib')) + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Options/PathOption.py 0.95.D001 2004/03/08 07:28:28 knight" + +__all__ = ('PathOption',) + +import os + +import SCons.Errors + +def _validator(key, val, env): + """ + """ + # todo: write validator, check for path + if not os.path.exists(val): + raise SCons.Errors.UserError( + 'Path does not exist for option %s: %s' % (key, val)) + + +def PathOption(key, help, default): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'path list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (seperated by space). + """ + return (key, '%s ( /path/to/%s )' % (help, key), default, + _validator, None) + diff --git a/scons/scons-local-0.95/SCons/Options/__init__.py b/scons/scons-local-0.95/SCons/Options/__init__.py new file mode 100644 index 0000000..7c48324 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Options/__init__.py @@ -0,0 +1,240 @@ +"""engine.SCons.Options + +This file defines the Options class that is used to add user-friendly +customizable variables to an SCons build. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Options/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Errors +import SCons.Util +import SCons.Warnings + +from BoolOption import BoolOption, True, False # okay +from EnumOption import EnumOption # okay +from ListOption import ListOption # naja +from PackageOption import PackageOption # naja +from PathOption import PathOption # okay + + +class Options: + """ + Holds all the options, updates the environment with the variables, + and renders the help text. + """ + def __init__(self, files=None, args={}): + """ + files - [optional] List of option configuration files to load + (backward compatibility) If a single string is passed it is + automatically placed in a file list + """ + + self.options = [] + self.args = args + self.files = None + if SCons.Util.is_String(files): + self.files = [ files ] + elif files: + self.files = files + + def _do_add(self, key, help="", default=None, validator=None, converter=None): + class Option: + pass + + option = Option() + option.key = key + option.help = help + option.default = default + option.validator = validator + option.converter = converter + + self.options.append(option) + + + def Add(self, key, help="", default=None, validator=None, converter=None, **kw): + """ + Add an option. + + key - the name of the variable, or a list or tuple of arguments + help - optional help text for the options + default - optional default value + validator - optional function that is called to validate the option's value + Called with (key, value, environment) + converter - optional function that is called to convert the option's value before + putting it in the environment. + """ + + if SCons.Util.is_List(key) or type(key) == type(()): + apply(self._do_add, key) + return + + if not SCons.Util.is_String(key) or \ + not SCons.Util.is_valid_construction_var(key): + raise SCons.Errors.UserError, "Illegal Options.Add() key `%s'" % str(key) + + if kw.has_key('validater'): + SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, + "The 'validater' keyword of the Options.Add() method is deprecated\n" +\ + "and should be changed to 'validator'.") + if validator is None: + validator = kw['validater'] + + self._do_add(key, help, default, validator, converter) + + + def AddOptions(self, *optlist): + """ + Add a list of options. + + Each list element is a tuple/list of arguments to be passed on + to the underlying method for adding options. + + Example: + opt.AddOptions( + ('debug', '', 0), + ('CC', 'The C compiler'), + ('VALIDATE', 'An option for testing validation', 'notset', + validator, None), + ) + """ + for o in optlist: + apply(self._do_add, o) + + + def Update(self, env, args=None): + """ + Update an environment with the option variables. + + env - the environment to update. + """ + + values = {} + + # first set the defaults: + for option in self.options: + if not option.default is None: + values[option.key] = option.default + + # next set the value specified in the options file + if self.files: + for filename in self.files: + if os.path.exists(filename): + execfile(filename, values) + + # finally set the values specified on the command line + if args is None: + args = self.args + values.update(args) + + # put the variables in the environment: + # (don't copy over variables that are not declared + # as options) + for option in self.options: + try: + env[option.key] = values[option.key] + except KeyError: + pass + + # Call the convert functions: + for option in self.options: + if option.converter and values.has_key(option.key): + value = env.subst('${%s}'%option.key) + try: + env[option.key] = option.converter(value) + except ValueError, x: + raise SCons.Errors.UserError, 'Error converting option: %s\n%s'%(option.key, x) + + + # Finally validate the values: + for option in self.options: + if option.validator: + option.validator(option.key, env.subst('${%s}'%option.key), env) + + def Save(self, filename, env): + """ + Saves all the options in the given file. This file can + then be used to load the options next run. This can be used + to create an option cache file. + + filename - Name of the file to save into + env - the environment get the option values from + """ + + # Create the file and write out the header + try: + fh = open(filename, 'w') + + try: + # Make an assignment in the file for each option within the environment + # that was assigned a value other than the default. + for option in self.options: + try: + value = env[option.key] + try: + eval(repr(value)) + except KeyboardInterrupt: + raise + except: + # Convert stuff that has a repr() that + # cannot be evaluated into a string + value = SCons.Util.to_String(value) + if env.subst('${%s}' % option.key) != \ + env.subst(SCons.Util.to_String(option.default)): + fh.write('%s = %s\n' % (option.key, repr(value))) + except KeyError: + pass + finally: + fh.close() + + except IOError, x: + raise SCons.Errors.UserError, 'Error writing options to file: %s\n%s' % (filename, x) + + def GenerateHelpText(self, env, sort=None): + """ + Generate the help text for the options. + + env - an environment that is used to get the current values + of the options. + """ + + help_text = "" + + if sort: + options = self.options[:] + options.sort(lambda x,y,func=sort: func(x.key,y.key)) + else: + options = self.options + + for option in options: + help_text = help_text + '\n%s: %s\n default: %s\n'%(option.key, option.help, option.default) + if env.has_key(option.key): + help_text = help_text + ' actual: %s\n'%env.subst('${%s}'%option.key) + else: + help_text = help_text + ' actual: None\n' + + return help_text diff --git a/scons/scons-local-0.95/SCons/Platform/__init__.py b/scons/scons-local-0.95/SCons/Platform/__init__.py new file mode 100644 index 0000000..6f7a45e --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/__init__.py @@ -0,0 +1,123 @@ +"""SCons.Platform + +SCons platform selection. + +This looks for modules that define a callable object that can modify a +construction environment as appropriate for a given platform. + +Note that we take a more simplistic view of "platform" than Python does. +We're looking for a single string that determines a set of +tool-independent variables with which to initialize a construction +environment. Consequently, we'll examine both sys.platform and os.name +(and anything else that might come in to play) in order to return some +specification which is unique enough for our purposes. + +Note that because this subsysem just *selects* a callable that can +modify a construction environment, it's possible for people to define +their own "platform specification" in an arbitrary callable function. +No one needs to use or tie in to this subsystem in order to roll +their own platform definition. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + +import imp +import os +import string +import sys + +import SCons.Errors + +def platform_default(): + """Return the platform string for our execution environment. + + The returned value should map to one of the SCons/Platform/*.py + files. Since we're architecture independent, though, we don't + care about the machine architecture. + """ + osname = os.name + if osname == 'java': + osname = os._osType + if osname == 'posix': + if sys.platform == 'cygwin': + return 'cygwin' + elif string.find(sys.platform, 'irix') != -1: + return 'irix' + elif string.find(sys.platform, 'sunos') != -1: + return 'sunos' + elif string.find(sys.platform, 'hp-ux') != -1: + return 'hpux' + elif string.find(sys.platform, 'aix') != -1: + return 'aix' + else: + return 'posix' + elif os.name == 'os2': + return 'os2' + else: + return sys.platform + +def platform_module(name = platform_default()): + """Return the imported module for the platform. + + This looks for a module name that matches the specified argument. + If the name is unspecified, we fetch the appropriate default for + our execution environment. + """ + full_name = 'SCons.Platform.' + name + if not sys.modules.has_key(full_name): + if os.name == 'java': + eval(full_name) + else: + try: + file, path, desc = imp.find_module(name, + sys.modules['SCons.Platform'].__path__) + mod = imp.load_module(full_name, file, path, desc) + setattr(SCons.Platform, name, mod) + except ImportError: + raise SCons.Errors.UserError, "No platform named '%s'" % name + if file: + file.close() + return sys.modules[full_name] + +def DefaultToolList(platform, env): + """Select a default tool list for the specified platform. + """ + return SCons.Tool.tool_list(platform, env) + +class PlatformSpec: + def __init__(self, name): + self.name = name + + def __str__(self): + return self.name + +def Platform(name = platform_default()): + """Select a canned Platform specification. + """ + module = platform_module(name) + spec = PlatformSpec(name) + spec.__call__ = module.generate + return spec diff --git a/scons/scons-local-0.95/SCons/Platform/aix.py b/scons/scons-local-0.95/SCons/Platform/aix.py new file mode 100644 index 0000000..c1f196c --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/aix.py @@ -0,0 +1,62 @@ +"""engine.SCons.Platform.aix + +Platform-specific initialization for IBM AIX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/aix.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import string + +import posix + +def get_xlc(env, xlc, xlc_r, packages): + # Use the AIX package installer tool lslpp to figure out where a + # given xl* compiler is installed and what version it is. + xlcPath = None + xlcVersion = None + + try: + xlc = env['CC'] + except KeyError: + xlc = 'xlc' + for package in packages: + cmd = "lslpp -fc " + package + " 2>/dev/null | egrep '" + xlc + "([^-_a-zA-Z0-9].*)?$'" + line = os.popen(cmd).readline() + if line: + v, p = string.split(line, ':')[1:3] + xlcVersion = string.split(v)[1] + xlcPath = string.split(p)[0] + xlcPath = xlcPath[:xlcPath.rindex('/')] + break + return (xlcPath, xlc, xlc_r, xlcVersion) + +def generate(env): + posix.generate(env) diff --git a/scons/scons-local-0.95/SCons/Platform/cygwin.py b/scons/scons-local-0.95/SCons/Platform/cygwin.py new file mode 100644 index 0000000..b912b94 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/cygwin.py @@ -0,0 +1,47 @@ +"""SCons.Platform.cygwin + +Platform-specific initialization for Cygwin systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/cygwin.py 0.95.D001 2004/03/08 07:28:28 knight" + +import posix +import win32 + +def generate(env): + posix.generate(env) + + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['TEMPFILE'] = win32.TempFileMunge diff --git a/scons/scons-local-0.95/SCons/Platform/hpux.py b/scons/scons-local-0.95/SCons/Platform/hpux.py new file mode 100644 index 0000000..3731f4f --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/hpux.py @@ -0,0 +1,38 @@ +"""engine.SCons.Platform.hpux + +Platform-specific initialization for HP-UX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/hpux.py 0.95.D001 2004/03/08 07:28:28 knight" + +import posix + +def generate(env): + posix.generate(env) diff --git a/scons/scons-local-0.95/SCons/Platform/irix.py b/scons/scons-local-0.95/SCons/Platform/irix.py new file mode 100644 index 0000000..6dbfcb2 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/irix.py @@ -0,0 +1,38 @@ +"""SCons.Platform.irix + +Platform-specific initialization for SGI IRIX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/irix.py 0.95.D001 2004/03/08 07:28:28 knight" + +import posix + +def generate(env): + posix.generate(env) diff --git a/scons/scons-local-0.95/SCons/Platform/os2.py b/scons/scons-local-0.95/SCons/Platform/os2.py new file mode 100644 index 0000000..5088a1c --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/os2.py @@ -0,0 +1,49 @@ +"""SCons.Platform.os2 + +Platform-specific initialization for OS/2 systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/os2.py 0.95.D001 2004/03/08 07:28:28 knight" + +def generate(env): + if not env.has_key('ENV'): + env['ENV'] = {} + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = '$LIBPREFIX' + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] diff --git a/scons/scons-local-0.95/SCons/Platform/posix.py b/scons/scons-local-0.95/SCons/Platform/posix.py new file mode 100644 index 0000000..62f2ce3 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/posix.py @@ -0,0 +1,231 @@ +"""SCons.Platform.posix + +Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/posix.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import popen2 +import string +import sys +import select + +import SCons.Util + +exitvalmap = { + 2 : 127, + 13 : 126, +} + +def escape(arg): + "escape shell special characters" + slash = '\\' + special = '"$' + + arg = string.replace(arg, slash, slash+slash) + for c in special: + arg = string.replace(arg, c, slash+c) + + return '"' + arg + '"' + +def _get_env_command(sh, escape, cmd, args, env): + if env: + s = 'env - ' + for key in env.keys(): + s = s + '%s=%s '%(key, escape(env[key])) + s = s + sh + ' -c ' + s = s + escape(string.join(args)) + else: + s = string.join(args) + return s + +def env_spawn(sh, escape, cmd, args, env): + s = _get_env_command( sh, escape, cmd, args, env) + stat = os.system(s) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def spawn_spawn(sh, escape, cmd, args, env): + args = [sh, '-c', string.join(args)] + stat = os.spawnvpe(os.P_WAIT, sh, args, env) + # os.spawnvpe() returns the actual exit code, not the encoding + # returned by os.waitpid() or os.system(). + return stat + +def fork_spawn(sh, escape, cmd, args, env): + pid = os.fork() + if not pid: + # Child process. + exitval = 127 + args = [sh, '-c', string.join(args)] + try: + os.execvpe(sh, args, env) + except OSError, e: + exitval = exitvalmap[e[0]] + sys.stderr.write("scons: %s: %s\n" % (cmd, e[1])) + os._exit(exitval) + else: + # Parent process. + pid, stat = os.waitpid(pid, 0) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr): + stdout_eof = stderr_eof = 0 + while not (stdout_eof and stderr_eof): + (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], []) + if cmd_stdout in i: + str = cmd_stdout.read() + if len(str) == 0: + stdout_eof = 1 + elif stdout != None: + stdout.write(str) + if cmd_stderr in i: + str = cmd_stderr.read() + if len(str) == 0: + #sys.__stderr__.write( "stderr_eof=1\n" ) + stderr_eof = 1 + else: + #sys.__stderr__.write( "str(stderr) = %s\n" % str ) + stderr.write(str) + + +def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr): + # spawn using Popen3 combined with the env command + # the command name and the command's stdout is written to stdout + # the command's stderr is written to stderr + s = _get_env_command( sh, escape, cmd, args, env) + proc = popen2.Popen3(s, 1) + process_cmd_output(proc.fromchild, proc.childerr, stdout, stderr) + stat = proc.wait() + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr): + # spawn using fork / exec and providing a pipe for the command's + # stdout / stderr stream + if stdout != stderr: + (rFdOut, wFdOut) = os.pipe() + (rFdErr, wFdErr) = os.pipe() + else: + (rFdOut, wFdOut) = os.pipe() + rFdErr = rFdOut + wFdErr = wFdOut + # do the fork + pid = os.fork() + if not pid: + # Child process + os.close( rFdOut ) + if rFdOut != rFdErr: + os.close( rFdErr ) + os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ? + os.dup2( wFdErr, 2 ) + os.close( wFdOut ) + if stdout != stderr: + os.close( wFdErr ) + exitval = 127 + args = [sh, '-c', string.join(args)] + try: + os.execvpe(sh, args, env) + except OSError, e: + exitval = exitvalmap[e[0]] + stderr.write("scons: %s: %s\n" % (cmd, e[1])) + os._exit(exitval) + else: + # Parent process + pid, stat = os.waitpid(pid, 0) + os.close( wFdOut ) + if stdout != stderr: + os.close( wFdErr ) + childOut = os.fdopen( rFdOut ) + if stdout != stderr: + childErr = os.fdopen( rFdErr ) + else: + childErr = childOut + process_cmd_output(childOut, childErr, stdout, stderr) + os.close( rFdOut ) + if stdout != stderr: + os.close( rFdErr ) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + + + +def generate(env): + # If os.spawnvpe() exists, we use it to spawn commands. Otherwise + # if the env utility exists, we use os.system() to spawn commands, + # finally we fall back on os.fork()/os.exec(). + # + # os.spawnvpe() is prefered because it is the most efficient. But + # for Python versions without it, os.system() is prefered because it + # is claimed that it works better with threads (i.e. -j) and is more + # efficient than forking Python. + # + # NB: Other people on the scons-users mailing list have claimed that + # os.fork()/os.exec() works better than os.system(). There may just + # not be a default that works best for all users. + + if os.__dict__.has_key('spawnvpe'): + spawn = spawn_spawn + elif env.Detect('env'): + spawn = env_spawn + else: + spawn = fork_spawn + + if env.Detect('env'): + pspawn = piped_env_spawn + else: + pspawn = piped_fork_spawn + + if not env.has_key('ENV'): + env['ENV'] = {} + env['ENV']['PATH'] = '/usr/local/bin:/bin:/usr/bin' + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.o' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['SHLIBPREFIX'] = '$LIBPREFIX' + env['SHLIBSUFFIX'] = '.so' + env['LIBPREFIXES'] = '$LIBPREFIX' + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['PSPAWN'] = pspawn + env['SPAWN'] = spawn + env['SHELL'] = 'sh' + env['ESCAPE'] = escape diff --git a/scons/scons-local-0.95/SCons/Platform/sunos.py b/scons/scons-local-0.95/SCons/Platform/sunos.py new file mode 100644 index 0000000..31f7aac --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/sunos.py @@ -0,0 +1,38 @@ +"""engine.SCons.Platform.sunos + +Platform-specific initialization for Sun systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/sunos.py 0.95.D001 2004/03/08 07:28:28 knight" + +import posix + +def generate(env): + posix.generate(env) diff --git a/scons/scons-local-0.95/SCons/Platform/win32.py b/scons/scons-local-0.95/SCons/Platform/win32.py new file mode 100644 index 0000000..2b48ff5 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Platform/win32.py @@ -0,0 +1,327 @@ +"""SCons.Platform.win32 + +Platform-specific initialization for Win32 systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Platform/win32.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import string +import sys +import tempfile +from SCons.Platform.posix import exitvalmap + +# XXX See note below about why importing SCons.Action should be +# eventually refactored. +import SCons.Action +import SCons.Util + +class TempFileMunge: + """A callable class. You can set an Environment variable to this, + then call it with a string argument, then it will perform temporary + file substitution on it. This is used to circumvent the win32 long command + line limitation. + + Example usage: + env["TEMPFILE"] = TempFileMunge + env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}" + """ + def __init__(self, cmd): + self.cmd = cmd + + def __call__(self, target, source, env, for_signature): + if for_signature: + return self.cmd + cmd = env.subst_list(self.cmd, 0, target, source)[0] + try: + maxline = int(env.subst('$MAXLINELENGTH')) + except ValueError: + maxline = 2048 + if (reduce(lambda x, y: x + len(y), cmd, 0) + len(cmd)) <= maxline: + return self.cmd + else: + # We do a normpath because mktemp() has what appears to be + # a bug in Win32 that will use a forward slash as a path + # delimiter. Win32's link mistakes that for a command line + # switch and barfs. + # + # We use the .lnk suffix for the benefit of the Phar Lap + # linkloc linker, which likes to append an .lnk suffix if + # none is given. + tmp = os.path.normpath(tempfile.mktemp('.lnk')) + native_tmp = SCons.Util.get_native_path(tmp) + + if env['SHELL'] and env['SHELL'] == 'sh': + # The sh shell will try to escape the backslashes in the + # path, so unescape them. + native_tmp = string.replace(native_tmp, '\\', r'\\\\') + # In Cygwin, we want to use rm to delete the temporary + # file, because del does not exist in the sh shell. + rm = env.Detect('rm') or 'del' + else: + # Don't use 'rm' if the shell is not sh, because rm won't + # work with the win32 shells (cmd.exe or command.com) or + # win32 path names. + rm = 'del' + + args = map(SCons.Util.quote_spaces, cmd[1:]) + open(tmp, 'w').write(string.join(args, " ") + "\n") + # XXX Using the SCons.Action.print_actions value directly + # like this is bogus, but expedient. This class should + # really be rewritten as an Action that defines the + # __call__() and strfunction() methods and lets the + # normal action-execution logic handle whether or not to + # print/execute the action. The problem, though, is all + # of that is decided before we execute this method as + # part of expanding the $TEMPFILE construction variable. + # Consequently, refactoring this will have to wait until + # we get more flexible with allowing Actions to exist + # independently and get strung together arbitrarily like + # Ant tasks. In the meantime, it's going to be more + # user-friendly to not let obsession with architectural + # purity get in the way of just being helpful, so we'll + # reach into SCons.Action directly. + if SCons.Action.print_actions: + print("Using tempfile "+native_tmp+" for command line:\n"+ + str(cmd[0]) + " " + string.join(args," ")) + return [ cmd[0], '@' + native_tmp + '\n' + rm, native_tmp ] + +# The upshot of all this is that, if you are using Python 1.5.2, +# you had better have cmd or command.com in your PATH when you run +# scons. + +def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): + # There is no direct way to do that in python. What we do + # here should work for most cases: + # In case stdout (stderr) is not redirected to a file, + # we redirect it into a temporary file tmpFileStdout + # (tmpFileStderr) and copy the contents of this file + # to stdout (stderr) given in the argument + if not sh: + sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") + return 127 + else: + # one temporary file for stdout and stderr + tmpFileStdout = os.path.normpath(tempfile.mktemp()) + tmpFileStderr = os.path.normpath(tempfile.mktemp()) + + # check if output is redirected + stdoutRedirected = 0 + stderrRedirected = 0 + for arg in args: + # are there more possibilities to redirect stdout ? + if (string.find( arg, ">", 0, 1 ) != -1 or + string.find( arg, "1>", 0, 2 ) != -1): + stdoutRedirected = 1 + # are there more possibilities to redirect stderr ? + if string.find( arg, "2>", 0, 2 ) != -1: + stderrRedirected = 1 + + # redirect output of non-redirected streams to our tempfiles + if stdoutRedirected == 0: + args.append(">" + str(tmpFileStdout)) + if stderrRedirected == 0: + args.append("2>" + str(tmpFileStderr)) + + # actually do the spawn + try: + args = [sh, '/C', escape(string.join(args)) ] + ret = os.spawnve(os.P_WAIT, sh, args, env) + except OSError, e: + # catch any error + ret = exitvalmap[e[0]] + if stderr != None: + stderr.write("scons: %s: %s\n" % (cmd, e[1])) + # copy child output from tempfiles to our streams + # and do clean up stuff + if stdout != None and stdoutRedirected == 0: + try: + stdout.write(open( tmpFileStdout, "r" ).read()) + os.remove( tmpFileStdout ) + except (IOError, OSError): + pass + + if stderr != None and stderrRedirected == 0: + try: + stderr.write(open( tmpFileStderr, "r" ).read()) + os.remove( tmpFileStderr ) + except (IOError, OSError): + pass + return ret + +def spawn(sh, escape, cmd, args, env): + if not sh: + sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") + return 127 + else: + try: + args = [sh, '/C', escape(string.join(args)) ] + ret = os.spawnve(os.P_WAIT, sh, args, env) + except OSError, e: + ret = exitvalmap[e[0]] + sys.stderr.write("scons: %s: %s\n" % (cmd, e[1])) + return ret + +# Windows does not allow special characters in file names anyway, so +# no need for a complex escape function, we will just quote the arg. +escape = lambda x: '"' + x + '"' + +# Get the windows system directory name +def get_system_root(): + # A resonable default if we can't read the registry + try: + val = os.environ['SYSTEMROOT'] + except KeyError: + val = "C:/WINDOWS" + pass + + # First see if we can look in the registry... + if SCons.Util.can_read_reg: + try: + # Look for Windows NT system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows NT\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + except SCons.Util.RegError: + try: + # Okay, try the Windows 9x system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + except KeyboardInterrupt: + raise + except: + pass + return val + +# Get the location of the program files directory +def get_program_files_dir(): + # Now see if we can look in the registry... + val = '' + if SCons.Util.can_read_reg: + try: + # Look for Windows Program Files directory + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir') + except SCons.Util.RegError: + val = '' + pass + + if val == '': + # A reasonable default if we can't read the registry + # (Actually, it's pretty reasonable even if we can :-) + val = os.path.join(os.path.dirname(get_system_root()),"Program Files") + + return val + +def generate(env): + # Attempt to find cmd.exe (for WinNT/2k/XP) or + # command.com for Win9x + cmd_interp = '' + # First see if we can look in the registry... + if SCons.Util.can_read_reg: + try: + # Look for Windows NT system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows NT\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + cmd_interp = os.path.join(val, 'System32\\cmd.exe') + except SCons.Util.RegError: + try: + # Okay, try the Windows 9x system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + cmd_interp = os.path.join(val, 'command.com') + except KeyboardInterrupt: + raise + except: + pass + + # For the special case of not having access to the registry, we + # use a temporary path and pathext to attempt to find the command + # interpreter. If we fail, we try to find the interpreter through + # the env's PATH. The problem with that is that it might not + # contain an ENV and a PATH. + if not cmd_interp: + systemroot = r'C:\Windows' + if os.environ.has_key('SYSTEMROOT'): + systemroot = os.environ['SYSTEMROOT'] + tmp_path = systemroot + os.pathsep + \ + os.path.join(systemroot,'System32') + tmp_pathext = '.com;.exe;.bat;.cmd' + if os.environ.has_key('PATHEXT'): + tmp_pathext = os.environ['PATHEXT'] + cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) + if not cmd_interp: + cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) + + if not cmd_interp: + cmd_interp = env.Detect('cmd') + if not cmd_interp: + cmd_interp = env.Detect('command') + + + if not env.has_key('ENV'): + env['ENV'] = {} + + # Import things from the external environment to the construction + # environment's ENV. This is a potential slippery slope, because we + # *don't* want to make builds dependent on the user's environment by + # default. We're doing this for SYSTEMROOT, though, because it's + # needed for anything that uses sockets, and seldom changes. Weigh + # the impact carefully before adding other variables to this list. + import_env = [ 'SYSTEMROOT' ] + for var in import_env: + v = os.environ.get(var) + if v: + env['ENV'][var] = v + + env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD' + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['PSPAWN'] = piped_spawn + env['SPAWN'] = spawn + env['SHELL'] = cmd_interp + env['TEMPFILE'] = TempFileMunge + env['MAXLINELENGTH'] = 2048 + env['ESCAPE'] = escape diff --git a/scons/scons-local-0.95/SCons/SConf.py b/scons/scons-local-0.95/SCons/SConf.py new file mode 100644 index 0000000..7e4ec81 --- /dev/null +++ b/scons/scons-local-0.95/SCons/SConf.py @@ -0,0 +1,727 @@ +"""SCons.SConf + +Autoconf-like configuration support. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/SConf.py 0.95.D001 2004/03/08 07:28:28 knight" + +import cPickle +import os +import string +import sys +import traceback +import types + +import SCons.Action +import SCons.Builder +import SCons.Errors +import SCons.Node.FS +import SCons.Taskmaster +import SCons.Util +import SCons.Warnings +import SCons.Conftest + +# First i thought of using a different filesystem as the default_fs, +# but it showed up that there are too many side effects in doing that. +SConfFS=SCons.Node.FS.default_fs + +# to be set, if we are in dry-run mode +dryrun = 0 + +_ac_build_counter = 0 +_ac_config_counter = 0 +_activeSConfObjects = {} + +class SConfWarning(SCons.Warnings.Warning): + pass +SCons.Warnings.enableWarningClass( SConfWarning ) + +# action to create the source +def _createSource( target, source, env ): + fd = open(target[0].get_path(), "w") + fd.write(env['SCONF_TEXT']) + fd.close() + +def _stringSource( target, source, env ): + import string + return (target[0].get_path() + ' <- \n |' + + string.replace( env['SCONF_TEXT'], "\n", "\n |" ) ) + +BooleanTypes = [types.IntType] +if hasattr(types, 'BooleanType'): BooleanTypes.append(types.BooleanType) + +class SConf: + """This is simply a class to represent a configure context. After + creating a SConf object, you can call any tests. After finished with your + tests, be sure to call the Finish() method, which returns the modified + environment. + Some words about caching: In most cases, it is not necessary to cache + Test results explicitely. Instead, we use the scons dependency checking + mechanism. For example, if one wants to compile a test program + (SConf.TryLink), the compiler is only called, if the program dependencies + have changed. However, if the program could not be compiled in a former + SConf run, we need to explicitely cache this error. + """ + + def __init__(self, env, custom_tests = {}, conf_dir='#/.sconf_temp', + log_file='#/config.log'): + """Constructor. Pass additional tests in the custom_tests-dictinary, + e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest + defines a custom test. + Note also the conf_dir and log_file arguments (you may want to + build tests in the BuildDir, not in the SourceDir) + """ + import SCons.Script.SConscript + if not SCons.Script.SConscript.sconscript_reading: + raise SCons.Errors.UserError, "Calling Configure from Builders is not supported." + global SConfFS + if not SConfFS: + SConfFS = SCons.Node.FS.FS(SCons.Node.FS.default_fs.pathTop) + if len(_activeSConfObjects.keys()) > 0: + raise (SCons.Errors.UserError, + "Only one SConf object may be active at one time") + self.env = env + if log_file != None: + self.logfile = SConfFS.File(log_file) + else: + self.logfile = None + self.logstream = None + self.lastTarget = None + + # add default tests + default_tests = { + 'CheckFunc' : CheckFunc, + 'CheckType' : CheckType, + 'CheckHeader' : CheckHeader, + 'CheckCHeader' : CheckCHeader, + 'CheckCXXHeader' : CheckCXXHeader, + 'CheckLib' : CheckLib, + 'CheckLibWithHeader' : CheckLibWithHeader + } + self.AddTests(default_tests) + self.AddTests(custom_tests) + self.confdir = SConfFS.Dir(conf_dir) + self.calc = None + self.cache = {} + self._startup() + + def Finish(self): + """Call this method after finished with your tests: + env = sconf.Finish()""" + global _lastSConfObj + _lastSConfObj = None + self._shutdown() + return self.env + + def _setCache(self, nodes, already_done = []): + # Set up actions used for caching errors + # Caching positive tests should not be necessary, cause + # the build system knows, if test objects/programs/outputs + # are up to date. + for n in nodes: + # The 'n in already_done' expression is not really efficient. + # We may do something more sophisticated in the future :-), + # but there should not be that many dependencies in configure + # tests + if (n.has_builder() and + not n in already_done): + n.add_pre_action(SCons.Action.Action(self._preCache, + self._stringCache)) + n.add_post_action(SCons.Action.Action(self._postCache, + self._stringCache)) + already_done.append( n ) + self._setCache(n.children()) + + def BuildNodes(self, nodes): + """ + Tries to build the given nodes immediately. Returns 1 on success, + 0 on error. + """ + + global SCons + import SCons.Script # really ugly, but we need BuildTask :-( + # Is it better to provide a seperate Task for SConf builds ? + class SConfBuildTask(SCons.Script.BuildTask): + """Errors in SConf builds are not fatal, so we override + the do_failed method""" + def do_failed(self, status=2): + pass + + class SConfDryRunTask(SConfBuildTask): + """Raise ConfiugreDryRunErrors whenever a target is to + be built. Pass these Errors to the main script.""" + def execute(self): + target = self.targets[0] + if (target.get_state() != SCons.Node.up_to_date and + target.has_builder() and + not hasattr(target.builder, 'status')): + + raise SCons.Errors.ConfigureDryRunError(target) + + def failed(self): + if sys.exc_type == SCons.Errors.ConfigureDryRunError: + raise + SConfBuildTask.failed(self) + + if self.logstream != None: + # override stdout / stderr to write in log file + oldStdout = sys.stdout + sys.stdout = self.logstream + oldStderr = sys.stderr + sys.stderr = self.logstream + + # the engine assumes the current path is the SConstruct directory ... + old_fs_dir = SConfFS.getcwd() + old_os_dir = os.getcwd() + SConfFS.chdir(SConfFS.Top, change_os_dir=1) + + self._setCache( nodes ) + ret = 1 + + try: + # ToDo: use user options for calc + self.calc = SCons.Sig.Calculator(max_drift=0) + if dryrun: + buildTask = SConfDryRunTask + else: + buildTask = SConfBuildTask + tm = SCons.Taskmaster.Taskmaster( nodes, + buildTask, + self.calc ) + # we don't want to build tests in parallel + jobs = SCons.Job.Jobs(1, tm ) + try: + jobs.run() + except SCons.Errors.BuildError, e: + sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr)) + if e.errstr == 'Exception': + traceback.print_exception(e.args[0], e.args[1], e.args[2]) + + for n in nodes: + state = n.get_state() + if (state != SCons.Node.executed and + state != SCons.Node.up_to_date): + # the node could not be built. we return 0 in this case + ret = 0 + finally: + os.chdir(old_os_dir) + SConfFS.chdir(old_fs_dir, change_os_dir=0) + if self.logstream != None: + # restore stdout / stderr + sys.stdout = oldStdout + sys.stderr = oldStderr + return ret + + + def TryBuild(self, builder, text = None, extension = ""): + """Low level TryBuild implementation. Normally you don't need to + call that - you can use TryCompile / TryLink / TryRun instead + """ + global _ac_build_counter + + nodesToBeBuilt = [] + + f = "conftest_" + str(_ac_build_counter) + pref = self.env.subst( builder.builder.prefix ) + suff = self.env.subst( builder.builder.suffix ) + target = self.confdir.File(pref + f + suff) + self.env['SCONF_TEXT'] = text + self.env['PIPE_BUILD'] = 1 + self.env['PSTDOUT'] = self.logstream + self.env['PSTDERR'] = self.logstream + if text != None: + source = self.confdir.File(f + extension) + sourceNode = self.env.SConfSourceBuilder(target=source, + source=None) + nodesToBeBuilt.append(sourceNode) + else: + source = None + + nodes = builder(target = target, source = source) + if not SCons.Util.is_List(nodes): + nodes = [nodes] + nodesToBeBuilt.extend(nodes) + ret = self.BuildNodes(nodesToBeBuilt) + + # clean up environment + del self.env['PIPE_BUILD'] + del self.env['PSTDOUT'] + del self.env['PSTDERR'] + del self.env['SCONF_TEXT'] + + _ac_build_counter = _ac_build_counter + 1 + if ret: + self.lastTarget = nodes[0] + else: + self.lastTarget = None + + return ret + + def TryAction(self, action, text = None, extension = ""): + """Tries to execute the given action with optional source file + contents and optional source file extension , + Returns the status (0 : failed, 1 : ok) and the contents of the + output file. + """ + builder = SCons.Builder.Builder(action=action) + self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) + ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) + del self.env['BUILDERS']['SConfActionBuilder'] + if ok: + outputStr = self.lastTarget.get_contents() + return (1, outputStr) + return (0, "") + + def TryCompile( self, text, extension): + """Compiles the program given in text to an env.Object, using extension + as file extension (e.g. '.c'). Returns 1, if compilation was + successful, 0 otherwise. The target is saved in self.lastTarget (for + further processing). + """ + return self.TryBuild(self.env.Object, text, extension) + + def TryLink( self, text, extension ): + """Compiles the program given in text to an executable env.Program, + using extension as file extension (e.g. '.c'). Returns 1, if + compilation was successful, 0 otherwise. The target is saved in + self.lastTarget (for further processing). + """ + return self.TryBuild(self.env.Program, text, extension ) + + def TryRun(self, text, extension ): + """Compiles and runs the program given in text, using extension + as file extension (e.g. '.c'). Returns (1, outputStr) on success, + (0, '') otherwise. The target (a file containing the program's stdout) + is saved in self.lastTarget (for further processing). + """ + ok = self.TryLink(text, extension) + if( ok ): + prog = self.lastTarget + output = SConfFS.File(prog.get_path()+'.out') + node = self.env.Command(output, prog, [ [ prog.get_path(), ">", "${TARGET}"] ]) + ok = self.BuildNodes([node]) + if ok: + outputStr = output.get_contents() + return( 1, outputStr) + return (0, "") + + class TestWrapper: + """A wrapper around Tests (to ensure sanity)""" + def __init__(self, test, sconf): + self.test = test + self.sconf = sconf + def __call__(self, *args, **kw): + if not self.sconf.active: + raise (SCons.Errors.UserError, + "Test called after sconf.Finish()") + context = CheckContext(self.sconf) + ret = apply(self.test, (context,) + args, kw) + context.Result("error: no result") + return ret + + def AddTest(self, test_name, test_instance): + """Adds test_class to this SConf instance. It can be called with + self.test_name(...)""" + setattr(self, test_name, SConf.TestWrapper(test_instance, self)) + + def AddTests(self, tests): + """Adds all the tests given in the tests dictionary to this SConf + instance + """ + for name in tests.keys(): + self.AddTest(name, tests[name]) + + def _preCache(self, target, source, env): + # Action before target is actually built + # + # We record errors in the cache. Only non-exisiting targets may + # have recorded errors + needs_rebuild = target[0].exists() + buildSig = target[0].calc_signature(self.calc) + for node in source: + if node.get_state() != SCons.Node.up_to_date: + # if any of the sources has changed, we cannot use our cache + needs_rebuild = 1 + if not self.cache.has_key( target[0].get_path() ): + # We have no recorded error, so we try to build the target + needs_rebuild = 1 + else: + lastBuildSig = self.cache[target[0].get_path()]['builder'] + if lastBuildSig != buildSig: + needs_rebuild = 1 + if not needs_rebuild: + # When we are here, we can savely pass the recorded error + print ('(cached): Building "%s" failed in a previous run.' % + target[0].get_path()) + return 1 + else: + # Otherwise, we try to record an error + self.cache[target[0].get_path()] = { + 'builder' : buildSig + } + + def _postCache(self, target, source, env): + # Action after target is successfully built + # + # No error during build -> remove the recorded error + del self.cache[target[0].get_path()] + + def _stringCache(self, target, source, env): + return None + + def _loadCache(self): + # try to load build-error cache + try: + cacheDesc = cPickle.load(open(self.confdir.File(".cache").get_path())) + if cacheDesc['scons_version'] != SCons.__version__: + raise Exception, "version mismatch" + self.cache = cacheDesc['data'] + except KeyboardInterrupt: + raise + except: + self.cache = {} + + def _dumpCache(self): + if dryrun: + return + # try to dump build-error cache + try: + cacheDesc = {'scons_version' : SCons.__version__, + 'data' : self.cache } + cPickle.dump(cacheDesc, open(self.confdir.File(".cache").get_path(),"w")) + except Exception, e: + # this is most likely not only an IO error, but an error + # inside SConf ... + SCons.Warnings.warn( SConfWarning, "Couldn't dump SConf cache" ) + + def _createDir( self, node ): + dirName = node.get_path() + if dryrun: + if not os.path.isdir( dirName ): + raise SCons.Errors.ConfigureDryRunError(dirName) + else: + if not os.path.isdir( dirName ): + os.makedirs( dirName ) + node._exists = 1 + + def _startup(self): + """Private method. Set up logstream, and set the environment + variables necessary for a piped build + """ + global _ac_config_counter + global _activeSConfObjects + global SConfFS + + self.lastEnvFs = self.env.fs + self.env.fs = SConfFS + self._createDir(self.confdir) + self.confdir.up().add_ignore( [self.confdir] ) + + if self.logfile != None and not dryrun: + # truncate logfile, if SConf.Configure is called for the first time + # in a build + if _ac_config_counter == 0: + log_mode = "w" + else: + log_mode = "a" + self.logstream = open(self.logfile.get_path(), log_mode) + # logfile may stay in a build directory, so we tell + # the build system not to override it with a eventually + # existing file with the same name in the source directory + self.logfile.dir.add_ignore( [self.logfile] ) + + tb = traceback.extract_stack()[-3] + + self.logstream.write( '\nfile %s,line %d:\n\tConfigure( confdir = %s )\n\n' % + (tb[0], tb[1], self.confdir.get_path()) ) + else: + self.logstream = None + # we use a special builder to create source files from TEXT + action = SCons.Action.Action(_createSource, + _stringSource, + varlist=['SCONF_TEXT']) + sconfSrcBld = SCons.Builder.Builder(action=action) + self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) + self.active = 1 + # only one SConf instance should be active at a time ... + _activeSConfObjects[self] = None + _ac_config_counter = _ac_config_counter + 1 + self._loadCache() + + def _shutdown(self): + """Private method. Reset to non-piped spawn""" + global _activeSConfObjets + + if not self.active: + raise SCons.Errors.UserError, "Finish may be called only once!" + if self.logstream != None: + self.logstream.close() + self.logstream = None + # remove the SConfSourceBuilder from the environment + blds = self.env['BUILDERS'] + del blds['SConfSourceBuilder'] + self.env.Replace( BUILDERS=blds ) + self.active = 0 + del _activeSConfObjects[self] + self._dumpCache() + self.env.fs = self.lastEnvFs + +class CheckContext: + """Provides a context for configure tests. Defines how a test writes to the + screen and log file. + + A typical test is just a callable with an instance of CheckContext as + first argument: + + def CheckCustom(context, ...) + context.Message('Checking my weird test ... ') + ret = myWeirdTestFunction(...) + context.Result(ret) + + Often, myWeirdTestFunction will be one of + context.TryCompile/context.TryLink/context.TryRun. The results of + those are cached, for they are only rebuild, if the dependencies have + changed. + """ + + def __init__(self, sconf): + """Constructor. Pass the corresponding SConf instance.""" + self.sconf = sconf + self.cached = 0 + self.did_show_result = 0 + + # for Conftest.py: + self.vardict = {} + self.havedict = {} + self.headerfilename = None # XXX may cause trouble! + + def Message(self, text): + """Inform about what we are doing right now, e.g. + 'Checking for SOMETHING ... ' + """ + # write to config.log + if self.sconf.logstream != None: + self.sconf.logstream.write(text + '\n') + sys.stdout.write(text) + self.did_show_result = 0 + + def Result(self, res): + """Inform about the result of the test. res may be an integer or a + string. In case of an integer, the written text will be 'ok' or + 'failed'. + The result is only displayed when self.did_show_result is not set. + """ + if type(res) in BooleanTypes: + if res: + text = "ok" + else: + text = "failed" + elif type(res) == types.StringType: + text = res + else: + raise TypeError, "Expected string, int or bool, got " + str(type(res)) + + if self.did_show_result == 0: + if self.cached: + text = text + " (cached)" + + # Didn't show result yet, do it now. + if self.sconf.logstream != None: + self.sconf.logstream.write("Result: " + text + "\n\n") + sys.stdout.write(text + "\n") + self.did_show_result = 1 + + + def TryBuild(self, *args, **kw): + return apply(self.sconf.TryBuild, args, kw) + + def TryAction(self, *args, **kw): + return apply(self.sconf.TryAction, args, kw) + + def TryCompile(self, *args, **kw): + return apply(self.sconf.TryCompile, args, kw) + + def TryLink(self, *args, **kw): + return apply(self.sconf.TryLink, args, kw) + + def TryRun(self, *args, **kw): + return apply(self.sconf.TryRun, args, kw) + + def __getattr__( self, attr ): + if( attr == 'env' ): + return self.sconf.env + elif( attr == 'lastTarget' ): + return self.sconf.lastTarget + else: + raise AttributeError, "CheckContext instance has no attribute '%s'" % attr + + #### Stuff used by Conftest.py (look there for explanations). + + def BuildProg(self, text, ext): + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + res = self.TryBuild(self.env.Program, text, ext) + if type(res) in BooleanTypes: + if res: + ret = "" + else: + ret = "failed to build test program" + elif type(res) == types.StringType: + ret = res + else: + raise TypeError, "Expected string or int" + return ret + + def CompileProg(self, text, ext): + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + res = self.TryBuild(self.env.Object, text, ext) + if type(res) in BooleanTypes: + if res: + ret = "" + else: + ret = "failed to compile test program" + elif type(res) == types.StringType: + ret = res + else: + raise TypeError, "Expected string or int" + return ret + + def AppendLIBS(self, lib_name_list): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Append(LIBS = lib_name_list) + return oldLIBS + + def SetLIBS(self, val): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Replace(LIBS = val) + return oldLIBS + + def Display(self, msg): + sys.stdout.write(msg) + self.Log(msg) + + def Log(self, msg): + if self.sconf.logstream != None: + self.sconf.logstream.write(msg) + + #### End of stuff used by Conftest.py. + + +def CheckFunc(context, function_name, language = None): + res = SCons.Conftest.CheckFunc(context, function_name, language = language) + context.did_show_result = 1 + if not res: + return 1 # Ok + return 0 # Failed + + +def CheckType(context, type_name, includes = "", language = None): + res = SCons.Conftest.CheckType(context, type_name, + header = includes, language = language) + context.did_show_result = 1 + if not res: + return 1 # Ok + return 0 # Failed + + +def CheckHeader(context, header, include_quotes = '<>', language = None): + """ + A test for a C or C++ header file. + """ + if not SCons.Util.is_List(header): + header = [header] + l = [] + for s in header[:-1]: + l.append("#include %s%s%s\n" % (include_quotes[0], s, include_quotes[1])) + res = SCons.Conftest.CheckHeader(context, header[-1], string.join(l, ''), + language = language, + include_quotes = include_quotes) + context.did_show_result = 1 + if not res: + return 1 # Ok + return 0 # Failed + + +# Bram: Make this function obsolete? CheckHeader() is more generic. + +def CheckCHeader(context, header, include_quotes = '""'): + """ + A test for a C header file. + """ + return CheckHeader(context, header, include_quotes, language = "C") + + +# Bram: Make this function obsolete? CheckHeader() is more generic. + +def CheckCXXHeader(context, header, include_quotes = '""'): + """ + A test for a C++ header file. + """ + return CheckHeader(context, header, include_quotes, language = "C++") + + +def CheckLib(context, library = None, symbol = "main", autoadd = 1, + header = None, language = None): + """ + A test for a library. See also CheckLibWithHeader. + Note that library may also be None to test whether the given symbol + compiles without flags. + """ + # ToDo: accept path for the library + res = SCons.Conftest.CheckLib(context, library, symbol, header = header, + language = language, autoadd = autoadd) + context.did_show_result = 1 + if not res: + return 1 # Ok + return 0 # Failed + + +# XXX +# Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H. + +def CheckLibWithHeader(context, library, header, language, + call = "main();", autoadd = 1): + # ToDo: accept path for library. Support system header files. + """ + Another (more sophisticated) test for a library. + Checks, if library and header is available for language (maybe 'C' + or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'. + As in CheckLib, we support library=None, to test if the call compiles + without extra link flags. + """ + + if not SCons.Util.is_List(header): + header = [header] + l = [] + for s in header: + l.append('#include "%s"\n' % (s)) + + res = SCons.Conftest.CheckLib(context, library, "main", string.join(l, ''), + call = call, language = language, autoadd = autoadd) + context.did_show_result = 1 + if not res: + return 1 # Ok + return 0 # Failed + diff --git a/scons/scons-local-0.95/SCons/Scanner/C.py b/scons/scons-local-0.95/SCons/Scanner/C.py new file mode 100644 index 0000000..96b5722 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Scanner/C.py @@ -0,0 +1,46 @@ +"""SCons.Scanner.C + +This module implements the depenency scanner for C/C++ code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Scanner/C.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Node.FS +import SCons.Scanner + +def CScan(fs = SCons.Node.FS.default_fs): + """Return a prototype Scanner instance for scanning source files + that use the C pre-processor""" + cs = SCons.Scanner.ClassicCPP("CScan", + [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", + ".h", ".H", ".hxx", ".hpp", ".hh", + ".F", ".fpp", ".FPP", + ".S", ".spp", ".SPP"], + "CPPPATH", + '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")', + fs = fs) + return cs diff --git a/scons/scons-local-0.95/SCons/Scanner/D.py b/scons/scons-local-0.95/SCons/Scanner/D.py new file mode 100644 index 0000000..6bffb4b --- /dev/null +++ b/scons/scons-local-0.95/SCons/Scanner/D.py @@ -0,0 +1,54 @@ +"""SCons.Scanner.D + +Scanner for the Digital Mars "D" programming language. + +Coded by Andy Friesen +17 Nov 2003 + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Scanner/D.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Scanner + +def DScan(fs = SCons.Node.FS.default_fs): + """Return a prototype Scanner instance for scanning D source files""" + ds = DScanner(name = "DScan", + suffixes = ['.d'], + path_variable = 'DPATH', + regex = 'import\s+([^\;]*)\;', + fs = fs) + return ds + +class DScanner(SCons.Scanner.Classic): + def find_include(self, include, source_dir, path): + # translate dots (package separators) to slashes + inc = include.replace('.', '/') + + i = SCons.Node.FS.find_file(inc + '.d', + (source_dir,) + path, + self.fs.File) + return i, include diff --git a/scons/scons-local-0.95/SCons/Scanner/Fortran.py b/scons/scons-local-0.95/SCons/Scanner/Fortran.py new file mode 100644 index 0000000..d803c91 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Scanner/Fortran.py @@ -0,0 +1,49 @@ +"""SCons.Scanner.Fortran + +This module implements the dependency scanner for Fortran code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Scanner/Fortran.py 0.95.D001 2004/03/08 07:28:28 knight" + + +import re + +import SCons.Node +import SCons.Node.FS +import SCons.Scanner +import SCons.Util +import SCons.Warnings + +def FortranScan(fs = SCons.Node.FS.default_fs): + """Return a prototype Scanner instance for scanning source files + for Fortran INCLUDE statements""" + scanner = SCons.Scanner.Classic("FortranScan", + [".f", ".F", ".for", ".FOR"], + "F77PATH", + "(?i)INCLUDE[ \t]+'([\\w./\\\\]+)'", + fs = fs) + return scanner diff --git a/scons/scons-local-0.95/SCons/Scanner/IDL.py b/scons/scons-local-0.95/SCons/Scanner/IDL.py new file mode 100644 index 0000000..01c5bf6 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Scanner/IDL.py @@ -0,0 +1,43 @@ +"""SCons.Scanner.IDL + +This module implements the depenency scanner for IDL (Interface +Definition Language) files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Scanner/IDL.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Node.FS +import SCons.Scanner + +def IDLScan(fs = SCons.Node.FS.default_fs): + """Return a prototype Scanner instance for scanning IDL source files""" + cs = SCons.Scanner.ClassicCPP("IDLScan", + [".idl", ".IDL"], + "CPPPATH", + '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")', + fs = fs) + return cs diff --git a/scons/scons-local-0.95/SCons/Scanner/Prog.py b/scons/scons-local-0.95/SCons/Scanner/Prog.py new file mode 100644 index 0000000..b083b17 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Scanner/Prog.py @@ -0,0 +1,85 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Scanner/Prog.py 0.95.D001 2004/03/08 07:28:28 knight" + +import string + +import SCons.Node +import SCons.Node.FS +import SCons.Scanner +import SCons.Util + +def ProgScan(fs = SCons.Node.FS.default_fs): + """Return a prototype Scanner instance for scanning executable + files for static-lib dependencies""" + pf = SCons.Scanner.FindPathDirs('LIBPATH', fs) + ps = SCons.Scanner.Base(scan, "ProgScan", path_function = pf) + return ps + +def scan(node, env, libpath = (), fs = SCons.Node.FS.default_fs): + """ + This scanner scans program files for static-library + dependencies. It will search the LIBPATH environment variable + for libraries specified in the LIBS variable, returning any + files it finds as dependencies. + """ + + try: + libs = env.Dictionary('LIBS') + except KeyError: + # There are no LIBS in this environment, so just return a null list: + return [] + if SCons.Util.is_String(libs): + libs = string.split(libs) + elif not SCons.Util.is_List(libs): + libs = [libs] + + try: + prefix = env.Dictionary('LIBPREFIXES') + if not SCons.Util.is_List(prefix): + prefix = [ prefix ] + except KeyError: + prefix = [ '' ] + + try: + suffix = env.Dictionary('LIBSUFFIXES') + if not SCons.Util.is_List(suffix): + suffix = [ suffix ] + except KeyError: + suffix = [ '' ] + + find_file = SCons.Node.FS.find_file + adjustixes = SCons.Util.adjustixes + result = [] + for suf in map(env.subst, suffix): + for pref in map(env.subst, prefix): + for lib in libs: + if SCons.Util.is_String(lib): + lib = adjustixes(lib, pref, suf) + lib = find_file(lib, libpath, fs.File) + if lib: + result.append(lib) + else: + result.append(lib) + return result diff --git a/scons/scons-local-0.95/SCons/Scanner/__init__.py b/scons/scons-local-0.95/SCons/Scanner/__init__.py new file mode 100644 index 0000000..ea8b219 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Scanner/__init__.py @@ -0,0 +1,298 @@ +"""SCons.Scanner + +The Scanner package for the SCons software construction utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Scanner/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + +import re + +import SCons.Node.FS +import SCons.Sig +import SCons.Util + + +class _Null: + pass + +# This is used instead of None as a default argument value so None can be +# used as an actual argument value. +_null = _Null + +class FindPathDirs: + """A class to bind a specific *PATH variable name and the fs object + to a function that will return all of the *path directories.""" + def __init__(self, variable, fs): + self.variable = variable + self.fs = fs + def __call__(self, env, dir, argument=None): + try: + path = env[self.variable] + except KeyError: + return () + + return tuple(self.fs.Rsearchall(env.subst_path(path), + must_exist = 0, + clazz = SCons.Node.FS.Dir, + cwd = dir)) + +class Base: + """ + The base class for dependency scanners. This implements + straightforward, single-pass scanning of a single file. + """ + + def __init__(self, + function, + name = "NONE", + argument = _null, + skeys = [], + path_function = None, + node_class = SCons.Node.FS.Entry, + node_factory = SCons.Node.FS.default_fs.File, + scan_check = None, + recursive = None): + """ + Construct a new scanner object given a scanner function. + + 'function' - a scanner function taking two or three + arguments and returning a list of strings. + + 'name' - a name for identifying this scanner object. + + 'argument' - an optional argument that, if specified, will be + passed to both the scanner function and the path_function. + + 'skeys' - an optional list argument that can be used to determine + which scanner should be used for a given Node. In the case of File + nodes, for example, the 'skeys' would be file suffixes. + + 'path_function' - a function that takes one to three arguments + (a construction environment, optional directory, and optional + argument for this instance) and returns a tuple of the + directories that can be searched for implicit dependency files. + + 'node_class' - the class of Nodes which this scan will return. + If node_class is None, then this scanner will not enforce any + Node conversion and will return the raw results from the + underlying scanner function. + + 'node_factory' - the factory function to be called to translate + the raw results returned by the scanner function into the + expected node_class objects. + + 'scan_check' - a function to be called to first check whether + this node really needs to be scanned. + + 'recursive' - specifies that this scanner should be invoked + recursively on the implicit dependencies it returns (the + canonical example being #include lines in C source files). + + The scanner function's first argument will be the name of a file + that should be scanned for dependencies, the second argument will + be an Environment object, the third argument will be the value + passed into 'argument', and the returned list should contain the + Nodes for all the direct dependencies of the file. + + Examples: + + s = Scanner(my_scanner_function) + + s = Scanner(function = my_scanner_function) + + s = Scanner(function = my_scanner_function, argument = 'foo') + + """ + + # Note: this class could easily work with scanner functions that take + # something other than a filename as an argument (e.g. a database + # node) and a dependencies list that aren't file names. All that + # would need to be changed is the documentation. + + self.function = function + self.path_function = path_function + self.name = name + self.argument = argument + self.skeys = skeys + self.node_class = node_class + self.node_factory = node_factory + self.scan_check = scan_check + self.recursive = recursive + + def path(self, env, dir = None): + if not self.path_function: + return () + if not self.argument is _null: + return self.path_function(env, dir, self.argument) + else: + return self.path_function(env, dir) + + def __call__(self, node, env, path = ()): + """ + This method scans a single object. 'node' is the node + that will be passed to the scanner function, and 'env' is the + environment that will be passed to the scanner function. A list of + direct dependency nodes for the specified node will be returned. + """ + if self.scan_check and not self.scan_check(node): + return [] + + if not self.argument is _null: + list = self.function(node, env, path, self.argument) + else: + list = self.function(node, env, path) + kw = {} + if hasattr(node, 'dir'): + kw['directory'] = node.dir + nodes = [] + for l in list: + if self.node_class and not isinstance(l, self.node_class): + l = apply(self.node_factory, (l,), kw) + nodes.append(l) + return nodes + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + + def __hash__(self): + return hash(repr(self)) + + def add_skey(self, skey): + """Add a skey to the list of skeys""" + self.skeys.append(skey) + +class Current(Base): + """ + A class for scanning files that are source files (have no builder) + or are derived files and are current (which implies that they exist, + either locally or in a repository). + """ + + def __init__(self, *args, **kw): + def current_check(node): + c = not node.has_builder() or node.current(SCons.Sig.default_calc) + return c + kw['scan_check'] = current_check + apply(Base.__init__, (self,) + args, kw) + +class Classic(Current): + """ + A Scanner subclass to contain the common logic for classic CPP-style + include scanning, but which can be customized to use different + regular expressions to find the includes. + + Note that in order for this to work "out of the box" (without + overriding the find_include() method), the regular expression passed + to the constructor must return the name of the include file in group + 0. + """ + + def __init__(self, name, suffixes, path_variable, regex, + fs=SCons.Node.FS.default_fs, *args, **kw): + + self.cre = re.compile(regex, re.M) + self.fs = fs + + def _scan(node, env, path, self=self, fs=fs): + return self.scan(node, env, path) + + kw['function'] = _scan + kw['path_function'] = FindPathDirs(path_variable, fs) + kw['recursive'] = 1 + kw['skeys'] = suffixes + + apply(Current.__init__, (self,) + args, kw) + + def find_include(self, include, source_dir, path): + n = SCons.Node.FS.find_file(include, (source_dir,) + path, self.fs.File) + return n, include + + def scan(self, node, env, path=()): + node = node.rfile() + + if not node.exists(): + return [] + + # cache the includes list in node so we only scan it once: + if node.includes != None: + includes = node.includes + else: + includes = self.cre.findall(node.get_contents()) + node.includes = includes + + nodes = [] + source_dir = node.get_dir() + for include in includes: + n, i = self.find_include(include, source_dir, path) + + if not n is None: + nodes.append(n) + else: + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) + + # Schwartzian transform from the Python FAQ Wizard + def st(List, Metric): + def pairing(element, M = Metric): + return (M(element), element) + def stripit(pair): + return pair[1] + paired = map(pairing, List) + paired.sort() + return map(stripit, paired) + + def normalize(node): + # We don't want the order of includes to be + # modified by case changes on case insensitive OSes, so + # normalize the case of the filename here: + # (see test/win32pathmadness.py for a test of this) + return SCons.Node.FS._my_normcase(str(node)) + + transformed = st(nodes, normalize) + # print "Classic: " + str(node) + " => " + str(map(lambda x: str(x),list(transformed))) + return transformed + +class ClassicCPP(Classic): + """ + A Classic Scanner subclass which takes into account the type of + bracketing used to include the file, and uses classic CPP rules + for searching for the files based on the bracketing. + + Note that in order for this to work, the regular expression passed + to the constructor must return the leading bracket in group 0, and + the contained filename in group 1. + """ + def find_include(self, include, source_dir, path): + if include[0] == '"': + n = SCons.Node.FS.find_file(include[1], + (source_dir,) + path, + self.fs.File) + else: + n = SCons.Node.FS.find_file(include[1], + path + (source_dir,), + self.fs.File) + return n, include[1] diff --git a/scons/scons-local-0.95/SCons/Script/SConscript.py b/scons/scons-local-0.95/SCons/Script/SConscript.py new file mode 100644 index 0000000..c725af3 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Script/SConscript.py @@ -0,0 +1,716 @@ +"""SCons.Script.SConscript + +This module defines the Python API provided to SConscript and SConstruct +files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Script/SConscript.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Environment +import SCons.Errors +import SCons.Node +import SCons.Node.Alias +import SCons.Node.FS +import SCons.Options +import SCons.Platform +import SCons.SConf +import SCons.Script +import SCons.Tool +import SCons.Util + +import os +import os.path +import re +import string +import sys +import traceback +import types +import UserList + +launch_dir = os.path.abspath(os.curdir) + +def do_nothing(text): pass +HelpFunction = do_nothing + +Arguments = {} +CommandLineTargets = [] +DefaultCalled = None +DefaultTargets = [] +GlobalDict = {} + +class TargetList(UserList.UserList): + def _do_nothing(self, *args, **kw): + pass + def _add_Default(self, list): + self.extend(list) + def _clear(self): + del self[:] +BuildTargets = TargetList() + +# global exports set by Export(): +global_exports = {} + +# chdir flag +sconscript_chdir = 1 + +# will be set to 1, if we are reading a SConscript +sconscript_reading = 0 + +def _scons_add_args(alist): + for arg in alist: + a, b = string.split(arg, '=', 1) + Arguments[a] = b + +def _scons_add_targets(tlist): + if tlist: + CommandLineTargets.extend(tlist) + BuildTargets.extend(tlist) + BuildTargets._add_Default = BuildTargets._do_nothing + BuildTargets._clear = BuildTargets._do_nothing + +def get_calling_namespaces(): + """Return the locals and globals for the function that called + into this module in the current callstack.""" + try: 1/0 + except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame + + while frame.f_globals.get("__name__") == __name__: frame = frame.f_back + + return frame.f_locals, frame.f_globals + + +def compute_exports(exports): + """Compute a dictionary of exports given one of the parameters + to the Export() function or the exports argument to SConscript().""" + + loc, glob = get_calling_namespaces() + + retval = {} + try: + for export in exports: + if SCons.Util.is_Dict(export): + retval.update(export) + else: + try: + retval[export] = loc[export] + except KeyError: + retval[export] = glob[export] + except KeyError, x: + raise SCons.Errors.UserError, "Export of non-existent variable '%s'"%x + + return retval + + +class Frame: + """A frame on the SConstruct/SConscript call stack""" + def __init__(self, exports, sconscript): + self.globals = BuildDefaultGlobals() + self.retval = None + self.prev_dir = SCons.Node.FS.default_fs.getcwd() + self.exports = compute_exports(exports) # exports from the calling SConscript + # make sure the sconscript attr is a Node. + if isinstance(sconscript, SCons.Node.Node): + self.sconscript = sconscript + else: + self.sconscript = SCons.Node.FS.default_fs.File(str(sconscript)) + +# the SConstruct/SConscript call stack: +stack = [] + +# For documentation on the methods in this file, see the scons man-page + +def Return(*vars): + retval = [] + try: + for var in vars: + for v in string.split(var): + retval.append(stack[-1].globals[v]) + except KeyError, x: + raise SCons.Errors.UserError, "Return of non-existent variable '%s'"%x + + if len(retval) == 1: + stack[-1].retval = retval[0] + else: + stack[-1].retval = tuple(retval) + +def _SConscript(fs, *files, **kw): + top = fs.Top + sd = fs.SConstruct_dir.rdir() + exports = kw.get('exports', []) + + # evaluate each SConscript file + results = [] + for fn in files: + stack.append(Frame(exports,fn)) + old_sys_path = sys.path + try: + global sconscript_reading + sconscript_reading = 1 + if fn == "-": + exec sys.stdin in stack[-1].globals + else: + if isinstance(fn, SCons.Node.Node): + f = fn + else: + f = fs.File(str(fn)) + _file_ = None + + # Change directory to the top of the source + # tree to make sure the os's cwd and the cwd of + # fs match so we can open the SConscript. + fs.chdir(top, change_os_dir=1) + if f.rexists(): + _file_ = open(f.rstr(), "r") + elif f.has_src_builder(): + # The SConscript file apparently exists in a source + # code management system. Build it, but then clear + # the builder so that it doesn't get built *again* + # during the actual build phase. + f.build() + f.builder_set(None) + s = str(f) + if os.path.exists(s): + _file_ = open(s, "r") + if _file_: + # Chdir to the SConscript directory. Use a path + # name relative to the SConstruct file so that if + # we're using the -f option, we're essentially + # creating a parallel SConscript directory structure + # in our local directory tree. + # + # XXX This is broken for multiple-repository cases + # where the SConstruct and SConscript files might be + # in different Repositories. For now, cross that + # bridge when someone comes to it. + ldir = fs.Dir(f.dir.get_path(sd)) + try: + fs.chdir(ldir, change_os_dir=sconscript_chdir) + except OSError: + # There was no local directory, so we should be + # able to chdir to the Repository directory. + # Note that we do this directly, not through + # fs.chdir(), because we still need to + # interpret the stuff within the SConscript file + # relative to where we are logically. + fs.chdir(ldir, change_os_dir=0) + os.chdir(f.rfile().dir.get_abspath()) + + # Append the SConscript directory to the beginning + # of sys.path so Python modules in the SConscript + # directory can be easily imported. + sys.path = [ f.dir.get_abspath() ] + sys.path + + # This is the magic line that actually reads up and + # executes the stuff in the SConscript file. We + # look for the "exec _file_ " from the beginning + # of this line to find the right stack frame (the + # next one) describing the SConscript file and line + # number that creates a node. + exec _file_ in stack[-1].globals + else: + SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, + "Ignoring missing SConscript '%s'" % f.path) + + finally: + sconscript_reading = 0 + sys.path = old_sys_path + frame = stack.pop() + try: + fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) + except OSError: + # There was no local directory, so chdir to the + # Repository directory. Like above, we do this + # directly. + fs.chdir(frame.prev_dir, change_os_dir=0) + os.chdir(frame.prev_dir.rdir().get_abspath()) + + results.append(frame.retval) + + # if we only have one script, don't return a tuple + if len(results) == 1: + return results[0] + else: + return tuple(results) + +def is_our_exec_statement(line): + return not line is None and line[:12] == "exec _file_ " + +def SConscript_exception(file=sys.stderr): + """Print an exception stack trace just for the SConscript file(s). + This will show users who have Python errors where the problem is, + without cluttering the output with all of the internal calls leading + up to where we exec the SConscript.""" + stack = traceback.extract_tb(sys.exc_traceback) + last_text = "" + found = 0 + i = 0 + for frame in stack: + if is_our_exec_statement(last_text): + found = 1 + break + i = i + 1 + last_text = frame[3] + if not found: + # We did not find our exec statement, so this was actually a bug + # in SCons itself. Show the whole stack. + i = 0 + type = str(sys.exc_type) + if type[:11] == "exceptions.": + type = type[11:] + file.write('%s: %s:\n' % (type, sys.exc_value)) + for fname, line, func, text in stack[i:]: + file.write(' File "%s", line %d:\n' % (fname, line)) + file.write(' %s\n' % text) + +def annotate(node): + """Annotate a node with the stack frame describing the + SConscript file and line number that created it.""" + stack = traceback.extract_stack() + last_text = "" + for frame in stack: + # If the script text of the previous frame begins with the + # magic "exec _file_ " string, then this frame describes the + # SConscript file and line number that caused this node to be + # created. Record the tuple and carry on. + if is_our_exec_statement(last_text): + node.creator = frame + return + last_text = frame[3] + +# The following line would cause each Node to be annotated using the +# above function. Unfortunately, this is a *huge* performance hit, so +# leave this disabled until we find a more efficient mechanism. +#SCons.Node.Annotate = annotate + +class SConsEnvironment(SCons.Environment.Base): + """An Environment subclass that contains all of the methods that + are particular to the wrapper SCons interface and which aren't + (or shouldn't be) part of the build engine itself. + + Note that not all of the methods of this class have corresponding + global functions, there are some private methods. + """ + + # + # Private methods of an SConsEnvironment. + # + def _exceeds_version(self, major, minor, v_major, v_minor): + """Return 1 if 'major' and 'minor' are greater than the version + in 'v_major' and 'v_minor', and 0 otherwise.""" + return (major > v_major or (major == v_major and minor > v_minor)) + + def _get_major_minor(self, version_string): + """Split a version string into major and minor parts. This + is complicated by the fact that a version string can be something + like 3.2b1.""" + version = string.split(string.split(version_string, ' ')[0], '.') + v_major = int(version[0]) + v_minor = int(re.match('\d+', version[1]).group()) + return v_major, v_minor + + def _get_SConscript_filenames(self, ls, kw): + """ + Convert the parameters passed to # SConscript() calls into a list + of files and export variables. If the parameters are invalid, + throws SCons.Errors.UserError. Returns a tuple (l, e) where l + is a list of SConscript filenames and e is a list of exports. + """ + exports = [] + + if len(ls) == 0: + try: + dirs = kw["dirs"] + except KeyError: + raise SCons.Errors.UserError, \ + "Invalid SConscript usage - no parameters" + + if not SCons.Util.is_List(dirs): + dirs = [ dirs ] + dirs = map(str, dirs) + + name = kw.get('name', 'SConscript') + + files = map(lambda n, name = name: os.path.join(n, name), dirs) + + elif len(ls) == 1: + + files = ls[0] + + elif len(ls) == 2: + + files = ls[0] + exports = self.Split(ls[1]) + + else: + + raise SCons.Errors.UserError, \ + "Invalid SConscript() usage - too many arguments" + + if not SCons.Util.is_List(files): + files = [ files ] + + if kw.get('exports'): + exports.extend(self.Split(kw['exports'])) + + build_dir = kw.get('build_dir') + if build_dir: + if len(files) != 1: + raise SCons.Errors.UserError, \ + "Invalid SConscript() usage - can only specify one SConscript with a build_dir" + duplicate = kw.get('duplicate', 1) + src_dir = kw.get('src_dir') + if not src_dir: + src_dir, fname = os.path.split(str(files[0])) + else: + if not isinstance(src_dir, SCons.Node.Node): + src_dir = self.fs.Dir(src_dir) + fn = files[0] + if not isinstance(fn, SCons.Node.Node): + fn = self.fs.File(fn) + if fn.is_under(src_dir): + # Get path relative to the source directory. + fname = fn.get_path(src_dir) + else: + # Fast way to only get the terminal path component of a Node. + fname = fn.get_path(fn.dir) + self.fs.BuildDir(build_dir, src_dir, duplicate) + files = [os.path.join(str(build_dir), fname)] + + return (files, exports) + + # + # Public methods of an SConsEnvironment. These get + # entry points in the global name space so they can be called + # as global functions. + # + + def Default(self, *targets): + global DefaultCalled + global DefaultTargets + DefaultCalled = 1 + for t in targets: + if t is None: + # Delete the elements from the list in-place, don't + # reassign an empty list to DefaultTargets, so that the + # DEFAULT_TARGETS variable will still point to the + # same object we point to. + del DefaultTargets[:] + BuildTargets._clear() + elif isinstance(t, SCons.Node.Node): + DefaultTargets.append(t) + BuildTargets._add_Default([t]) + else: + nodes = self.arg2nodes(t, self.fs.Entry) + DefaultTargets.extend(nodes) + BuildTargets._add_Default(nodes) + + def EnsureSConsVersion(self, major, minor): + """Exit abnormally if the SCons version is not late enough.""" + v_major, v_minor = self._get_major_minor(SCons.__version__) + if self._exceeds_version(major, minor, v_major, v_minor): + print "SCons %d.%d or greater required, but you have SCons %s" %(major,minor,SCons.__version__) + sys.exit(2) + + def EnsurePythonVersion(self, major, minor): + """Exit abnormally if the Python version is not late enough.""" + try: + v_major, v_minor, v_micro, release, serial = sys.version_info + except AttributeError: + v_major, v_minor = self._get_major_minor(sys.version) + if self._exceeds_version(major, minor, v_major, v_minor): + v = string.split(sys.version, " ", 1)[0] + print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v) + sys.exit(2) + + def Exit(self, value=0): + sys.exit(value) + + def Export(self, *vars): + for var in vars: + global_exports.update(compute_exports(self.Split(var))) + + def GetLaunchDir(self): + global launch_dir + return launch_dir + + def GetOption(self, name): + name = self.subst(name) + return SCons.Script.ssoptions.get(name) + + def Help(self, text): + text = self.subst(text, raw=1) + HelpFunction(text) + + def Import(self, *vars): + try: + for var in vars: + var = self.Split(var) + for v in var: + if v == '*': + stack[-1].globals.update(global_exports) + stack[-1].globals.update(stack[-1].exports) + else: + if stack[-1].exports.has_key(v): + stack[-1].globals[v] = stack[-1].exports[v] + else: + stack[-1].globals[v] = global_exports[v] + except KeyError,x: + raise SCons.Errors.UserError, "Import of non-existent variable '%s'"%x + + def SConscript(self, *ls, **kw): + ls = map(lambda l, self=self: self.subst(l), ls) + subst_kw = {} + for key, val in kw.items(): + if SCons.Util.is_String(val): + val = self.subst(val) + subst_kw[key] = val + + files, exports = self._get_SConscript_filenames(ls, subst_kw) + + return apply(_SConscript, [self.fs,] + files, {'exports' : exports}) + + def SConscriptChdir(self, flag): + global sconscript_chdir + sconscript_chdir = flag + + def SetOption(self, name, value): + name = self.subst(name) + SCons.Script.ssoptions.set(name, value) + +# +# +# +SCons.Environment.Environment = SConsEnvironment + +def Options(files=None, args=Arguments): + return SCons.Options.Options(files, args) + +def SetBuildSignatureType(type): + SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, + "The SetBuildSignatureType() function has been deprecated;\n" +\ + "\tuse the TargetSignatures() function instead.") + SCons.Defaults.DefaultEnvironment().TargetSignatures(type) + +def SetContentSignatureType(type): + SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, + "The SetContentSignatureType() function has been deprecated;\n" +\ + "\tuse the SourceSignatures() function instead.") + SCons.Defaults.DefaultEnvironment().SourceSignatures(type) + +def GetJobs(): + SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, + "The GetJobs() function has been deprecated;\n" +\ + "\tuse GetOption('num_jobs') instead.") + + return GetOption('num_jobs') + +def SetJobs(num): + SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, + "The SetJobs() function has been deprecated;\n" +\ + "\tuse SetOption('num_jobs', num) instead.") + SetOption('num_jobs', num) + +def ParseConfig(env, command, function=None): + SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning, + "The ParseConfig() function has been deprecated;\n" +\ + "\tuse the env.ParseConfig() method instead.") + return env.ParseConfig(command, function) + +# +_DefaultEnvironmentProxy = None + +def get_DefaultEnvironmentProxy(): + global _DefaultEnvironmentProxy + if not _DefaultEnvironmentProxy: + class EnvironmentProxy(SCons.Environment.Environment): + """A proxy subclass for an environment instance that overrides + the subst() and subst_list() methods so they don't actually + actually perform construction variable substitution. This is + specifically intended to be the shim layer in between global + function calls (which don't want want construction variable + substitution) and the DefaultEnvironment() (which would + substitute variables if left to its own devices).""" + def __init__(self, subject): + self.__dict__['__subject'] = subject + def __getattr__(self, name): + return getattr(self.__dict__['__subject'], name) + def __setattr__(self, name, value): + return setattr(self.__dict__['__subject'], name, value) + def subst(self, string, raw=0, target=None, source=None): + return string + def subst_kw(self, kw, raw=0, target=None, source=None): + return kw + def subst_list(self, string, raw=0, target=None, source=None): + return string + default_env = SCons.Defaults.DefaultEnvironment() + _DefaultEnvironmentProxy = EnvironmentProxy(default_env) + return _DefaultEnvironmentProxy + +class DefaultEnvironmentCall: + """A class that implements "global function" calls of + Environment methods by fetching the specified method from the + DefaultEnvironment's class. Note that this uses an intermediate + proxy class instead of calling the DefaultEnvironment method + directly so that the proxy can override the subst() method and + thereby prevent expansion of construction variables (since from + the user's point of view this was called as a global function, + with no associated construction environment).""" + def __init__(self, method_name): + self.method_name = method_name + def __call__(self, *args, **kw): + proxy = get_DefaultEnvironmentProxy() + method = getattr(proxy, self.method_name) + return apply(method, args, kw) + +# The list of global functions to add to the SConscript name space +# that end up calling corresponding methods or Builders in the +# DefaultEnvironment(). +GlobalDefaultEnvironmentFunctions = [ + # Methods from the SConsEnvironment class, above. + 'Default', + 'EnsurePythonVersion', + 'EnsureSConsVersion', + 'Exit', + 'Export', + 'GetLaunchDir', + 'GetOption', + 'Help', + 'Import', + 'SConscript', + 'SConscriptChdir', + 'SetOption', + + # Methods from the Environment.Base class. + 'AddPostAction', + 'AddPreAction', + 'Alias', + 'AlwaysBuild', + 'BuildDir', + 'CacheDir', + 'Clean', + 'Command', + 'Depends', + 'Dir', + 'File', + 'FindFile', + 'GetBuildPath', + 'Ignore', + 'Install', + 'InstallAs', + 'Literal', + 'Local', + 'Precious', + 'Repository', + 'SConsignFile', + 'SideEffect', + 'SourceCode', + 'SourceSignatures', + 'Split', + 'TargetSignatures', + 'Value', +] + +GlobalDefaultBuilders = [ + # Supported builders. + 'CFile', + 'CXXFile', + 'DVI', + 'Jar', + 'Java', + 'JavaH', + 'Library', + 'M4', + 'MSVSProject', + 'Object', + 'PCH', + 'PDF', + 'PostScript', + 'Program', + 'RES', + 'RMIC', + 'SharedLibrary', + 'SharedObject', + 'StaticLibrary', + 'StaticObject', + 'Tar', + 'TypeLibrary', + 'Zip', +] + +for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: + GlobalDict[name] = DefaultEnvironmentCall(name) + +def BuildDefaultGlobals(): + """ + Create a dictionary containing all the default globals for + SConstruct and SConscript files. + """ + + globals = { + # Global functions that don't get executed through the + # default Environment. + 'Action' : SCons.Action.Action, + 'BoolOption' : SCons.Options.BoolOption, + 'Builder' : SCons.Builder.Builder, + 'Configure' : SCons.SConf.SConf, + 'EnumOption' : SCons.Options.EnumOption, + 'Environment' : SCons.Environment.Environment, + 'ListOption' : SCons.Options.ListOption, + 'Options' : Options, + 'PackageOption' : SCons.Options.PackageOption, + 'PathOption' : SCons.Options.PathOption, + 'Platform' : SCons.Platform.Platform, + 'Return' : Return, + 'Scanner' : SCons.Scanner.Base, + 'Tool' : SCons.Tool.Tool, + 'WhereIs' : SCons.Util.WhereIs, + + # Other variables we provide. + 'ARGUMENTS' : Arguments, + 'BUILD_TARGETS' : BuildTargets, + 'COMMAND_LINE_TARGETS' : CommandLineTargets, + 'DEFAULT_TARGETS' : DefaultTargets, + } + + # Functions we might still convert to Environment methods. + globals['CScan'] = SCons.Defaults.CScan + globals['DefaultEnvironment'] = SCons.Defaults.DefaultEnvironment + + # Deprecated functions, leave these here for now. + globals['GetJobs'] = GetJobs + globals['ParseConfig'] = ParseConfig + globals['SetBuildSignatureType'] = SetBuildSignatureType + globals['SetContentSignatureType'] = SetContentSignatureType + globals['SetJobs'] = SetJobs + + globals.update(GlobalDict) + + return globals diff --git a/scons/scons-local-0.95/SCons/Script/__init__.py b/scons/scons-local-0.95/SCons/Script/__init__.py new file mode 100644 index 0000000..81d990e --- /dev/null +++ b/scons/scons-local-0.95/SCons/Script/__init__.py @@ -0,0 +1,1104 @@ +"""SCons.Script + +This file implements the main() function used by the scons script. + +Architecturally, this *is* the scons script, and will likely only be +called from the external "scons" wrapper. Consequently, anything here +should not be, or be considered, part of the build engine. If it's +something that we expect other software to want to use, it should go in +some other module. If it's specific to the "scons" script invocation, +it goes here. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Script/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + +import time +start_time = time.time() + +import os +import os.path +import random +import string +import sys +import traceback + +# Strip the script directory from sys.path() so on case-insensitive +# (WIN32) systems Python doesn't think that the "scons" script is the +# "SCons" package. Replace it with our own version directory so, if +# if they're there, we pick up the right version of the build engine +# modules. +#sys.path = [os.path.join(sys.prefix, +# 'lib', +# 'scons-%d' % SCons.__version__)] + sys.path[1:] + +import SCons.Debug +import SCons.Defaults +import SCons.Environment +import SCons.Errors +import SCons.Job +import SCons.Node +import SCons.Node.FS +from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError +import SCons.Script.SConscript +import SCons.Sig +import SCons.Taskmaster +import SCons.Util +import SCons.Warnings + +# +display = SCons.Util.display +progress_display = SCons.Util.DisplayEngine() + +# Task control. +# +class BuildTask(SCons.Taskmaster.Task): + """An SCons build task.""" + def display(self, message): + display('scons: ' + message) + + def execute(self): + target = self.targets[0] + if target.get_state() == SCons.Node.up_to_date: + if self.top and target.has_builder(): + display("scons: `%s' is up to date." % str(target)) + elif target.has_builder() and not hasattr(target.builder, 'status'): + if print_time: + start_time = time.time() + SCons.Taskmaster.Task.execute(self) + if print_time: + finish_time = time.time() + global command_time + command_time = command_time+finish_time-start_time + print "Command execution time: %f seconds"%(finish_time-start_time) + + def do_failed(self, status=2): + global exit_status + if ignore_errors: + SCons.Taskmaster.Task.executed(self) + elif keep_going_on_error: + SCons.Taskmaster.Task.fail_continue(self) + exit_status = status + else: + SCons.Taskmaster.Task.fail_stop(self) + exit_status = status + + def executed(self): + t = self.targets[0] + if self.top and not t.has_builder() and not t.side_effect: + if not t.exists(): + sys.stderr.write("scons: *** Do not know how to make target `%s'." % t) + if not keep_going_on_error: + sys.stderr.write(" Stop.") + sys.stderr.write("\n") + self.do_failed() + else: + print "scons: Nothing to be done for `%s'." % t + SCons.Taskmaster.Task.executed(self) + else: + SCons.Taskmaster.Task.executed(self) + + # print the tree here instead of in execute() because + # this method is serialized, but execute isn't: + if print_tree and self.top: + print + print SCons.Util.render_tree(self.targets[0], get_all_children) + if print_dtree and self.top: + print + print SCons.Util.render_tree(self.targets[0], get_derived_children) + if print_includes and self.top: + t = self.targets[0] + tree = t.render_include_tree() + if tree: + print + print tree + + def failed(self): + # Handle the failure of a build task. The primary purpose here + # is to display the various types of Errors and Exceptions + # appropriately. + status = 2 + e = sys.exc_value + t = sys.exc_type + tb = None + if t is SCons.Errors.TaskmasterException: + # The Taskmaster received an Error or Exception while trying + # to process or build the Nodes and dependencies, which it + # wrapped up for us in the object recorded as the value of + # the Exception, so process the wrapped info instead of the + # TaskmasterException itself. + t = e.type + tb = e.traceback + e = e.value + + if t == SCons.Errors.BuildError: + sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr)) + if e.errstr == 'Exception': + traceback.print_exception(e.args[0], e.args[1], e.args[2]) + elif t == SCons.Errors.ExplicitExit: + status = e.status + sys.stderr.write("scons: *** [%s] Explicit exit, status %s\n" % (e.node, e.status)) + else: + if e is None: + e = t + s = str(e) + if t == SCons.Errors.StopError and not keep_going_on_error: + s = s + ' Stop.' + sys.stderr.write("scons: *** %s\n" % s) + + if tb: + sys.stderr.write("scons: internal stack trace:\n") + traceback.print_tb(tb, file=sys.stderr) + + self.do_failed(status) + +class CleanTask(SCons.Taskmaster.Task): + """An SCons clean task.""" + def show(self): + if (self.targets[0].has_builder() or self.targets[0].side_effect) \ + and not os.path.isdir(str(self.targets[0])): + display("Removed " + str(self.targets[0])) + if SCons.Environment.CleanTargets.has_key(self.targets[0]): + files = SCons.Environment.CleanTargets[self.targets[0]] + for f in files: + SCons.Util.fs_delete(str(f), 0) + + def remove(self): + if self.targets[0].has_builder() or self.targets[0].side_effect: + for t in self.targets: + try: + removed = t.remove() + except OSError, e: + print "scons: Could not remove '%s':" % str(t), e.strerror + else: + if removed: + display("Removed " + str(t)) + if SCons.Environment.CleanTargets.has_key(self.targets[0]): + files = SCons.Environment.CleanTargets[self.targets[0]] + for f in files: + SCons.Util.fs_delete(str(f)) + + execute = remove + + def prepare(self): + pass + +class QuestionTask(SCons.Taskmaster.Task): + """An SCons task for the -q (question) option.""" + def prepare(self): + pass + + def execute(self): + if self.targets[0].get_state() != SCons.Node.up_to_date: + global exit_status + exit_status = 1 + self.tm.stop() + + def executed(self): + pass + +# Global variables + +keep_going_on_error = 0 +print_count = 0 +print_dtree = 0 +print_includes = 0 +print_objects = 0 +print_time = 0 +print_tree = 0 +memory_stats = None +ignore_errors = 0 +sconscript_time = 0 +command_time = 0 +exit_status = 0 # exit status, assume success by default +profiling = 0 +repositories = [] +num_jobs = 1 # this is modifed by SConscript.SetJobs() + +# Exceptions for this module +class PrintHelp(Exception): + pass + +# utility functions + +def get_all_children(node): return node.all_children(None) + +def get_derived_children(node): + children = node.all_children(None) + return filter(lambda x: x.has_builder(), children) + +def _scons_syntax_error(e): + """Handle syntax errors. Print out a message and show where the error + occurred. + """ + etype, value, tb = sys.exc_info() + lines = traceback.format_exception_only(etype, value) + for line in lines: + sys.stderr.write(line+'\n') + sys.exit(2) + +def find_deepest_user_frame(tb): + """ + Find the deepest stack frame that is not part of SCons. + + Input is a "pre-processed" stack trace in the form + returned by traceback.extract_tb() or traceback.extract_stack() + """ + + tb.reverse() + + # find the deepest traceback frame that is not part + # of SCons: + for frame in tb: + filename = frame[0] + if string.find(filename, os.sep+'SCons'+os.sep) == -1: + return frame + return tb[0] + +def _scons_user_error(e): + """Handle user errors. Print out a message and a description of the + error, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + etype, value, tb = sys.exc_info() + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: *** %s\n" % value) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + sys.exit(2) + +def _scons_user_warning(e): + """Handle user warnings. Print out a message and a description of + the warning, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + etype, value, tb = sys.exc_info() + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: warning: %s\n" % e) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_warning(e): + """Slightly different from _scons_user_warning in that we use the + *current call stack* rather than sys.exc_info() to get our stack trace. + This is used by the warnings framework to print warnings.""" + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) + sys.stderr.write("\nscons: warning: %s\n" % e) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_error(): + """Handle all errors but user errors. Print out a message telling + the user what to do in this case and print a normal trace. + """ + print 'internal error' + traceback.print_exc() + sys.exit(2) + +def _varargs(option, parser): + value = None + if parser.rargs: + arg = parser.rargs[0] + if arg[0] != "-": + value = arg + del parser.rargs[0] + return value + +def _setup_warn(arg): + """The --warn option. An argument to this option + should be of the form or no-. + The warning class is munged in order to get an actual class + name from the SCons.Warnings module to enable or disable. + The supplied is split on hyphens, each element + is captialized, then smushed back together. Then the string + "SCons.Warnings." is added to the front and "Warning" is added + to the back to get the fully qualified class name. + + For example, --warn=deprecated will enable the + SCons.Warnings.DeprecatedWarning class. + + --warn=no-dependency will disable the + SCons.Warnings.DependencyWarning class. + + As a special case, --warn=all and --warn=no-all + will enable or disable (respectively) the base + class of all warnings, which is SCons.Warning.Warning.""" + + elems = string.split(string.lower(arg), '-') + enable = 1 + if elems[0] == 'no': + enable = 0 + del elems[0] + + if len(elems) == 1 and elems[0] == 'all': + class_name = "Warning" + else: + def _capitalize(s): + if s[:5] == "scons": + return "SCons" + s[5:] + else: + return string.capitalize(s) + class_name = string.join(map(_capitalize, elems), '') + "Warning" + try: + clazz = getattr(SCons.Warnings, class_name) + except AttributeError: + sys.stderr.write("No warning type: '%s'\n" % arg) + else: + if enable: + SCons.Warnings.enableWarningClass(clazz) + else: + SCons.Warnings.suppressWarningClass(clazz) + +def _SConstruct_exists(dirname=''): + """This function checks that an SConstruct file exists in a directory. + If so, it returns the path of the file. By default, it checks the + current directory. + """ + global repositories + for file in ['SConstruct', 'Sconstruct', 'sconstruct']: + sfile = os.path.join(dirname, file) + if os.path.isfile(sfile): + return sfile + if not os.path.isabs(sfile): + for rep in repositories: + if os.path.isfile(os.path.join(rep, sfile)): + return sfile + return None + +def _set_globals(options): + global repositories, keep_going_on_error, ignore_errors + global print_count, print_dtree, print_includes + global print_objects, print_time, print_tree + global memory_outf, memory_stats + + if options.repository: + repositories.extend(options.repository) + keep_going_on_error = options.keep_going + try: + if options.debug: + if options.debug == "count": + print_count = 1 + elif options.debug == "dtree": + print_dtree = 1 + elif options.debug == "includes": + print_includes = 1 + elif options.debug == "memory": + memory_stats = [] + memory_outf = sys.stdout + elif options.debug == "objects": + print_objects = 1 + elif options.debug == "time": + print_time = 1 + elif options.debug == "tree": + print_tree = 1 + except AttributeError: + pass + ignore_errors = options.ignore_errors + +def _create_path(plist): + path = '.' + for d in plist: + if os.path.isabs(d): + path = d + else: + path = path + '/' + d + return path + + +class OptParser(OptionParser): + def __init__(self): + import __main__ + import SCons + parts = ["SCons by Steven Knight et al.:\n"] + try: + parts.append("\tscript: v%s.%s, %s, by %s on %s\n" % (__main__.__version__, + __main__.__build__, + __main__.__date__, + __main__.__developer__, + __main__.__buildsys__)) + except KeyboardInterrupt: + raise + except: + # On win32 there is no scons.py, so there is no __main__.__version__, + # hence there is no script version. + pass + parts.append("\tengine: v%s.%s, %s, by %s on %s\n" % (SCons.__version__, + SCons.__build__, + SCons.__date__, + SCons.__developer__, + SCons.__buildsys__)) + parts.append("Copyright (c) 2001, 2002, 2003, 2004 Steven Knight") + OptionParser.__init__(self, version=string.join(parts, ''), + usage="usage: scons [OPTION] [TARGET] ...") + + # options ignored for compatibility + def opt_ignore(option, opt, value, parser): + sys.stderr.write("Warning: ignoring %s option\n" % opt) + self.add_option("-b", "-m", "-S", "-t", "--no-keep-going", "--stop", + "--touch", action="callback", callback=opt_ignore, + help="Ignored for compatibility.") + + self.add_option('-c', '--clean', '--remove', action="store_true", + dest="clean", + help="Remove specified targets and dependencies.") + + self.add_option('-C', '--directory', type="string", action = "append", + metavar="DIR", + help="Change to DIR before doing anything.") + + self.add_option('--cache-disable', '--no-cache', + action="store_true", dest='cache_disable', default=0, + help="Do not retrieve built targets from CacheDir.") + + self.add_option('--cache-force', '--cache-populate', + action="store_true", dest='cache_force', default=0, + help="Copy already-built targets into the CacheDir.") + + self.add_option('--cache-show', + action="store_true", dest='cache_show', default=0, + help="Print build actions for files from CacheDir.") + + def opt_not_yet(option, opt, value, parser): + sys.stderr.write("Warning: the %s option is not yet implemented\n" % opt) + sys.exit(0) + self.add_option('-d', action="callback", + callback=opt_not_yet, + help = "Print file dependency information.") + + self.add_option('-D', action="store_const", const=2, dest="climb_up", + help="Search up directory tree for SConstruct, " + "build all Default() targets.") + + def opt_debug(option, opt, value, parser): + if value in ["count", "dtree", "includes", "memory", "objects", "pdb", "time", "tree"]: + setattr(parser.values, 'debug', value) + else: + raise OptionValueError("Warning: %s is not a valid debug type" % value) + self.add_option('--debug', action="callback", type="string", + callback=opt_debug, nargs=1, dest="debug", + metavar="TYPE", + help="Print various types of debugging information: " + "count, dtree, includes, memory, objects, pdb, time, tree.") + + self.add_option('-f', '--file', '--makefile', '--sconstruct', + action="append", nargs=1, + help="Read FILE as the top-level SConstruct file.") + + self.add_option('-h', '--help', action="store_true", default=0, + dest="help_msg", + help="Print defined help message, or this one.") + + self.add_option("-H", "--help-options", + action="help", + help="Print this message and exit.") + + self.add_option('-i', '--ignore-errors', action="store_true", + default=0, dest='ignore_errors', + help="Ignore errors from build actions.") + + self.add_option('-I', '--include-dir', action="append", + dest='include_dir', metavar="DIR", + help="Search DIR for imported Python modules.") + + self.add_option('--implicit-cache', action="store_true", + dest='implicit_cache', + help="Cache implicit dependencies") + + self.add_option('--implicit-deps-changed', action="store_true", + default=0, dest='implicit_deps_changed', + help="Ignore cached implicit dependencies.") + self.add_option('--implicit-deps-unchanged', action="store_true", + default=0, dest='implicit_deps_unchanged', + help="Ignore changes in implicit dependencies.") + + def opt_j(option, opt, value, parser): + value = int(value) + setattr(parser.values, 'num_jobs', value) + self.add_option('-j', '--jobs', action="callback", type="int", + callback=opt_j, metavar="N", + help="Allow N jobs at once.") + + self.add_option('-k', '--keep-going', action="store_true", default=0, + dest='keep_going', + help="Keep going when a target can't be made.") + + self.add_option('--max-drift', type="int", action="store", + dest='max_drift', metavar="N", + help="Set maximum system clock drift to N seconds.") + + self.add_option('-n', '--no-exec', '--just-print', '--dry-run', + '--recon', action="store_true", dest='noexec', + default=0, help="Don't build; just print commands.") + + def opt_profile(option, opt, value, parser): + global profiling + if not profiling: + profiling = 1 + import profile + profile.run('SCons.Script.main()', value) + sys.exit(exit_status) + self.add_option('--profile', nargs=1, action="callback", + callback=opt_profile, type="string", dest="profile", + metavar="FILE", + help="Profile SCons and put results in FILE.") + + self.add_option('-q', '--question', action="store_true", default=0, + help="Don't build; exit status says if up to date.") + + self.add_option('-Q', dest='no_progress', action="store_true", + default=0, + help="Suppress \"Reading/Building\" progress messages.") + + self.add_option('--random', dest="random", action="store_true", + default=0, help="Build dependencies in random order.") + + self.add_option('-s', '--silent', '--quiet', action="store_true", + default=0, help="Don't print commands.") + + self.add_option('-u', '--up', '--search-up', action="store_const", + dest="climb_up", default=0, const=1, + help="Search up directory tree for SConstruct, " + "build targets at or below current directory.") + self.add_option('-U', action="store_const", dest="climb_up", + default=0, const=3, + help="Search up directory tree for SConstruct, " + "build Default() targets from local SConscript.") + + self.add_option("-v", "--version", + action="version", + help="Print the SCons version number and exit.") + + self.add_option('--warn', '--warning', nargs=1, action="store", + metavar="WARNING-SPEC", + help="Enable or disable warnings.") + + self.add_option('-Y', '--repository', nargs=1, action="append", + help="Search REPOSITORY for source and target files.") + + self.add_option('-e', '--environment-overrides', action="callback", + callback=opt_not_yet, + # help="Environment variables override makefiles." + help=SUPPRESS_HELP) + self.add_option('-l', '--load-average', '--max-load', action="callback", + callback=opt_not_yet, type="int", dest="load_average", + # action="store", + # help="Don't start multiple jobs unless load is below " + # "LOAD-AVERAGE." + # type="int", + help=SUPPRESS_HELP) + def opt_duplicate(option, opt, value, parser): + if not value in SCons.Node.FS.Valid_Duplicates: + raise OptionValueError("`%s' is not a valid duplication style." % value) + setattr(parser.values, 'duplicate', value) + # Set the duplicate stye right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + self.add_option('--duplicate', action="callback", type="string", + callback=opt_duplicate, nargs=1, dest="duplicate", + help="Set the preferred duplication methods. Must be one of " + + string.join(SCons.Node.FS.Valid_Duplicates, ", ")) + self.add_option('--list-derived', action="callback", + callback=opt_not_yet, + # help="Don't build; list files that would be built." + help=SUPPRESS_HELP) + self.add_option('--list-actions', action="callback", + callback=opt_not_yet, + # help="Don't build; list files and build actions." + help=SUPPRESS_HELP) + self.add_option('--list-where', action="callback", + callback=opt_not_yet, + # help="Don't build; list files and where defined." + help=SUPPRESS_HELP) + self.add_option('-o', '--old-file', '--assume-old', action="callback", + callback=opt_not_yet, type="string", dest="old_file", + # help = "Consider FILE to be old; don't rebuild it." + help=SUPPRESS_HELP) + self.add_option('--override', action="callback", dest="override", + callback=opt_not_yet, type="string", + # help="Override variables as specified in FILE." + help=SUPPRESS_HELP) + self.add_option('-p', action="callback", + callback=opt_not_yet, + # help="Print internal environments/objects." + help=SUPPRESS_HELP) + self.add_option('-r', '-R', '--no-builtin-rules', + '--no-builtin-variables', action="callback", + callback=opt_not_yet, + # help="Clear default environments and variables." + help=SUPPRESS_HELP) + self.add_option('-w', '--print-directory', action="callback", + callback=opt_not_yet, + # help="Print the current directory." + help=SUPPRESS_HELP) + self.add_option('--no-print-directory', action="callback", + callback=opt_not_yet, + # help="Turn off -w, even if it was turned on implicitly." + help=SUPPRESS_HELP) + self.add_option('--write-filenames', action="callback", + callback=opt_not_yet, type="string", dest="write_filenames", + # help="Write all filenames examined into FILE." + help=SUPPRESS_HELP) + self.add_option('-W', '--what-if', '--new-file', '--assume-new', + dest="new_file", + action="callback", callback=opt_not_yet, type="string", + # help="Consider FILE to be changed." + help=SUPPRESS_HELP) + self.add_option('--warn-undefined-variables', action="callback", + callback=opt_not_yet, + # help="Warn when an undefined variable is referenced." + help=SUPPRESS_HELP) + + def parse_args(self, args=None, values=None): + opt, arglist = OptionParser.parse_args(self, args, values) + if opt.implicit_deps_changed or opt.implicit_deps_unchanged: + opt.implicit_cache = 1 + return opt, arglist + +class SConscriptSettableOptions: + """This class wraps an OptParser instance and provides + uniform access to options that can be either set on the command + line or from a SConscript file. A value specified on the command + line always overrides a value set in a SConscript file. + Not all command line options are SConscript settable, and the ones + that are must be explicitly added to settable dictionary and optionally + validated and coerced in the set() method.""" + + def __init__(self, options): + self.options = options + + # This dictionary stores the defaults for all the SConscript + # settable options, as well as indicating which options + # are SConscript settable. + self.settable = {'num_jobs':1, + 'max_drift':SCons.Sig.default_max_drift, + 'implicit_cache':0, + 'clean':0, + 'duplicate':'hard-soft-copy'} + + def get(self, name): + if not self.settable.has_key(name): + raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name + if hasattr(self.options, name) and getattr(self.options, name) is not None: + return getattr(self.options, name) + else: + return self.settable[name] + + def set(self, name, value): + if not self.settable.has_key(name): + raise SCons.Error.UserError, "This option is not settable from a SConscript file: %s"%name + + if name == 'num_jobs': + try: + value = int(value) + if value < 1: + raise ValueError + except ValueError, x: + raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(value) + elif name == 'max_drift': + try: + value = int(value) + except ValueError, x: + raise SCons.Errors.UserError, "An integer is required: %s"%repr(value) + elif name == 'duplicate': + try: + value = str(value) + except ValueError: + raise SCons.Errors.UserError, "A string is required: %s"%repr(value) + if not value in SCons.Node.FS.Valid_Duplicates: + raise SCons.Errors.UserError, "Not a valid duplication style: %s" % value + # Set the duplicate stye right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + + self.settable[name] = value + + +def _main(args, parser): + targets = [] + fs = SCons.Node.FS.default_fs + + # Enable deprecated warnings by default. + SCons.Warnings._warningOut = _scons_internal_warning + SCons.Warnings.enableWarningClass(SCons.Warnings.CorruptSConsignWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.DuplicateEnvironmentWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.MissingSConscriptWarning) + SCons.Warnings.enableWarningClass(SCons.Warnings.NoParallelSupportWarning) + + global ssoptions + ssoptions = SConscriptSettableOptions(options) + + if options.help_msg: + def raisePrintHelp(text): + raise PrintHelp, text + SCons.Script.SConscript.HelpFunction = raisePrintHelp + + _set_globals(options) + SCons.Node.implicit_cache = options.implicit_cache + SCons.Node.implicit_deps_changed = options.implicit_deps_changed + SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged + if options.warn: + _setup_warn(options.warn) + if options.noexec: + SCons.SConf.dryrun = 1 + SCons.Action.execute_actions = None + CleanTask.execute = CleanTask.show + if options.question: + SCons.SConf.dryrun = 1 + + if options.no_progress or options.silent: + progress_display.set_mode(0) + if options.silent: + display.set_mode(0) + if options.silent: + SCons.Action.print_actions = None + if options.cache_disable: + def disable(self): pass + fs.CacheDir = disable + if options.cache_force: + fs.cache_force = 1 + if options.cache_show: + fs.cache_show = 1 + if options.directory: + cdir = _create_path(options.directory) + try: + os.chdir(cdir) + except OSError: + sys.stderr.write("Could not change directory to %s\n" % cdir) + + xmit_args = [] + for a in args: + if '=' in a: + xmit_args.append(a) + else: + targets.append(a) + SCons.Script.SConscript._scons_add_args(xmit_args) + SCons.Script.SConscript._scons_add_targets(targets) + + target_top = None + if options.climb_up: + target_top = '.' # directory to prepend to targets + script_dir = os.getcwd() # location of script + while script_dir and not _SConstruct_exists(script_dir): + script_dir, last_part = os.path.split(script_dir) + if last_part: + target_top = os.path.join(last_part, target_top) + else: + script_dir = '' + if script_dir: + display("scons: Entering directory `%s'" % script_dir) + os.chdir(script_dir) + else: + raise SCons.Errors.UserError, "No SConstruct file found." + + fs.set_toplevel_dir(os.getcwd()) + + scripts = [] + if options.file: + scripts.extend(options.file) + if not scripts: + sfile = _SConstruct_exists() + if sfile: + scripts.append(sfile) + + if options.help_msg: + if not scripts: + # There's no SConstruct, but they specified -h. + # Give them the options usage now, before we fail + # trying to read a non-existent SConstruct file. + parser.print_help() + sys.exit(0) + SCons.Script.SConscript.print_help = 1 + + if not scripts: + raise SCons.Errors.UserError, "No SConstruct file found." + + if scripts[0] == "-": + d = fs.getcwd() + else: + d = fs.File(scripts[0]).dir + fs.set_SConstruct_dir(d) + + class Unbuffered: + def __init__(self, file): + self.file = file + def write(self, arg): + self.file.write(arg) + self.file.flush() + def __getattr__(self, attr): + return getattr(self.file, attr) + + sys.stdout = Unbuffered(sys.stdout) + + if options.include_dir: + sys.path = options.include_dir + sys.path + + global repositories + for rep in repositories: + fs.Repository(rep) + + if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) + + progress_display("scons: Reading SConscript files ...") + try: + start_time = time.time() + try: + for script in scripts: + SCons.Script.SConscript._SConscript(fs, script) + except SCons.Errors.StopError, e: + # We had problems reading an SConscript file, such as it + # couldn't be copied in to the BuildDir. Since we're just + # reading SConscript files and haven't started building + # things yet, stop regardless of whether they used -i or -k + # or anything else, but don't say "Stop." on the message. + global exit_status + sys.stderr.write("scons: *** %s\n" % e) + exit_status = 2 + sys.exit(exit_status) + global sconscript_time + sconscript_time = time.time() - start_time + except PrintHelp, text: + progress_display("scons: done reading SConscript files.") + print text + print "Use scons -H for help about command-line options." + sys.exit(0) + progress_display("scons: done reading SConscript files.") + + if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) + + fs.chdir(fs.Top) + + if options.help_msg: + # They specified -h, but there was no Help() inside the + # SConscript files. Give them the options usage. + parser.print_help(sys.stdout) + sys.exit(0) + + # Now that we've read the SConscripts we can set the options + # that are SConscript settable: + SCons.Node.implicit_cache = ssoptions.get('implicit_cache') + SCons.Node.FS.set_duplicate(ssoptions.get('duplicate')) + + lookup_top = None + if targets: + # They specified targets on the command line, so if they + # used -u, -U or -D, we have to look up targets relative + # to the top, but we build whatever they specified. + if target_top: + lookup_top = fs.Dir(target_top) + target_top = None + else: + # There are no targets specified on the command line, + # so if they used -u, -U or -D, we may have to restrict + # what actually gets built. + d = None + if target_top: + if options.climb_up == 1: + # -u, local directory and below + target_top = fs.Dir(target_top) + lookup_top = target_top + elif options.climb_up == 2: + # -D, all Default() targets + target_top = None + lookup_top = None + elif options.climb_up == 3: + # -U, local SConscript Default() targets + target_top = fs.Dir(target_top) + def check_dir(x, target_top=target_top): + if hasattr(x, 'cwd') and not x.cwd is None: + cwd = x.cwd.srcnode() + return cwd == target_top + else: + # x doesn't have a cwd, so it's either not a target, + # or not a file, so go ahead and keep it as a default + # target and let the engine sort it out: + return 1 + d = filter(check_dir, SCons.Script.SConscript.DefaultTargets) + SCons.Script.SConscript.DefaultTargets[:] = d + target_top = None + lookup_top = None + + if SCons.Script.SConscript.DefaultCalled: + targets = SCons.Script.SConscript.DefaultTargets + else: + if d is None: + d = [fs.Dir('.')] + targets = d + + + if not targets: + sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") + sys.exit(2) + + def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): + if isinstance(x, SCons.Node.Node): + node = x + else: + node = SCons.Node.Alias.default_ans.lookup(x) + if node is None: + node = fs.Entry(x, directory=ltop, create=1) + if ttop and not node.is_under(ttop): + if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): + node = ttop + else: + node = None + return node + + nodes = filter(lambda x: x is not None, map(Entry, targets)) + + calc = None + task_class = BuildTask # default action is to build targets + opening_message = "Building targets ..." + closing_message = "done building targets." + failure_message = "building terminated because of errors." + if options.question: + task_class = QuestionTask + try: + if ssoptions.get('clean'): + task_class = CleanTask + class CleanCalculator: + def bsig(self, node): + return None + def csig(self, node): + return None + def current(self, node, sig): + return 0 + def write(self): + pass + calc = CleanCalculator() + opening_message = "Cleaning targets ..." + closing_message = "done cleaning targets." + failure_message = "cleaning terminated because of errors." + except AttributeError: + pass + + SCons.Environment.CalculatorArgs['max_drift'] = ssoptions.get('max_drift') + + if options.random: + def order(dependencies): + """Randomize the dependencies.""" + # This is cribbed from the implementation of + # random.shuffle() in Python 2.X. + d = dependencies + for i in xrange(len(d)-1, 0, -1): + j = int(random.random() * (i+1)) + d[i], d[j] = d[j], d[i] + return d + else: + def order(dependencies): + """Leave the order of dependencies alone.""" + return dependencies + + progress_display("scons: " + opening_message) + taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, calc, order) + + nj = ssoptions.get('num_jobs') + jobs = SCons.Job.Jobs(nj, taskmaster) + if nj > 1 and jobs.num_jobs == 1: + msg = "parallel builds are unsupported by this version of Python;\n" + \ + "\tignoring -j or num_jobs option.\n" + SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) + + if not memory_stats is None: memory_stats.append(SCons.Debug.memory()) + + try: + jobs.run() + finally: + if exit_status: + progress_display("scons: " + failure_message) + else: + progress_display("scons: " + closing_message) + if not options.noexec: + SCons.Sig.write() + + if not memory_stats is None: + memory_stats.append(SCons.Debug.memory()) + when = [ + 'before SConscript files', + 'after SConscript files', + 'before building', + 'after building', + ] + for i in xrange(len(when)): + memory_outf.write('Memory %s: %d\n' % (when[i], memory_stats[i])) + + if print_count: + SCons.Debug.countLoggedInstances('*') + + if print_objects: + SCons.Debug.listLoggedInstances('*') + #SCons.Debug.dumpLoggedInstances('*') + +def _exec_main(): + all_args = sys.argv[1:] + try: + all_args = string.split(os.environ['SCONSFLAGS']) + all_args + except KeyError: + # it's OK if there's no SCONSFLAGS + pass + parser = OptParser() + global options + options, args = parser.parse_args(all_args) + if options.debug == "pdb": + import pdb + pdb.Pdb().runcall(_main, args, parser) + else: + _main(args, parser) + +def main(): + global exit_status + + try: + _exec_main() + except SystemExit, s: + if s: + exit_status = s + except KeyboardInterrupt: + print "Build interrupted." + sys.exit(2) + except SyntaxError, e: + _scons_syntax_error(e) + except SCons.Errors.InternalError: + _scons_internal_error() + except SCons.Errors.UserError, e: + _scons_user_error(e) + except SCons.Errors.ConfigureDryRunError, e: + _scons_configure_dryrun_error(e) + except: + # An exception here is likely a builtin Python exception Python + # code in an SConscript file. Show them precisely what the + # problem was and where it happened. + SCons.Script.SConscript.SConscript_exception() + sys.exit(2) + + if print_time: + total_time = time.time()-start_time + scons_time = total_time-sconscript_time-command_time + print "Total build time: %f seconds"%total_time + print "Total SConscript file execution time: %f seconds"%sconscript_time + print "Total SCons execution time: %f seconds"%scons_time + print "Total command execution time: %f seconds"%command_time + + sys.exit(exit_status) diff --git a/scons/scons-local-0.95/SCons/Sig/MD5.py b/scons/scons-local-0.95/SCons/Sig/MD5.py new file mode 100644 index 0000000..5899017 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Sig/MD5.py @@ -0,0 +1,93 @@ +"""SCons.Sig.MD5 + +The MD5 signature package for the SCons software construction +utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Sig/MD5.py 0.95.D001 2004/03/08 07:28:28 knight" + +import imp +import string + +# Force Python to load the builtin "md5" module. If we do this with a +# normal import statement, then case-insensitive systems (Win32) get +# confused and thinks there's a case mismatch with *this* MD5.py module. +file, name, desc = imp.find_module('md5') +try: + md5 = imp.load_module('md5', file, name, desc) +finally: + if file: + file.close() + +def current(new, old): + """Return whether a new signature is up-to-date with + respect to an old signature. + """ + return new == old + +def hexdigest(s): + """Return a signature as a string of hex characters. + """ + # NOTE: This routine is a method in the Python 2.0 interface + # of the native md5 module, but we want SCons to operate all + # the way back to at least Python 1.5.2, which doesn't have it. + h = string.hexdigits + r = '' + for c in s: + i = ord(c) + r = r + h[(i >> 4) & 0xF] + h[i & 0xF] + return r + +def collect(signatures): + """ + Collect a list of signatures into an aggregate signature. + + signatures - a list of signatures + returns - the aggregate signature + """ + if len(signatures) == 1: + return signatures[0] + else: + contents = string.join(signatures, ', ') + return hexdigest(md5.new(contents).digest()) + +def signature(obj): + """Generate a signature for an object + """ + try: + gc = obj.get_contents + except AttributeError: + raise AttributeError, "unable to fetch contents of '%s'" % str(obj) + return hexdigest(md5.new(str(gc())).digest()) + +def to_string(signature): + """Convert a signature to a string""" + return signature + +def from_string(string): + """Convert a string to a signature""" + return string diff --git a/scons/scons-local-0.95/SCons/Sig/TimeStamp.py b/scons/scons-local-0.95/SCons/Sig/TimeStamp.py new file mode 100644 index 0000000..3067169 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Sig/TimeStamp.py @@ -0,0 +1,75 @@ +"""SCons.Sig.TimeStamp + +The TimeStamp signature package for the SCons software construction +utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Sig/TimeStamp.py 0.95.D001 2004/03/08 07:28:28 knight" + +def current(new, old): + """Return whether a new timestamp is up-to-date with + respect to an old timestamp. + """ + return not old is None and new <= old + +def collect(signatures): + """ + Collect a list of timestamps, returning + the most-recent timestamp from the list + + signatures - a list of timestamps + returns - the most recent timestamp + """ + + if len(signatures) == 0: + return 0 + elif len(signatures) == 1: + return signatures[0] + else: + return max(signatures) + +def signature(obj): + """Generate a timestamp. + """ + return obj.get_timestamp() + +def to_string(signature): + """Convert a timestamp to a string""" + return str(signature) + +def from_string(string): + """Convert a string to a timestamp""" + try: + return int(string) + except ValueError: + # if the signature isn't an int, then + # the user probably just switched from + # MD5 signatures to timestamp signatures, + # so ignore the error: + return None + + diff --git a/scons/scons-local-0.95/SCons/Sig/__init__.py b/scons/scons-local-0.95/SCons/Sig/__init__.py new file mode 100644 index 0000000..f4d1d25 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Sig/__init__.py @@ -0,0 +1,446 @@ +"""SCons.Sig + +The Signature package for the scons software construction utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Sig/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + +import cPickle +import os +import os.path +import time + +import SCons.Node +import SCons.Warnings + +try: + import MD5 + default_module = MD5 +except ImportError: + import TimeStamp + default_module = TimeStamp + +default_max_drift = 2*24*60*60 + +#XXX Get rid of the global array so this becomes re-entrant. +sig_files = [] + +SConsign_db = None + +def write(): + global sig_files + for sig_file in sig_files: + sig_file.write() + + +class SConsignEntry: + + """Objects of this type are pickled to the .sconsign file, so it + should only contain simple builtin Python datatypes and no methods. + + This class is used to store cache information about nodes between + scons runs for efficiency, and to store the build signature for + nodes so that scons can determine if they are out of date. """ + + # setup the default value for various attributes: + # (We make the class variables so the default values won't get pickled + # with the instances, which would waste a lot of space) + timestamp = None + bsig = None + csig = None + implicit = None + +class _SConsign: + """ + This is the controlling class for the signatures for the collection of + entries associated with a specific directory. The actual directory + association will be maintained by a subclass that is specific to + the underlying storage method. This class provides a common set of + methods for fetching and storing the individual bits of information + that make up signature entry. + """ + def __init__(self, module=None): + """ + module - the signature module being used + """ + + if module is None: + self.module = default_calc.module + else: + self.module = module + self.entries = {} + self.dirty = 0 + + # A null .sconsign entry. We define this here so that it will + # be easy to keep this in sync if/whenever we change the type of + # information returned by the get() method, below. + null_siginfo = (None, None, None) + + def get(self, filename): + """ + Get the .sconsign entry for a file + + filename - the filename whose signature will be returned + returns - (timestamp, bsig, csig) + """ + entry = self.get_entry(filename) + return (entry.timestamp, entry.bsig, entry.csig) + + def get_entry(self, filename): + """ + Create an entry for the filename and return it, or if one already exists, + then return it. + """ + try: + return self.entries[filename] + except (KeyError, AttributeError): + return SConsignEntry() + + def set_entry(self, filename, entry): + """ + Set the entry. + """ + self.entries[filename] = entry + self.dirty = 1 + + def set_csig(self, filename, csig): + """ + Set the csig .sconsign entry for a file + + filename - the filename whose signature will be set + csig - the file's content signature + """ + + entry = self.get_entry(filename) + entry.csig = csig + self.set_entry(filename, entry) + + def set_bsig(self, filename, bsig): + """ + Set the csig .sconsign entry for a file + + filename - the filename whose signature will be set + bsig - the file's built signature + """ + + entry = self.get_entry(filename) + entry.bsig = bsig + self.set_entry(filename, entry) + + def set_timestamp(self, filename, timestamp): + """ + Set the csig .sconsign entry for a file + + filename - the filename whose signature will be set + timestamp - the file's timestamp + """ + + entry = self.get_entry(filename) + entry.timestamp = timestamp + self.set_entry(filename, entry) + + def get_implicit(self, filename): + """Fetch the cached implicit dependencies for 'filename'""" + entry = self.get_entry(filename) + return entry.implicit + + def set_implicit(self, filename, implicit): + """Cache the implicit dependencies for 'filename'.""" + entry = self.get_entry(filename) + if SCons.Util.is_String(implicit): + implicit = [implicit] + implicit = map(str, implicit) + entry.implicit = implicit + self.set_entry(filename, entry) + +class SConsignDB(_SConsign): + """ + A _SConsign subclass that reads and writes signature information + from a global .sconsign.dbm file. + """ + def __init__(self, dir, module=None): + _SConsign.__init__(self, module) + + self.dir = dir + + try: + global SConsign_db + rawentries = SConsign_db[self.dir.path] + except KeyError: + pass + else: + try: + self.entries = cPickle.loads(rawentries) + if type(self.entries) is not type({}): + self.entries = {} + raise TypeError + except KeyboardInterrupt: + raise + except: + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt sconsign entry : %s"%self.dir.path) + + global sig_files + sig_files.append(self) + + def write(self): + if self.dirty: + global SConsign_db + SConsign_db[self.dir.path] = cPickle.dumps(self.entries, 1) + try: + SConsign_db.sync() + except AttributeError: + # Not all anydbm modules have sync() methods. + pass + +class SConsignDir(_SConsign): + def __init__(self, fp=None, module=None): + """ + fp - file pointer to read entries from + module - the signature module being used + """ + _SConsign.__init__(self, module) + + if fp: + self.entries = cPickle.load(fp) + if type(self.entries) is not type({}): + self.entries = {} + raise TypeError + +class SConsignDirFile(SConsignDir): + """ + Encapsulates reading and writing a per-directory .sconsign file. + """ + def __init__(self, dir, module=None): + """ + dir - the directory for the file + module - the signature module being used + """ + + self.dir = dir + self.sconsign = os.path.join(dir.path, '.sconsign') + + try: + fp = open(self.sconsign, 'rb') + except IOError: + fp = None + + try: + SConsignDir.__init__(self, fp, module) + except KeyboardInterrupt: + raise + except: + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt .sconsign file: %s"%self.sconsign) + + global sig_files + sig_files.append(self) + + def write(self): + """ + Write the .sconsign file to disk. + + Try to write to a temporary file first, and rename it if we + succeed. If we can't write to the temporary file, it's + probably because the directory isn't writable (and if so, + how did we build anything in this directory, anyway?), so + try to write directly to the .sconsign file as a backup. + If we can't rename, try to copy the temporary contents back + to the .sconsign file. Either way, always try to remove + the temporary file at the end. + """ + if self.dirty: + temp = os.path.join(self.dir.path, '.scons%d' % os.getpid()) + try: + file = open(temp, 'wb') + fname = temp + except IOError: + try: + file = open(self.sconsign, 'wb') + fname = self.sconsign + except IOError: + return + cPickle.dump(self.entries, file, 1) + file.close() + if fname != self.sconsign: + try: + mode = os.stat(self.sconsign)[0] + os.chmod(self.sconsign, 0666) + os.unlink(self.sconsign) + except OSError: + pass + try: + os.rename(fname, self.sconsign) + except OSError: + open(self.sconsign, 'wb').write(open(fname, 'rb').read()) + os.chmod(self.sconsign, mode) + try: + os.unlink(temp) + except OSError: + pass + +SConsignForDirectory = SConsignDirFile + +def SConsignFile(name, dbm_module=None): + """ + Arrange for all signatures to be stored in a global .sconsign.dbm + file. + """ + global SConsign_db + if SConsign_db is None: + if dbm_module is None: + import anydbm + dbm_module = anydbm + SConsign_db = dbm_module.open(name, "c") + + global SConsignForDirectory + SConsignForDirectory = SConsignDB + +class Calculator: + """ + Encapsulates signature calculations and .sconsign file generating + for the build engine. + """ + + def __init__(self, module=default_module, max_drift=default_max_drift): + """ + Initialize the calculator. + + module - the signature module to use for signature calculations + max_drift - the maximum system clock drift used to determine when to + cache content signatures. A negative value means to never cache + content signatures. (defaults to 2 days) + """ + self.module = module + self.max_drift = max_drift + + def bsig(self, node, cache=None): + """ + Generate a node's build signature, the digested signatures + of its dependency files and build information. + + node - the node whose sources will be collected + cache - alternate node to use for the signature cache + returns - the build signature + + This no longer handles the recursive descent of the + node's children's signatures. We expect that they're + already built and updated by someone else, if that's + what's wanted. + """ + + if cache is None: cache = node + + bsig = cache.get_bsig() + if bsig is not None: + return bsig + + children = node.children() + + # double check bsig, because the call to childre() above may + # have set it: + bsig = cache.get_bsig() + if bsig is not None: + return bsig + + sigs = map(lambda n, c=self: n.calc_signature(c), children) + if node.has_builder(): + sigs.append(self.module.signature(node.get_executor())) + + bsig = self.module.collect(filter(lambda x: not x is None, sigs)) + + cache.set_bsig(bsig) + + # don't store the bsig here, because it isn't accurate until + # the node is actually built. + + return bsig + + def csig(self, node, cache=None): + """ + Generate a node's content signature, the digested signature + of its content. + + node - the node + cache - alternate node to use for the signature cache + returns - the content signature + """ + + if cache is None: cache = node + + csig = cache.get_csig() + if csig is not None: + return csig + + if self.max_drift >= 0: + oldtime, oldbsig, oldcsig = node.get_prevsiginfo() + else: + oldtime, oldbsig, oldcsig = _SConsign.null_siginfo + + mtime = node.get_timestamp() + + if (oldtime and oldcsig and oldtime == mtime): + # use the signature stored in the .sconsign file + csig = oldcsig + # Set the csig here so it doesn't get recalculated unnecessarily + # and so it's set when the .sconsign file gets written + cache.set_csig(csig) + else: + csig = self.module.signature(node) + # Set the csig here so it doesn't get recalculated unnecessarily + # and so it's set when the .sconsign file gets written + cache.set_csig(csig) + + if self.max_drift >= 0 and (time.time() - mtime) > self.max_drift: + node.store_csig() + node.store_timestamp() + + return csig + + def current(self, node, newsig): + """ + Check if a signature is up to date with respect to a node. + + node - the node whose signature will be checked + newsig - the (presumably current) signature of the file + + returns - 1 if the file is current with the specified signature, + 0 if it isn't + """ + + if node.always_build: + return 0 + + oldtime, oldbsig, oldcsig = node.get_prevsiginfo() + + if not node.has_builder() and node.get_timestamp() == oldtime: + return 1 + + return self.module.current(newsig, oldbsig) + + +default_calc = Calculator() diff --git a/scons/scons-local-0.95/SCons/Taskmaster.py b/scons/scons-local-0.95/SCons/Taskmaster.py new file mode 100644 index 0000000..02ffad3 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Taskmaster.py @@ -0,0 +1,439 @@ +"""SCons.Taskmaster + +Generic Taskmaster. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Taskmaster.py 0.95.D001 2004/03/08 07:28:28 knight" + +import string +import sys +import traceback + +import SCons.Node +import SCons.Errors + +class Task: + """Default SCons build engine task. + + This controls the interaction of the actual building of node + and the rest of the engine. + + This is expected to handle all of the normally-customizable + aspects of controlling a build, so any given application + *should* be able to do what it wants by sub-classing this + class and overriding methods as appropriate. If an application + needs to customze something by sub-classing Taskmaster (or + some other build engine class), we should first try to migrate + that functionality into this class. + + Note that it's generally a good idea for sub-classes to call + these methods explicitly to update state, etc., rather than + roll their own interaction with Taskmaster from scratch.""" + def __init__(self, tm, targets, top, node): + self.tm = tm + self.targets = targets + self.top = top + self.node = node + + def display(self, message): + """Allow the calling interface to display a message + """ + pass + + def prepare(self): + """Called just before the task is executed. + + This unlinks all targets and makes all directories before + building anything.""" + + # Now that it's the appropriate time, give the TaskMaster a + # chance to raise any exceptions it encountered while preparing + # this task. + self.tm.exception_raise() + + if self.tm.message: + self.display(self.tm.message) + self.tm.message = None + + for t in self.targets: + t.prepare() + for s in t.side_effects: + s.prepare() + + def execute(self): + """Called to execute the task. + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + prepare(), executed() or failed().""" + + try: + everything_was_cached = 1 + for t in self.targets: + if not t.retrieve_from_cache(): + everything_was_cached = 0 + break + if not everything_was_cached: + self.targets[0].build() + except KeyboardInterrupt: + raise + except SystemExit: + raise SCons.Errors.ExplicitExit(self.targets[0], sys.exc_value.code) + except SCons.Errors.UserError: + raise + except SCons.Errors.BuildError: + raise + except: + raise SCons.Errors.BuildError(self.targets[0], + "Exception", + sys.exc_type, + sys.exc_value, + sys.exc_traceback) + + def get_target(self): + """Fetch the target being built or updated by this task. + """ + return self.node + + def executed(self): + """Called when the task has been successfully executed. + + This may have been a do-nothing operation (to preserve + build order), so check the node's state before updating + things. Most importantly, this calls back to the + Taskmaster to put any node tasks waiting on this one + back on the pending list.""" + + if self.targets[0].get_state() == SCons.Node.executing: + for t in self.targets: + for side_effect in t.side_effects: + side_effect.set_state(None) + t.set_state(SCons.Node.executed) + t.built() + else: + for t in self.targets: + t.visited() + + self.tm.executed(self.node) + + def failed(self): + """Default action when a task fails: stop the build.""" + self.fail_stop() + + def fail_stop(self): + """Explicit stop-the-build failure.""" + for t in self.targets: + t.set_state(SCons.Node.failed) + self.tm.failed(self.node) + self.tm.stop() + + def fail_continue(self): + """Explicit continue-the-build failure. + + This sets failure status on the target nodes and all of + their dependent parent nodes. + """ + for t in self.targets: + def get_parents(node, parent): return node.get_parents() + def set_state(node, parent): node.set_state(SCons.Node.failed) + walker = SCons.Node.Walker(t, get_parents, eval_func=set_state) + n = walker.next() + while n: + n = walker.next() + + self.tm.executed(self.node) + + def make_ready(self): + """Make a task ready for execution.""" + state = SCons.Node.up_to_date + calc = self.tm.calc + for t in self.targets: + c = calc or t.calculator() + if not t.current(c): + state = SCons.Node.executing + for t in self.targets: + if state == SCons.Node.executing: + for side_effect in t.side_effects: + side_effect.set_state(state) + t.set_state(state) + + def postprocess(self): + """Post process a task after it's been executed.""" + for t in self.targets: + t.postprocess() + + + +def order(dependencies): + """Re-order a list of dependencies (if we need to).""" + return dependencies + +class Calc: + def bsig(self, node): + """ + """ + return None + + def current(self, node, sig): + """Default SCons build engine is-it-current function. + + This returns "always out of date," so every node is always + built/visited. + """ + return 0 + +class Taskmaster: + """A generic Taskmaster for handling a bunch of targets. + + Classes that override methods of this class should call + the base class method, so this class can do its thing. + """ + + def __init__(self, targets=[], tasker=Task, calc=Calc(), order=order): + self.targets = targets # top level targets + self.candidates = targets[:] # nodes that might be ready to be executed + self.candidates.reverse() + self.executing = [] # nodes that are currently executing + self.pending = [] # nodes that depend on a currently executing node + self.tasker = tasker + self.ready = None # the next task that is ready to be executed + self.calc = calc + self.order = order + self.exception_set(None, None) + self.message = None + + def _find_next_ready_node(self): + """Find the next node that is ready to be built""" + + if self.ready: + return + + while self.candidates: + node = self.candidates[-1] + state = node.get_state() + + # Skip this node if it has already been executed: + if state != None and state != SCons.Node.stack: + self.candidates.pop() + continue + + # Mark this node as being on the execution stack: + node.set_state(SCons.Node.stack) + + try: + children = node.children() + except SystemExit: + e = SCons.Errors.ExplicitExit(node, sys.exc_value.code) + self.exception_set(SCons.Errors.ExplicitExit, e) + self.candidates.pop() + self.ready = node + break + except KeyboardInterrupt: + raise + except: + # We had a problem just trying to figure out the + # children (like a child couldn't be linked in to a + # BuildDir, or a Scanner threw something). Arrange to + # raise the exception when the Task is "executed." + x = SCons.Errors.TaskmasterException(sys.exc_type, + sys.exc_value, + sys.exc_traceback) + self.exception_set(x) + self.candidates.pop() + self.ready = node + break + + # Detect dependency cycles: + def in_stack(node): return node.get_state() == SCons.Node.stack + cycle = filter(in_stack, children) + if cycle: + nodes = filter(in_stack, self.candidates) + cycle + nodes.reverse() + desc = "Dependency cycle: " + string.join(map(str, nodes), " -> ") + raise SCons.Errors.UserError, desc + + # Find all of the derived dependencies (that is, + # children who have builders or are side effects): + try: + def derived_nodes(node): return node.is_derived() or node.is_pseudo_derived() + derived = filter(derived_nodes, children) + except KeyboardInterrupt: + raise + except: + # We had a problem just trying to figure out if any of + # the kids are derived (like a child couldn't be linked + # from a repository). Arrange to raise the exception + # when the Task is "executed." + x = SCons.Errors.TaskmasterException(sys.exc_type, + sys.exc_value, + sys.exc_traceback) + self.exception_set(x) + self.candidates.pop() + self.ready = node + break + + # If there aren't any children with builders and this + # was a top-level argument, then see if we can find any + # corresponding targets in linked build directories: + if not derived and node in self.targets: + alt, message = node.alter_targets() + if alt: + self.message = message + self.candidates.pop() + self.candidates.extend(alt) + continue + + # Add derived files that have not been built + # to the candidates list: + def unbuilt_nodes(node): return node.get_state() == None + not_built = filter(unbuilt_nodes, derived) + if not_built: + not_built.reverse() + self.candidates.extend(self.order(not_built)) + continue + + # Skip this node if it has side-effects that are + # currently being built: + cont = 0 + for side_effect in node.side_effects: + if side_effect.get_state() == SCons.Node.executing: + self.pending.append(node) + node.set_state(SCons.Node.pending) + self.candidates.pop() + cont = 1 + break + if cont: continue + + # Skip this node if it is pending on a currently + # executing node: + if node.depends_on(self.executing) or node.depends_on(self.pending): + self.pending.append(node) + node.set_state(SCons.Node.pending) + self.candidates.pop() + continue + + # The default when we've gotten through all of the checks above: + # this node is ready to be built. + self.candidates.pop() + self.ready = node + break + + def next_task(self): + """Return the next task to be executed.""" + + self._find_next_ready_node() + + node = self.ready + + if node is None: + return None + + try: + tlist = node.builder.targets(node) + except AttributeError: + tlist = [node] + self.executing.extend(tlist) + self.executing.extend(node.side_effects) + + task = self.tasker(self, tlist, node in self.targets, node) + try: + task.make_ready() + except KeyboardInterrupt: + raise + except: + # We had a problem just trying to get this task ready (like + # a child couldn't be linked in to a BuildDir when deciding + # whether this node is current). Arrange to raise the + # exception when the Task is "executed." + x = SCons.Errors.TaskmasterException(sys.exc_type, + sys.exc_value, + sys.exc_traceback) + self.exception_set(x) + self.ready = None + + return task + + def is_blocked(self): + self._find_next_ready_node() + + return not self.ready and (self.pending or self.executing) + + def stop(self): + """Stop the current build completely.""" + self.candidates = [] + self.ready = None + self.pending = [] + + def failed(self, node): + try: + tlist = node.builder.targets(node) + except AttributeError: + tlist = [node] + for t in tlist: + self.executing.remove(t) + for side_effect in node.side_effects: + self.executing.remove(side_effect) + + def executed(self, node): + try: + tlist = node.builder.targets(node) + except AttributeError: + tlist = [node] + for t in tlist: + self.executing.remove(t) + for side_effect in node.side_effects: + self.executing.remove(side_effect) + + # move the current pending nodes to the candidates list: + # (they may not all be ready to build, but _find_next_ready_node() + # will figure out which ones are really ready) + for node in self.pending: + node.set_state(None) + self.pending.reverse() + self.candidates.extend(self.pending) + self.pending = [] + + def exception_set(self, type, value=None): + """Record an exception type and value to raise later, at an + appropriate time.""" + self.exc_type = type + self.exc_value = value + self.exc_traceback = traceback + + def exception_raise(self): + """Raise any pending exception that was recorded while + getting a Task ready for execution.""" + if self.exc_type: + try: + try: + raise self.exc_type, self.exc_value + except TypeError: + # exc_type was probably an instance, + # so raise it by itself. + raise self.exc_type + finally: + self.exception_set(None, None) diff --git a/scons/scons-local-0.95/SCons/Tool/386asm.py b/scons/scons-local-0.95/SCons/Tool/386asm.py new file mode 100644 index 0000000..74ee6e7 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/386asm.py @@ -0,0 +1,54 @@ +"""SCons.Tool.386asm + +Tool specification for the 386ASM assembler for the Phar Lap ETS embedded +operating system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/386asm.py 0.95.D001 2004/03/08 07:28:28 knight" + +from SCons.Tool.PharLapCommon import addPharLapPaths +import SCons.Util + +import as + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + as.generate(env) + + env['AS'] = '386asm' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET' + env['ASPPCOM'] = '$CC $ASFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET' + + addPharLapPaths(env) + +def exists(env): + return env.Detect('386asm') diff --git a/scons/scons-local-0.95/SCons/Tool/BitKeeper.py b/scons/scons-local-0.95/SCons/Tool/BitKeeper.py new file mode 100644 index 0000000..ce01863 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/BitKeeper.py @@ -0,0 +1,58 @@ +"""SCons.Tool.BitKeeper.py + +Tool-specific initialization for the BitKeeper source code control +system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/BitKeeper.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + BitKeeper to an Environment.""" + + def BitKeeperFactory(env=env): + """ """ + return SCons.Builder.Builder(action = "$BITKEEPERCOM", env = env) + + setattr(env, 'BitKeeper', BitKeeperFactory) + + env['BITKEEPER'] = 'bk' + env['BITKEEPERGET'] = '$BITKEEPER get' + env['BITKEEPERGETFLAGS'] = SCons.Util.CLVar('') + env['BITKEEPERCOM'] = '$BITKEEPERGET $BITKEEPERGETFLAGS $TARGET' + +def exists(env): + return env.Detect('bk') diff --git a/scons/scons-local-0.95/SCons/Tool/CVS.py b/scons/scons-local-0.95/SCons/Tool/CVS.py new file mode 100644 index 0000000..7d877b2 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/CVS.py @@ -0,0 +1,66 @@ +"""SCons.Tool.CVS.py + +Tool-specific initialization for CVS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/CVS.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + CVS to an Environment.""" + + def CVSFactory(repos, module='', env=env): + """ """ + # fail if repos is not an absolute path name? + if module != '': + # Don't use os.path.join() because the name we fetch might + # be across a network and must use POSIX slashes as separators. + module = module + '/' + env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS -d ${TARGET.dir} $CVSMODULE${TARGET.posix}' + return SCons.Builder.Builder(action = '$CVSCOM', + env = env, + CVSREPOSITORY = repos, + CVSMODULE = module) + + setattr(env, 'CVS', CVSFactory) + + env['CVS'] = 'cvs' + env['CVSFLAGS'] = SCons.Util.CLVar('-d $CVSREPOSITORY') + env['CVSCOFLAGS'] = SCons.Util.CLVar('') + env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS ${TARGET.posix}' + +def exists(env): + return env.Detect('cvs') diff --git a/scons/scons-local-0.95/SCons/Tool/JavaCommon.py b/scons/scons-local-0.95/SCons/Tool/JavaCommon.py new file mode 100644 index 0000000..ffc80e5 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/JavaCommon.py @@ -0,0 +1,225 @@ +"""SCons.Tool.JavaCommon + +Stuff for processing Java. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/JavaCommon.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import re +import string + +java_parsing = 1 + +if java_parsing: + # Parse Java files for class names. + # + # This is a really cool parser from Charles Crain + # that finds appropriate class names in Java source. + + # A regular expression that will find, in a java file: newlines; + # any alphanumeric token (keyword, class name, specifier); open or + # close brackets; a single-line comment "//"; the multi-line comment + # begin and end tokens /* and */; single or double quotes; and + # single or double quotes preceeded by a backslash. + _reToken = re.compile(r'(\n|//|\\[\'"]|[\'"\{\}]|[A-Za-z_][\w\.]*|' + + r'/\*|\*/)') + + class OuterState: + """The initial state for parsing a Java file for classes, + interfaces, and anonymous inner classes.""" + def __init__(self): + self.listClasses = [] + self.listOutputs = [] + self.stackBrackets = [] + self.brackets = 0 + self.nextAnon = 1 + self.package = None + + def __getClassState(self): + try: + return self.classState + except AttributeError: + ret = ClassState(self) + self.classState = ret + return ret + + def __getPackageState(self): + try: + return self.packageState + except AttributeError: + ret = PackageState(self) + self.packageState = ret + return ret + + def __getAnonClassState(self): + try: + return self.anonState + except AttributeError: + ret = SkipState(1, AnonClassState(self)) + self.anonState = ret + return ret + + def __getSkipState(self): + try: + return self.skipState + except AttributeError: + ret = SkipState(1, self) + self.skipState = ret + return ret + + def parseToken(self, token): + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '{': + self.brackets = self.brackets + 1 + elif token == '}': + self.brackets = self.brackets - 1 + if len(self.stackBrackets) and \ + self.brackets == self.stackBrackets[-1]: + self.listOutputs.append(string.join(self.listClasses, '$')) + self.listClasses.pop() + self.stackBrackets.pop() + elif token == '"' or token == "'": + return IgnoreState(token, self) + elif token == "new": + # anonymous inner class + if len(self.listClasses) > 0: + return self.__getAnonClassState() + return self.__getSkipState() # Skip the class name + elif token == 'class' or token == 'interface': + if len(self.listClasses) == 0: + self.nextAnon = 1 + self.stackBrackets.append(self.brackets) + return self.__getClassState() + elif token == 'package': + return self.__getPackageState() + return self + + def addAnonClass(self): + """Add an anonymous inner class""" + clazz = self.listClasses[0] + self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) + self.brackets = self.brackets + 1 + self.nextAnon = self.nextAnon + 1 + + def setPackage(self, package): + self.package = package + + class AnonClassState: + """A state that looks for anonymous inner classes.""" + def __init__(self, outer_state): + # outer_state is always an instance of OuterState + self.outer_state = outer_state + self.tokens_to_find = 2 + def parseToken(self, token): + # This is an anonymous class if and only if the next token is a bracket + if token == '{': + self.outer_state.addAnonClass() + return self.outer_state + + class SkipState: + """A state that will skip a specified number of tokens before + reverting to the previous state.""" + def __init__(self, tokens_to_skip, old_state): + self.tokens_to_skip = tokens_to_skip + self.old_state = old_state + def parseToken(self, token): + self.tokens_to_skip = self.tokens_to_skip - 1 + if self.tokens_to_skip < 1: + return self.old_state + return self + + class ClassState: + """A state we go into when we hit a class or interface keyword.""" + def __init__(self, outer_state): + # outer_state is always an instance of OuterState + self.outer_state = outer_state + def parseToken(self, token): + # the next non-whitespace token should be the name of the class + if token == '\n': + return self + self.outer_state.listClasses.append(token) + return self.outer_state + + class IgnoreState: + """A state that will ignore all tokens until it gets to a + specified token.""" + def __init__(self, ignore_until, old_state): + self.ignore_until = ignore_until + self.old_state = old_state + def parseToken(self, token): + if self.ignore_until == token: + return self.old_state + return self + + class PackageState: + """The state we enter when we encounter the package keyword. + We assume the next token will be the package name.""" + def __init__(self, outer_state): + # outer_state is always an instance of OuterState + self.outer_state = outer_state + def parseToken(self, token): + self.outer_state.setPackage(token) + return self.outer_state + + def parse_java_file(fn): + return parse_java(open(fn, 'r').read()) + + def parse_java(contents): + """Parse a .java file and return a double of package directory, + plus a list of .class files that compiling that .java file will + produce""" + package = None + initial = OuterState() + currstate = initial + for token in _reToken.findall(contents): + # The regex produces a bunch of groups, but only one will + # have anything in it. + currstate = currstate.parseToken(token) + if initial.package: + package = string.replace(initial.package, '.', os.sep) + return (package, initial.listOutputs) + +else: + # Don't actually parse Java files for class names. + # + # We might make this a configurable option in the future if + # Java-file parsing takes too long (although it shouldn't relative + # to how long the Java compiler itself seems to take...). + + def parse_java_file(fn): + """ "Parse" a .java file. + + This actually just splits the file name, so the assumption here + is that the file name matches the public class name, and that + the path to the file is the same as the package name. + """ + return os.path.split(file) diff --git a/scons/scons-local-0.95/SCons/Tool/Perforce.py b/scons/scons-local-0.95/SCons/Tool/Perforce.py new file mode 100644 index 0000000..ec76780 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/Perforce.py @@ -0,0 +1,93 @@ +"""SCons.Tool.Perforce.py + +Tool-specific initialization for Perforce Source Code Management system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/Perforce.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os + +import SCons.Builder +import SCons.Node.FS +import SCons.Util + +# This function should maybe be moved to SCons.Util? +from SCons.Tool.PharLapCommon import addPathIfNotExists + +# Variables that we want to import from the base OS environment. +_import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD', + 'P4CHARSET', 'P4LANGUAGE', 'SYSTEMROOT' ] + +def generate(env): + """Add a Builder factory function and construction variables for + Perforce to an Environment.""" + + def PerforceFactory(env=env): + """ """ + return SCons.Builder.Builder(action = '$P4COM', + env = env) + + setattr(env, 'Perforce', PerforceFactory) + + env['P4'] = 'p4' + env['P4FLAGS'] = SCons.Util.CLVar('') + env['P4COM'] = '$P4 $P4FLAGS sync $TARGET' + try: + environ = env['ENV'] + except KeyError: + environ = {} + env['ENV'] = environ + + # Perforce seems to use the PWD environment variable rather than + # calling getcwd() for itself, which is odd. If no PWD variable + # is present, p4 WILL call getcwd, but this seems to cause problems + # with good ol' Win32's tilde-mangling for long file names. + environ['PWD'] = SCons.Node.FS.default_fs.Dir('#').get_abspath() + + for var in _import_env: + v = os.environ.get(var) + if v: + environ[var] = v + + if SCons.Util.can_read_reg: + # If we can read the registry, add the path to Perforce to our environment. + try: + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Perforce\\environment') + val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT') + addPathIfNotExists(environ, 'PATH', val) + except SCons.Util.RegError: + # Can't detect where Perforce is, hope the user has it set in the + # PATH. + pass + +def exists(env): + return env.Detect('p4') diff --git a/scons/scons-local-0.95/SCons/Tool/PharLapCommon.py b/scons/scons-local-0.95/SCons/Tool/PharLapCommon.py new file mode 100644 index 0000000..9b08fd7 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/PharLapCommon.py @@ -0,0 +1,132 @@ +"""SCons.Tool.PharLapCommon + +This module contains common code used by all Tools for the +Phar Lap ETS tool chain. Right now, this is linkloc and +386asm. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/PharLapCommon.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import SCons.Errors +import SCons.Util +import re +import string + +def getPharLapPath(): + """Reads the registry to find the installed path of the Phar Lap ETS + development kit. + + Raises UserError if no installed version of Phar Lap can + be found.""" + + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError, "No Windows registry module was found" + try: + k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, + 'SOFTWARE\\Pharlap\\ETS') + val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir') + + # The following is a hack...there is (not surprisingly) + # an odd issue in the Phar Lap plug in that inserts + # a bunch of junk data after the phar lap path in the + # registry. We must trim it. + idx=val.find('\0') + if idx >= 0: + val = val[:idx] + + return os.path.normpath(val) + except SCons.Util.RegError: + raise SCons.Errors.UserError, "Cannot find Phar Lap ETS path in the registry. Is it installed properly?" + +REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)') + +def getPharLapVersion(): + """Returns the version of the installed ETS Tool Suite as a + decimal number. This version comes from the ETS_VER #define in + the embkern.h header. For example, '#define ETS_VER 1010' (which + is what Phar Lap 10.1 defines) would cause this method to return + 1010. Phar Lap 9.1 does not have such a #define, but this method + will return 910 as a default. + + Raises UserError if no installed version of Phar Lap can + be found.""" + + include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) + if not os.path.exists(include_path): + raise SCons.Errors.UserError, "Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?" + mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) + if mo: + return int(mo.group(1)) + # Default return for Phar Lap 9.1 + return 910 + +def addPathIfNotExists(env_dict, key, path, sep=os.pathsep): + """This function will take 'key' out of the dictionary + 'env_dict', then add the path 'path' to that key if it is not + already there. This treats the value of env_dict[key] as if it + has a similar format to the PATH variable...a list of paths + separated by tokens. The 'path' will get added to the list if it + is not already there.""" + try: + is_list = 1 + paths = env_dict[key] + if not SCons.Util.is_List(env_dict[key]): + paths = string.split(paths, sep) + is_list = 0 + if not os.path.normcase(path) in map(os.path.normcase, paths): + paths = [ path ] + paths + if is_list: + env_dict[key] = paths + else: + env_dict[key] = string.join(paths, sep) + except KeyError: + env_dict[key] = path + +def addPharLapPaths(env): + """This function adds the path to the Phar Lap binaries, includes, + and libraries, if they are not already there.""" + ph_path = getPharLapPath() + + try: + env_dict = env['ENV'] + except KeyError: + env_dict = {} + env['ENV'] = env_dict + addPathIfNotExists(env_dict, 'PATH', + os.path.join(ph_path, 'bin')) + addPathIfNotExists(env_dict, 'INCLUDE', + os.path.join(ph_path, 'include')) + addPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, 'lib')) + addPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, os.path.normpath('lib/vclib'))) + + env['PHARLAP_PATH'] = getPharLapPath() + env['PHARLAP_VERSION'] = str(getPharLapVersion()) + diff --git a/scons/scons-local-0.95/SCons/Tool/RCS.py b/scons/scons-local-0.95/SCons/Tool/RCS.py new file mode 100644 index 0000000..9d4a32d --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/RCS.py @@ -0,0 +1,55 @@ +"""SCons.Tool.RCS.py + +Tool-specific initialization for RCS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/RCS.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + RCS to an Environment.""" + + def RCSFactory(env=env): + """ """ + return SCons.Builder.Builder(action = '$RCS_COCOM', env = env) + + setattr(env, 'RCS', RCSFactory) + + env['RCS'] = 'rcs' + env['RCS_CO'] = 'co' + env['RCS_COFLAGS'] = SCons.Util.CLVar('') + env['RCS_COCOM'] = '$RCS_CO $RCS_COFLAGS $TARGET' + +def exists(env): + return env.Detect('rcs') diff --git a/scons/scons-local-0.95/SCons/Tool/SCCS.py b/scons/scons-local-0.95/SCons/Tool/SCCS.py new file mode 100644 index 0000000..b41d090 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/SCCS.py @@ -0,0 +1,55 @@ +"""SCons.Tool.SCCS.py + +Tool-specific initialization for SCCS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/SCCS.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + SCCS to an Environment.""" + + def SCCSFactory(env=env): + """ """ + return SCons.Builder.Builder(action = '$SCCSCOM', env = env) + + setattr(env, 'SCCS', SCCSFactory) + + env['SCCS'] = 'sccs' + env['SCCSFLAGS'] = SCons.Util.CLVar('') + env['SCCSGETFLAGS'] = SCons.Util.CLVar('') + env['SCCSCOM'] = '$SCCS $SCCSFLAGS get $SCCSGETFLAGS $TARGET' + +def exists(env): + return env.Detect('sccs') diff --git a/scons/scons-local-0.95/SCons/Tool/Subversion.py b/scons/scons-local-0.95/SCons/Tool/Subversion.py new file mode 100644 index 0000000..677dd7b --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/Subversion.py @@ -0,0 +1,62 @@ +"""SCons.Tool.Subversion.py + +Tool-specific initialization for Subversion. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/Subversion.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + Subversion to an Environment.""" + + def SubversionFactory(repos, module='', env=env): + """ """ + # fail if repos is not an absolute path name? + if module != '': + module = os.path.join(module, '') + return SCons.Builder.Builder(action = '$SVNCOM', + env = env, + SVNREPOSITORY = repos, + SVNMODULE = module) + + setattr(env, 'Subversion', SubversionFactory) + + env['SVN'] = 'svn' + env['SVNFLAGS'] = SCons.Util.CLVar('') + env['SVNCOM'] = '$SVN $SVNFLAGS cat $SVNREPOSITORY/$SVNMODULE$TARGET > $TARGET' + +def exists(env): + return env.Detect('svn') diff --git a/scons/scons-local-0.95/SCons/Tool/__init__.py b/scons/scons-local-0.95/SCons/Tool/__init__.py new file mode 100644 index 0000000..48bae48 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/__init__.py @@ -0,0 +1,341 @@ +"""SCons.Tool + +SCons tool selection. + +This looks for modules that define a callable object that can modify +a construction environment as appropriate for a given tool (or tool +chain). + +Note that because this subsysem just *selects* a callable that can +modify a construction environment, it's possible for people to define +their own "tool specification" in an arbitrary callable function. No +one needs to use or tie in to this subsystem in order to roll their own +tool definition. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/__init__.py 0.95.D001 2004/03/08 07:28:28 knight" + +import imp +import sys + +import SCons.Errors +import SCons.Defaults + +class ToolSpec: + def __init__(self, name): + self.name = name + + def __call__(self, env, *args, **kw): + env.Append(TOOLS = [ self.name ]) + apply(self.generate, ( env, ) + args, kw) + + def __str__(self): + return self.name + +def Tool(name, toolpath=[]): + "Select a canned Tool specification, optionally searching in toolpath." + + try: + file, path, desc = imp.find_module(name, toolpath) + try: + module = imp.load_module(name, file, path, desc) + spec = ToolSpec(name) + spec.generate = module.generate + spec.exists = module.exists + return spec + finally: + if file: + file.close() + except ImportError, e: + pass + + full_name = 'SCons.Tool.' + name + if not sys.modules.has_key(full_name): + try: + file, path, desc = imp.find_module(name, + sys.modules['SCons.Tool'].__path__) + mod = imp.load_module(full_name, file, path, desc) + setattr(SCons.Tool, name, mod) + except ImportError, e: + raise SCons.Errors.UserError, "No tool named '%s': %s" % (name, e) + if file: + file.close() + spec = ToolSpec(name) + spec.generate = sys.modules[full_name].generate + spec.exists = sys.modules[full_name].exists + return spec + +def createProgBuilder(env): + """This is a utility function that creates the Program + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + program = env['BUILDERS']['Program'] + except KeyError: + program = SCons.Builder.Builder(action = '$LINKCOM', + emitter = '$PROGEMITTER', + prefix = '$PROGPREFIX', + suffix = '$PROGSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'Object', + scanner = SCons.Defaults.ProgScan) + env['BUILDERS']['Program'] = program + + return program + +def createStaticLibBuilder(env): + """This is a utility function that creates the StaticLibrary + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + static_lib = env['BUILDERS']['StaticLibrary'] + except KeyError: + static_lib = SCons.Builder.Builder(action = "$ARCOM", + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'StaticObject') + env['BUILDERS']['StaticLibrary'] = static_lib + env['BUILDERS']['Library'] = static_lib + + return static_lib + +def createSharedLibBuilder(env): + """This is a utility function that creates the SharedLibrary + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + shared_lib = env['BUILDERS']['SharedLibrary'] + except KeyError: + action_list = [ SCons.Defaults.SharedCheck, "$SHLINKCOM" ] + shared_lib = SCons.Builder.Builder(action = action_list, + emitter = "$SHLIBEMITTER", + prefix = '$SHLIBPREFIX', + suffix = '$SHLIBSUFFIX', + scanner = SCons.Defaults.ProgScan, + src_suffix = '$SHOBJSUFFIX', + src_builder = 'SharedObject') + env['BUILDERS']['SharedLibrary'] = shared_lib + + return shared_lib + +def createObjBuilders(env): + """This is a utility function that creates the StaticObject + and SharedObject Builders in an Environment if they + are not there already. + + If they are there already, we return the existing ones. + + This is a separate function because soooo many Tools + use this functionality. + + The return is a 2-tuple of (StaticObject, SharedObject) + """ + + try: + static_obj = env['BUILDERS']['StaticObject'] + except KeyError: + static_obj = SCons.Builder.Builder(action = {}, + emitter = "$OBJEMITTER", + prefix = '$OBJPREFIX', + suffix = '$OBJSUFFIX', + src_builder = ['CFile', 'CXXFile']) + env['BUILDERS']['StaticObject'] = static_obj + env['BUILDERS']['Object'] = static_obj + env['OBJEMITTER'] = SCons.Defaults.StaticObjectEmitter + + try: + shared_obj = env['BUILDERS']['SharedObject'] + except KeyError: + shared_obj = SCons.Builder.Builder(action = {}, + emitter = "$SHOBJEMITTER", + prefix = '$SHOBJPREFIX', + suffix = '$SHOBJSUFFIX', + src_builder = ['CFile', 'CXXFile']) + env['BUILDERS']['SharedObject'] = shared_obj + env['SHOBJEMITTER'] = SCons.Defaults.SharedObjectEmitter + + return (static_obj, shared_obj) + +def createCFileBuilders(env): + """This is a utility function that creates the CFile/CXXFile + Builders in an Environment if they + are not there already. + + If they are there already, we return the existing ones. + + This is a separate function because soooo many Tools + use this functionality. + + The return is a 2-tuple of (CFile, CXXFile) + """ + + try: + c_file = env['BUILDERS']['CFile'] + except KeyError: + c_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$CFILESUFFIX'}) + env['BUILDERS']['CFile'] = c_file + env['CFILESUFFIX'] = '.c' + + try: + cxx_file = env['BUILDERS']['CXXFile'] + except KeyError: + cxx_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$CXXFILESUFFIX'}) + env['BUILDERS']['CXXFile'] = cxx_file + env['CXXFILESUFFIX'] = '.cc' + + return (c_file, cxx_file) + +def FindTool(tools, env): + for tool in tools: + t = Tool(tool) + if t.exists(env): + return tool + return None + +def FindAllTools(tools, env): + def ToolExists(tool, env=env): + return Tool(tool).exists(env) + return filter (ToolExists, tools) + +def tool_list(platform, env): + + # XXX this logic about what tool to prefer on which platform + # should be moved into either the platform files or + # the tool files themselves. + # The search orders here are described in the man page. If you + # change these search orders, update the man page as well. + if str(platform) == 'win32': + "prefer Microsoft tools on Windows" + linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ] + c_compilers = ['msvc', 'mingw', 'gcc', 'icl', 'icc', 'cc', 'bcc32' ] + cxx_compilers = ['msvc', 'icc', 'g++', 'c++', 'bcc32' ] + assemblers = ['masm', 'nasm', 'gas', '386asm' ] + fortran_compilers = ['g77', 'ifl'] + ars = ['mslib', 'ar', 'tlib'] + elif str(platform) == 'os2': + "prefer IBM tools on OS/2" + linkers = ['ilink', 'gnulink', 'mslink'] + c_compilers = ['icc', 'gcc', 'msvc', 'cc'] + cxx_compilers = ['icc', 'g++', 'msvc', 'c++'] + assemblers = ['nasm', 'masm', 'gas'] + fortran_compilers = ['ifl', 'g77'] + ars = ['ar', 'mslib'] + elif str(platform) == 'irix': + "prefer MIPSPro on IRIX" + linkers = ['sgilink', 'gnulink'] + c_compilers = ['sgicc', 'gcc', 'cc'] + cxx_compilers = ['sgic++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f77', 'g77'] + ars = ['sgiar'] + elif str(platform) == 'sunos': + "prefer Forte tools on SunOS" + linkers = ['sunlink', 'gnulink'] + c_compilers = ['suncc', 'gcc', 'cc'] + cxx_compilers = ['sunc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f77', 'g77'] + ars = ['sunar'] + elif str(platform) == 'hpux': + "prefer aCC tools on HP-UX" + linkers = ['hplink', 'gnulink'] + c_compilers = ['hpcc', 'gcc', 'cc'] + cxx_compilers = ['hpc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f77', 'g77'] + ars = ['ar'] + elif str(platform) == 'aix': + "prefer AIX Visual Age tools on AIX" + linkers = ['aixlink', 'gnulink'] + c_compilers = ['aixcc', 'gcc', 'cc'] + cxx_compilers = ['aixc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['aixf77', 'g77'] + ars = ['ar'] + else: + "prefer GNU tools on all other platforms" + linkers = ['gnulink', 'mslink', 'ilink'] + c_compilers = ['gcc', 'msvc', 'icc', 'cc'] + cxx_compilers = ['g++', 'msvc', 'icc', 'c++'] + assemblers = ['gas', 'nasm', 'masm'] + fortran_compilers = ['g77', 'ifl'] + ars = ['ar', 'mslib'] + + c_compiler = FindTool(c_compilers, env) or c_compilers[0] + + # XXX this logic about what tool provides what should somehow be + # moved into the tool files themselves. + if c_compiler and c_compiler == 'mingw': + # MinGW contains a linker, C compiler, C++ compiler, + # Fortran compiler, archiver and assembler: + cxx_compiler = None + linker = None + assembler = None + fortran_compiler = None + ar = None + else: + # Don't use g++ if the C compiler has built-in C++ support: + if c_compiler in ('msvc', 'icc'): + cxx_compiler = None + else: + cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0] + linker = FindTool(linkers, env) or linkers[0] + assembler = FindTool(assemblers, env) or assemblers[0] + fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0] + ar = FindTool(ars, env) or ars[0] + + other_tools = FindAllTools(['BitKeeper', 'CVS', + 'dmd', + 'dvipdf', 'dvips', 'gs', + 'jar', 'javac', 'javah', + 'latex', 'lex', 'm4', 'midl', 'msvs', + 'pdflatex', 'pdftex', 'Perforce', + 'RCS', 'rmic', 'SCCS', + # 'Subversion', + 'swig', + 'tar', 'tex', 'yacc', 'zip'], + env) + + tools = ([linker, c_compiler, cxx_compiler, + fortran_compiler, assembler, ar] + + other_tools) + + return filter(lambda x: x, tools) diff --git a/scons/scons-local-0.95/SCons/Tool/aixc++.py b/scons/scons-local-0.95/SCons/Tool/aixc++.py new file mode 100644 index 0000000..5cb6639 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/aixc++.py @@ -0,0 +1,53 @@ +"""SCons.Tool.aixc++ + +Tool-specific initialization for IBM xlC / Visual Age C++ compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" +__revision__ = "" + +import os.path + +import SCons.Platform.aix +import SCons.Script.SConscript + +cplusplus = __import__('c++', globals(), locals(), []) + +packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp'] + +def get_xlc(env): + xlc = env.get('CXX', 'xlC') + xlc_r = env.get('SHCXX', 'xlC_r') + return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) + +def smart_cxxflags(source, target, env, for_signature): + build_dir = SCons.Script.SConscript.GetBuildPath() + if build_dir: + return '-qtempinc=' + os.path.join(build_dir, 'tempinc') + return '' + +def generate(env): + """Add Builders and construction variables for xlC / Visual Age + suite to an Environment.""" + path, _cxx, _shcxx, version = get_xlc(env) + if path: + _cxx = os.path.join(path, _cxx) + _shcxx = os.path.join(path, _shcxx) + + cplusplus.generate(env) + + env['CXX'] = _cxx + env['SHCXX'] = _shcxx + env['CXXVERSION'] = version + env['SHOBJSUFFIX'] = '.pic.o' + +def exists(env): + path, _cxx, _shcxx, version = get_xlc(env) + if path and _cxx: + xlc = os.path.join(path, _cxx) + if os.path.exists(xlc): + return xlc + return None diff --git a/scons/scons-local-0.95/SCons/Tool/aixcc.py b/scons/scons-local-0.95/SCons/Tool/aixcc.py new file mode 100644 index 0000000..8ca578f --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/aixcc.py @@ -0,0 +1,68 @@ +"""SCons.Tool.aixcc + +Tool-specific initialization for IBM xlc / Visual Age C compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/aixcc.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Platform.aix + +import cc + +packages = ['vac.C', 'ibmcxx.cmp'] + +def get_xlc(env): + xlc = env.get('CC', 'xlc') + xlc_r = env.get('SHCC', 'xlc_r') + return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) + +def generate(env): + """Add Builders and construction variables for xlc / Visual Age + suite to an Environment.""" + path, _cc, _shcc, version = get_xlc(env) + if path: + _cc = os.path.join(path, _cc) + _shcc = os.path.join(path, _shcc) + + cc.generate(env) + + env['CC'] = _cc + env['SHCC'] = _shcc + env['CCVERSION'] = version + +def exists(env): + path, _cc, _shcc, version = get_xlc(env) + if path and _cc: + xlc = os.path.join(path, _cc) + if os.path.exists(xlc): + return xlc + return None diff --git a/scons/scons-local-0.95/SCons/Tool/aixf77.py b/scons/scons-local-0.95/SCons/Tool/aixf77.py new file mode 100644 index 0000000..9224165 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/aixf77.py @@ -0,0 +1,74 @@ +"""engine.SCons.Tool.aixf77 + +Tool-specific initialization for IBM Visual Age f77 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/aixf77.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Platform.aix + +import f77 + +# It would be good to look for the AIX F77 package the same way we're now +# looking for the C and C++ packages. This should be as easy as supplying +# the correct package names in the following list and uncommenting the +# SCons.Platform.aix_get_xlc() call the in the function below. +packages = [] + +def get_xlf77(env): + xlf77 = env.get('F77', 'xlf77') + xlf77_r = env.get('SHF77', 'xlf77_r') + #return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages) + return (None, xlf77, xlf77_r, None) + +def generate(env): + """ + Add Builders and construction variables for the Visual Age FORTRAN + compiler to an Environment. + """ + path, _f77, _shf77, version = get_xlf77(env) + if path: + _f77 = os.path.join(path, _f77) + _shf77 = os.path.join(path, _shf77) + + f77.generate(env) + + env['F77'] = _f77 + env['SHF77'] = _shf77 + +def exists(env): + path, _f77, _shf77, version = get_xlf77(env) + if path and _f77: + xlf77 = os.path.join(path, _f77) + if os.path.exists(xlf77): + return xlf77 + return None diff --git a/scons/scons-local-0.95/SCons/Tool/aixlink.py b/scons/scons-local-0.95/SCons/Tool/aixlink.py new file mode 100644 index 0000000..b8183f8 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/aixlink.py @@ -0,0 +1,70 @@ +"""SCons.Tool.aixlink + +Tool-specific initialization for the IBM Visual Age linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/aixlink.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path + +import SCons.Util + +import aixcc +import link + +cplusplus = __import__('c++', globals(), locals(), []) + +def smart_linkflags(source, target, env, for_signature): + if cplusplus.iscplusplus(source): + build_dir = env.subst('$BUILDDIR') + if build_dir: + return '-qtempinc=' + os.path.join(build_dir, 'tempinc') + return '' + +def generate(env): + """ + Add Builders and construction variables for Visual Age linker to + an Environment. + """ + link.generate(env) + + env['SMARTLINKFLAGS'] = smart_linkflags + env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') + env['SHLIBSUFFIX'] = '.a' + +def exists(env): + path, _cc, _shcc, version = aixcc.get_xlc(env) + if path and _cc: + xlc = os.path.join(path, _cc) + if os.path.exists(xlc): + return xlc + return None diff --git a/scons/scons-local-0.95/SCons/Tool/ar.py b/scons/scons-local-0.95/SCons/Tool/ar.py new file mode 100644 index 0000000..b7153c3 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/ar.py @@ -0,0 +1,56 @@ +"""SCons.Tool.ar + +Tool-specific initialization for ar (library archive). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/ar.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + arcom = '$AR $ARFLAGS $TARGET $SOURCES' + ranlib = 'ranlib' + if env.Detect(ranlib): + arcom = arcom + '\n$RANLIB $RANLIBFLAGS $TARGET' + + env['AR'] = 'ar' + env['ARFLAGS'] = SCons.Util.CLVar('r') + env['RANLIB'] = ranlib + env['RANLIBFLAGS'] = SCons.Util.CLVar('') + env['ARCOM'] = arcom + +def exists(env): + return env.Detect('ar') diff --git a/scons/scons-local-0.95/SCons/Tool/as.py b/scons/scons-local-0.95/SCons/Tool/as.py new file mode 100644 index 0000000..424ee2f --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/as.py @@ -0,0 +1,67 @@ +"""SCons.Tool.as + +Tool-specific initialization for as, the generic Posix assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/as.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +assemblers = ['as'] + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for as to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + + env['AS'] = env.Detect(assemblers) or 'as' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' + env['ASPPCOM'] = '$CC $ASFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + +def exists(env): + return env.Detect(assemblers) diff --git a/scons/scons-local-0.95/SCons/Tool/bcc32.py b/scons/scons-local-0.95/SCons/Tool/bcc32.py new file mode 100644 index 0000000..32eed3a --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/bcc32.py @@ -0,0 +1,76 @@ +"""SCons.Tool.bcc32 + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/bcc32.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import string + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def findIt(program, env): + # First search in the SCons path and then the OS path: + borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) + if borwin: + dir = os.path.dirname(borwin) + path = env['ENV'].get('PATH', []) + if not path: + path = [] + if SCons.Util.is_String(path): + path = string.split(path, os.pathsep) + env['ENV']['PATH'] = string.join([dir]+path, os.pathsep) + return borwin + +def generate(env): + findIt('bcc32', env) + """Add Builders and construction variables for bcc to an + Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + for suffix in ['.c', '.cpp']: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + env['CC'] = 'bcc32' + env['CCFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC -q $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['SHCCCOM'] = '$SHCC -WD $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.dll' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + env['CFILESUFFIX'] = '.cpp' + +def exists(env): + return findIt('bcc32', env) diff --git a/scons/scons-local-0.95/SCons/Tool/c++.py b/scons/scons-local-0.95/SCons/Tool/c++.py new file mode 100644 index 0000000..be27881 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/c++.py @@ -0,0 +1,87 @@ +"""SCons.Tool.c++ + +Tool-specific initialization for generic Posix C++ compilers. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/c++.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Tool +import SCons.Defaults +import SCons.Util + +compilers = ['CC', 'c++'] + +CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++'] +if SCons.Util.case_sensitive_suffixes('.c', '.C'): + CXXSuffixes.append('.C') + +def iscplusplus(source): + if not source: + # Source might be None for unusual cases like SConf. + return 0 + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext in CXXSuffixes: + return 1 + return 0 + +def generate(env): + """ + Add Builders and construction variables for Visual Age C++ compilers + to an Environment. + """ + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CXXAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) + + env['CXX'] = 'c++' + env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.os' + env['OBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + + env['CXXFILESUFFIX'] = '.cc' + +def exists(env): + return env.Detect(compilers) diff --git a/scons/scons-local-0.95/SCons/Tool/cc.py b/scons/scons-local-0.95/SCons/Tool/cc.py new file mode 100644 index 0000000..73027f8 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/cc.py @@ -0,0 +1,72 @@ +"""SCons.Tool.cc + +Tool-specific initialization for generic Posix C compilers. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/cc.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Tool +import SCons.Defaults +import SCons.Util + +CSuffixes = ['.c'] +if not SCons.Util.case_sensitive_suffixes('.c', '.C'): + CSuffixes.append('.C') + +def generate(env): + """ + Add Builders and construction variables for C compilers to an Environment. + """ + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + + env['CC'] = 'cc' + env['CCFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.os' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + + env['CFILESUFFIX'] = '.c' + +def exists(env): + return env.Detect('cc') diff --git a/scons/scons-local-0.95/SCons/Tool/default.py b/scons/scons-local-0.95/SCons/Tool/default.py new file mode 100644 index 0000000..e705d1c --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/default.py @@ -0,0 +1,44 @@ +"""SCons.Tool.default + +Initialization with a default tool list. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/default.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Tool + +def generate(env): + """Add default tools.""" + for t in SCons.Tool.tool_list(env['PLATFORM'], env): + SCons.Tool.Tool(t)(env) + +def exists(env): + return 1 diff --git a/scons/scons-local-0.95/SCons/Tool/dmd.py b/scons/scons-local-0.95/SCons/Tool/dmd.py new file mode 100644 index 0000000..1445fda --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/dmd.py @@ -0,0 +1,196 @@ +"""SCons.Tool.dmd + +Tool-specific initialization for the Digital Mars D compiler. +(http://digitalmars.com/d) + +Coded by Andy Friesen (andy@ikagames.com) +15 November 2003 + +There are a number of problems with this script at this point in time. +The one that irritates me the most is the win32 linker setup. The D +linker doesn't have a way to add lib paths on the commandline, as far +as I can see. You have to specify paths relative to the SConscript or +use absolute paths. To hack around it, add '#/blah'. This will link +blah.lib from the directory where SConstruct resides. + +Compiler variables: + DC - The name of the D compiler to use. Defaults to dmd. + DPATH - List of paths to search for import modules. + DVERSIONS - List of version tags to enable when compiling. + DDEBUG - List of debug tags to enable when compiling. + +Linker related variables: + LIBS - List of library files to link in. + DLINK - Name of the linker to use. Defaults to dmd. + DLINKFLAGS - List of linker flags. + +Lib tool variables: + DLIB - Name of the lib tool to use. Defaults to lib. + DLIBFLAGS - List of flags to pass to the lib tool. + LIBS - Same as for the linker. (libraries to pull into the .lib) +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/dmd.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import string + +import SCons.Tool +import SCons.Scanner.D +import SCons.Builder + +# Adapted from c++.py +def isD(source): + if not source: + return 0 + + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext == '.d': + return 1 + return 0 + +smart_link = {} + +smart_lib = {} + +def generate(env): + global smart_link + global smart_lib + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + static_obj.add_action('.d', '$DCOM') + shared_obj.add_action('.d', '$DCOM') + + env['DC'] = 'dmd' + env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' + env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs)} $)' + env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' + env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' + env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' + + env['DPATH'] = ['#/'] + env['DFLAGS'] = [] + env['DVERSIONS'] = [] + env['DDEBUG'] = [] + + # Add the path to the standard library. + # This is merely for the convenience of the dependency scanner. + dmd_path = env.WhereIs('dmd') + if dmd_path: + x = string.rindex(dmd_path, 'dmd') + phobosDir = dmd_path[:x] + '/../src/phobos' + if os.path.isdir(phobosDir): + env.Append(DPATH = [phobosDir]) + + env['DINCPREFIX'] = '-I' + env['DINCSUFFIX'] = '' + env['DVERPREFIX'] = '-version=' + env['DVERSUFFIX'] = '' + env['DDEBUGPREFIX'] = '-debug=' + env['DDEBUGSUFFIX'] = '' + env['DFLAGPREFIX'] = '-' + env['DFLAGSUFFIX'] = '' + env['DFILESUFFIX'] = '.d' + + # Need to use the Digital Mars linker/lib on windows. + # *nix can just use GNU link. + if env['PLATFORM'] == 'win32': + env['DLINK'] = '$DC' + env['DLINKCOM'] = '$DLINK -of$TARGET $SOURCES $DFLAGS $DLINKFLAGS $_DLINKLIBFLAGS' + env['DLIB'] = 'lib' + env['DLIBCOM'] = '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLINKLIBFLAGS' + + env['_DLINKLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs)} $)' + env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' + env['DLINKFLAGS'] = [] + env['DLIBLINKPREFIX'] = '' + env['DLIBLINKSUFFIX'] = '.lib' + env['DLIBFLAGPREFIX'] = '-' + env['DLIBFLAGSUFFIX'] = '' + env['DLINKFLAGPREFIX'] = '-' + env['DLINKFLAGSUFFIX'] = '' + + static_lib = SCons.Tool.createStaticLibBuilder(env) + + # Basically, we hijack the link and ar builders with our own. + # these builders check for the presence of D source, and swap out + # the system's defaults for the Digital Mars tools. If there's no D + # source, then we silently return the previous settings. + linkcom = env.get('LINKCOM') + try: + env['SMART_LINKCOM'] = smart_link[linkcom] + except KeyError: + def _smartLink(source, target, env, for_signature, + defaultLinker=linkcom): + if isD(source): + return '$DLINKCOM' + else: + return defaultLinker + env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink + + arcom = env.get('ARCOM') + try: + env['SMART_ARCOM'] = smart_lib[arcom] + except KeyError: + def _smartLib(source, target, env, for_signature, + defaultLib=arcom): + if isD(source): + return '$DLIBCOM' + else: + return defaultLib + env['SMART_ARCOM'] = smart_lib[arcom] = _smartLib + + # It is worth noting that the final space in these strings is + # absolutely pivotal. SCons sees these as actions and not generators + # if it is not there. (very bad) + env['ARCOM'] = '$SMART_ARCOM ' + env['LINKCOM'] = '$SMART_LINKCOM ' + else: # assuming linux + linkcom = env.get('LINKCOM') + try: + env['SMART_LINKCOM'] = smart_link[linkcom] + except KeyError: + def _smartLink(source, target, env, for_signature, + defaultLinker=linkcom): + if isD(source): + try: + libs = env['LIBS'] + except KeyError: + libs = [] + if 'phobos' not in libs: + env.Append(LIBS = ['phobos']) + if 'pthread' not in libs: + env.Append(LIBS = ['pthread']) + return defaultLinker + env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink + + env['LINKCOM'] = '$SMART_LINKCOM ' + +def exists(env): + return env.Detect('dmd') diff --git a/scons/scons-local-0.95/SCons/Tool/dvipdf.py b/scons/scons-local-0.95/SCons/Tool/dvipdf.py new file mode 100644 index 0000000..388a313 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/dvipdf.py @@ -0,0 +1,56 @@ +"""SCons.Tool.dvipdf + +Tool-specific initialization for dvipdf. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/dvipdf.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Defaults +import SCons.Util + +def generate(env): + """Add Builders and construction variables for dvipdf to an Environment.""" + try: + bld = env['BUILDERS']['PDF'] + except KeyError: + bld = SCons.Defaults.PDF() + env['BUILDERS']['PDF'] = bld + bld.add_action('.dvi', '$PDFCOM') + + env['DVIPDF'] = 'dvipdf' + env['DVIPDFFLAGS'] = SCons.Util.CLVar('') + env['DVIPDFCOM'] = '$DVIPDF $DVIPDFFLAGS $SOURCES $TARGET' + + # Deprecated synonym. + env['PDFCOM'] = ['$DVIPDFCOM'] + +def exists(env): + return env.Detect('dvipdf') diff --git a/scons/scons-local-0.95/SCons/Tool/dvips.py b/scons/scons-local-0.95/SCons/Tool/dvips.py new file mode 100644 index 0000000..1baf3a1 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/dvips.py @@ -0,0 +1,55 @@ +"""SCons.Tool.dvips + +Tool-specific initialization for dvips. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/dvips.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Action +import SCons.Defaults +import SCons.Util + +PostScript = SCons.Builder.Builder(action = '$PSCOM', + prefix = '$PSPREFIX', + suffix = '$PSSUFFIX', + src_suffix = '.dvi', + src_builder = 'DVI') + +def generate(env): + """Add Builders and construction variables for dvips to an Environment.""" + env['BUILDERS']['PostScript'] = PostScript + + env['DVIPS'] = 'dvips' + env['DVIPSFLAGS'] = SCons.Util.CLVar('') + env['PSCOM'] = '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES' + +def exists(env): + return env.Detect('dvips') diff --git a/scons/scons-local-0.95/SCons/Tool/f77.py b/scons/scons-local-0.95/SCons/Tool/f77.py new file mode 100644 index 0000000..74b0706 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/f77.py @@ -0,0 +1,73 @@ +"""engine.SCons.Tool.f77 + +Tool-specific initialization for the generic Posix f77 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/f77.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +compilers = ['f77'] + +F77Suffixes = ['.f', '.for', '.FOR'] +F77PPSuffixes = ['.fpp', '.FPP'] +if SCons.Util.case_sensitive_suffixes('.f', '.F'): + F77PPSuffixes.append('.F') +else: + F77Suffixes.append('.F') + +def generate(env): + """Add Builders and construction variables for f77 to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in F77Suffixes: + static_obj.add_action(suffix, SCons.Defaults.F77Action) + shared_obj.add_action(suffix, SCons.Defaults.ShF77Action) + + for suffix in F77PPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.F77PPAction) + shared_obj.add_action(suffix, SCons.Defaults.ShF77PPAction) + + env['F77'] = env.Detect(compilers) or 'f77' + env['F77FLAGS'] = SCons.Util.CLVar('') + env['F77COM'] = '$F77 $F77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES' + env['F77PPCOM'] = '$F77 $F77FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES' + env['SHF77'] = '$F77' + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') + env['SHF77COM'] = '$SHF77 $SHF77FLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES' + env['SHF77PPCOM'] = '$SHF77 $SHF77FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS -c -o $TARGET $SOURCES' + +def exists(env): + return env.Detect(compilers) diff --git a/scons/scons-local-0.95/SCons/Tool/g++.py b/scons/scons-local-0.95/SCons/Tool/g++.py new file mode 100644 index 0000000..92ac964 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/g++.py @@ -0,0 +1,87 @@ +"""SCons.Tool.g++ + +Tool-specific initialization for g++. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/g++.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path +import re + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +compilers = ['g++'] + +def generate(env): + """Add Builders and construction variables for g++ to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + cplusplus.generate(env) + + env['CXX'] = env.Detect(compilers) + + # platform specific settings + if env['PLATFORM'] == 'cygwin': + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + elif env['PLATFORM'] == 'aix': + # Original line from Christian Engel added -DPIC: + #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -DPIC -mminimal-toc') + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + elif env['PLATFORM'] == 'hpux': + # Original line from Christian Engel added -DPIC: + #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC') + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC') + env['SHOBJSUFFIX'] = '.pic.o' + elif env['PLATFORM'] == 'sunos': + # Original line from Christian Engel added -DPIC: + #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC') + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC') + env['SHOBJSUFFIX'] = '.pic.o' + else: + # Original line from Christian Engel added -DPIC: + #env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC -DPIC') + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -fPIC') + # determine compiler version + if env['CXX']: + line = os.popen(env['CXX'] + ' --version').readline() + match = re.search(r'[0-9]+(\.[0-9]+)+', line) + if match: + env['CXXVERSION'] = match.group(0) + + +def exists(env): + return env.Detect(compilers) diff --git a/scons/scons-local-0.95/SCons/Tool/g77.py b/scons/scons-local-0.95/SCons/Tool/g77.py new file mode 100644 index 0000000..0425515 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/g77.py @@ -0,0 +1,47 @@ +"""engine.SCons.Tool.g77 + +Tool-specific initialization for g77. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/g77.py 0.95.D001 2004/03/08 07:28:28 knight" + +import f77 + +compilers = ['g77', 'f77'] + +def generate(env): + """Add Builders and construction variables for g77 to an Environment.""" + f77.generate(env) + + env['F77'] = env.Detect(compilers) or 'g77' + +def exists(env): + return env.Detect(compilers) diff --git a/scons/scons-local-0.95/SCons/Tool/gas.py b/scons/scons-local-0.95/SCons/Tool/gas.py new file mode 100644 index 0000000..2ed2de2 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/gas.py @@ -0,0 +1,47 @@ +"""SCons.Tool.gas + +Tool-specific initialization for as, the Gnu assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/gas.py 0.95.D001 2004/03/08 07:28:28 knight" + +import as + +assemblers = ['as', 'gas'] + +def generate(env): + """Add Builders and construction variables for as to an Environment.""" + as.generate(env) + + env['AS'] = env.Detect(assemblers) or 'as' + +def exists(env): + return env.Detect(assemblers) diff --git a/scons/scons-local-0.95/SCons/Tool/gcc.py b/scons/scons-local-0.95/SCons/Tool/gcc.py new file mode 100644 index 0000000..015d367 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/gcc.py @@ -0,0 +1,53 @@ +"""SCons.Tool.gcc + +Tool-specific initialization for gcc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/gcc.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Util + +import cc + +compilers = ['gcc', 'cc'] + +def generate(env): + """Add Builders and construction variables for gcc to an Environment.""" + cc.generate(env) + + env['CC'] = env.Detect(compilers) or 'gcc' + if env['PLATFORM'] == 'cygwin': + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + else: + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') + +def exists(env): + return env.Detect(compilers) diff --git a/scons/scons-local-0.95/SCons/Tool/gnulink.py b/scons/scons-local-0.95/SCons/Tool/gnulink.py new file mode 100644 index 0000000..3aa91e1 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/gnulink.py @@ -0,0 +1,50 @@ +"""SCons.Tool.gnulink + +Tool-specific initialization for the gnu linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/gnulink.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Util + +import link + +linkers = ['g++', 'gcc'] + +def generate(env): + """Add Builders and construction variables for gnulink to an Environment.""" + link.generate(env) + + if env['PLATFORM'] == 'hpux': + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared -fPIC') + +def exists(env): + return env.Detect(linkers) diff --git a/scons/scons-local-0.95/SCons/Tool/gs.py b/scons/scons-local-0.95/SCons/Tool/gs.py new file mode 100644 index 0000000..982f453 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/gs.py @@ -0,0 +1,70 @@ +"""SCons.Tool.gs + +Tool-specific initialization for Ghostscript. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/gs.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Defaults +import SCons.Platform +import SCons.Util + +# Ghostscript goes by different names on different platforms... +platform = SCons.Platform.platform_default() + +if platform == 'os2': + gs = 'gsos2' +elif platform == 'win32': + gs = 'gswin32c' +else: + gs = 'gs' + +def generate(env): + """Add Builders and construction variables for Ghostscript to an + Environment.""" + try: + bld = env['BUILDERS']['PDF'] + except KeyError: + bld = SCons.Defaults.PDF() + env['BUILDERS']['PDF'] = bld + + bld.add_action('.ps', '$GSCOM') + + env['GS'] = gs + env['GSFLAGS'] = SCons.Util.CLVar('-dNOPAUSE -dBATCH -sDEVICE=pdfwrite') + env['GSCOM'] = '$GS $GSFLAGS -sOutputFile=$TARGET $SOURCES' + + +def exists(env): + if env.has_key('PS2PDF'): + return env.Detect(env['PS2PDF']) + else: + return env.Detect(gs) or SCons.Util.WhereIs(gs) diff --git a/scons/scons-local-0.95/SCons/Tool/hpc++.py b/scons/scons-local-0.95/SCons/Tool/hpc++.py new file mode 100644 index 0000000..7d62f2f --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/hpc++.py @@ -0,0 +1,76 @@ +"""SCons.Tool.hpc++ + +Tool-specific initialization for c++ on HP/UX. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/hpc++.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path +import string + +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +acc = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except OSError: + dirs = [] + +for dir in dirs: + cc = '/opt/' + dir + '/bin/aCC' + if os.path.exists(cc): + acc = cc + break + + +def generate(env): + """Add Builders and construction variables for g++ to an Environment.""" + cplusplus.generate(env) + + if acc: + env['CXX'] = acc or 'aCC' + # determine version of aCC + line = os.popen(acc + ' -V 2>&1').readline().rstrip() + if string.find(line, 'aCC: HP ANSI C++') == 0: + env['CXXVERSION'] = string.split(line)[-1] + + if env['PLATFORM'] == 'cygwin': + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + else: + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') + +def exists(env): + return acc diff --git a/scons/scons-local-0.95/SCons/Tool/hpcc.py b/scons/scons-local-0.95/SCons/Tool/hpcc.py new file mode 100644 index 0000000..53723b0 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/hpcc.py @@ -0,0 +1,44 @@ +"""SCons.Tool.hpcc + +Tool-specific initialization for HP aCC and cc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/hpcc.py 0.95.D001 2004/03/08 07:28:28 knight" + +import cc + +def generate(env): + """Add Builders and construction variables for aCC & cc to an Environment.""" + cc.generate(env) + + env['CXX'] = 'aCC' + +def exists(env): + return env.Detect('aCC') diff --git a/scons/scons-local-0.95/SCons/Tool/hplink.py b/scons/scons-local-0.95/SCons/Tool/hplink.py new file mode 100644 index 0000000..748aa35 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/hplink.py @@ -0,0 +1,69 @@ +"""SCons.Tool.hplink + +Tool-specific initialization for the HP linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/hplink.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path + +import SCons.Util + +import link + +ccLinker = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except OSError: + dirs = [] + +for dir in dirs: + linker = '/opt/' + dir + '/bin/aCC' + if os.path.exists(linker): + ccLinker = linker + break + +def generate(env): + """ + Add Builders and construction variables for Visual Age linker to + an Environment. + """ + link.generate(env) + + env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,+s -Wl,+vnocompatwarnings') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -b') + env['SHLIBSUFFIX'] = '.sl' + +def exists(env): + return ccLinker diff --git a/scons/scons-local-0.95/SCons/Tool/icc.py b/scons/scons-local-0.95/SCons/Tool/icc.py new file mode 100644 index 0000000..8acfad1 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/icc.py @@ -0,0 +1,55 @@ +"""engine.SCons.Tool.icc + +Tool-specific initialization for the OS/2 icc compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/icc.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Util + +import cc + +def generate(env): + """Add Builders and construction variables for the OS/2 to an Environment.""" + cc.generate(env) + + env['CC'] = 'icc' + env['CCCOM'] = '$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' + env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' + env['CPPDEFPREFIX'] = '/D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '/I' + env['INCSUFFIX'] = '' + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cc' + +def exists(env): + return env.Detect('icc') diff --git a/scons/scons-local-0.95/SCons/Tool/icl.py b/scons/scons-local-0.95/SCons/Tool/icl.py new file mode 100644 index 0000000..95026a6 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/icl.py @@ -0,0 +1,112 @@ +"""engine.SCons.Tool.icl + +Tool-specific initialization for the Intel C/C++ compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/icl.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path +import string + +import SCons.Tool.msvc +import SCons.Util + +# Find Intel compiler: +# Could enumerate subkeys here to be more flexible. +def get_intel_compiler_top(version): + """ + Return the main path to the top-level dir of the Intel compiler, + using the given version or latest if 0. + The compiler will be in /Bin/icl.exe, + the include dir is /Include, etc. + """ + + if version == 0: + version = "7.0" # XXX: should scan for latest + + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError, "No Windows registry module was found" + + K = ('Software\\Intel\\' + + 'Intel(R) C/C++ Compiler for 32-bit apps, Version ' + version) + # Note: v5 had slightly different key: + # HKCU\Software\Intel\Intel C/C++ Compiler for 32-bit apps, Version 5.0 + # Note no (R). + try: + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_CURRENT_USER, K) + except SCons.Util.RegError: + return None + + try: + # On my machine, this returns: + # c:\Program Files\Intel\Compiler70 + top = SCons.Util.RegQueryValueEx(k, "Directory")[0] + except SCons.Util.RegError: + raise SCons.Errors.InternalError, "%s was not found in the registry."%K + + if os.path.exists(os.path.join(top, "ia32")): + top = os.path.join(top, "ia32") + + if not os.path.exists(os.path.join(top, "Bin", "icl.exe")): + raise SCons.Errors.InternalError, "Can't find Intel compiler in %s"%top + + return top + + +def generate(env): + """Add Builders and construction variables for icl to an Environment.""" + SCons.Tool.msvc.generate(env) + + try: + icltop = get_intel_compiler_top(0) + except (SCons.Util.RegError, SCons.Errors.InternalError): + icltop = None + + if icltop: + env.PrependENVPath('INCLUDE', os.path.join(icltop, 'Include')) + env.PrependENVPath('LIB', os.path.join(icltop, 'Lib')) + env.PrependENVPath('PATH', os.path.join(icltop, 'Bin')) + + env['CC'] = 'icl' + env['CXX'] = 'icl' + env['LINK'] = 'xilink' + + env['ENV']['INTEL_LICENSE_FILE'] = r'C:\Program Files\Common Files\Intel\Licenses' + +def exists(env): + try: + top = get_intel_compiler_top(0) + except (SCons.Util.RegError, SCons.Errors.InternalError): + top = None + + if not top: + return env.Detect('icl') + return top is not None diff --git a/scons/scons-local-0.95/SCons/Tool/ifl.py b/scons/scons-local-0.95/SCons/Tool/ifl.py new file mode 100644 index 0000000..ded94b2 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/ifl.py @@ -0,0 +1,54 @@ +"""engine.SCons.Tool.ifl + +Tool-specific initialization for the Intel Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/ifl.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Util + +import f77 + +def generate(env): + """Add Builders and construction variables for ifl to an Environment.""" + f77.generate(env) + + env['F77'] = 'ifl' + env['F77FLAGS'] = SCons.Util.CLVar('') + env['F77COM'] = '$F77 $F77FLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET' + env['F77PPCOM'] = '$F77 $F77FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET' + env['SHF77'] = '$F77' + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') + env['SHF77COM'] = '$SHF77 $SHF77FLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET' + env['SHF77PPCOM'] = '$SHF77 $SHF77FLAGS $CPPFLAGS $_CPPDEFFLAGS $_F77INCFLAGS /c $SOURCES /Fo$TARGET' + +def exists(env): + return env.Detect('ifl') diff --git a/scons/scons-local-0.95/SCons/Tool/ilink.py b/scons/scons-local-0.95/SCons/Tool/ilink.py new file mode 100644 index 0000000..5830f75 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/ilink.py @@ -0,0 +1,53 @@ +"""SCons.Tool.ilink + +Tool-specific initialization for the OS/2 ilink linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/ilink.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ilink to an Environment.""" + SCons.Tool.createProgBuilder(env) + + env['LINK'] = 'ilink' + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '$LINK $LINKFLAGS /O:$TARGET $SOURCES $( $_LIBDIRFLAGS $) $_LIBFLAGS' + env['LIBDIRPREFIX']='/LIBPATH:' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + +def exists(env): + return env.Detect('ilink') diff --git a/scons/scons-local-0.95/SCons/Tool/ilink32.py b/scons/scons-local-0.95/SCons/Tool/ilink32.py new file mode 100644 index 0000000..b033353 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/ilink32.py @@ -0,0 +1,52 @@ +"""SCons.Tool.ilink32 + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/ilink32.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Tool +import SCons.Tool.bcc32 +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ilink to an + Environment.""" + SCons.Tool.createProgBuilder(env) + + env['LINK'] = '$CC' + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '$LINK -q $LINKFLAGS $SOURCES $LIBS' + env['LIBDIRPREFIX']='' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + +def exists(env): + # Uses bcc32 to do linking as it generally knows where the standard + # LIBS are and set up the linking correctly + return SCons.Tool.bcc32.findIt('bcc32', env) diff --git a/scons/scons-local-0.95/SCons/Tool/jar.py b/scons/scons-local-0.95/SCons/Tool/jar.py new file mode 100644 index 0000000..b971e6e --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/jar.py @@ -0,0 +1,105 @@ +"""SCons.Tool.jar + +Tool-specific initialization for jar. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/jar.py 0.95.D001 2004/03/08 07:28:28 knight" + +import glob +import os.path + +import SCons.Builder +import SCons.Util + +def jarSources(target, source, env, for_signature): + """Only include sources that are not a manifest file.""" + ret = [] + for src in source: + contents = src.get_contents() + if contents[:16] != "Manifest-Version": + if env.has_key('JARCHDIR'): + # If we are changing the dir with -C, then sources should + # be relative to that directory. + ret.append(src.get_path(src.fs.Dir(env['JARCHDIR']))) + else: + ret.append(src) + return ret + +def jarManifest(target, source, env, for_signature): + """Look in sources for a manifest file, if any.""" + for src in source: + contents = src.get_contents() + if contents[:16] == "Manifest-Version": + return src + return '' + +def jarFlags(target, source, env, for_signature): + """If we have a manifest, make sure that the 'm' + flag is specified.""" + jarflags = env.subst('$JARFLAGS') + for src in source: + contents = src.get_contents() + if contents[:16] == "Manifest-Version": + if not 'm' in jarflags: + return jarflags + 'm' + break + return jarflags + +def jarChdir(target, source, env, for_signature): + """If we have an Environment variable by the name + of JARCHDIR, then supply the command line option + '-C ' to Jar.""" + if env.has_key('JARCHDIR'): + return [ '-C', '$JARCHDIR' ] + return '' + +JarBuilder = SCons.Builder.Builder(action = '$JARCOM', + source_factory = SCons.Node.FS.default_fs.Entry, + suffix = '$JARSUFFIX') + +def generate(env): + """Add Builders and construction variables for jar to an Environment.""" + try: + bld = env['BUILDERS']['Jar'] + except KeyError: + env['BUILDERS']['Jar'] = JarBuilder + + env['JAR'] = 'jar' + env['JARFLAGS'] = SCons.Util.CLVar('cf') + env['_JARFLAGS'] = jarFlags + env['_JARMANIFEST'] = jarManifest + env['_JARSOURCES'] = jarSources + env['_JARCHDIR'] = jarChdir + env['JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARCHDIR $_JARSOURCES' + env['JARSUFFIX'] = '.jar' + +def exists(env): + return env.Detect('jar') diff --git a/scons/scons-local-0.95/SCons/Tool/javac.py b/scons/scons-local-0.95/SCons/Tool/javac.py new file mode 100644 index 0000000..115dd1b --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/javac.py @@ -0,0 +1,110 @@ +"""SCons.Tool.javac + +Tool-specific initialization for javac. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/javac.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import re +import string + +import SCons.Builder +from SCons.Node.FS import _my_normcase +from SCons.Tool.JavaCommon import parse_java_file +import SCons.Util + +def classname(path): + """Turn a string (path name) into a Java class name.""" + return string.replace(os.path.normpath(path), os.sep, '.') + +def emit_java_classes(target, source, env): + """Create and return lists of source java files + and their corresponding target class files. + """ + java_suffix = env.get('JAVASUFFIX', '.java') + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + + slist = [] + js = _my_normcase(java_suffix) + def visit(arg, dirname, names, js=js, dirnode=source[0].rdir()): + java_files = filter(lambda n, js=js: + _my_normcase(n[-len(js):]) == js, + names) + mydir = dirnode.Dir(dirname) + java_paths = map(lambda f, d=mydir: d.File(f), java_files) + arg.extend(java_paths) + os.path.walk(source[0].rdir().get_abspath(), visit, slist) + + tlist = [] + for file in slist: + pkg_dir, classes = parse_java_file(file.get_abspath()) + if pkg_dir: + for c in classes: + t = target[0].Dir(pkg_dir).File(c+class_suffix) + t.attributes.java_classdir = target[0] + t.attributes.java_classname = classname(pkg_dir + os.sep + c) + tlist.append(t) + elif classes: + for c in classes: + t = target[0].File(c+class_suffix) + t.attributes.java_classdir = target[0] + t.attributes.java_classname = classname(c) + tlist.append(t) + else: + # This is an odd end case: no package and no classes. + # Just do our best based on the source file name. + base = str(file)[:-len(java_suffix)] + t = target[0].File(base + class_suffix) + t.attributes.java_classdir = target[0] + t.attributes.java_classname = classname(base) + tlist.append(t) + + return tlist, slist + +JavaBuilder = SCons.Builder.Builder(action = '$JAVACCOM', + emitter = emit_java_classes, + target_factory = SCons.Node.FS.default_fs.Dir, + source_factory = SCons.Node.FS.default_fs.Dir) + +def generate(env): + """Add Builders and construction variables for javac to an Environment.""" + env['BUILDERS']['Java'] = JavaBuilder + + env['JAVAC'] = 'javac' + env['JAVACFLAGS'] = SCons.Util.CLVar('') + env['JAVACCOM'] = '$JAVAC $JAVACFLAGS -d ${TARGET.attributes.java_classdir} -sourcepath ${SOURCE.dir.rdir()} $SOURCES' + env['JAVACLASSSUFFIX'] = '.class' + env['JAVASUFFIX'] = '.java' + +def exists(env): + return env.Detect('javac') diff --git a/scons/scons-local-0.95/SCons/Tool/javah.py b/scons/scons-local-0.95/SCons/Tool/javah.py new file mode 100644 index 0000000..def3eb1 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/javah.py @@ -0,0 +1,127 @@ +"""SCons.Tool.javah + +Tool-specific initialization for javah. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/javah.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path +import re +import string + +import SCons.Builder +import SCons.Node.FS +import SCons.Tool.javac +import SCons.Util + +def emit_java_headers(target, source, env): + """Create and return lists of Java stub header files that will + be created from a set of class files. + """ + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + classdir = env.get('JAVACLASSDIR') + + if not classdir: + try: + s = source[0] + except IndexError: + classdir = '.' + else: + try: + classdir = s.attributes.java_classdir + except AttributeError: + classdir = '.' + classdir = SCons.Node.FS.default_fs.Dir(classdir).rdir() + if str(classdir) == '.': + c_ = None + else: + c_ = str(classdir) + os.sep + + slist = [] + for src in source: + try: + classname = src.attributes.java_classname + except AttributeError: + classname = str(src) + if c_ and classname[:len(c_)] == c_: + classname = classname[len(c_):] + if class_suffix and classname[-len(class_suffix):] == class_suffix: + classname = classname[:-len(class_suffix)] + classname = SCons.Tool.javac.classname(classname) + s = src.rfile() + s.attributes.java_classdir = classdir + s.attributes.java_classname = classname + slist.append(s) + + if target[0].__class__ is SCons.Node.FS.File: + tlist = target + else: + if not isinstance(target[0], SCons.Node.FS.Dir): + target[0].__class__ = SCons.Node.FS.Dir + target[0]._morph() + File = SCons.Node.FS.default_fs.File + tlist = [] + for s in source: + fname = string.replace(s.attributes.java_classname, '.', '_') + '.h' + t = target[0].File(fname) + t.attributes.java_lookupdir = target[0] + tlist.append(t) + + return tlist, source + +def JavaHOutFlagGenerator(target, source, env, for_signature): + try: + t = target[0] + except AttributeError, TypeError: + t = target + try: + return '-d ' + str(t.attributes.java_lookupdir) + except AttributeError: + return '-o ' + str(t) + +JavaHBuilder = SCons.Builder.Builder(action = '$JAVAHCOM', + emitter = emit_java_headers, + src_suffix = '$JAVACLASSSUFFIX', + target_factory = SCons.Node.FS.default_fs.Entry, + source_factory = SCons.Node.FS.default_fs.File) + +def generate(env): + """Add Builders and construction variables for javah to an Environment.""" + env['BUILDERS']['JavaH'] = JavaHBuilder + + env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator + env['JAVAH'] = 'javah' + env['JAVAHFLAGS'] = SCons.Util.CLVar('') + env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}' + env['JAVACLASSSUFFIX'] = '.class' + +def exists(env): + return env.Detect('javah') diff --git a/scons/scons-local-0.95/SCons/Tool/latex.py b/scons/scons-local-0.95/SCons/Tool/latex.py new file mode 100644 index 0000000..ce6e754 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/latex.py @@ -0,0 +1,59 @@ +"""SCons.Tool.latex + +Tool-specific initialization for LaTeX. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/latex.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Action +import SCons.Defaults +import SCons.Util + +LaTeXAction = SCons.Action.Action('$LATEXCOM') + +def generate(env): + """Add Builders and construction variables for LaTeX to an Environment.""" + + try: + bld = env['BUILDERS']['DVI'] + except KeyError: + bld = SCons.Defaults.DVI() + env['BUILDERS']['DVI'] = bld + + bld.add_action('.ltx', LaTeXAction) + bld.add_action('.latex', LaTeXAction) + + env['LATEX'] = 'latex' + env['LATEXFLAGS'] = SCons.Util.CLVar('') + env['LATEXCOM'] = '$LATEX $LATEXFLAGS $SOURCES' + +def exists(env): + return env.Detect('latex') diff --git a/scons/scons-local-0.95/SCons/Tool/lex.py b/scons/scons-local-0.95/SCons/Tool/lex.py new file mode 100644 index 0000000..757ce82 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/lex.py @@ -0,0 +1,52 @@ +"""SCons.Tool.lex + +Tool-specific initialization for lex. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/lex.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for lex to an Environment.""" + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + c_file.add_action('.l', '$LEXCOM') + cxx_file.add_action('.ll', '$LEXCOM') + + env['LEX'] = env.Detect('flex') or 'lex' + env['LEXFLAGS'] = SCons.Util.CLVar('') + env['LEXCOM'] = '$LEX $LEXFLAGS -t $SOURCES > $TARGET' + +def exists(env): + return env.Detect(['flex', 'lex']) diff --git a/scons/scons-local-0.95/SCons/Tool/link.py b/scons/scons-local-0.95/SCons/Tool/link.py new file mode 100644 index 0000000..3543301 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/link.py @@ -0,0 +1,75 @@ +"""SCons.Tool.link + +Tool-specific initialization for the generic Posix linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/link.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +def smart_link(source, target, env, for_signature): + if cplusplus.iscplusplus(source): + return '$CXX' + return '$CC' + +def generate(env): + """Add Builders and construction variables for gnulink to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['SHLIBEMITTER']= None + env['SMARTLINK'] = smart_link + env['LINK'] = "$SMARTLINK" + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBDIRPREFIX']='-L' + env['LIBDIRSUFFIX']='' + env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIX, LIBSUFFIX, __env__)}' + env['LIBLINKPREFIX']='-l' + env['LIBLINKSUFFIX']='' + + if env['PLATFORM'] == 'hpux': + env['SHLIBSUFFIX'] = '.sl' + elif env['PLATFORM'] == 'aix': + env['SHLIBSUFFIX'] = '.a' + + +def exists(env): + # This module isn't really a Tool on its own, it's common logic for + # other linkers. + return None diff --git a/scons/scons-local-0.95/SCons/Tool/linkloc.py b/scons/scons-local-0.95/SCons/Tool/linkloc.py new file mode 100644 index 0000000..f382b95 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/linkloc.py @@ -0,0 +1,104 @@ +"""SCons.Tool.linkloc + +Tool specification for the LinkLoc linker for the Phar Lap ETS embedded +operating system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/linkloc.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path +import re + +import SCons.Action +import SCons.Defaults +import SCons.Errors +import SCons.Tool +import SCons.Util + +from SCons.Tool.msvc import get_msvc_paths +from SCons.Tool.PharLapCommon import addPharLapPaths + +_re_linker_command = re.compile(r'(\s)@\s*([^\s]+)') + +def repl_linker_command(m): + # Replaces any linker command file directives (e.g. "@foo.lnk") with + # the actual contents of the file. + try: + f=open(m.group(2), "r") + return m.group(1) + f.read() + except IOError: + # the linker should return an error if it can't + # find the linker command file so we will remain quiet. + # However, we will replace the @ with a # so we will not continue + # to find it with recursive substitution + return m.group(1) + '#' + m.group(2) + +class LinklocGenerator: + def __init__(self, cmdline): + self.cmdline = cmdline + + def __call__(self, env, target, source, for_signature): + if for_signature: + # Expand the contents of any linker command files recursively + subs = 1 + strsub = env.subst(self.cmdline) + while subs: + strsub, subs = _re_linker_command.subn(repl_linker_command, strsub) + return strsub + else: + return "${TEMPFILE('" + self.cmdline + "')}" + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SUBST_CMD_FILE'] = LinklocGenerator + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') + env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -dll $TARGET $SOURCES")}' + env['SHLIBEMITTER']= None + env['LINK'] = "linkloc" + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -exe $TARGET $SOURCES")}' + env['LIBDIRPREFIX']='-libpath ' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='-lib ' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + include_path, lib_path, exe_path = get_msvc_paths() + env['ENV']['LIB'] = lib_path + env['ENV']['PATH'] = exe_path + + addPharLapPaths(env) + +def exists(env): + return env.Detect('linkloc') diff --git a/scons/scons-local-0.95/SCons/Tool/m4.py b/scons/scons-local-0.95/SCons/Tool/m4.py new file mode 100644 index 0000000..563ba99 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/m4.py @@ -0,0 +1,55 @@ +"""SCons.Tool.m4 + +Tool-specific initialization for m4. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/m4.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Builder +import SCons.Util + +def generate(env): + """Add Builders and construction variables for m4 to an Environment.""" + bld = SCons.Builder.Builder(action = '$M4COM', src_suffix = '.m4') + + env['BUILDERS']['M4'] = bld + + # .m4 files might include other files, and it would be pretty hard + # to write a scanner for it, so let's just cd to the dir of the m4 + # file and run from there. + # The src_suffix setup is like so: file.c.m4 -> file.c, + # file.cpp.m4 -> file.cpp etc. + env['M4'] = 'm4' + env['M4FLAGS'] = SCons.Util.CLVar('-E') + env['M4COM'] = 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ${TARGET.abspath}' + +def exists(env): + return env.Detect('m4') diff --git a/scons/scons-local-0.95/SCons/Tool/masm.py b/scons/scons-local-0.95/SCons/Tool/masm.py new file mode 100644 index 0000000..51974ee --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/masm.py @@ -0,0 +1,68 @@ +"""SCons.Tool.masm + +Tool-specific initialization for the Microsoft Assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/masm.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for masm to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + shared_obj.add_action(suffix, SCons.Defaults.ASAction) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) + + env['AS'] = 'ml' + env['ASFLAGS'] = SCons.Util.CLVar('/nologo') + env['ASCOM'] = '$AS $ASFLAGS /c /Fo$TARGET $SOURCES' + env['ASPPCOM'] = '$CC $ASFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c /Fo$TARGET $SOURCES' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + +def exists(env): + return env.Detect('ml') diff --git a/scons/scons-local-0.95/SCons/Tool/midl.py b/scons/scons-local-0.95/SCons/Tool/midl.py new file mode 100644 index 0000000..e3cb755 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/midl.py @@ -0,0 +1,72 @@ +"""SCons.Tool.midl + +Tool-specific initialization for midl (Microsoft IDL compiler). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/midl.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Defaults +import SCons.Scanner.IDL +import SCons.Util + +def midl_emitter(target, source, env): + """Produces a list of outputs from the MIDL compiler""" + base, ext = SCons.Util.splitext(str(target[0])) + tlb = target[0] + incl = base + '.h' + interface = base + '_i.c' + proxy = base + '_p.c' + dlldata = base + '_data.c' + + t = [tlb, incl, interface, proxy, dlldata] + + return (t,source) + +idl_scanner = SCons.Scanner.IDL.IDLScan() + +midl_builder = SCons.Builder.Builder(action='$MIDLCOM', + src_suffix = '.idl', + suffix='.tlb', + emitter = midl_emitter, + scanner = idl_scanner) + +def generate(env): + """Add Builders and construction variables for midl to an Environment.""" + + env['MIDL'] = 'MIDL.EXE' + env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo') + env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL' + env['BUILDERS']['TypeLibrary'] = midl_builder + +def exists(env): + return env.Detect('midl') diff --git a/scons/scons-local-0.95/SCons/Tool/mingw.py b/scons/scons-local-0.95/SCons/Tool/mingw.py new file mode 100644 index 0000000..9ee51b0 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/mingw.py @@ -0,0 +1,151 @@ +"""SCons.Tool.gcc + +Tool-specific initialization for MinGW (http://www.mingw.org/) + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/mingw.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os +import os.path +import string + +import SCons.Tool +import SCons.Util + +# This is what we search for to find mingw: +key_program = 'mingw32-gcc' + +def find(env): + # First search in the SCons path and then the OS path: + return env.WhereIs(key_program) or SCons.Util.WhereIs(key_program) + +def shlib_generator(target, source, env, for_signature): + cmd = SCons.Util.CLVar('$SHLINK', '$SHLINKFLAGS') + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) + + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) + + return [cmd] + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX')) + + # Append a def file target if there isn't already a def file target + # or a def file source. There is no option to disable def file + # target emitting, because I can't figure out why someone would ever + # want to turn it off. + def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX') + if not def_source and not def_target: + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')) + + return (target, source) + + +shlib_action = SCons.Action.CommandGenerator(shlib_generator) + +res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.o') + +def generate(env): + mingw = find(env) + if mingw: + dir = os.path.dirname(mingw) + + # The mingw bin directory must be added to the path: + path = env['ENV'].get('PATH', []) + if not path: + path = [] + if SCons.Util.is_String(path): + path = string.split(path, os.pathsep) + + env['ENV']['PATH'] = string.join([dir] + path, os.pathsep) + + + # Most of mingw is the same as gcc and friends... + gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas'] + for tool in gnu_tools: + SCons.Tool.Tool(tool)(env) + + #... but a few things differ: + env['CC'] = 'gcc' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXX'] = 'g++' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = shlib_action + env['SHLIBEMITTER']= shlib_emitter + env['LINK'] = 'g++' + env['AS'] = 'as' + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = 'windres' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCINCFLAGS'] = SCons.Util.CLVar('$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs)} $)') + env['RCINCPREFIX'] = '--include-dir ' + env['RCINCSUFFIX'] = '' + env['RCCOM'] = '$RC $RCINCFLAGS $RCFLAGS -i $SOURCE -o $TARGET' + CScan = env.get_scanner('.c') + if CScan: + CScan.add_skey('.rc') + env['BUILDERS']['RES'] = res_builder + + # Some setting from the platform also have to be overridden: + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + +def exists(env): + return find(env) diff --git a/scons/scons-local-0.95/SCons/Tool/mslib.py b/scons/scons-local-0.95/SCons/Tool/mslib.py new file mode 100644 index 0000000..8577a43 --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/mslib.py @@ -0,0 +1,74 @@ +"""SCons.Tool.mslib + +Tool-specific initialization for lib (MicroSoft library archiver). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/mslib.py 0.95.D001 2004/03/08 07:28:28 knight" + +import SCons.Defaults +import SCons.Tool +import SCons.Tool.msvs +import SCons.Tool.msvc +import SCons.Util + +def generate(env): + """Add Builders and construction variables for lib to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + try: + version = SCons.Tool.msvs.get_default_visualstudio_version(env) + + if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(version) + else: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(version) + + # since other tools can set this, we just make sure that the + # relevant stuff from MSVS is in there somewhere. + env.PrependENVPath('PATH', exe_path) + except (SCons.Util.RegError, SCons.Errors.InternalError): + pass + + env['AR'] = 'lib' + env['ARFLAGS'] = SCons.Util.CLVar('/nologo') + env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" + +def exists(env): + try: + v = SCons.Tool.msvs.get_visualstudio_versions() + except (SCons.Util.RegError, SCons.Errors.InternalError): + pass + + if not v: + return env.Detect('lib') + else: + # there's at least one version of MSVS installed. + return 1 diff --git a/scons/scons-local-0.95/SCons/Tool/mslink.py b/scons/scons-local-0.95/SCons/Tool/mslink.py new file mode 100644 index 0000000..65770cc --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/mslink.py @@ -0,0 +1,192 @@ +"""SCons.Tool.mslink + +Tool-specific initialization for the Microsoft linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/mslink.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path + +import SCons.Action +import SCons.Defaults +import SCons.Errors +import SCons.Platform.win32 +import SCons.Tool +import SCons.Tool.msvc +import SCons.Tool.msvs +import SCons.Util + +def pdbGenerator(env, target, source, for_signature): + if target and env.has_key('PDB') and env['PDB']: + return ['/PDB:%s'%target[0].File(env['PDB']).get_string(for_signature), + '/DEBUG'] + +def win32ShlinkTargets(target, source, env, for_signature): + listCmd = [] + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: listCmd.append("/out:%s"%dll.get_string(for_signature)) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature)) + + return listCmd + +def win32ShlinkSources(target, source, env, for_signature): + listCmd = [] + + deffile = env.FindIxes(source, "WIN32DEFPREFIX", "WIN32DEFSUFFIX") + for src in source: + if src == deffile: + # Treat this source as a .def file. + listCmd.append("/def:%s" % src.get_string(for_signature)) + else: + # Just treat it as a generic source file. + listCmd.append(src) + return listCmd + +def win32LibEmitter(target, source, env): + SCons.Tool.msvc.validate_vars(env) + + dll = env.FindIxes(target, "SHLIBPREFIX", "SHLIBSUFFIX") + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX") + + if env.get("WIN32_INSERT_DEF", 0) and \ + not env.FindIxes(source, "WIN32DEFPREFIX", "WIN32DEFSUFFIX"): + + # append a def file to the list of sources + source.append(env.ReplaceIxes(dll, + "SHLIBPREFIX", "SHLIBSUFFIX", + "WIN32DEFPREFIX", "WIN32DEFSUFFIX")) + + if env.has_key('PDB') and env['PDB']: + env.SideEffect(env['PDB'], target) + env.Precious(env['PDB']) + + if not no_import_lib and \ + not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + "SHLIBPREFIX", "SHLIBSUFFIX", + "LIBPREFIX", "LIBSUFFIX")) + # and .exp file is created if there are exports from a DLL + target.append(env.ReplaceIxes(dll, + "SHLIBPREFIX", "SHLIBSUFFIX", + "WIN32EXPPREFIX", "WIN32EXPSUFFIX")) + + return (target, source) + +def prog_emitter(target, source, env): + SCons.Tool.msvc.validate_vars(env) + + if env.has_key('PDB') and env['PDB']: + env.SideEffect(env['PDB'], target) + env.Precious(env['PDB']) + + return (target,source) + +def RegServerFunc(target, source, env): + if env.has_key('register') and env['register']: + ret = regServerAction([target[0]], [source[0]], env) + if ret: + raise SCons.Errors.UserError, "Unable to register %s" % target[0] + else: + print "Registered %s sucessfully" % target[0] + return ret + return 0 + +regServerAction = SCons.Action.Action("$REGSVRCOM") +regServerCheck = SCons.Action.Action(RegServerFunc, None) +shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}') +compositeLinkAction = shlibLinkAction + regServerCheck + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll') + env['_SHLINK_TARGETS'] = win32ShlinkTargets + env['_SHLINK_SOURCES'] = win32ShlinkSources + env['SHLINKCOM'] = compositeLinkAction + env['SHLIBEMITTER']= win32LibEmitter + env['LINK'] = 'link' + env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') + env['_PDB'] = pdbGenerator + env['LINKCOM'] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $SOURCES")}' + env['PROGEMITTER'] = prog_emitter + env['LIBDIRPREFIX']='/LIBPATH:' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['WIN32_INSERT_DEF'] = 0 + + env['WIN32EXPPREFIX'] = '' + env['WIN32EXPSUFFIX'] = '.exp' + + env['REGSVRACTION'] = regServerCheck + env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32') + env['REGSVRFLAGS'] = '/s ' + env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS $TARGET' + + try: + version = SCons.Tool.msvs.get_default_visualstudio_version(env) + + if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(version) + else: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(version) + + # since other tools can set these, we just make sure that the + # relevant stuff from MSVS is in there somewhere. + env.PrependENVPath('INCLUDE', include_path) + env.PrependENVPath('LIB', lib_path) + env.PrependENVPath('PATH', exe_path) + except (SCons.Util.RegError, SCons.Errors.InternalError): + pass + +def exists(env): + try: + v = SCons.Tool.msvs.get_visualstudio_versions() + except (SCons.Util.RegError, SCons.Errors.InternalError): + pass + + if not v: + return env.Detect('link') + else: + # there's at least one version of MSVS installed. + return 1 diff --git a/scons/scons-local-0.95/SCons/Tool/msvc.py b/scons/scons-local-0.95/SCons/Tool/msvc.py new file mode 100644 index 0000000..8c6b22e --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/msvc.py @@ -0,0 +1,491 @@ +"""engine.SCons.Tool.msvc + +Tool-specific initialization for Microsoft Visual C/C++. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/msvc.py 0.95.D001 2004/03/08 07:28:28 knight" + +import os.path +import re +import string +import types + +import SCons.Action +import SCons.Builder +import SCons.Errors +import SCons.Platform.win32 +import SCons.Tool +import SCons.Tool.msvs +import SCons.Util +import SCons.Warnings + +CSuffixes = ['.c', '.C'] +CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] + +def _parse_msvc7_overrides(version): + """ Parse any overridden defaults for MSVS directory locations in MSVS .NET. """ + + # First, we get the shell folder for this user: + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError, "No Windows registry module was found" + + comps = "" + try: + (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, + r'Software\Microsoft\Windows\CurrentVersion' +\ + r'\Explorer\Shell Folders\Local AppData') + except SCons.Util.RegError: + raise SCons.Errors.InternalError, "The Local AppData directory was not found in the registry." + + comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VCComponents.dat' + dirs = {} + + if os.path.exists(comps): + # now we parse the directories from this file, if it exists. + # We only look for entries after: [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories], + # since this file could contain a number of things... + f = open(comps,'r') + line = f.readline() + found = 0 + while line: + line.strip() + if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]') >= 0: + found = 1 + elif line == '' or line[:1] == '[': + found = 0 + elif found == 1: + kv = line.split('=', 1) + if len(kv) == 2: + (key, val) = kv + key = key.replace(' Dirs','') + dirs[key.upper()] = val + line = f.readline() + f.close() + else: + # since the file didn't exist, we have only the defaults in + # the registry to work with. + try: + K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version + K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories' + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K) + i = 0 + while 1: + try: + (key,val,t) = SCons.Util.RegEnumValue(k,i) + key = key.replace(' Dirs','') + dirs[key.upper()] = val + i = i + 1 + except SCons.Util.RegError: + break + except SCons.Util.RegError: + # if we got here, then we didn't find the registry entries: + raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry." + return dirs + +def _get_msvc7_path(path, version, platform): + """ + Get Visual Studio directories from version 7 (MSVS .NET) + (it has a different registry structure than versions before it) + """ + # first, look for a customization of the default values in the + # registry: These are sometimes stored in the Local Settings area + # for Visual Studio, in a file, so we have to parse it. + dirs = _parse_msvc7_overrides(version) + + if dirs.has_key(path): + p = dirs[path] + else: + raise SCons.Errors.InternalError, "Unable to retrieve the %s path from MS VC++."%path + + # collect some useful information for later expansions... + paths = SCons.Tool.msvs.get_msvs_install_dirs(version) + + # expand the directory path variables that we support. If there + # is a variable we don't support, then replace that entry with + # "---Unknown Location VSInstallDir---" or something similar, to clue + # people in that we didn't find something, and so env expansion doesn't + # do weird things with the $(xxx)'s + s = re.compile('\$\(([a-zA-Z0-9_]+?)\)') + + def repl(match, paths=paths): + key = string.upper(match.group(1)) + if paths.has_key(key): + return paths[key] + else: + return '---Unknown Location %s---' % match.group() + + rv = [] + for entry in p.split(os.pathsep): + entry = s.sub(repl,entry) + rv.append(entry) + + return string.join(rv,os.pathsep) + +def get_msvc_path (path, version, platform='x86'): + """ + Get a list of visualstudio directories (include, lib or path). Return + a string delimited by ';'. An exception will be raised if unable to + access the registry or appropriate registry keys not found. + """ + + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError, "No Windows registry module was found" + + # normalize the case for comparisons (since the registry is case + # insensitive) + path = string.upper(path) + + if path=='LIB': + path= 'LIBRARY' + + if float(version) >= 7.0: + return _get_msvc7_path(path, version, platform) + + path = string.upper(path + ' Dirs') + K = ('Software\\Microsoft\\Devstudio\\%s\\' + + 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \ + (version,platform) + for base in (SCons.Util.HKEY_CURRENT_USER, + SCons.Util.HKEY_LOCAL_MACHINE): + try: + k = SCons.Util.RegOpenKeyEx(base,K) + i = 0 + while 1: + try: + (p,v,t) = SCons.Util.RegEnumValue(k,i) + if string.upper(p) == path: + return v + i = i + 1 + except SCons.Util.RegError: + break + except SCons.Util.RegError: + pass + + # if we got here, then we didn't find the registry entries: + raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path + +def _get_msvc6_default_paths(version, use_mfc_dirs): + """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those + three environment variables that should be set in order to execute + the MSVC 6.0 tools properly, if the information wasn't available + from the registry.""" + MVSdir = None + paths = {} + exe_path = '' + lib_path = '' + include_path = '' + try: + paths = SCons.Tool.msvs.get_msvs_install_dirs(version) + MVSdir = paths['VSINSTALLDIR'] + except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError): + if os.environ.has_key('MSDEVDIR'): + MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..')) + else: + MVSdir = r'C:\Program Files\Microsoft Visual Studio' + if MVSdir: + if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): + MVSVCdir = paths['VCINSTALLDIR'] + else: + MVSVCdir = os.path.join(MVSdir,'VC98') + + MVSCommondir = r'%s\Common' % MVSdir + if use_mfc_dirs: + mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir) + mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir + else: + mfc_include_ = '' + mfc_lib_ = '' + include_path = r'%s%s\include' % (mfc_include_, MVSVCdir) + lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir) + + if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT": + osdir = 'WINNT' + else: + osdir = 'WIN95' + + exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir, MVSCommondir, MVSVCdir) + return (include_path, lib_path, exe_path) + +def _get_msvc7_default_paths(version, use_mfc_dirs): + """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those + three environment variables that should be set in order to execute + the MSVC .NET tools properly, if the information wasn't available + from the registry.""" + + MVSdir = None + paths = {} + exe_path = '' + lib_path = '' + include_path = '' + try: + paths = SCons.Tool.msvs.get_msvs_install_dirs(version) + MVSdir = paths['VSINSTALLDIR'] + except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError): + if os.environ.has_key('VSCOMNTOOLS'): + MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..')) + else: + # last resort -- default install location + MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET' + + if MVSdir: + if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): + MVSVCdir = paths['VCINSTALLDIR'] + else: + MVSVCdir = os.path.join(MVSdir,'Vc7') + + MVSCommondir = r'%s\Common7' % MVSdir + if use_mfc_dirs: + mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir + mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir + else: + mfc_include_ = '' + mfc_lib_ = '' + include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir) + lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir) + exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir ) + + if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'): + include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR'] + lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR'] + exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR'] + + if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'): + exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION']) + + return (include_path, lib_path, exe_path) + +def get_msvc_paths(version=None, use_mfc_dirs=0): + """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values + of those three environment variables that should be set + in order to execute the MSVC tools properly.""" + exe_path = '' + lib_path = '' + include_path = '' + + if not version: + versions = SCons.Tool.msvs.get_visualstudio_versions() + if versions: + version = versions[0] #use highest version by default + else: + version = '6.0' + + # Some of the configured directories only + # appear if the user changes them from the default. + # Therefore, we'll see if we can get the path to the MSDev + # base installation from the registry and deduce the default + # directories. + if float(version) >= 7.0: + defpaths = _get_msvc7_default_paths(version, use_mfc_dirs) + else: + defpaths = _get_msvc6_default_paths(version, use_mfc_dirs) + + try: + include_path = get_msvc_path("include", version) + except (SCons.Util.RegError, SCons.Errors.InternalError): + include_path = defpaths[0] + + try: + lib_path = get_msvc_path("lib", version) + except (SCons.Util.RegError, SCons.Errors.InternalError): + lib_path = defpaths[1] + + try: + exe_path = get_msvc_path("path", version) + except (SCons.Util.RegError, SCons.Errors.InternalError): + exe_path = defpaths[2] + + return (include_path, lib_path, exe_path) + +def get_msvc_default_paths(version = None): + """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those + three environment variables that should be set in order to execute + the MSVC tools properly. This will only return the default + locations for the tools, not the values used by MSVS in their + directory setup area. This can help avoid problems with different + developers having different settings, and should allow the tools + to run in most cases.""" + + if not version and not SCons.Util.can_read_reg: + version = '6.0' + + try: + if not version: + version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version + except KeyboardInterrupt: + raise + except: + pass + + if float(version) >= 7.0: + return _get_msvc7_default_paths(version) + else: + return _get_msvc6_default_paths(version) + +def validate_vars(env): + """Validate the PDB, PCH, and PCHSTOP construction variables.""" + if env.has_key('PCH') and env['PCH']: + if not env.has_key('PCHSTOP'): + raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined." + if not SCons.Util.is_String(env['PCHSTOP']): + raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP'] + +def pch_emitter(target, source, env): + """Sets up the PDB dependencies for a pch file, and adds the object + file target.""" + + validate_vars(env) + + pch = None + obj = None + + for t in target: + if SCons.Util.splitext(str(t))[1] == '.pch': + pch = t + if SCons.Util.splitext(str(t))[1] == '.obj': + obj = t + + if not obj: + obj = SCons.Util.splitext(str(pch))[0]+'.obj' + + target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work + + if env.has_key('PDB') and env['PDB']: + env.SideEffect(env['PDB'], target) + env.Precious(env['PDB']) + + return (target, source) + +def object_emitter(target, source, env, parent_emitter): + """Sets up the PDB and PCH dependencies for an object file.""" + + validate_vars(env) + + parent_emitter(target, source, env) + + if env.has_key('PDB') and env['PDB']: + env.SideEffect(env['PDB'], target) + env.Precious(env['PDB']) + + if env.has_key('PCH') and env['PCH']: + env.Depends(target, env['PCH']) + + return (target, source) + +def static_object_emitter(target, source, env): + return object_emitter(target, source, env, + SCons.Defaults.StaticObjectEmitter) + +def shared_object_emitter(target, source, env): + return object_emitter(target, source, env, + SCons.Defaults.SharedObjectEmitter) + +pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter) +res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res') + +def generate(env): + """Add Builders and construction variables for MSVC++ to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CXXAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) + + env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Zi /Fd%s"%File(PDB)) or ""}']) + env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}']) + env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS' + env['CC'] = 'cl' + env['CCFLAGS'] = SCons.Util.CLVar('/nologo') + env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS' + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS' + env['CXX'] = '$CC' + env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)') + env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS' + env['CPPDEFPREFIX'] = '/D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '/I' + env['INCSUFFIX'] = '' + env['OBJEMITTER'] = static_object_emitter + env['SHOBJEMITTER'] = shared_object_emitter + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = 'rc' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES' + CScan = env.get_scanner('.c') + if CScan: + CScan.add_skey('.rc') + env['BUILDERS']['RES'] = res_builder + + try: + version = SCons.Tool.msvs.get_default_visualstudio_version(env) + + if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: + include_path, lib_path, exe_path = get_msvc_default_paths(version) + else: + # By default, add the MFC directories, because this is what + # we've been doing for a long time. We may change this. + use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 1) + include_path, lib_path, exe_path = get_msvc_paths(version, use_mfc_dirs) + + # since other tools can set these, we just make sure that the + # relevant stuff from MSVS is in there somewhere. + env.PrependENVPath('INCLUDE', include_path) + env.PrependENVPath('LIB', lib_path) + env.PrependENVPath('PATH', exe_path) + except (SCons.Util.RegError, SCons.Errors.InternalError): + pass + + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cc' + + env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS' + env['BUILDERS']['PCH'] = pch_builder + +def exists(env): + try: + v = SCons.Tool.msvs.get_visualstudio_versions() + except (SCons.Util.RegError, SCons.Errors.InternalError): + pass + + if not v: + return env.Detect('cl') + else: + # there's at least one version of MSVS installed. + return 1 diff --git a/scons/scons-local-0.95/SCons/Tool/msvs.py b/scons/scons-local-0.95/SCons/Tool/msvs.py new file mode 100644 index 0000000..4c2320b --- /dev/null +++ b/scons/scons-local-0.95/SCons/Tool/msvs.py @@ -0,0 +1,1119 @@ +"""SCons.Tool.msvs + +Tool-specific initialization for Microsoft Visual Studio project files. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004 Steven Knight +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "/home/scons/scons/branch.0/baseline/src/engine/SCons/Tool/msvs.py 0.95.D001 2004/03/08 07:28:28 knight" + +import base64 +import md5 +import os.path +import pickle +import re +import string +import sys +import types + +import SCons.Builder +import SCons.Node.FS +import SCons.Platform.win32 +import SCons.Script.SConscript +import SCons.Util +import SCons.Warnings + +############################################################################## +# Below here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def _hexdigest(s): + """Return a string as a string of hex characters. + """ + # NOTE: This routine is a method in the Python 2.0 interface + # of the native md5 module, but we want SCons to operate all + # the way back to at least Python 1.5.2, which doesn't have it. + h = string.hexdigits + r = '' + for c in s: + i = ord(c) + r = r + h[(i >> 4) & 0xF] + h[i & 0xF] + return r + +def _generateGUID(slnfile, name): + """This generates a dummy GUID for the sln file to use. It is + based on the MD5 signatures of the sln filename plus the name of + the project. It basically just needs to be unique, and not + change with each invocation.""" + solution = _hexdigest(md5.new(str(slnfile)+str(name)).digest()).upper() + # convert most of the signature to GUID form (discard the rest) + solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:28] + "}" + return solution + +# This is how we re-invoke SCons from inside MSVS Project files. +# The problem is that we might have been invoked as either scons.bat +# or scons.py. If we were invoked directly as scons.py, then we could +# use sys.argv[0] to find the SCons "executable," but that doesn't work +# if we were invoked as scons.bat, which uses "python -c" to execute +# things and ends up with "-c" as sys.argv[0]. Consequently, we have +# the MSVS Project file invoke SCons the same way that scons.bat does, +# which works regardless of how we were invoked. +exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-0.95'), join(sys.prefix, 'scons-0.95'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" +exec_script_main_xml = string.replace(exec_script_main, "'", "'") + +# The string for the Python executable we tell the Project file to use +# is either sys.executable or, if an external PYTHON_ROOT environment +# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to +# pluck the actual executable name from sys.executable). +try: + python_root = os.environ['PYTHON_ROOT'] +except KeyError: + python_executable = sys.executable +else: + python_executable = os.path.join('$(PYTHON_ROOT)', + os.path.split(sys.executable)[1]) + +class Config: + pass + +class _DSPGenerator: + """ Base class for DSP generators """ + def __init__(self, dspfile, source, env): + if type(dspfile) == types.StringType: + self.dspfile = os.path.abspath(dspfile) + else: + self.dspfile = dspfile.get_abspath() + + try: + self.conspath = source[0].attributes.sconstruct.get_abspath() + except KeyError: + raise SCons.Errors.InternalError, \ + "Unable to determine where the SConstruct is" + + self.config = Config() + if env.has_key('variant'): + self.config.variant = env['variant'].capitalize() + else: + raise SCons.Errors.InternalError, \ + "You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVSProject." + + if env.has_key('buildtarget'): + if type(env['buildtarget']) == types.StringType: + self.config.buildtarget = os.path.abspath(env['buildtarget']) + elif type(env['buildtarget']) == types.ListType: + self.config.buildtarget = env['buildtarget'][0].get_abspath() + else: + self.config.buildtarget = env['buildtarget'].get_abspath() + else: + raise SCons.Errors.InternalError, \ + "You must specify a target 'buildtarget' file argument (such as the target" +\ + " executable) to create an MSVSProject." + + self.config.outdir = os.path.dirname(self.config.buildtarget) + + if type(source[0]) == types.StringType: + self.source = os.path.abspath(source[0]) + else: + self.source = source[0].get_abspath() + + self.env = env + + if self.env.has_key('name'): + self.name = self.env['name'] + else: + self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) + + print "Adding '" + self.name + ' - ' + self.config.variant + "' to Visual Studio Project '" + str(dspfile) + "'" + + sourcenames = [ + ' Source Files', + 'Header Files', + 'Local Headers', + 'Resource Files', + 'Other Files'] + + srcargs = [ + 'srcs', + 'incs', + 'localincs', + 'resources', + 'misc'] + + self.sources = {} + for n in sourcenames: + self.sources[n] = [] + + self.configs = {} + + if os.path.exists(self.dspfile): + self.Parse() + + for t in zip(sourcenames,srcargs): + if self.env.has_key(t[1]): + if type(self.env[t[1]]) == types.ListType: + for i in self.env[t[1]]: + if not i in self.sources[t[0]]: + self.sources[t[0]].append(i) + else: + if not self.env[t[1]] in self.sources[t[0]]: + self.sources[t[0]].append(self.env[t[1]]) + + for n in sourcenames: + self.sources[n].sort() + + self.configs[self.config.variant] = self.config + + def Build(self): + pass + +class _GenerateV6DSP(_DSPGenerator): + """Generates a Project file for MSVS 6.0""" + + def PrintHeader(self): + name = self.name + # pick a default config + confkeys = self.configs.keys() + confkeys.sort() + + self.file.write('# Microsoft Developer Studio Project File - Name="%s" - Package Owner=<4>\n' + '# Microsoft Developer Studio Generated Build File, Format Version 6.00\n' + '# ** DO NOT EDIT **\n\n' + '# TARGTYPE "Win32 (x86) External Target" 0x0106\n\n' + 'CFG=%s - Win32 %s\n' + '!MESSAGE This is not a valid makefile. To build this project using NMAKE,\n' + '!MESSAGE use the Export Makefile command and run\n' + '!MESSAGE \n' + '!MESSAGE NMAKE /f "%s.mak".\n' + '!MESSAGE \n' + '!MESSAGE You can specify a configuration when running NMAKE\n' + '!MESSAGE by defining the macro CFG on the command line. For example:\n' + '!MESSAGE \n' + '!MESSAGE NMAKE /f "%s.mak" CFG="%s - Win32 %s"\n' + '!MESSAGE \n' + '!MESSAGE Possible choices for configuration are:\n' + '!MESSAGE \n' % (name,name,confkeys[0],name,name,name,confkeys[0])) + + for kind in confkeys: + self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) + + self.file.write('!MESSAGE \n\n') + + def PrintProject(self): + name = self.name + self.file.write('# Begin Project\n' + '# PROP AllowPerConfigDependencies 0\n' + '# PROP Scc_ProjName ""\n' + '# PROP Scc_LocalPath ""\n\n') + + first = 1 + confkeys = self.configs.keys() + confkeys.sort() + for kind in confkeys: + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + if first == 1: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + first = 0 + else: + self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + + # have to write this twice, once with the BASE settings, and once without + for base in ("BASE ",""): + self.file.write('# PROP %sUse_MFC 0\n' + '# PROP %sUse_Debug_Libraries ' % (base, base)) + if kind.lower().find('debug') < 0: + self.file.write('0\n') + else: + self.file.write('1\n') + self.file.write('# PROP %sOutput_Dir "%s"\n' + '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) + (d,c) = os.path.split(str(self.conspath)) + cmd = '%s -c "%s" -C %s -f %s %s' % (python_executable, + exec_script_main, + d, c, buildtarget) + self.file.write('# PROP %sCmd_Line "%s"\n' + '# PROP %sRebuild_Opt "-c && %s"\n' + '# PROP %sTarget_File "%s"\n' + '# PROP %sBsc_Name ""\n' + '# PROP %sTarget_Dir ""\n'\ + %(base,cmd,base,cmd,base,buildtarget,base,base)) + + self.file.write('\n!ENDIF\n\n' + '# Begin Target\n\n') + for kind in confkeys: + self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) + self.file.write('\n') + first = 0 + for kind in confkeys: + if first == 0: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + first = 1 + else: + self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + self.file.write('!ENDIF \n\n') + self.PrintSourceFiles() + self.file.write('# End Target\n' + '# End Project\n') + + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + pdata = pickle.dumps(self.sources,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def PrintSourceFiles(self): + categories = {' Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', + 'Header Files': 'h|hpp|hxx|hm|inl', + 'Local Headers': 'h|hpp|hxx|hm|inl', + 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', + 'Other Files': ''} + + cats = categories.keys() + cats.sort() + for kind in cats: + if not self.sources[kind]: + continue # skip empty groups + + self.file.write('# Begin Group "' + kind + '"\n\n') + typelist = categories[kind].replace('|',';') + self.file.write('# PROP Default_Filter "' + typelist + '"\n') + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + file + '"\n' + '# End Source File\n') + self.file.write('# End Group\n') + + # add the Conscript file outside of the groups + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + str(self.source) + '"\n' + '# End Source File\n') + + def Parse(self): + try: + dspfile = open(self.dspfile,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find("# End Project") > -1: + break + line = dspfile.readline() + + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.configs.update(data) + + data = None + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + # it has a "# " in front of it, so we strip that. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.sources.update(data) + + def Build(self): + try: + self.file = open(self.dspfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail) + else: + self.PrintHeader() + self.PrintProject() + self.file.close() + +class _GenerateV7DSP(_DSPGenerator): + """Generates a Project file for MSVS .NET""" + + def __init__(self, dspfile, source, env): + _DSPGenerator.__init__(self, dspfile, source, env) + self.version = float(env['MSVS_VERSION']) + self.versionstr = '7.00' + if self.version >= 7.1: + self.versionstr = '7.10' + + def PrintHeader(self): + self.file.write('\n' + '\n' + ' \n' + ' \n' + ' \n' % (self.versionstr, self.name)) + + def PrintProject(self): + + + self.file.write(' \n') + + first = 1 + confkeys = self.configs.keys() + confkeys.sort() + for kind in confkeys: + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + + (d,c) = os.path.split(str(self.conspath)) + cmd = '%s -c "%s" -C %s -f %s %s' % (python_executable, + exec_script_main_xml, + d, c, buildtarget) + + cleancmd = '%s -c "%s" -C %s -f %s -c %s' % (python_executable, + exec_script_main_xml, + d, c, buildtarget) + + self.file.write(' \n' + ' \n' + ' \n' % (kind.capitalize(),outdir,outdir,\ + cmd,cleancmd,cmd,buildtarget)) + + self.file.write(' \n') + if self.version >= 7.1: + self.file.write(' \n') + self.file.write(' \n') + + self.PrintSourceFiles() + + self.file.write('\n') + + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write('\n') + + def PrintSourceFiles(self): + categories = {' Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', + 'Header Files': 'h;hpp;hxx;hm;inl', + 'Local Headers': 'h;hpp;hxx;hm;inl', + 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', + 'Other Files': ''} + + self.file.write(' \n') + + cats = categories.keys() + cats.sort() + for kind in cats: + if not self.sources[kind]: + continue # skip empty groups + + self.file.write(' \n' % (kind, categories[kind])) + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write(' \n' + ' \n' % file) + + self.file.write(' \n') + + # add the Conscript file outside of the groups + self.file.write(' \n' + ' \n' + ' \n' + ' \n' + ' \n' % str(self.source)) + + def Parse(self): + try: + dspfile = open(self.dspfile,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find(' + // + + AttrDict attrs; + attrs["id"] = storer.CreateID(this); + attrs["dir"] = relativePath; + attrs["fileindex"] = fmt("%d", mFileIndex); + + storer.StoreBeginNode("ManagedFileContext", attrs); + + std::set::const_iterator i; + for(i = filesToStore.begin(); i != filesToStore.end(); i++) + (*i)->Store(storer); + + storer.StoreEndNode("ManagedFileContext"); +} + +ManagedFileContext::ManagedFileContext(Loader& loader): + mRefCount(0), + mReleased(false) +{ + mContexts.push_back(this); + + Loader::Token tok = loader.GetNextToken(); + + ClientAssert(tok.name == "ManagedFileContext" && tok.type == Loader::Token::beginNode, + "Attempted to load node " + tok.name + " with ManagedFileContext constructor"); + + // TODO: paste CWD to "dir" if dir is not absolute + mDir = tok.attrs["dir"]; + mFileIndex = strtol(tok.attrs["fileindex"].c_str(), NULL, 0); + + // Clients of this context will need to refer to it by id + loader.RegisterObj(tok.attrs["id"], this); + + while(1) { + tok = loader.PeekNextToken(); + + if(tok.name == "ManagedFileContext" && tok.type == Loader::Token::endNode) + { + // Gobble the closing token + tok = loader.GetNextToken(); + break; + } + else if(tok.name == "SeqDataFileBlock" && tok.type == Loader::Token::beginNode) + { + // + SeqDataFileBlock *newBlock = new SeqDataFileBlock(loader, mDir); + + // Loaded blocks should start out locked so that they don't disappear unless + // unlocked. + newBlock->mLocked = true; + + // Loaded blocks should start out with a reference count of 0, because the + // reference count will be incremented once Sequences start referencing it + newBlock->mRefCount = 0; + + mFiles.insert(newBlock); + } + else + { + ClientAssert(false, "ManagedFileContext read unrecognized node " + tok.name); + } + } +} + + +// +// Private functions +// + +std::string +ManagedFileContext::GenerateFileName(std::string extension) +{ + // we construct a full path to make sure that we are not generating + // a filename that already exists. However, we return a filename relative + // to the given directory + + // for now, just code a single-directory solution like we've used for a while. + // later we can revise to be a deep directory structure + char fileName[1000]; + do { + snprintf(fileName, 1000, "b%05d.%s", mFileIndex++, extension.c_str()); + } while( Platform::FileExists(fileName) ); + + return fileName; +} + +// +// SeqBlockContext methods +// + +SeqBlock* +ManagedFileContext::NewSeqBlock(Buffer buf) +{ + std::string fileName = GenerateFileName(SeqDataFileBlock::GetExtension()); + std::string fullPathName = mDir + Platform::DirSeparator + fileName; + SeqDataFileBlock *newBlock = new SeqDataFileBlock(fileName, fullPathName, buf); + + AddFileToContext(newBlock); + + return newBlock; +} + +SeqBlock *ManagedFileContext::GetSeqBlockRef(SeqBlock *block) +{ + ManagedFile *thisBlock = dynamic_cast(block); + + if(thisBlock == NULL) + { + // we are copying from a SeqBlock that isn't a file. + + // For a SeqSilentBlock always make a copy. There is no disk file and they + // are incredibly small in memory + SeqSilentBlock *silentBlock = dynamic_cast(block); + if(silentBlock) + return new SeqSilentBlock(*silentBlock); + else + { + Buffer buffer = block->Get(); + std::string fileName = GenerateFileName(SeqDataFileBlock::GetExtension()); + std::string fullPathName = mDir + Platform::DirSeparator + fileName; + SeqDataFileBlock *newRef = new SeqDataFileBlock(fileName, fullPathName, buffer); + AddFileToContext(newRef); + return newRef; + } + } + else + { + ManagedFile *newRef = ManagedFileContext::GetRef(thisBlock); + SeqBlock *newSeqBlockRef = dynamic_cast(newRef); + + // this isn't going to be false unless the derived SeqDataFileBlock::Copy() screws + // up and constructs the wrong object. + // assert(newSeqBlockRef) + return newSeqBlockRef; + } +} + +SeqBlock *ManagedFileContext::GetSeqBlockRef(Loader& loader, std::string id) +{ + Storable *obj = loader.GetObj(id); + + ManagedFile *file = dynamic_cast(obj); + InternalAssert(file && mFiles.count(file) == 1, + "Attempted to load an object by id that isn't part of this context"); + + file->mRefCount++; +#ifdef DEBUG_REFCOUNTING + printf("RefCount of file %s incremented in GetSeqBlockRef()\n", file->mFullPathName.GetFullPath().c_str()); +#endif + + SeqBlock* block = dynamic_cast(file); + InternalAssert(block, "Attempted to load object as a SeqBlock that isn't a SeqBlock"); + + return block; +} + +void ManagedFileContext::ReleaseSeqBlockRef(SeqBlock *block) +{ + ManagedFile *thisBlock = dynamic_cast(block); + + ClientAssert(thisBlock, + "Attempted to release a reference to a block that doesn't " + "belong to this context"); + + ManagedFileContext::ReleaseRef(thisBlock); +} + +int ManagedFileContext::GetNumSeqBlocks() +{ + return mFiles.size(); +} + +void ManagedFileContext::AddFileToContext(ManagedFile *file) +{ + mFiles.insert(file); +} + +// static +void ManagedFileContext::LoadBackgroundRequests() +{ + for(unsigned int c = 0; c < mContexts.size(); c++) + { + std::set::const_iterator file; + for(file = mContexts[c]->mFiles.begin(); file != mContexts[c]->mFiles.end(); file++) + { + std::vector caches = (*file)->GetCacheObjects(); + for(unsigned int i = 0; i < caches.size(); i++) + { + if(caches[i]->GUIReadRequestCount > 0 && !caches[i]->dataIsLoaded) + { + caches[i]->Load(); + caches[i]->dataIsLoaded = true; + printf("loaded block\n"); + } + + if(caches[i]->GUIReadRequestCount == 0 && caches[i]->dataIsLoaded) + { + caches[i]->deletePending = true;; + + if(caches[i]->GUIReadRequestCount != 0) + continue; + + caches[i]->dataIsLoaded = false; + caches[i]->Unload(); + + caches[i]->deletePending = false; + printf("UNloaded block\n"); + } + } + } + } +} + +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/ManagedFileContext.h b/src/ManagedFileContext.h new file mode 100644 index 0000000..2e5e15a --- /dev/null +++ b/src/ManagedFileContext.h @@ -0,0 +1,123 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + ManagedFileContext.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_MANAGED_FILE_CONTEXT__ +#define __MEZZO_MANAGED_FILE_CONTEXT__ + +#include +#include +#include + +#include "Buffer.h" +#include "SeqBlockContext.h" + +namespace Mezzo { + +class ManagedFile; +class ManagedFileContextData; + +/// A class that creates and tracks ManagedFiles in a particular directory + +/// A class that creates and tracks ManagedFiles in a particular directory. +/// The files in this directory are reference-counted by ManagedFileContext +/// and moved, deleted, or copied as needed. +/// +/// Classes that are using ManagedFiles need to be careful to release all +/// references to them when they are finished. When a ManagedFileContext +/// is destroyed by going out of scope or being deleted it will throw +/// an exception if any references to files in that context remain. +class ManagedFileContext : public SeqBlockContext +{ +public: + ManagedFileContext(std::string dir); + ManagedFileContext(Loader& loader); + + /// Free the context, throwing an exception if references to its files are live + ~ManagedFileContext(); + + /// Get a reference suitable for use in this context + ManagedFile* GetRef(ManagedFile *file); + + void ReleaseRef(ManagedFile *file); + + /// Move or copy all files to a new directory + void MoveToNewDirectory(std::string dir); + + /// Lock the given set of files in this context. + void LockFiles(std::set filesToCommit); + + /// Allow this context to delete itself when all its files are abandoned + void Release(); + + virtual void Store(Storer& storer); + virtual void Store(Storer& storer, std::string relativePath, std::set filesToStore); + + // + // These methods come from SeqBlockContext + // + + // not thread-safe! + SeqBlock *NewSeqBlock(Buffer buffer); + + /// Get a reference that is suitable for use in this context, whether the + /// given SeqBlock is from this context or not. In practice this will add + /// one to the refcount and return the same block if the given block is from + /// this context, otherwise it will make a copy in this context. + /// This method must be called from the disk thread, because it does a lot + /// of serious disk I/O. + SeqBlock *GetSeqBlockRef(SeqBlock *block); + + SeqBlock *GetSeqBlockRef(Loader& loader, std::string id); + + /// Free use of a block previously acquired using either NewSeqBlock or GetRef. + /// In practice this will decrement the reference count by one, and delete + /// the block if the reference count hits zero. + void ReleaseSeqBlockRef(SeqBlock *block); + + /// Get the number of SeqBlocks currently allocated in this context. If the + /// owner of this context thinks that all blocks have been freed but finds + /// that this method returns nonzero, the client knows it leaked some blocks. + int GetNumSeqBlocks(); + + void Ref(); + void Deref(); + + static void LoadBackgroundRequests(); + + private: + std::string GenerateFileName(std::string extension); + void AddFileToContext(ManagedFile *file); + + std::set mFiles; + std::string mDir; + int mFileIndex; + int mRefCount; + bool mReleased; + + static std::vector mContexts; +}; + +} // namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + +#endif + diff --git a/src/RTDiskIONode.cpp b/src/RTDiskIONode.cpp new file mode 100644 index 0000000..5cab68c --- /dev/null +++ b/src/RTDiskIONode.cpp @@ -0,0 +1,81 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + RTDiskIONode.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "RTDiskIONode.h" + +#include "Sequence.h" +#include "BufferGroup.h" + +extern "C" { +#include "srsw_queue.h" +} + +namespace Mezzo { + +RTDiskIONode::RTDiskIONode(Sequence *s): + mSequence(s), + mOffset(0), + mQueue(srsw_queue_new()) +{ + SetNumPorts(0, 1); +} + +void RTDiskIONode::DiskIOThread(int bufSize, BufferGroup *g) +{ + //while(1) + //{ + if(srsw_queue_get_count(mQueue) > 20) + return; + + if(mOffset + bufSize > mSequence->GetLength()) + mOffset = 0; + + float *buf = g->Allocate(); + FloatBuffer seqBuf = mSequence->Get(mOffset, bufSize).AsFloat(); + seqBuf.Get(buf, 0, bufSize); + + srsw_queue_enqueue(mQueue, buf); + mOffset += bufSize; + //printf("pushed buffer %p\n", buf); + //} +} + +void RTDiskIONode::Run(int bufSize, BufferGroup *g, float **inBuffers, float **outBuffers) +{ + void *out; + + if(srsw_queue_dequeue(mQueue, &out) == 0) + { + /* bad! */ + printf("failed to get any buffers!\n"); + } + else + { + //printf("got a buffer!\n"); + } + + outBuffers[0] = (float*)out; +} + +} // namespace Mezzo + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/RTDiskIONode.h b/src/RTDiskIONode.h new file mode 100644 index 0000000..469d99c --- /dev/null +++ b/src/RTDiskIONode.h @@ -0,0 +1,51 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + RTDiskIONode.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_DISKIONODE__ +#define __MEZZO_DISKIONODE__ + +#include "RTNode.h" +#include "Types.h" + +struct SRSW_Queue; + +namespace Mezzo { + +class Sequence; + +class RTDiskIONode : public RTNode { +public: + RTDiskIONode(Sequence *s); + void Run(int bufSize, BufferGroup *g, float **inBuffers, float **outBuffers); + void DiskIOThread(int bufSize, BufferGroup *g); + +private: + Sequence *mSequence; + long_sample_count mOffset; + SRSW_Queue *mQueue; +}; + +} // namespace Mezzo + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/RTGraph.cpp b/src/RTGraph.cpp new file mode 100644 index 0000000..0113085 --- /dev/null +++ b/src/RTGraph.cpp @@ -0,0 +1,240 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + RTGraph.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "RTGraph.h" + +#include +#include + +#include "BufferGroup.h" +#include "RTNode.h" + +namespace Mezzo { + +class DummyRTNode : public RTNode +{ +public: + DummyRTNode() { } + + void Run(int bufSize, BufferGroup *g, float **inBuffers, float **outBuffers) + { + } +}; + +RTGraph::RTGraph(int bufferSize): + mNodes(new DummyRTNode), + mIntoGraphConnections(NULL), + mOutOfGraphConnections(NULL), + mBufferSize(bufferSize) +{ + mNodes->next = NULL; +} + +void RTGraph::AddNode(RTNode *node) +{ + node->next = NULL; + node->connections = NULL; + + // simple append into a linked list + RTNode *n = mNodes; + while(n->next != NULL) + n = n->next; + + n->next = node; + node->next = NULL; +} + +void RTGraph::Connect(RTNode *from, int fromPort, RTNode *to, int toPort) +{ + // + // Sanity checks + // + if(from == NULL && to == NULL) + { + printf("can't connect NULL to NULL\n"); + return; + } + + // Make sure both nodes are in this graph + bool fromInList = false, toInList = false; + for(RTNode *n = mNodes; n != NULL; n = n->next) + { + if(n == from) + fromInList = true; + if(n == to) + toInList = true; + + if(fromInList && toInList) + break; + } + + if((!fromInList && from != NULL) || (!toInList && to != NULL)) + { + printf("Tried to connect nodes that aren't in the list!\n"); + return; + } + + // Make sure that the referred-to ports actually exist + if(from && (fromPort >= from->numOutPorts)) + { + printf("Tried to connect from port %d, but node only has %d out ports!\n", + fromPort, from->numOutPorts); + return; + } + + if(to && (toPort >= to->numInPorts)) + { + printf("Tried to connect to port %d, but node only has %d in ports!\n", + toPort, to->numInPorts); + } + + // create the connection + if(from == NULL) + { + // we are connecting into the graph + RTGraph::ConnectionIntoGraph *newConnection = new RTGraph::ConnectionIntoGraph; + newConnection->to = to; + newConnection->intoGraphPort = fromPort; + newConnection->toNodePort = toPort; + newConnection->next = NULL; + + if(mIntoGraphConnections == NULL) + mIntoGraphConnections = newConnection; + else + { + RTGraph::ConnectionIntoGraph *c = mIntoGraphConnections; + while(c->next) + c = c->next; + c->next = newConnection; + } + } + else if(to == NULL) + { + // we are connecting out of the graph + RTGraph::ConnectionOutOfGraph *newConnection = new RTGraph::ConnectionOutOfGraph; + newConnection->from = from; + newConnection->fromNodePort = fromPort; + newConnection->outOfGraphPort = toPort; + newConnection->next = NULL; + + if(mOutOfGraphConnections == NULL) + mOutOfGraphConnections = newConnection; + else + { + RTGraph::ConnectionOutOfGraph *c = mOutOfGraphConnections; + while(c->next) + c = c->next; + c->next = newConnection; + } + } + else + { + RTNode::Connection *newConnection = new RTNode::Connection; + newConnection->from = from; + newConnection->fromTheirPort = fromPort; + newConnection->toMyPort = toPort; + newConnection->next = NULL; + + if(to->connections == NULL) + { + to->connections = newConnection; + } + else + { + RTNode::Connection *n = to->connections; + while(n->next) + n = n->next; + n->next = newConnection; + } + } +} + +void RTGraph::Prepare() +{ + // skip dummy node + RTNode *n = mNodes->next; + + while(n) + { + n->Prepare(); + n = n->next; + } +} + +int RTGraph::GetNumNodes() +{ + // skip dummy node + RTNode *n = mNodes->next; + int count = 0; + + while(n) + { + count++; + n = n->next; + } + + return count; +} + +void RTGraph::Run(BufferGroup *g, float **in, float **out) +{ + float *from[20]; + + // skip dummy node + RTNode *n = mNodes->next; + while(n) + { + // if the input to the graph feeds this node, copy that + for(RTGraph::ConnectionIntoGraph *c = mIntoGraphConnections; c != NULL; c = c->next) + if(n == c->to && in != NULL) + from[c->toNodePort] = in[c->intoGraphPort]; + + // pull data from nodes that feed this node + for(RTNode::Connection *c = n->connections; c != NULL; c = c->next) + from[c->toMyPort] = c->from->tmpOutBuffers[c->fromTheirPort]; + + n->Run(mBufferSize, g, from, n->tmpOutBuffers); + + n = n->next; + } + + if(out != NULL) + for(RTGraph::ConnectionOutOfGraph *c = mOutOfGraphConnections; c != NULL; c = c->next) + out[c->outOfGraphPort] = c->from->tmpOutBuffers[c->fromNodePort]; +} + +void RTGraph::Finish() +{ + // skip dummy node + RTNode *n = mNodes->next; + + while(n) + { + n->Finish(); + n = n->next; + } +} + +} + + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/RTGraph.h b/src/RTGraph.h new file mode 100644 index 0000000..fcacef7 --- /dev/null +++ b/src/RTGraph.h @@ -0,0 +1,65 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + RTGraph.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_RTGRAPH__ +#define __MEZZO_RTGRAPH__ + +namespace Mezzo { + +class RTNode; +class BufferGroup; +class Buffer; + +class RTGraph { +public: + RTGraph(int bufferSize); + + void AddNode(RTNode *node); + void Connect(RTNode *from, int fromPort, RTNode *to, int toPort); + int GetNumNodes(); + + void Prepare(); + void Run(BufferGroup *g, float **in, float **out); + void Finish(); + +private: + RTNode *mNodes; + struct ConnectionIntoGraph { + RTNode *to; + int intoGraphPort; + int toNodePort; + ConnectionIntoGraph *next; + } *mIntoGraphConnections; + struct ConnectionOutOfGraph { + RTNode *from; + int fromNodePort; + int outOfGraphPort; + ConnectionOutOfGraph *next; + } *mOutOfGraphConnections; + int mBufferSize; +}; + +} + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/RTNode.h b/src/RTNode.h new file mode 100644 index 0000000..fa70215 --- /dev/null +++ b/src/RTNode.h @@ -0,0 +1,67 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + RTNode.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_RTNODE__ +#define __MEZZO_RTNODE__ + +#include + +namespace Mezzo { + +class BufferGroup; +class Buffer; + +class RTNode { +public: + RTNode():next(NULL),connections(NULL),numInPorts(0),numOutPorts(0),tmpOutBuffers(NULL){} + virtual void Prepare() { } + virtual void Run(int bufSize, BufferGroup *g, float **inBuffers, float **outBuffers) = 0; + virtual void Finish() { } + +protected: + void SetNumPorts(int inPorts, int outPorts) + { + if(tmpOutBuffers) delete tmpOutBuffers; + tmpOutBuffers = new float*[outPorts]; + + numInPorts = inPorts; + numOutPorts = outPorts; + } + +private: + class RTNode *next; + struct Connection { + RTNode *from; + int fromTheirPort; + int toMyPort; + Connection *next; + } *connections; + int numInPorts, numOutPorts; + float **tmpOutBuffers; + friend class RTGraph; +}; + +} // namespace Mezzo + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/RTSineWaveNode.cpp b/src/RTSineWaveNode.cpp new file mode 100644 index 0000000..9918abc --- /dev/null +++ b/src/RTSineWaveNode.cpp @@ -0,0 +1,53 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + RTSineWaveNode.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "RTSineWaveNode.h" +#include "BufferGroup.h" + +#include + +namespace Mezzo { + +RTSineWaveNode::RTSineWaveNode(float freq): + mFreq(freq), + mPhase(0) +{ + SetNumPorts(0, 1); +} + +void RTSineWaveNode::Run(int bufSize, BufferGroup *g, float **inBuffers, float **outBuffers) +{ + float *buf = g->Allocate(); + + for(int i = 0; i < bufSize; i++) + { + buf[i] = sin(mPhase); + mPhase += 2 * M_PI * mFreq / 44100.0; + } + + outBuffers[0] = buf; + +} + +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/RTSineWaveNode.h b/src/RTSineWaveNode.h new file mode 100644 index 0000000..bc2e8ba --- /dev/null +++ b/src/RTSineWaveNode.h @@ -0,0 +1,45 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + RTSineWaveNode.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_SINEWAVENODE__ +#define __MEZZO_SINEWAVENODE__ + +#include "RTNode.h" + +namespace Mezzo { + +class RTSineWaveNode : public RTNode { +public: + RTSineWaveNode(float freq); + + void Run(int bufSize, BufferGroup *g, float **inBuffers, float **outBuffers); + +private: + float mFreq; + float mPhase; +}; + +} + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 0000000..b3558a8 --- /dev/null +++ b/src/SConscript @@ -0,0 +1,27 @@ + +Import('env') + +source_files = [ + "Buffer.cpp", + "BufferGroup.cpp", + "Sequence.cpp", + "Cubic.cpp", + "DiskIO.cpp", + "Exceptions.cpp", + "RTDiskIONode.cpp", + "RTGraph.cpp", + "RTSineWaveNode.cpp", + "SeqBlock.cpp", + "SeqMemBlock.cpp", + "SeqDataFileBlock.cpp", + "Storable.cpp", + "srsw_queue.c", + "ManagedFile.cpp", + "ManagedFileContext.cpp", + "Util.cpp", + "XMLLoadStore.cpp", + "platform/posix/DiskFunctions.cpp" +] + +env.StaticLibrary("audacity", source = source_files) + diff --git a/src/SeqBlock.cpp b/src/SeqBlock.cpp new file mode 100644 index 0000000..1afebe5 --- /dev/null +++ b/src/SeqBlock.cpp @@ -0,0 +1,429 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + SeqBlock.cpp + + Copyright (c) 2004 Dominic Mazzoni + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include + +#include "SeqBlock.h" +#include "Util.h" + +#include + +namespace Mezzo { + +// +// SeqBlockMetadata +// +SeqBlockMetadataType::SeqBlockMetadataType(std::string type): + typeStr(type) +{ + UserAssert(typeStr.size() == 4, "SeqBlockMetadataTypes must be 4 characters long"); + + if(type != "256i" && + type != "256m" && + type != "256M" && + type != "256s" && + type != "64ki" && + type != "64km" && + type != "64kM" && + type != "smry") + UserAssert(false, fmt("Summary type %s unknown", type.c_str())); +} + +SeqBlockMetadataType::SeqBlockMetadataType(char _type[5]): + typeStr(_type) +{ + // TODO +} + +bool SeqBlockMetadataType::operator==(SeqBlockMetadataType& other) +{ + return typeStr == other.typeStr; +} + +SeqBlockMetadata::SeqBlockMetadata(): + mNativeEndian(false) +{ +} + +SeqBlockMetadata::SeqBlockMetadata(char *data, int len) +{ + posh_u32_t riffID; + + memcpy(&riffID, data, 4); + data += 4; + + if(riffID == 'RIFF') + mNativeEndian = true; + else if(riffID == 'FFIR') + mNativeEndian = false; + else + UserAssert(false, "Metadata is not in RIFF format"); + + posh_u32_t riffSize; + memcpy(&riffSize, data, 4); + data += 4; + + if(!mNativeEndian) + riffSize = POSH_SwapU32(riffSize); + + while(riffSize > 0) + { + char id[5]; + memcpy(id, data, 4); + data += 4; + id[4] = '\0'; + + posh_u32_t sectionSize; + memcpy(§ionSize, data, 4); + data += 4; + + if(!mNativeEndian) + sectionSize = POSH_SwapU32(sectionSize); + + char *sectionData = new char[sectionSize]; + memcpy(sectionData, data, sectionSize); + data += sectionSize; + + riffSize -= 8 + sectionSize; + + mSections.push_back(section(id, sectionSize, sectionData)); + } +} + +SeqBlockMetadata::~SeqBlockMetadata() +{ + for(unsigned int i = 0; i < mSections.size(); i++) + delete[] mSections[i].data; +} + +void SeqBlockMetadata::AddSection(SeqBlockMetadataType type, char *data, int len) +{ + char *dataCopy = new char[len]; + memcpy(dataCopy, data, len); + + section s(type, len, dataCopy); + + mSections.push_back(s); +} + +bool SeqBlockMetadata::HasSection(SeqBlockMetadataType type) +{ + for(unsigned int i = 0; i < mSections.size(); i++) + if(mSections[i].type == type) + return true; + return false; +} + +char *SeqBlockMetadata::GetSectionData(SeqBlockMetadataType type) +{ + for(unsigned int i = 0; i < mSections.size(); i++) + if(mSections[i].type == type) + return mSections[i].data; + + UserAssert(false, "Tried to get metadata for a section that doesn't exist. " + "Use HasSection() to avoid this error"); + return NULL; +} + +int SeqBlockMetadata::GetSectionLen(SeqBlockMetadataType type) +{ + for(unsigned int i = 0; i < mSections.size(); i++) + if(mSections[i].type == type) + return mSections[i].len; + + UserAssert(false, "Tried to get metadata length for a section that doesn't exist. " + "Use HasSection() to avoid this error"); + return 0; +} + +int SeqBlockMetadata::GetPackedLen() +{ + int size = 8; // 4 for 'RIFF' and 4 for 32-bit size of entire block + for(unsigned int i = 0; i < mSections.size(); i++) + { + size += 8; // 4 for section type, 4 for 32-bit section length + size += mSections[i].len; + } + + return size; +} + +void SeqBlockMetadata::GetPackedData(char *outData) +{ + // Write out everything to be host-endian + + posh_u32_t riffID = 'RIFF'; // we intentionally want this to be host-endian + memcpy(outData, &riffID, 4); + outData += 4; + + posh_u32_t totalSize = GetPackedLen() - 8; // don't count id and size overhead + memcpy(outData, &totalSize, 4); + outData += 4; + + for(unsigned int i = 0; i < mSections.size(); i++) + { + memcpy(outData, mSections[i].type.typeStr.c_str(), 4); + outData += 4; + + memcpy(outData, &mSections[i].len, 4); + outData += 4; + + memcpy(outData, mSections[i].data, mSections[i].len); + outData += mSections[i].len; + } +} + +/* +SeqBlockMetadataHeader::SeqBlockMetadataHeader(FILE *file) +{ + // TODO +} + +bool SeqBlockMetadataHeader::HasSection(enum SeqBlockMetadata::metadataType type) +{ + for(unsigned int i = 0; i < mSections.size(); i++) + if(mSections[i].type == type) + return true; + return false; +} + +int SeqBlockMetadataHeader::GetSectionOffset(enum SeqBlockMetadata::metadataType type) +{ + for(unsigned int i = 0; i < mSections.size(); i++) + if(mSections[i].type == type) + return mSections[i].offset; + + UserAssert(false, "Tried to get metadata offset for a section that doesn't exist. " + "Use HasSection() to avoid this error"); + return 0; +} + +int SeqBlockMetadataHeader::GetSectionLen(enum SeqBlockMetadata::metadataType type) +{ + for(unsigned int i = 0; i < mSections.size(); i++) + if(mSections[i].type == type) + return mSections[i].len; + + UserAssert(false, "Tried to get metadata len for a section that doesn't exist. " + "Use HasSection() to avoid this error"); + return 0; +} +*/ + +// +// SeqBlockSummary +// + +// Create summary data given uncompressed samples +SeqBlockSummary::SeqBlockSummary(Buffer samples): + // the 256 summary has one summary sample for every 256 samples. We add + // 255 to ensure that the division always rounds up, because we will create + // one full summary sample for however many samples remain. + m256Len((samples.GetLength()+255)/256), + // the 64k summary has one summary sample for every 65535 samples, which is + // the same as one summary sample for every 256 256-summary samples. + m64kLen((m256Len+255)/256), + m256Min(m256Len), + m256Max(m256Len), + m256SumSq(m256Len), + m64kMin(m64kLen), + m64kMax(m64kLen), + m64kSumSq(m64kLen) +{ + FloatBuffer floatSamples = samples.AsFloat(); + int len = floatSamples.GetLength(); + + int i; + + // + // create the 256 summaries + // + for(i=0; i len? len - i*256: 256; + FloatBuffer sub = floatSamples.Get(i*256, subLen); + m256Min[i] = sub.GetMin(); + m256Max[i] = sub.GetMax(); + m256SumSq[i] = sub.GetSumSq(); + } + + // + // create the 64k summaries + // + for(i=0; i m256Len? m256Len - i*256: 256; + m64kMin[i] = m256Min.Get(i*256, subLen).GetMin(); + m64kMax[i] = m256Max.Get(i*256, subLen).GetMax(); + + // We don't want to use SumSq here because all of the original samples have + // already been squared, and squaring the samples of the 256 summary would + // be meaningless. Instead we get a value that is all the samples in the + // buffer added together + m64kSumSq[i] = m256SumSq.Get(i*256, subLen).GetSum(); + } + + mMin = m64kMin.GetMin(); + mMax = m64kMax.GetMax(); + mSumSq = m64kSumSq.GetSum(); +} + +// +// The current summary format is to save all data in this order: +// +// 256Min +// 256Max +// 256SumSq +// 64kMin +// 64kMax +// 64kSumSq +// Min +// Max +// SumSq + +SeqBlockSummary::SeqBlockSummary(SeqBlockMetadata *md): + m256Min(md->GetSectionLen("256m")), + m256Max(md->GetSectionLen("256M")), + m256SumSq(md->GetSectionLen("256s")), + m64kMin(md->GetSectionLen("64km")), + m64kMax(md->GetSectionLen("64kM")), + m64kSumSq(md->GetSectionLen("64ks")) +{ + m256Min.Set((float*)md->GetSectionData("256m"), 0, m256Min.GetLength()); + m256Max.Set((float*)md->GetSectionData("256M"), 0, m256Max.GetLength()); + m256SumSq.Set((float*)md->GetSectionData("256s"), 0, m256SumSq.GetLength()); + + m64kMin.Set((float*)md->GetSectionData("64km"), 0, m64kMin.GetLength()); + m64kMax.Set((float*)md->GetSectionData("64kM"), 0, m64kMax.GetLength()); + m64kSumSq.Set((float*)md->GetSectionData("64ks"), 0, m64kSumSq.GetLength()); + + float *blockSummary = (float*)md->GetSectionData("smry"); + + mMin = blockSummary[0]; + mMax = blockSummary[1]; + mSumSq = blockSummary[2]; +} + +void SeqBlockSummary::Write(SeqBlockMetadata *md) +{ + md->AddSection("256m", (char*)m256Min.GetPtr(), m256Min.GetLength() * sizeof(float)); + md->AddSection("256M", (char*)m256Max.GetPtr(), m256Max.GetLength() * sizeof(float)); + md->AddSection("256s", (char*)m256SumSq.GetPtr(), m256SumSq.GetLength() * sizeof(float)); + + md->AddSection("64km", (char*)m64kMin.GetPtr(), m64kMin.GetLength() * sizeof(float)); + md->AddSection("64kM", (char*)m64kMax.GetPtr(), m64kMax.GetLength() * sizeof(float)); + md->AddSection("64ks", (char*)m64kSumSq.GetPtr(), m64kSumSq.GetLength() * sizeof(float)); + + FloatBuffer blockSummary(3); + blockSummary[0] = mMin; + blockSummary[1] = mMax; + blockSummary[2] = mSumSq; + + md->AddSection("smry", (char*)blockSummary.GetPtr(), 3*sizeof(float)); +} + + +// static +void SeqBlockSummary::Get256(SeqBlockMetadata *md, int start, int len, + FloatBuffer& min, FloatBuffer& max, FloatBuffer& sumSq) +{ + + if(md->HasSection("256m")) + { + min.Resize(md->GetSectionLen("256m")); + min.Set((float*)md->GetSectionData("256m"), 0, md->GetSectionLen("256m")); + } + else + printf("no 256m section!\n"); + + + if(md->HasSection("256M")) + { + max.Resize(md->GetSectionLen("256M")); + max.Set((float*)md->GetSectionData("256M"), 0, md->GetSectionLen("256M")); + } + else + printf("no 256M section!\n"); + + if(md->HasSection("256s")) + { + sumSq.Resize(md->GetSectionLen("256s")); + sumSq.Set((float*)md->GetSectionData("256s"), 0, md->GetSectionLen("256s")); + } + else + printf("no 256s section!\n"); +} + +//static + +void SeqBlockSummary::Get64k(SeqBlockMetadata *md, int start, int len, + FloatBuffer& min, FloatBuffer& max, FloatBuffer& sumSq) +{ + min.Resize(md->GetSectionLen("64km")); + max.Resize(md->GetSectionLen("64kM")); + sumSq.Resize(md->GetSectionLen("64ks")); + + min.Set((float*)md->GetSectionData("64km"), 0, md->GetSectionLen("64km")); + max.Set((float*)md->GetSectionData("64kM"), 0, md->GetSectionLen("64kM")); + sumSq.Set((float*)md->GetSectionData("64ks"), 0, md->GetSectionLen("64ks")); +} + +// +// SeqBlock +// + +/* +void SeqBlock::GetMinMax(int start, int len, + float *outMin, float *outMax, float *outRMS) const +{ + // TODO: raise exception if start or len are out of range + + if ((start == 0) && (len == mLen)) { + if (outMin) + *outMin = mMin; + if (outMax) + *outMax = mMax; + if (outRMS) + *outRMS = mRMS; + + return; + } + + FloatBuffer subset = Get(start, len).AsFloat(); + + if (outMin) + *outMin = subset.GetMin(); + if (outMax) + *outMax = subset.GetMax(); + if (outRMS) + *outRMS = subset.GetRMS(); +} */ + +void SeqSilentBlock::Store(Storer& storer) +{ + // + AttrDict attrs; + attrs["len"] = fmt("%d", mLen); + storer.StoreLeafNode("SeqSilentBlock", attrs); +} + +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/SeqBlock.h b/src/SeqBlock.h new file mode 100644 index 0000000..94cf3e2 --- /dev/null +++ b/src/SeqBlock.h @@ -0,0 +1,181 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + SeqBlock.h + + Copyright (c) 2004 Dominic Mazzoni, Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_SEQ_BLOCK__ +#define __MEZZO_SEQ_BLOCK__ + +#include + +#include "Buffer.h" +#include "Storable.h" + +namespace Mezzo { + +class SeqBlockMetadataType { + public: + SeqBlockMetadataType(std::string type); + SeqBlockMetadataType(char type[5]); + bool operator==(SeqBlockMetadataType& other); + std::string typeStr; +}; + +/// Encapsulates the metadata format used by SeqBlocks that are written to disk +class SeqBlockMetadata { + public: + SeqBlockMetadata(); + SeqBlockMetadata(char *data, int len); + + ~SeqBlockMetadata(); + + // the data will be given back to you in this endianness. if your data + // is endian-dependent, you must either store the endianness, or + // (more advisably) store it in host-endian format and convert when + // you read if IsNativeEndian() returns false + void AddSection(SeqBlockMetadataType type, char *data, int len); + + bool HasSection(SeqBlockMetadataType type); + + // if IsNativeEndian() is false, you will probably need to byte-swap + // any data you read + char *GetSectionData(SeqBlockMetadataType type); + int GetSectionLen(SeqBlockMetadataType type); + + int GetPackedLen(); // in bytes + void GetPackedData(char *outData); + + bool IsNativeEndian() { return mNativeEndian; } + + private: + struct section { + section(SeqBlockMetadataType _type, int _len, char *_data): + type(_type),len(_len),data(_data) + { } + SeqBlockMetadataType type; + int len; + char *data; + }; + std::vector
    mSections; + bool mNativeEndian; +}; + +class SeqBlockSummary { + public: + /// Create summary data given uncompressed samples. + SeqBlockSummary(Buffer samples); + + /// Create from packed summary data that had been previously + /// saved from GetPackedData. Also needs to know the number + /// of samples in the original block in order to load correctly. + SeqBlockSummary(SeqBlockMetadata *metadata); + + void Write(SeqBlockMetadata *metadata); + + static void Get256(SeqBlockMetadata *md, int start, int len, + FloatBuffer& min, FloatBuffer& max, FloatBuffer& sumSq); + static void Get64k(SeqBlockMetadata *md, int start, int len, + FloatBuffer& min, FloatBuffer& max, FloatBuffer& sumSq); + + int m256Len; + int m64kLen; + FloatBuffer m256Min; + FloatBuffer m256Max; + FloatBuffer m256SumSq; + FloatBuffer m64kMin; + FloatBuffer m64kMax; + FloatBuffer m64kSumSq; + float mMin; + float mMax; + float mSumSq; +}; + +/// Represents an immutable block of samples in a Sequence data structure. +class SeqBlock : public virtual Storable { + public: + virtual ~SeqBlock() {} + + int GetLength() { return mLen; } + + /// Retrieves audio data from this SeqBlock + virtual Buffer Get(int start = 0) = 0; + /// Retrieves audio data from this SeqBlock + virtual Buffer Get(int start, int len) = 0; + + virtual bool GetIfCached(Buffer &buf, int start = 0) = 0; + virtual bool GetIfCached(Buffer &buf, int start, int len) = 0; + + /// Gets extreme values for the whole block (these do not go to disk for the data) + virtual float GetMin() = 0; + virtual float GetMax() = 0; + virtual float GetSumSq() = 0; + + virtual SeqBlockSummary *GetSummary() = 0; + virtual bool GetSummaryIfCached(SeqBlockSummary **summary) = 0; + + virtual void IncrementDataReadRequestCount() { } + virtual void DecrementDataReadRequestCount() { } + virtual void IncrementSummaryReadRequestCount() { } + virtual void DecrementSummaryReadRequestCount() { } + + + virtual int GetRAMBytesUsed() = 0; + virtual int GetDiskBytesUsed() = 0; + + //virtual SeqBlock* LoadSeqBlock(Loader& loader) const = 0; + + protected: + int mLen; +}; + +class SeqSilentBlock : public SeqBlock { + public: + SeqSilentBlock(int len) { mLen = len; } + + ~SeqSilentBlock() {} + + Buffer Get(int start = 0) { + return FloatBuffer(mLen-start); + } + Buffer Get(int start, int len) { + return FloatBuffer(len); + } + + float GetMin() { return 0; } + float GetMax() { return 0; } + float GetSumSq() { return 0; } + + bool GetIfCached(Buffer &buf, int start = 0) { Get(start); return true; } + bool GetIfCached(Buffer &buf, int start, int len) { Get(start, len); return true; } + + SeqBlockSummary *GetSummary() { return new SeqBlockSummary(FloatBuffer(mLen)); } // TODO memory leak!! + bool GetSummaryIfCached(SeqBlockSummary **summary) { *summary = new SeqBlockSummary(FloatBuffer(mLen)); return true; } + + int GetRAMBytesUsed() { return sizeof(*this); } + int GetDiskBytesUsed() { return 0; } + + void Store(Storer& storer); +}; + +} // namespace + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/SeqBlockContext.h b/src/SeqBlockContext.h new file mode 100644 index 0000000..6daf6a2 --- /dev/null +++ b/src/SeqBlockContext.h @@ -0,0 +1,69 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + SeqBlockContext.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_SEQ_BLOCK_CONTEXT__ +#define __MEZZO_SEQ_BLOCK_CONTEXT__ + +#include "Storable.h" +#include "Buffer.h" + +namespace Mezzo { + +class SeqBlock; + +class SeqBlockContext : public virtual Storable +{ + friend class BlockedSequence; + + public: + virtual ~SeqBlockContext() { } + + private: + virtual SeqBlock *NewSeqBlock(Buffer buffer) = 0; + + /// Get a reference that is suitable for use in this context, whether the + /// given SeqBlock is from this context or not. In practice this will add + /// one to the refcount and return the same block if the given block is from + /// this context, otherwise it will make a copy in this context. + virtual SeqBlock *GetSeqBlockRef(SeqBlock *block) = 0; + + virtual SeqBlock *GetSeqBlockRef(Loader& loader, std::string id) = 0; + + /// Free use of a block previously acquired using either NewSeqBlock or GetRef. + /// In practice this will decrement the reference count by one, and delete + /// the block if the reference count hits zero. + virtual void ReleaseSeqBlockRef(SeqBlock *block) = 0; + + virtual void Ref() = 0; + virtual void Deref() = 0; + + /// Get the number of SeqBlocks currently allocated in this context. If the + /// owner of this context thinks that all blocks have been freed but finds + /// that this method returns nonzero, the client knows it leaked some blocks. + virtual int GetNumSeqBlocks() = 0; +}; + +} // namespace + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/SeqDataFileBlock.cpp b/src/SeqDataFileBlock.cpp new file mode 100644 index 0000000..dda83c7 --- /dev/null +++ b/src/SeqDataFileBlock.cpp @@ -0,0 +1,324 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + SeqDataFileBlock.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "SeqDataFileBlock.h" + +#include "Exceptions.h" +#include "platform/DiskFunctions.h" +#include "Util.h" + +#include + +#include +#include + +namespace Mezzo { + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +// The AU formats we care about +enum { + AU_SAMPLE_FORMAT_16 = 3, + AU_SAMPLE_FORMAT_24 = 4, + AU_SAMPLE_FORMAT_FLOAT = 6, +}; + +typedef struct { + posh_u32_t magic; // magic number + posh_u32_t dataOffset; // byte offset to start of audio data + posh_u32_t dataSize; // data length, in bytes (optional) + posh_u32_t encoding; // data encoding enumeration + posh_u32_t sampleRate; // samples per second + posh_u32_t channels; // number of interleaved channels +} auHeader; + +#endif + +SeqDataFileBlock::SeqDataFileBlock(std::string fileName, std::string fullPathName, Buffer buffer): + ManagedFile(fileName, fullPathName), + mCachedBufferData(this), + mCachedMetaData(this) +{ + mLen = buffer.GetLength(); + + FILE* file = fopen(mFullPathName.GetFullPath().c_str(), "wb"); + + UserAssert(file != NULL, fmt("Unable to open file %s, errno = %d", mFullPathName.GetFullPath().c_str(), errno)); + + // + // Write header data + // + + SeqBlockMetadata md; + + // Create summary + SeqBlockSummary summary(buffer); + summary.Write(&md); + + int mdBytes = md.GetPackedLen(); + char *mdBuffer = new char[mdBytes]; + md.GetPackedData(mdBuffer); + + auHeader header; + + // AU files can be either big or little endian. Which it is is + // determined implicitly by the byte-order of the magic 0x2e736e64 + // (.snd). We want it to be native-endian, so we write the magic + // to memory and then let it write that to a file in native + // endianness + header.magic = 0x2e736e64; + + // We store the summary data at the end of the header, so the data + // offset is the length of the summary data plus the length of the header + header.dataOffset = sizeof(auHeader) + mdBytes; + + // dataSize is optional, and we opt out + header.dataSize = 0xffffffff; + + switch(buffer.GetSampleFormat()) { + case Buffer::Int16Sample: + header.encoding = AU_SAMPLE_FORMAT_16; + break; + + case Buffer::Int24Sample: + header.encoding = AU_SAMPLE_FORMAT_24; + break; + + case Buffer::FloatSample: + header.encoding = AU_SAMPLE_FORMAT_FLOAT; + break; + } + + // unfortunately we can never give a meaningful value here since + // Sequence is by nature sample-rate neutral. + header.sampleRate = 44100; + + // BlockFiles are always mono + header.channels = 1; + + fwrite(&header, sizeof(header), 1, file); + fwrite(mdBuffer, mdBytes, 1, file); + + delete[] mdBuffer; + + // + // Write sample data + // + + int dataLen = buffer.GetPackedBytesPerSample() * buffer.GetLength(); + char *packedData = new char[dataLen]; + + buffer.GetPackedData(packedData); + + fwrite(packedData, dataLen, 1, file); + fclose(file); + + + delete[] packedData; +} + +Buffer +SeqDataFileBlock::Get(int start) +{ + return Get(start, mLen - start); +} + +Buffer +SeqDataFileBlock::Get(int start, int len) +{ + InternalAssert(start >= 0 && len >= 0 && start <= mLen && + start+len <= mLen, + fmt("Attempt to Get from a SeqDataFileBlock with " + "start=%d and len=%d, when block len is %d", + start, len, mLen)); + + // TODO -- support non-native endianness + FILE* file = fopen(mFullPathName.GetFullPath().c_str(), "rb"); + + InternalAssert(file, "Unable to open SeqDataFileBlock file " + mFullPathName.GetFullPath()); + + // + // read header + // + + auHeader header; + + fread(&header, sizeof(header), 1, file); + + // + // read data + // + + fseek(file, header.dataOffset, SEEK_SET); // seek to beginning of data + + switch(header.encoding) + { + case AU_SAMPLE_FORMAT_16: + { + fseek(file, start * header.channels * sizeof(short), SEEK_CUR); + short *int16Samples = new short[len]; + fread(int16Samples, len * sizeof(short), 1, file); + fclose(file); + return Int16Buffer(int16Samples, 0, len, Buffer::DisposeWithDelete); + } + + case AU_SAMPLE_FORMAT_24: + { + // TODO: make sure we unpack correctly + fseek(file, start * header.channels * sizeof(int), SEEK_CUR); + int *int24Samples = new int[len]; + fread(int24Samples, len * sizeof(int), 1, file); + fclose(file); + return Int24Buffer(int24Samples, 0, len, 1, Buffer::DisposeWithDelete); + } + + case AU_SAMPLE_FORMAT_FLOAT: + { + fseek(file, start * header.channels * sizeof(float), SEEK_CUR); + float *floatSamples = new float[len]; + fread(floatSamples, len * sizeof(float), 1, file); + fclose(file); + return FloatBuffer(floatSamples, 0, len, 1, Buffer::DisposeWithDelete); + } + + default: + UserAssert(false, fmt("SeqDataFileBlock %s has a bad data format", mFullPathName.GetFullPath().c_str())); + return FloatBuffer(0); + } +} + +bool SeqDataFileBlock::GetIfCached(Buffer &buf, int start) +{ + return GetIfCached(buf, start, mLen - start); +} + +bool SeqDataFileBlock::GetIfCached(Buffer &buf, int start, int len) +{ + if(mCachedBufferData.IsLoaded()) + { + buf = mCachedBufferData.data; + return true; + } + else + { + buf = FloatBuffer(len); + return false; + } +} + +ManagedFile* +SeqDataFileBlock::Copy(std::string fileName) +{ + return new SeqDataFileBlock(*this); +} + +SeqBlockSummary *SeqDataFileBlock::GetSummary() +{ + // TODO -- support non-native endianness + FILE* file = fopen(mFullPathName.GetFullPath().c_str(), "rb"); + + InternalAssert(file, "Unable to open SeqDataFileBlock file " + mFullPathName.GetFullPath()); + + // + // read header + // + + auHeader header; + + fread(&header, sizeof(header), 1, file); + + int mdSize = header.dataOffset - sizeof(auHeader); + + char metadata[mdSize]; + + fread(metadata, mdSize, 1, file); + + SeqBlockMetadata md(metadata, mdSize); + + fclose(file); + + return new SeqBlockSummary(&md); +} + +bool SeqDataFileBlock::GetSummaryIfCached(SeqBlockSummary **summary) +{ + if(mCachedMetaData.IsLoaded()) + { + *summary = mCachedMetaData.summary; + return true; + } + else + { + // TODO: memory leak!!! + *summary = new SeqBlockSummary(FloatBuffer(mLen)); + return false; + } +} + +int +SeqDataFileBlock::GetRAMBytesUsed() +{ + return 0; +} + +int +SeqDataFileBlock::GetDiskBytesUsed() +{ + return 0; +} + +void SeqDataFileBlock::Store(Storer& storer) +{ + // + AttrDict attrs; + attrs["id"] = storer.CreateID(this); + attrs["len"] = fmt("%d", mLen); + attrs["file"] = mFileName; + + storer.StoreLeafNode("SeqDataFileBlock", attrs); +} + +SeqDataFileBlock::SeqDataFileBlock(Loader& loader, std::string absDir): + mCachedBufferData(this), + mCachedMetaData(this) +{ + Loader::Token tok = loader.GetNextToken(); + InternalAssert(tok.name == "SeqDataFileBlock" && tok.type == Loader::Token::beginNode, + "Attempted to load node " + tok.name + " with SeqDataFileBlock constructor"); + + mFileName = tok.attrs["file"]; + mFullPathName = absDir + Platform::DirSeparator + mFileName; + mLen = strtol(tok.attrs["len"].c_str(), NULL, 0); + + loader.RegisterObj(tok.attrs["id"], this); + + UserAssert(Platform::FileExists(mFullPathName), + "Attempted to load sound file " + mFullPathName.GetFullPath() + ", which doesn't exist"); + + tok = loader.GetNextToken(); + InternalAssert(tok.name == "SeqDataFileBlock" && tok.type == Loader::Token::endNode, + "Node SeqDataFileBlock doesn't expect children"); +} + +} // Namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/SeqDataFileBlock.h b/src/SeqDataFileBlock.h new file mode 100644 index 0000000..2f66fe0 --- /dev/null +++ b/src/SeqDataFileBlock.h @@ -0,0 +1,116 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + SeqDataFileBlock.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include + +#include "SeqBlock.h" +#include "ManagedFile.h" + +namespace Mezzo { + +class SeqDataFileBlock; + +class SeqDataFileBlock : public SeqBlock, public ManagedFile +{ + public: + // Construction is not complete until you give it a buffer to write + // using one of the following two methods + SeqDataFileBlock(std::string fullPathName, std::string fileName, Buffer data); + + SeqDataFileBlock(Loader& loader, std::string absDir); + + /// Retrieves audio data from this BlockFile + Buffer Get(int start = 0); + /// Retrieves audio data from this BlockFile + Buffer Get(int start, int len); + + /// Same functions as above, but to be used for GUI drawing when + /// it's okay to defer and get the data later + bool GetIfCached(Buffer &buf, int start = 0); + bool GetIfCached(Buffer &buf, int start, int len); + + float GetMin() { return mMin; } + float GetMax() { return mMax; } + float GetSumSq() { return mSumSq; } + + SeqBlockSummary *GetSummary(); + bool GetSummaryIfCached(SeqBlockSummary **summary); + + void IncrementDataReadRequestCount() { mCachedBufferData.IncrementReadRequestCount(); } + void DecrementDataReadRequestCount() { mCachedBufferData.DecrementReadRequestCount(); } + void IncrementSummaryReadRequestCount() { mCachedMetaData.IncrementReadRequestCount(); } + void DecrementSummaryReadRequestCount() { mCachedMetaData.DecrementReadRequestCount(); } + + ManagedFile *Copy(std::string fileName); + + int GetRAMBytesUsed(); + int GetDiskBytesUsed(); + + void Store(Storer& storer); + + static std::string GetExtension() { return "au"; } + + private: + class CachedBufferData : public CachedDiskData + { + CachedBufferData(SeqDataFileBlock *b): block(b) { } + + SeqDataFileBlock *block; + Buffer data; + + void Load() { data = block->Get(); } + void Unload() { data = FloatBuffer(0); } + + friend class SeqDataFileBlock; + }mCachedBufferData; + + class CachedMetaData : public CachedDiskData + { + CachedMetaData(SeqDataFileBlock *b): block(b) { } + + SeqDataFileBlock *block; + SeqBlockSummary *summary; + + void Load() { summary = block->GetSummary(); } + void Unload() { summary = NULL; } + + friend class SeqDataFileBlock; + }mCachedMetaData; + + std::vector GetCacheObjects() + { + std::vector objects; + objects.push_back(&mCachedBufferData); + objects.push_back(&mCachedMetaData); + return objects; + } + + + float mMin; + float mMax; + float mSumSq; +}; + + +} // namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/SeqMemBlock.cpp b/src/SeqMemBlock.cpp new file mode 100644 index 0000000..0e86d68 --- /dev/null +++ b/src/SeqMemBlock.cpp @@ -0,0 +1,188 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + SeqMemBlock.cpp + + Copyright (c) 2004 Dominic Mazzoni + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "SeqMemBlock.h" +#include "Util.h" + +namespace Mezzo { + +SeqMemBlock::SeqMemBlock(Buffer buffer): + mBuffer(buffer), + mSummary(buffer), + mRefCount(1) +{ + mLen = buffer.GetLength(); +} + +Buffer SeqMemBlock::Get(int start) +{ + return mBuffer.Get(start); +} + +Buffer SeqMemBlock::Get(int start, int len) +{ + return mBuffer.Get(start, len); +} + +float +SeqMemBlock::GetMin() +{ + return mSummary.mMin; +} + +float +SeqMemBlock::GetMax() +{ + return mSummary.mMax; +} + +float SeqMemBlock::GetSumSq() +{ + return mSummary.mSumSq; +} + +int SeqMemBlock::GetRAMBytesUsed() +{ + return + //mSummary.GetByteCount() + + mBuffer.GetBytesPerSample() * mBuffer.GetLength() + + sizeof(*this); +} + +void SeqMemBlock::Store(Storer& storer) +{ + // + // + // + // + // + // + + int i; + AttrDict attrs; + attrs["id"] = storer.CreateID(this); + storer.StoreBeginNode("SeqMemBlock", attrs); + + // + + FloatBuffer saveBuffer = mBuffer.AsFloat(); + storer.StoreBeginNode("Buffer", attrs); + for(i = 0; i < saveBuffer.GetLength(); i++) + { + attrs.clear(); + attrs["value"] = fmt("%f", saveBuffer[i]); + storer.StoreLeafNode("Sample", attrs); + } + storer.StoreEndNode("Buffer"); + + storer.StoreEndNode("SeqMemBlock"); +} + +// ------------------------------------------------------------------------------------- +// +// SeqMemBlockContext +// +// ------------------------------------------------------------------------------------- + +SeqMemBlockContext::SeqMemBlockContext(): + mRefCount(0) +{ +} + +void SeqMemBlockContext::Ref() +{ + mRefCount++; +} + +void SeqMemBlockContext::Deref() +{ + mRefCount--; +} + +SeqBlock* +SeqMemBlockContext::NewSeqBlock(Buffer buffer) +{ + // it is constructed with refcount of 1 + SeqMemBlock *newBlock = new SeqMemBlock(buffer); + mSeqBlocks.insert(newBlock); + return newBlock; +} + +SeqBlock* +SeqMemBlockContext::GetSeqBlockRef(SeqBlock *block) +{ + SeqMemBlock *seqMemBlock = dynamic_cast(block); + + // if it's not a SeqMemBlock it can't be from this context, and + // if it's not in our list, it's not from this context + if((seqMemBlock == NULL) || mSeqBlocks.count(seqMemBlock) == 0) + { + // constructed with refcount of 1 + SeqMemBlock *newBlock = new SeqMemBlock(block->Get()); + mSeqBlocks.insert(newBlock); + return newBlock; + } + else + { + seqMemBlock->mRefCount++; + return seqMemBlock; + } +} + +void SeqMemBlockContext::ReleaseSeqBlockRef(SeqBlock *block) +{ + SeqMemBlock *seqMemBlock = dynamic_cast(block); + + InternalAssert(seqMemBlock && mSeqBlocks.count(seqMemBlock) == 1, + "Attempted to release a SeqBlock that isn't from this context"); + + if(seqMemBlock->mRefCount-- == 0) + { + mSeqBlocks.erase(seqMemBlock); + delete seqMemBlock; + } +} + +int SeqMemBlockContext::GetNumSeqBlocks() +{ + return mSeqBlocks.size(); +} + +SeqMemBlockContext::~SeqMemBlockContext() +{ + if(mSeqBlocks.size() != 0) + { + // we can't throw an exception (it's bad C++, we could be already + // handling an exception) so this is the best we can do) + printf("FATAL ERROR in SeqMemBlockContext::~SeqMemBlockContext():\n" + "\tAttempted to delete a SeqMemBlockContext " + "that has live ManagedFiles\n"); + } + + std::set::const_iterator i; + for(i = mSeqBlocks.begin(); i != mSeqBlocks.end(); i++) + delete *i; +} + +} // namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/SeqMemBlock.h b/src/SeqMemBlock.h new file mode 100644 index 0000000..af763c0 --- /dev/null +++ b/src/SeqMemBlock.h @@ -0,0 +1,89 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + SeqMemBlock.h + + Copyright (c) 2004 Dominic Mazzoni, Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_SEQ_MEM_BLOCK__ +#define __MEZZO_SEQ_MEM_BLOCK__ + +#include + +#include "SeqBlock.h" +#include "SeqBlockContext.h" + +namespace Mezzo { + +class SeqMemBlock : public SeqBlock { + public: + /// Construct a new SeqMemBlock given some data + SeqMemBlock(Buffer buffer); + + ~SeqMemBlock() {} + + /// Retrieves audio data from this SeqMemBlock + Buffer Get(int start = 0); + /// Retrieves audio data from this SeqMemBlock + Buffer Get(int start, int len); + + bool GetIfCached(Buffer &buf, int start = 0) { Get(start); return true; } + bool GetIfCached(Buffer &buf, int start, int len) { Get(start, len); return true; } + + virtual SeqBlockSummary *GetSummary() { return &mSummary; } + virtual bool GetSummaryIfCached(SeqBlockSummary **summary) { *summary = &mSummary; return true; } + + float GetMin(); + float GetMax(); + float GetSumSq(); + + int GetRAMBytesUsed(); + int GetDiskBytesUsed() { return 0; } + + void Store(Storer& storer); + + protected: + Buffer mBuffer; + SeqBlockSummary mSummary; + int mRefCount; + + friend class SeqMemBlockContext; +}; + +class SeqMemBlockContext : public SeqBlockContext { +public: + SeqMemBlockContext(); + + SeqBlock *NewSeqBlock(Buffer buffer); + SeqBlock *GetSeqBlockRef(SeqBlock *block); + void ReleaseSeqBlockRef(SeqBlock *block); + void Ref(); + void Deref(); + int GetNumSeqBlocks(); + ~SeqMemBlockContext(); + +private: + std::set mSeqBlocks; + int mRefCount; +}; + +} // namespace + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/Sequence.Paste b/src/Sequence.Paste new file mode 100644 index 0000000..8dd4057 --- /dev/null +++ b/src/Sequence.Paste @@ -0,0 +1,151 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + BlockedSequence::Paste + + Copyright (c) 2004 Dominic Mazzoni + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +// The BlockedSequence class implements an efficient, disk-based +// sequence of audio samples, where all editing operations (Cut, Copy, +// Paste, Delete, etc) are constant-time in terms of disk access. +// Specifically, the samples are broken up into SeqBlocks, each one of +// which corresponds to one file on disk. To achieve the constant-time +// property, all methods enforce the invariant that all blocks except +// for the first and last must be between mMaxSamples/2 and mMaxSamples +// in length (inclusive). When this invariant is maintained, a Cut or +// Paste operation needs to modify at most 5 blocks on disk. +// +// Paste is the most tricky operation; the code path is different +// depending on both the insertion location ("start"), and the length +// of the Sequence being inserted. + +void BlockedSequence::Paste(long_sample_count start, const Sequence *src) +{ + ClientAssert(start >= 0 && start <= mNumSamples, + fmt("Attempt to Paste into a BlockedSequence with " + "start=%s, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + ClientAssert(mNumSamples + (double)src->GetLength() < LONG_SAMPLE_COUNT_MAX, + fmt("Pasting %s samples in BlockedSequence of length %s " + "would overflow.", + LongSampleCountToStr(src->GetLength()).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + if (src->GetLength() == 0) + return; + + const BlockedSequence *bsrc = dynamic_cast(src); + if (!bsrc) { + // If it's some other type of Sequence, we convert it to + // a BlockedSequence, then call Paste again... + BlockedSequence tempSequence(mContext); + long_sample_count len = src->GetLength(); + int i = 0; + while(i < len) { + int chunk = mMaxSamples < len-i? mMaxSamples: len-i; + tempSequence.AppendBlock(mContext->NewSeqBlock(src->Get(i, chunk))); + i += chunk; + } + Paste(start, &tempSequence); + return; + } + + long_sample_count srcLen = bsrc->mNumSamples; + int numNodes = mNodes.size(); + SeqBlockNodeVec srcNodes = bsrc->mNodes; + int srcNumNodes = srcNodes.size(); + int n; + + if (numNodes == 0 || + (start == mNumSamples && mNodes[numNodes-1].len() >= mMinSamples)) { + // Special case: this seq is currently empty, or it's safe to append + // onto the end because the current last block is longer than the + // minimum size + + for(n=0; nGetSeqBlockRef(srcNodes[n].block)); + + return; + } + + int n0 = FindNode(start); + + if (mNodes[n0].len() + srcLen < mMaxSamples) { + // Special case: we can fit all of the new samples inside of + // one block! + + long_sample_count splitPoint = start - mNodes[n0].start; + Buffer left = mNodes[n0].Get(0, splitPoint); + Buffer right = mNodes[n0].Get(splitPoint); + Buffer newBlockData = Buffer::Append(left, bsrc->Get(0, srcLen), right); + mContext->ReleaseSeqBlockRef(mNodes[n0].block); + mNodes[n0].block = mContext->NewSeqBlock(newBlockData); + mNumSamples += srcLen; + for(n=n0+1; nGet(0, srcLen), right); + SplitAndAppend(newBlockData); + } + else { + // The final case is that we're inserting at least five blocks. + // We divide these into three groups: the first two get merged + // with the first half of the split block, the middle ones get + // copied in as is, and the last two get merged with the last + // half of the split block. + + Buffer groupOneData = Buffer::Append(oldNodes[n0].Get(0, splitPoint), + srcNodes[0].Get(), + srcNodes[1].Get()); + SplitAndAppend(groupOneData); + + for(n=2; nGetSeqBlockRef(srcNodes[n].block)); + } + + Buffer groupThreeData = Buffer::Append(srcNodes[srcNumNodes-2].Get(), + srcNodes[srcNumNodes-1].Get(), + oldNodes[n0].Get(splitPoint)); + SplitAndAppend(groupThreeData); + } + + mContext->ReleaseSeqBlockRef(oldNodes[n0].block); + + for(n=n0+1; n +#include + +namespace Mezzo { + +// +// Sequence +// + +Sequence *Sequence::Cut(long_sample_count start, long_sample_count len) +{ + Sequence *result = Copy(start, len); + Delete(start, len); + return result; +} + +// +// MemorySequence +// + +MemorySequence::MemorySequence(Buffer::sampleFormat format) : + Sequence(format), + mBuffer(FloatBuffer(0)) +{ +} + +MemorySequence::~MemorySequence() +{ +} + +Sequence *MemorySequence::Duplicate() const +{ + MemorySequence *newSeq = new MemorySequence(); + newSeq->mBuffer = mBuffer; + return newSeq; +} + +// +// Editing +// + +long_sample_count MemorySequence::GetLength() const +{ + return mBuffer.GetLength(); +} + +Buffer MemorySequence::Get(long_sample_count start, int len) const +{ + ClientAssert(start >= 0 && start <= mBuffer.GetLength() && + len >= 0 && start+len <= mBuffer.GetLength(), + fmt("Attempt to Get from a MemorySequence with start=%d and " + "len=%d, when Sequence len is %d", + start, len, mBuffer.GetLength())); + + return mBuffer.Get(start, len); +} + +void MemorySequence::Set(long_sample_count start, Buffer newData) +{ + ClientAssert(start >= 0 && start <= mBuffer.GetLength() && + start+newData.GetLength() <= mBuffer.GetLength(), + fmt("Attempt to Set in a MemorySequence with start=%d and " + "len=%d, when Sequence len is %d", + start, newData.GetLength(), mBuffer.GetLength())); + + mBuffer.Set(start, newData); +} + +Sequence *MemorySequence::Copy(long_sample_count start, long_sample_count len) const +{ + ClientAssert(start >= 0 && start <= mBuffer.GetLength() && + len >= 0 && start+len <= mBuffer.GetLength(), + fmt("Attempt to Copy from a MemorySequence with start=%d and " + "len=%d, when Sequence len is %d", + start, len, mBuffer.GetLength())); + + MemorySequence *newSeq = new MemorySequence(); + newSeq->Append(Get(start, len)); + return newSeq; +} + +void MemorySequence::Paste(long_sample_count start, const Sequence *src) +{ + ClientAssert(start >= 0 && start <= mBuffer.GetLength(), + fmt("Attempt to Paste a MemorySequence at start=%d, " + "when Sequence len is %d", + start, mBuffer.GetLength())); + + Buffer oldBuffer = mBuffer; + mBuffer = Buffer::Append(oldBuffer.Get(0, start), + src->Get(0, src->GetLength()), + oldBuffer.Get(start, + oldBuffer.GetLength()-start)); +} + +void MemorySequence::Append(Buffer data) +{ + mBuffer = Buffer::Append(mBuffer, data); +} + +void MemorySequence::Delete(long_sample_count start, long_sample_count len) +{ + ClientAssert(start >= 0 && start <= mBuffer.GetLength() && + len >= 0 && start+len <= mBuffer.GetLength(), + fmt("Attempt to Delete in a MemorySequence with start=%d and " + "len=%d, when Sequence len is %d", + start, len, mBuffer.GetLength())); + + int origLen = mBuffer.GetLength(); + mBuffer = Buffer::Append(mBuffer.Get(0, start), + mBuffer.Get(start+len, + origLen-(start+len))); +} + +void MemorySequence::SetSilence(long_sample_count start, long_sample_count len) +{ + ClientAssert(start >= 0 && start <= mBuffer.GetLength() && + len >= 0 && start+len <= mBuffer.GetLength(), + fmt("Attempt to SetSilence in a MemorySequence with " + "start=%d and len=%d, when Sequence len is %d", + start, len, mBuffer.GetLength())); + + Buffer silence(mFormat, len); + mBuffer.Set(start, silence); +} + +void MemorySequence::InsertSilence(long_sample_count start, long_sample_count len) +{ + ClientAssert(start >= 0 && start <= mBuffer.GetLength() && + len >= 0, + fmt("Attempt to Insert Silence in a MemorySequence " + "at start=%d, when Sequence len is %d", + start, mBuffer.GetLength())); + + Buffer oldBuffer = mBuffer; + Buffer silence(mFormat, len); + mBuffer = Buffer::Append(oldBuffer.Get(0, start), + silence, + oldBuffer.Get(start, + oldBuffer.GetLength()-start)); +} + +void MemorySequence::Store(Storer& storer) +{ + // TODO +} + +void MemorySequence::GetWaveDisplay(FloatBuffer& outMin, FloatBuffer& outMax, FloatBuffer& outRMS, + Int16Buffer& outFlag, + long_sample_count start, long_sample_count len, int pixelLen, + double samplesPerPixel) +{ + // TODO +} + +// +// BlockedSequence +// + +BlockedSequence::BlockedSequence(SeqBlockContext* context, Buffer::sampleFormat format) : + Sequence(format), + mMinSamples(16384), + mMaxSamples(32768), + mNumSamples(0), + mContext(context), + mPrevWaveDisplayBlock0(0), + mPrevWaveDisplayBlock1(0), + mPrevWaveDisplayDivisor(0) +{ + mContext->Ref(); +} + +BlockedSequence::~BlockedSequence() +{ + unsigned int i; + + for(i=0; iReleaseSeqBlockRef(mNodes[i].block); + + mContext->Deref(); +} + +Sequence *BlockedSequence::Duplicate(SeqBlockContext* context) const +{ + BlockedSequence *newSeq = new BlockedSequence(context); + unsigned int i; + // we need to ref the blocks when we add them to the new sequence becuase + // right now they belong to this sequence + for(i=0; iAppendBlock(newSeq->mContext->GetSeqBlockRef(mNodes[i].block)); + return newSeq; +} + +Sequence *BlockedSequence::Duplicate() const +{ + return Duplicate(mContext); +} + +// +// Editing +// + +long_sample_count BlockedSequence::GetLength() const +{ + return mNumSamples; +} + +Buffer BlockedSequence::Get(long_sample_count start, int len) const +{ + ClientAssert(start >= 0 && start <= mNumSamples && + len >= 0 && start+len <= mNumSamples, + fmt("Attempt to Get from a BlockedSequence with " + "start=%s and len=%d, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), len, + LongSampleCountToStr(mNumSamples).c_str())); + + if (len==0) + return Buffer(mFormat, 0); + + int n = FindNode(start); + Buffer result(mFormat, len); + int i = 0; + + while(len) { + int bstart = start - mNodes[n].start; + int blen = mNodes[n].end() - start; + if (blen > len) + blen = len; + result.Set(i, mNodes[n].Get(bstart, blen)); + start += blen; + len -= blen; + i += blen; + n++; + } + + return result; +} + +void BlockedSequence::Set(long_sample_count start, Buffer newData) +{ + ClientAssert(start >= 0 && start <= mNumSamples && + start+newData.GetLength() <= mNumSamples, + fmt("Attempt to Set in a BlockedSequence with " + "start=%s and len=%d, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), newData.GetLength(), + LongSampleCountToStr(mNumSamples).c_str())); + + long_sample_count len = newData.GetLength(); + int n = FindNode(start); + int i = 0; + + while(len) { + int bstart = start - mNodes[n].start; + int blen = mNodes[n].end() - start; + if (blen > len) + blen = len; + Buffer newBlockData = mNodes[n].Get(); + newBlockData.Set(bstart, newData.Get(i, blen)); + mContext->ReleaseSeqBlockRef(mNodes[n].block); + mNodes[n].block = mContext->NewSeqBlock(newBlockData); + start += blen; + len -= blen; + i += blen; + n++; + } +} + +Sequence *BlockedSequence::Copy(long_sample_count start, long_sample_count len) const +{ + ClientAssert(start >= 0 && start <= mNumSamples && + len >= 0 && start+len <= mNumSamples, + fmt("Attempt to Copy from a BlockedSequence with " + "start=%d and len=%d, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), len, + LongSampleCountToStr(mNumSamples).c_str())); + + BlockedSequence *newSeq = new BlockedSequence(mContext); + int numNodes = mNodes.size(); + int n0 = FindNode(start); + int n1 = FindNode(start+len); + + // Do the first block + if (n0 >= 0 && n0 < numNodes && start != mNodes[n0].start) { + int blockStart = start - mNodes[n0].start; + int blockLen = mNodes[n0].len() - blockStart; + if (blockLen > len) + blockLen = len; + Buffer nodeData = mNodes[n0].Get(blockStart, blockLen); + newSeq->AppendBlock(newSeq->mContext->NewSeqBlock(nodeData)); + } + + if (n0 >= 0 && n0 < numNodes && start == mNodes[n0].start) + n0--; + + // If there are blocks in the middle, copy the blockfiles directly + for (int n = n0 + 1; n < n1; n++) + newSeq->AppendBlock(newSeq->mContext->GetSeqBlockRef(mNodes[n].block)); + + // Do the last block + if (n1 > n0 && n1 < numNodes) { + long_sample_count blen = start + len - mNodes[n1].start; + Buffer nodeData = mNodes[n1].Get(0, blen); + newSeq->AppendBlock(newSeq->mContext->NewSeqBlock(nodeData)); + } + + InternalAssert(newSeq->mNumSamples == len, + fmt("Copy failed: expected to get %s samples, " + "but produced a Sequence with %s samples.", + LongSampleCountToStr(len).c_str(), + LongSampleCountToStr(newSeq->mNumSamples).c_str())); + + return newSeq; +} + +void BlockedSequence::Paste(long_sample_count start, const Sequence *src) +{ + ClientAssert(start >= 0 && start <= mNumSamples, + fmt("Attempt to Paste into a BlockedSequence with " + "start=%s, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + ClientAssert(mNumSamples + (double)src->GetLength() < LONG_SAMPLE_COUNT_MAX, + fmt("Pasting %s samples in BlockedSequence of length %s " + "would overflow.", + LongSampleCountToStr(src->GetLength()).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + if (src->GetLength() == 0) + return; + + const BlockedSequence *bsrc = dynamic_cast(src); + if (!bsrc) { + // If it's some other type of Sequence, we convert it to + // a BlockedSequence, then call Paste again... + BlockedSequence tempSequence(mContext); + long_sample_count len = src->GetLength(); + int i = 0; + while(i < len) { + int chunk = mMaxSamples < len-i? mMaxSamples: len-i; + tempSequence.AppendBlock(mContext->NewSeqBlock(src->Get(i, chunk))); + i += chunk; + } + Paste(start, &tempSequence); + return; + } + + long_sample_count srcLen = bsrc->mNumSamples; + int numNodes = mNodes.size(); + SeqBlockNodeVec srcNodes = bsrc->mNodes; + int srcNumNodes = srcNodes.size(); + int n; + + if (numNodes == 0 || + (start == mNumSamples && mNodes[numNodes-1].len() >= mMinSamples)) { + // Special case: this seq is currently empty, or it's safe to append + // onto the end because the current last block is longer than the + // minimum size + + for(n=0; nGetSeqBlockRef(srcNodes[n].block)); + + return; + } + + int n0 = FindNode(start); + + if (mNodes[n0].len() + srcLen <= mMaxSamples) { + // Special case: we can fit all of the new samples inside of + // one block! + + long_sample_count splitPoint = start - mNodes[n0].start; + Buffer left = mNodes[n0].Get(0, splitPoint); + Buffer right = mNodes[n0].Get(splitPoint); + Buffer newBlockData = Buffer::Append(left, bsrc->Get(0, srcLen), right); + mContext->ReleaseSeqBlockRef(mNodes[n0].block); + mNodes[n0].block = mContext->NewSeqBlock(newBlockData); + mNumSamples += srcLen; + for(n=n0+1; nGet(0, srcLen), right); + SplitAndAppend(newBlockData); + } + else { + // The final case is that we're inserting at least five blocks. + // We divide these into three groups: the first two get merged + // with the first half of the split block, the middle ones get + // copied in as is, and the last two get merged with the last + // half of the split block. + + Buffer groupOneData = Buffer::Append(oldNodes[n0].Get(0, splitPoint), + srcNodes[0].Get(), + srcNodes[1].Get()); + SplitAndAppend(groupOneData); + + for(n=2; nGetSeqBlockRef(srcNodes[n].block)); + } + + Buffer groupThreeData = Buffer::Append(srcNodes[srcNumNodes-2].Get(), + srcNodes[srcNumNodes-1].Get(), + oldNodes[n0].Get(splitPoint)); + SplitAndAppend(groupThreeData); + } + + mContext->ReleaseSeqBlockRef(oldNodes[n0].block); + + for(n=n0+1; n 0 && mNodes[numNodes-1].len() < mMinSamples) { + SeqBlockNode *lastNode = &mNodes[numNodes-1]; + int lastLen = lastNode->len(); + int addLen = (lastLen + dataLen <= mMaxSamples)? + dataLen: mMaxSamples - lastLen; + Buffer newBlockData = Buffer::Append(lastNode->Get(), + data.Get(0, addLen)); + mContext->ReleaseSeqBlockRef(lastNode->block); + lastNode->block = mContext->NewSeqBlock(newBlockData); + mNumSamples += addLen; + data = data.Get(addLen, dataLen-addLen); + dataLen -= addLen; + } + + // Append the rest as new blocks + while(dataLen > 0) { + int addLen = dataLen <= mMaxSamples? dataLen: mMaxSamples; + AppendBlock(mContext->NewSeqBlock(data.Get(0, addLen))); + + // JHtoDM: this seems like it's going to be pretty inefficient: + // why not just increment the starting offset instead of copying + // all that data to a new buffer? + data = data.Get(addLen, dataLen-addLen); + dataLen -= addLen; + } +} + +void BlockedSequence::Delete(long_sample_count start, long_sample_count len) +{ + ClientAssert(start >= 0 && start <= mNumSamples && + len >= 0 && start+len <= mNumSamples, + fmt("Attempt to Delete from a BlockedSequence with " + "start=%s and len=%s, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), + LongSampleCountToStr(len).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + int numNodes = mNodes.size(); + int n0 = FindNode(start); + int n1 = FindNode(start+len); + int n; + + // Special case: if the samples to delete are all within a single + // block and the resulting length is not too small, perform the + // deletion within this block: + if (n0 == n1 && mNodes[n0].len() - len >= mMinSamples) { + Buffer left = mNodes[n0].Get(0, start-mNodes[n0].start); + Buffer right = mNodes[n0].Get(start+len-mNodes[n0].start, + mNodes[n0].end()-(start+len)); + mContext->ReleaseSeqBlockRef(mNodes[n0].block); + mNodes[n0].block = mContext->NewSeqBlock(Buffer::Append(left, right)); + mNumSamples -= len; + for(n=n0+1; n= mMinSamples || n0 == 0) { + AppendBlock(mContext->NewSeqBlock(oldNodes[n0].Get(0, preBufferLen))); + } + else { + mNumSamples -= oldNodes[n0-1].len(); + mNodes.pop_back(); + SplitAndAppend(Buffer::Append(oldNodes[n0-1].Get(), + oldNodes[n0].Get(0, preBufferLen))); + mContext->ReleaseSeqBlockRef(oldNodes[n0-1].block); + } + } + + if (n0 != n1) + // if n0 == n1 then the block will be deleted in a second + mContext->ReleaseSeqBlockRef(oldNodes[n0].block); + + // Delete blocks strictly between n0 and n1 + for (n = n0 + 1; n < n1; n++) + mContext->ReleaseSeqBlockRef(oldNodes[n].block); + + // Now, symmetrically, grab the samples in node n1 after the + // deletion point into postBuffer. If this is enough samples + // for its own block, or if this would be the last block in + // the array, write it out. Otherwise combine it with the + // subsequent block (splitting them 50/50 if necessary). + + int postBufferLen = oldNodes[n1].end() - (start + len); + if (postBufferLen != 0) // if there are samples in block n1 after the deletion region + { + int blockStart = oldNodes[n1].len() - postBufferLen; + if (postBufferLen >= mMinSamples || n1 == numNodes-1) { + AppendBlock(mContext->NewSeqBlock(oldNodes[n1].Get(blockStart, + postBufferLen))); + mContext->ReleaseSeqBlockRef(oldNodes[n1].block); + } + else { + SplitAndAppend(Buffer::Append(oldNodes[n1].Get(blockStart, + postBufferLen), + oldNodes[n1+1].Get())); + mContext->ReleaseSeqBlockRef(oldNodes[n1].block); + mContext->ReleaseSeqBlockRef(oldNodes[n1+1].block); + n1++; + } + } + else + { + mContext->ReleaseSeqBlockRef(oldNodes[n1].block); + } + + + // Copy the remaining blocks over from the old array + for(n = n1 + 1; n < numNodes; n++) { + oldNodes[n].start -= len; + mNodes.push_back(oldNodes[n]); + mNumSamples += oldNodes[n].len(); + } +} + +void BlockedSequence::SetSilence(long_sample_count start, long_sample_count len) +{ + ClientAssert(start >= 0 && start <= mNumSamples && + len >= 0 && start+len <= mNumSamples, + fmt("Attempt to SetSilence in a BlockedSequence with " + "start=%s and len=%s, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), + LongSampleCountToStr(len).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + int n = FindNode(start); + int i = 0; + + while(len) { + int bstart = start - mNodes[n].start; + int blen = mNodes[n].end() - start; + if (blen > len) + blen = len; + if (bstart == 0 && blen == mNodes[n].len()) { + mContext->ReleaseSeqBlockRef(mNodes[n].block); + mNodes[n].block = new SeqSilentBlock(blen); + } + else { + Buffer newBlockData = mNodes[n].Get(); + newBlockData.Set(bstart, FloatBuffer(blen)); + mContext->ReleaseSeqBlockRef(mNodes[n].block); + mNodes[n].block = mContext->NewSeqBlock(newBlockData); + } + start += blen; + len -= blen; + i += blen; + n++; + } +} + +void BlockedSequence::InsertSilence(long_sample_count start, long_sample_count len) +{ + ClientAssert(start >= 0 && start <= mNumSamples && len >= 0, + fmt("Attempt to Insert Silence into a BlockedSequence with " + "start=%s, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + // Create a new track containing as much silence as we + // need to insert, and then call Paste to do the insertion. + // We make use of a SeqSilentBlock, which takes up no + // space on disk. + + BlockedSequence *silence = new BlockedSequence(mContext); + while(len) { + int l = (len > mMaxSamples? mMaxSamples: len); + silence->AppendBlock(new SeqSilentBlock(l)); + len -= l; + } + + Paste(start, silence); + + delete silence; +} + +void BlockedSequence::AppendBlock(SeqBlock *block) +{ + mNodes.push_back(SeqBlockNode(block, mNumSamples)); + mNumSamples += block->GetLength(); +} + +void BlockedSequence::SplitAndAppend(const Buffer& data) +{ + int len = data.GetLength(); + int num = (len + (mMaxSamples - 1)) / mMaxSamples; + int i; + + for (i = 0; i < num; i++) { + int blockStart = i * len / num; + int blockLen = ((i + 1) * len / num) - blockStart; + AppendBlock(mContext->NewSeqBlock(data.Get(blockStart, blockLen))); + } +} + +int BlockedSequence::FindNode(long_sample_count pos) const +{ + InternalAssert(pos >= 0 && pos <= mNumSamples, + fmt("FindNode: Asked for pos=%s, mNumSamples=%s!", + LongSampleCountToStr(pos).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + int numNodes = mNodes.size(); + if (pos == mNumSamples) + return numNodes-1; + return FindNode(pos, 0, numNodes / 2, numNodes); +} + +int BlockedSequence::FindNode(long_sample_count pos, long_sample_count lo, + long_sample_count guess, long_sample_count hi) const +{ + // Finds the block containing a given sample number (pos) using + // a binary search. + + if (pos >= mNodes[guess].start && + pos < mNodes[guess].start + mNodes[guess].block->GetLength()) + return guess; + + if (pos < mNodes[guess].start) + return FindNode(pos, lo, (lo + guess) / 2, guess); + else + return FindNode(pos, guess + 1, (guess + 1 + hi) / 2, hi); +} + +void BlockedSequence::GetWaveDisplay(FloatBuffer& outMin, FloatBuffer& outMax, FloatBuffer& outRMS, + Int16Buffer& outFlag, + long_sample_count start, long_sample_count len, int pixelLen, + double samplesPerPixel) +{ + ClientAssert(start >= 0 && start <= mNumSamples && + len >= 0 && start+len <= mNumSamples, + fmt("Attempt to GetWaveDisplay from a BlockedSequence with " + "start=%s and len=%s, when Sequence len is %s", + LongSampleCountToStr(start).c_str(), + LongSampleCountToStr(len).c_str(), + LongSampleCountToStr(mNumSamples).c_str())); + + outMin.Resize(pixelLen); + outMax.Resize(pixelLen); + outRMS.Resize(pixelLen); + outFlag.Resize(pixelLen); + + long_sample_count where[pixelLen+1]; + for(int i = 0; i <= pixelLen; i++) + where[i] = (long_sample_count)floor(start + (i*samplesPerPixel) + 0.5); + + long_sample_count s0 = start; + long_sample_count s1 = start + len; + + if (s1 > mNumSamples) + s1 = mNumSamples; + + unsigned int block0 = FindNode(s0); + unsigned int block1 = FindNode(s1); + + int divisor; + if (samplesPerPixel >= 65536) + divisor = 65536; + else if (samplesPerPixel >= 256) + divisor = 256; + else + divisor = 1; + + unsigned int block; + + // mark everything this routine will use as used + for(block = block0; block <= block1; block++) + { + switch(divisor) { + case 1: + mNodes[block].block->IncrementDataReadRequestCount(); + break; + + default: + mNodes[block].block->IncrementSummaryReadRequestCount(); + break; + } + } + + // mark everything the last run used as unused + // DANGER WILL ROBINSON -- intolerant to the Sequence changing between calls! + if(mPrevWaveDisplayDivisor != 0) + { + for(block = mPrevWaveDisplayBlock0; block <= mPrevWaveDisplayBlock1; block++) + { + switch(mPrevWaveDisplayDivisor) { + case 1: + mNodes[block].block->DecrementDataReadRequestCount(); + break; + + default: + mNodes[block].block->DecrementSummaryReadRequestCount(); + break; + + } + } + } + + mPrevWaveDisplayBlock0 = block0; + mPrevWaveDisplayBlock1 = block1; + mPrevWaveDisplayDivisor = divisor; + + long_sample_count srcX = s0; + + FloatBuffer temp(0); + FloatBuffer tempMin(0); + FloatBuffer tempMax(0); + FloatBuffer tempSumSq(0); + + int pixel = 0; + float theMin = 0; + float theMax = 0; + float sumsq = float(0.0); + unsigned int b = block0; + int jcount = 0; + bool wasLoaded = false; + + while (srcX < s1) { + // Get more samples + long_sample_count num; + + num = ((mNodes[b].block->GetLength() - + (srcX - mNodes[b].start)) + divisor - 1) + / divisor; + + if (num > (s1 - srcX + divisor - 1) / divisor) + num = (s1 - srcX + divisor - 1) / divisor; + + SeqBlockSummary *summary; + switch (divisor) { + default: + case 1: + if(mNodes[b].block->GetIfCached(temp, srcX - mNodes[b].start, num)) + { + temp = temp.AsFloat(); + wasLoaded = true; + } + else + { + wasLoaded = false; + } + break; + case 256: + if(mNodes[b].block->GetSummaryIfCached(&summary)) + { + wasLoaded = true; + } + else + { + wasLoaded = false; + } + tempMin = summary->m256Min; + tempMax = summary->m256Max; + tempSumSq = summary->m256SumSq; + + break; + case 65536: + if(mNodes[b].block->GetSummaryIfCached(&summary)) + { + wasLoaded = true; + } + else + { + wasLoaded = false; + } + tempMin = summary->m64kMin; + tempMax = summary->m64kMax; + tempSumSq = summary->m64kSumSq; + + break; + } + + // Get min/max/rms of samples for each pixel we can + int x = 0; + + if (b==block0) { + if (divisor > 1) { + theMin = tempMin[0]; + theMax = tempMax[0]; + } + else { + theMin = temp[0]; + theMax = temp[0]; + } + sumsq = float(0.0); + jcount = 0; + } + + while (x < num) { + + while (pixel < pixelLen && + where[pixel] / divisor == srcX / divisor + x) { + if (pixel > 0) { + outMin[pixel - 1] = theMin; + outMax[pixel - 1] = theMax; + if (jcount > 0) + outRMS[pixel - 1] = (float)sqrt(sumsq / jcount); + else + outRMS[pixel - 1] = 0.0f; + + outFlag[pixel-1] = wasLoaded; + } + pixel++; + if (where[pixel] != where[pixel-1]) { + theMin = FLT_MAX; + theMax = -FLT_MAX; + sumsq = float(0.0); + jcount = 0; + } + } + + long_sample_count stop = (where[pixel] - srcX) / divisor; + + if (stop == x) + stop++; + if (stop > num) + stop = num; + + switch (divisor) { + default: + case 1: + while (x < stop) { + if (temp[x] < theMin) + theMin = temp[x]; + if (temp[x] > theMax) + theMax = temp[x]; + sumsq += (temp[x]) * (temp[x]); + x++; + jcount++; + } + break; + case 256: + case 65536: + while (x < stop) { + if (tempMin[x] < theMin) + theMin = tempMin[x]; + if (tempMax[x] > theMax) + theMax = tempMax[x]; + sumsq += tempSumSq[x]; + x++; + jcount++; + } + + break; + } + } + + b++; + + srcX += num * divisor; + + if (b >= mNodes.size()) + break; + + srcX = mNodes[b].start; + + } + + // Make sure that min[pixel - 1] doesn't segfault + if (pixel <= 0) + pixel = 1; + + if (pixel == 0) + pixel++; + + while (pixel <= pixelLen) { + outMin[pixel - 1] = theMin; + outMax[pixel - 1] = theMax; + if (jcount > 0) + outRMS[pixel - 1] = (float)sqrt(sumsq / jcount); + else + outRMS[pixel - 1] = 0.0f; + pixel++; + } +} + +bool BlockedSequence::NewWaveDisplayDataAvailable() +{ + return true; +} + +// +// Debugging +// + +void BlockedSequence::ConsistencyCheck(bool verbose) +{ + long_sample_count len = 0; + int errs = 0; + unsigned int i; + + for(i=0; i 0 && i < mNodes.size()-1 && + (mNodes[i].len() < mMinSamples || mNodes[i].len() > mMaxSamples)) { + printf("Error: invariant violated in node %d: len=%d " + "(min=%d, max=%d)\n", + i, mNodes[i].len(), mMinSamples, mMaxSamples); + errs++; + } + len += mNodes[i].len(); + } + + if (len != mNumSamples) { + printf("Error: mNumSamples is %lld, should be %lld\n", + mNumSamples, len); + errs++; + } + + if (verbose || errs) { + printf("Total errors: %d\n", errs); + + len = 0; + for(i=0; i(loader.GetObj(tok.attrs["contextid"])); + mContext->Ref(); + + while(1) { + // Get + tok = loader.GetNextToken(); + + if(tok.name == "BlockedSequence" && tok.type == Loader::Token::endNode) + { + break; + } + else if(tok.name == "SeqBlockNode" && tok.type == Loader::Token::beginNode) + { + long_sample_count nodeStart = StrToLongSampleCount(tok.attrs["start"]); + + // It would be possible for us to obtain the SeqBlock directly from the loader + // using Loader::GetObj(), but we need the Context to be in on the transaction + // so the reference-counting is handled properly + + // Get + tok = loader.GetNextToken(); + ClientAssert(tok.name == "SeqBlock" && tok.type == Loader::Token::beginNode, + "SeqBlockNode only expects SeqBlock children, not " + tok.name); + + SeqBlock *block = mContext->GetSeqBlockRef(loader, tok.attrs["id"]); + mNodes.push_back(SeqBlockNode(block, nodeStart)); + + // Get + tok = loader.GetNextToken(); + ClientAssert(tok.name == "SeqBlock" && tok.type == Loader::Token::endNode, + "SeqBlock doesn't expect any children, but got " + tok.name); + + // Get + tok = loader.GetNextToken(); + ClientAssert(tok.name == "SeqBlockNode" && tok.type == Loader::Token::endNode, + "SeqBlockNode expects one SeqBlock child, but got a second child of" + tok.name); + } + else + { + ClientAssert(false, "BlockedSequence only expects SeqBlockNode children, not " + tok.name); + } + } + + +} + +void BlockedSequence::Store(Storer& storer) +{ + // + // + // + // + // + + AttrDict attrs; + attrs["len"] = LongSampleCountToStr(mNumSamples); + attrs["minsamples"] = fmt("%d", mMinSamples); + attrs["maxsamples"] = fmt("%d", mMaxSamples); + attrs["contextid"] = storer.GetID(mContext); + attrs["format"] = SampleFormatToStr(mFormat); + + storer.StoreBeginNode("BlockedSequence", attrs); + + for(unsigned int i = 0; i < mNodes.size(); i++) + { + attrs.clear(); + attrs["start"] = LongSampleCountToStr(mNodes[i].start); + + storer.StoreBeginNode("SeqBlockNode", attrs); + + // + // Store the SeqBlock + // + attrs.clear(); + try { + attrs["id"] = storer.GetID(mNodes[i].block); + storer.StoreLeafNode("SeqBlock", attrs); + } + catch(ClientException& e) + { + ClientAssert(false, "Attempted to save a Sequence without saving its context first"); + } + + storer.StoreEndNode("SeqBlockNode"); + } + + storer.StoreEndNode("BlockedSequence"); +} + +std::set +BlockedSequence::GetPrereqStorables() +{ + std::set storables; + storables.insert(mContext); + + return storables; +} + +std::set +BlockedSequence::GetManagedFilesInUse() +{ + std::set inUse; + ManagedFile *file; + + for(unsigned int i = 0; i < mNodes.size(); i++) + { + file = dynamic_cast(mNodes[i].block); + + if(file) + inUse.insert(file); + } + + return inUse; +} + +} // namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/Sequence.h b/src/Sequence.h new file mode 100644 index 0000000..04c444d --- /dev/null +++ b/src/Sequence.h @@ -0,0 +1,210 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + Sequence.h + + Copyright (c) 2004 Dominic Mazzoni + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_SEQUENCE__ +#define __MEZZO_SEQUENCE__ + +#include + +#include "Types.h" +#include "Buffer.h" +#include "SeqBlock.h" + +namespace Mezzo { + +class SeqBlockContext; + +class Sequence : public virtual Storable { + public: + + virtual ~Sequence() {} + virtual Sequence *Duplicate() const = 0; + + // + // Editing + // + + virtual long_sample_count GetLength() const = 0; + + virtual Buffer Get(long_sample_count start, int len) const = 0; + virtual void Set(long_sample_count start, Buffer newData) = 0; + + virtual Sequence *Cut(long_sample_count start, long_sample_count len); + virtual Sequence *Copy(long_sample_count start, long_sample_count len) const = 0; + virtual void Paste(long_sample_count start, const Sequence *src) = 0; + + virtual void Append(Buffer data) = 0; + + virtual void Delete(long_sample_count start, long_sample_count len) = 0; + + virtual void SetSilence(long_sample_count start, long_sample_count len) = 0; + virtual void InsertSilence(long_sample_count start, long_sample_count len) = 0; + + virtual void GetWaveDisplay(FloatBuffer& outMin, FloatBuffer& outMax, FloatBuffer& outRMS, + Int16Buffer& outFlag, + long_sample_count start, long_sample_count len, int pixelLen, + double samplesPerPixel) = 0; + + protected: + Sequence(Buffer::sampleFormat format): mFormat(format) {} + Buffer::sampleFormat mFormat; +}; + +class MemorySequence : public Sequence { + public: + + MemorySequence(Buffer::sampleFormat format = Buffer::FloatSample); + virtual ~MemorySequence(); + + virtual Sequence *Duplicate() const; + + // + // Editing + // + + virtual long_sample_count GetLength() const; + + virtual Buffer Get(long_sample_count start, int len) const; + virtual void Set(long_sample_count start, Buffer newData); + + virtual Sequence *Copy(long_sample_count start, long_sample_count len) const; + virtual void Paste(long_sample_count start, const Sequence *src); + + virtual void Append(Buffer data); + + virtual void Delete(long_sample_count start, long_sample_count len); + + virtual void SetSilence(long_sample_count start, long_sample_count len); + virtual void InsertSilence(long_sample_count start, long_sample_count len); + + virtual void GetWaveDisplay(FloatBuffer& outMin, FloatBuffer& outMax, FloatBuffer& outRMS, + Int16Buffer& outFlag, + long_sample_count start, long_sample_count len, int pixelLen, + double samplesPerPixel); + + void Store(Storer& storer); + + protected: + Buffer mBuffer; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +class SeqBlockNode { + public: + SeqBlockNode(SeqBlock *_block, long_sample_count _start) { + block = _block; start = _start; + } + int len() const { return block->GetLength(); } + long_sample_count end() const { return start + len(); } + Buffer Get() const { + return block->Get(0, len()); + } + Buffer Get(long_sample_count start) const { + return block->Get(start, len()-start); + } + Buffer Get(long_sample_count start, int len) const { + return block->Get(start, len); + } + SeqBlock *block; + long_sample_count start; +}; + +typedef std::vector SeqBlockNodeVec; + +#endif + +class BlockedSequence : public Sequence { + public: + + BlockedSequence(SeqBlockContext *context, + Buffer::sampleFormat format = Buffer::FloatSample); + BlockedSequence(Loader& loader); + + virtual ~BlockedSequence(); + + // duplicate in the same context + virtual Sequence *Duplicate() const; + virtual Sequence *Duplicate(SeqBlockContext* context) const; + + // + // Editing + // + + virtual long_sample_count GetLength() const; + + virtual Buffer Get(long_sample_count start, int len) const; + virtual void Set(long_sample_count start, Buffer newData); + + virtual Sequence *Copy(long_sample_count start, long_sample_count len) const; + virtual void Paste(long_sample_count start, const Sequence *src); + + virtual void Append(Buffer data); + + virtual void Delete(long_sample_count start, long_sample_count len); + + virtual void SetSilence(long_sample_count start, long_sample_count len); + virtual void InsertSilence(long_sample_count start, long_sample_count len); + + virtual void GetWaveDisplay(FloatBuffer& outMin, FloatBuffer& outMax, FloatBuffer& outRMS, + Int16Buffer& outFlag, + long_sample_count start, long_sample_count len, int pixelLen, + double samplesPerPixel); + + + virtual bool NewWaveDisplayDataAvailable(); + + // + // Debugging + // + + virtual void ConsistencyCheck(bool verbose); + + virtual void Store(Storer& storer); + + virtual std::set GetPrereqStorables(); + virtual std::set GetManagedFilesInUse(); + + protected: + virtual void AppendBlock(SeqBlock *block); + virtual void SplitAndAppend(const Buffer& data); + + int FindNode(long_sample_count pos) const; + int FindNode(long_sample_count pos, long_sample_count lo, long_sample_count guess, + long_sample_count hi) const; + + int mMinSamples; + int mMaxSamples; + long_sample_count mNumSamples; + SeqBlockNodeVec mNodes; + SeqBlockContext *mContext; + + long_sample_count mPrevWaveDisplayBlock0; + long_sample_count mPrevWaveDisplayBlock1; + int mPrevWaveDisplayDivisor; +}; + +} // namespace + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/Storable.cpp b/src/Storable.cpp new file mode 100644 index 0000000..a0d9af3 --- /dev/null +++ b/src/Storable.cpp @@ -0,0 +1,55 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + Storable.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "Storable.h" +#include "Util.h" +#include "Exceptions.h" + +namespace Mezzo { + +std::string Storer::CreateID(Storable *obj) +{ + std::string id = fmt("%d", ++mLastID); + mIdObjects[obj] = id; + return id; +} + +std::string Storer::GetID(Storable *obj) +{ + ClientAssert(mIdObjects.count(obj) == 1, "This storer doesn't have an ID for that object"); + return mIdObjects[obj]; +} + +void Loader::RegisterObj(std::string id, Storable *obj) +{ + mIdObjects[id] = obj; +} + +Storable *Loader::GetObj(std::string id) +{ + ClientAssert(mIdObjects.count(id) == 1, "This loader doesn't have an object for ID " + id); + return mIdObjects[id]; +} + +} // namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/Storable.h b/src/Storable.h new file mode 100644 index 0000000..248155d --- /dev/null +++ b/src/Storable.h @@ -0,0 +1,137 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + Storable.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_STORABLE__ +#define __MEZZO_STORABLE__ + +#include +#include +#include + +namespace Mezzo { + +typedef std::map AttrDict; + +class ManagedFile; +class Storable; + +/// An object that can store hierarchical data in a persistent medium (eg. a disk file) + +/// An abstract interface for an object that can store hierarchical data in +/// a persistent medium (eg. to a disk file). The most common implementation +/// is to store to an XML file, as in XMLStorer. +/// +/// Since the structures being stored are not strictly hierarchical, there +/// is a facility for creating identifiers for objects that makes it possible +/// to create references between objects from different hierarchies. When +/// an object that may be referenced saves itself, it will create an ID +/// that is associated with its memory address. Later, when a different object +/// wants to refer to the first object, it asks the storer if there is an +/// ID associated with that address, and if so it can just store the ID. + +class Storer +{ + public: + virtual ~Storer() { } + + /// Store the beginning of a hierarchical node. + virtual void StoreBeginNode(std::string name, AttrDict attr) = 0; + + /// Store the end of a hierarchical node. + virtual void StoreEndNode(std::string name) = 0; + + /// Store a node that doesn't have any children. + virtual void StoreLeafNode(std::string name, AttrDict attr) = 0; + + /// Create an ID that is associated with this object + std::string CreateID(Storable *obj); + + /// Retrieve an ID associated with this object + std::string GetID(Storable *obj); + + protected: + Storer():mLastID(0) { } + + private: + std::map mIdObjects; + int mLastID; +}; + +/// An object that can load hierarchical data from a persistent medium (eg. a disk file) + +/// An abstract interface for an object that can load hierarchical data from +/// a persistent medium (eg. to a disk file). The most common implementation +/// is to load from an XML file, as in XMLLoader. This class loads data +/// stored by a corresponding Storer class. +/// +/// Since the structures being loaded are not strictly hierarchical, there +/// is a facility for creating identifiers for objects that makes it possible +/// to create references between objects from different hierarchies. When +/// an object that that is being loaded sees that an ID has been stored for that +/// object, it will register itself with the loader so that other objects can +/// obtain a pointer to this object. + +class Loader +{ + public: + virtual ~Loader() { } + + struct Token { + enum { + beginNode, + endNode + } type; + std::string name; + AttrDict attrs; + }; + + virtual Token GetNextToken() = 0; + virtual Token PeekNextToken() = 0; + + void RegisterObj(std::string id, Storable *obj); + Storable *GetObj(std::string id); + + private: + std::map mIdObjects; +}; + +class Storable +{ + public: + virtual ~Storable() { } + + virtual void Store(Storer& storer) = 0; + + /// Get the list of storables (if any) that must be stored before this object can be stored + virtual std::set GetPrereqStorables() { std::set x; return x; } + + /// Get the list of ManagedFiles (if any) that must be locked for this object to be loadable + virtual std::set GetManagedFilesInUse() { std::set x; return x; } + + static Storable* Load(Loader& loader); +}; + +} // namespace + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/Types.h b/src/Types.h new file mode 100644 index 0000000..b70bb8e --- /dev/null +++ b/src/Types.h @@ -0,0 +1,71 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + Types.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_TYPES__ +#define __MEZZO_TYPES__ + +#include + +#include + +#ifdef POSH_64BIT_INTEGER + +// For use in situations where 32 bits could be a limiting factor. For example, Buffer +// does not need to use it because it makes no sense to have a buffer longer than two +// million samples long. SeqBlock does not need it. Sequence does, and WaveTrack does. +typedef posh_i64_t long_sample_count; +#define LONG_SAMPLE_COUNT_MAX POSH_I64_MAX + +inline std::string LongSampleCountToStr(long_sample_count num) +{ + char tmp[100]; + snprintf(tmp, sizeof(tmp), "%lld", num); + return std::string(tmp); +} + +inline long_sample_count StrToLongSampleCount(std::string str) +{ + return strtoll(str.c_str(), NULL, 0); +} + +#else + +typedef posh_i32_t long_sample_count; +#define LONG_SAMPLE_COUNT_MAX POSH_I32_MAX + +inline std::string LongSampleCountToStr(long_sample_count num) +{ + char tmp[100]; + snprintf(tmp, sizeof(tmp), "%d", num); + return std::string(tmp); +} + +inline long_sample_count StrToLongSampleCount(std::string str) +{ + return strtol(str.c_str(), NULL, 0); +} + +#endif + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..0a8d173 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,43 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + Util.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "Util.h" + +#include + +std::string fmt(const char *format, ...) +{ + va_list args; + char buffer[1000]; + + va_start(args, format); + /** vsnprintf seems better, but it doesn't guarantee null termination, + ** so it's not available as a normal function from VC++... + vsnprintf(buffer, sizeof(buffer), format, args); + **/ + vsprintf(buffer, format, args); + va_end(args); + + return std::string(buffer); +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/Util.h b/src/Util.h new file mode 100644 index 0000000..91e4b73 --- /dev/null +++ b/src/Util.h @@ -0,0 +1,46 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + Util.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_UTIL__ +#define __MEZZO_UTIL__ + +#include +#include + +std::string fmt(const char *format, ...); + +// It is kind of silly to me that STL doesn't have a set_intesection function with +// this signature. To use set_intersection directly, you have to construct an +// insert_iterator and do other stuff that isn't pretty. +template +std::set SetIntersection(std::set x, std::set y) +{ + std::set out; + std::insert_iterator > out_iterator(out, out.begin()); + set_intersection(x.begin(), x.end(), y.begin(), y.end(), out_iterator); + return out; +} + + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/XMLLoadStore.cpp b/src/XMLLoadStore.cpp new file mode 100644 index 0000000..9608e8a --- /dev/null +++ b/src/XMLLoadStore.cpp @@ -0,0 +1,161 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + XMLLoadStore.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "XMLLoadStore.h" + +#include "Exceptions.h" +#include "Util.h" + +namespace Mezzo { + +static void PrintTabs(FILE *file, int numTabs) +{ + for(int i = 0; i < numTabs; i++) + fprintf(file, "\t"); +} + +XMLStorer::XMLStorer(std::string outFile, int startingIndent): + mFile(fopen(outFile.c_str(), "w")), + mLevel(startingIndent) +{ + UserAssert(mFile, "Unable to open output file " + outFile); +} + +XMLStorer::~XMLStorer() +{ + fclose(mFile); +} + +void XMLStorer::StoreBeginNode(std::string name, AttrDict attrs) +{ + PrintTabs(mFile, mLevel++); + fprintf(mFile, "<%s", name.c_str()); + + for(AttrDict::const_iterator i = attrs.begin(); i != attrs.end(); i++) + fprintf(mFile, " %s=\"%s\"", i->first.c_str(), i->second.c_str()); + + fprintf(mFile, ">\n"); +} + +void XMLStorer::StoreEndNode(std::string name) +{ + PrintTabs(mFile, --mLevel); + fprintf(mFile, "\n", name.c_str()); +} + +void XMLStorer::StoreLeafNode(std::string name, AttrDict attrs) +{ + PrintTabs(mFile, mLevel); + fprintf(mFile, "<%s", name.c_str()); + + for(AttrDict::const_iterator i = attrs.begin(); i != attrs.end(); i++) + fprintf(mFile, " %s=\"%s\"", i->first.c_str(), i->second.c_str()); + + fprintf(mFile, "/>\n"); +} + +// ------------------------------------------------------------------------------------- +// +// XMLLoader +// +// ------------------------------------------------------------------------------------- + +XMLLoader::XMLLoader(std::string fileName): + mFile(fopen(fileName.c_str(), "r")), + mParser(XML_ParserCreate(NULL)) +{ + UserAssert(mFile, fmt("Unable to open XML file %s for reading", fileName.c_str())); + UserAssert(mParser, "Unable to create expat XML Parser"); + + XML_SetElementHandler(mParser, XMLLoader::StartElementCallback, XMLLoader::EndElementCallback); + XML_SetUserData(mParser, this); +} + +XMLLoader::~XMLLoader() +{ + fclose(mFile); + XML_ParserFree(mParser); +} + +Loader::Token XMLLoader::PeekNextToken() +{ + while(mPendingTokens.size() == 0) + { + const int CHARS_PER_RUN = 50; + void *buf = XML_GetBuffer(mParser, CHARS_PER_RUN); + int read = fread(buf, 1, CHARS_PER_RUN, mFile); + int ret = XML_ParseBuffer(mParser, read, feof(mFile)); + + UserAssert(ret != 0, fmt("Error parsing XML: %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(mParser)), + XML_GetCurrentLineNumber(mParser), + XML_GetCurrentColumnNumber(mParser))); + } + + return mPendingTokens.front(); +} + +Loader::Token XMLLoader::GetNextToken() +{ + Token toReturn = PeekNextToken(); + mPendingTokens.pop(); + return toReturn; +} + +// static +void XMLLoader::StartElementCallback(void *userData, const char *name, const char **attrs) +{ + XMLLoader *This = static_cast(userData); + + Token token; + token.name = name; + token.type = Loader::Token::beginNode; + + while(*attrs) + { + const char *attr = *attrs++; + const char *value = *attrs++; + + if(!value) + break; + + token.attrs[attr] = value; + } + + This->mPendingTokens.push(token); +} + +// static +void XMLLoader::EndElementCallback(void *userData, const char *name) +{ + XMLLoader *This = static_cast(userData); + + Token token; + token.name = name; + token.type = Loader::Token::endNode; + + This->mPendingTokens.push(token); +} + +} // namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/XMLLoadStore.h b/src/XMLLoadStore.h new file mode 100644 index 0000000..fe6da20 --- /dev/null +++ b/src/XMLLoadStore.h @@ -0,0 +1,71 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + XMLLoadStore.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_XMLLOADSTORE__ +#define __MEZZO_XMLLOADSTORE__ + +#include +#include + +#include +#include "Storable.h" + +namespace Mezzo { + +class XMLStorer : public Storer { + public: + XMLStorer(std::string outFile, int startingIndent = 0); + + ~XMLStorer(); + void StoreBeginNode(std::string name, AttrDict attrs); + void StoreEndNode(std::string name); + + // short for StoreBeginNode() and StoreEndNode() + void StoreLeafNode(std::string name, AttrDict attrs); + + private: + FILE* mFile; + int mLevel; +}; + +class XMLLoader : public Loader { + public: + XMLLoader(std::string fileName); + ~XMLLoader(); + + Token GetNextToken(); + Token PeekNextToken(); + + private: + static void StartElementCallback(void *userData, const char *name, const char **attr); + static void EndElementCallback(void *userData, const char *name); + + FILE* mFile; + std::queue mPendingTokens; + XML_Parser mParser; +}; + +} // namespace + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/platform/.sconsign b/src/platform/.sconsign new file mode 100644 index 0000000..32dbe74 --- /dev/null +++ b/src/platform/.sconsign @@ -0,0 +1,3 @@ +}qUDiskFunctions.hq(cSCons.Sig +SConsignEntry +qoq}q(U timestampqJêôÜAUcsigqU 5e2e4519e0ecedee9864595457c65fe8qubs. \ No newline at end of file diff --git a/src/platform/DiskFunctions.h b/src/platform/DiskFunctions.h new file mode 100644 index 0000000..117a3df --- /dev/null +++ b/src/platform/DiskFunctions.h @@ -0,0 +1,68 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + platform/DiskFunctions.h + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#ifndef __MEZZO_DISK_FUNCTIONS__ +#define __MEZZO_DISK_FUNCTIONS__ + +#include + +namespace Mezzo { + +namespace Platform { + + class FileName { + public: + FileName(std::string fileName):mFileName(fileName) { } + FileName(char *fileName):mFileName(fileName) { } + FileName() { } + + const FileName& operator=(const std::string fileName) { mFileName = fileName; return *this; } + + std::string GetFullPath() { return mFileName; } + + private: + std::string mFileName; + }; + + bool FileExists(FileName fileName); + bool DirExists(std::string dirName); + + // if directory cannot be created, or is already created but cannot be + // written to (perhaps for permissions reasons) throw an exception. + void CreateDirectory(std::string dirName); + void DeleteDirectory(std::string dirName, bool recurse=true); + int GetFilesInDir(std::string dirName); + + void DeleteFile(FileName fileName); + + void CopyFile(FileName from, FileName to); + void MoveFile(FileName from, FileName to); + + extern const char DirSeparator; + +} // namespace + +} // namespace + +#endif + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/platform/posix/.sconsign b/src/platform/posix/.sconsign new file mode 100644 index 0000000..f0c5f3a --- /dev/null +++ b/src/platform/posix/.sconsign @@ -0,0 +1,3 @@ +}qUDiskFunctions.cppq(cSCons.Sig +SConsignEntry +qoq}q(U timestampqJêôÜAUcsigqNubs. \ No newline at end of file diff --git a/src/platform/posix/DiskFunctions.cpp b/src/platform/posix/DiskFunctions.cpp new file mode 100644 index 0000000..f41e1f9 --- /dev/null +++ b/src/platform/posix/DiskFunctions.cpp @@ -0,0 +1,116 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + platform/posix/DiskFunctions.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "../DiskFunctions.h" + +#include +#include +#include +#include +#include +#include + +namespace Mezzo { + +namespace Platform { + +const char DirSeparator = '/'; + +bool FileExists(FileName fileName) +{ + struct stat fileInfo; + int ret = stat(fileName.GetFullPath().c_str(), &fileInfo); + + return (ret == 0 && S_ISREG(fileInfo.st_mode)); +} + +bool DirExists(std::string dirName) +{ + struct stat fileInfo; + int ret = stat(dirName.c_str(), &fileInfo); + + return (ret == 0 && S_ISDIR(fileInfo.st_mode)); +} + +// if directory cannot be created, or is already created but cannot be +// written to (perhaps for permissions reasons) throw an exception. +void CreateDirectory(std::string dirName) +{ + if(DirExists(dirName)) return; + + //mkdir(dirName.c_str(), DEFFILEMODE); + std::string command = "mkdir " + dirName; + system(command.c_str()); +} + +void DeleteDirectory(std::string dirName, bool recurse) +{ + std::string command; + if(recurse) + command = "rm -rf " + dirName; + else + command = "rmdir " + dirName; + + //printf(command.c_str()); + system(command.c_str()); +} + +int GetFilesInDir(std::string dirName) +{ + DIR *dir = opendir(dirName.c_str()); + int numFiles = -2; // there are always two entries for . and .. + + while(readdir(dir)) + numFiles++; + + return numFiles; +} + +void DeleteFile(FileName fileName) +{ + unlink(fileName.GetFullPath().c_str()); +} + +void CopyFile(FileName from, FileName to) +{ + /* + std::string destFileName = dirName; + int sourceFile = open(fileName.c_str(), O_RDONLY, 0); + int destFile = open(destFileName.c_str(), O_WRONLY, DEFFILEMODE); + */ + + // ... more to come +} + +void MoveFile(FileName from, FileName to) +{ + // if on the same volume, link new and unlink old, otherwise copy + // and delete + std::string command = "mv " + from.GetFullPath() + " " + to.GetFullPath(); + system(command.c_str()); +} + +} // namespace + +} // namespace + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/src/srsw_queue.c b/src/srsw_queue.c new file mode 100644 index 0000000..43af647 --- /dev/null +++ b/src/srsw_queue.c @@ -0,0 +1,71 @@ + +#include + +typedef struct Node { + int value; + struct Node *next; +}Node; + +typedef struct SRSW_Queue { + Node *head, *tail; +}SRSW_Queue; + + +SRSW_Queue *srsw_queue_new() +{ + SRSW_Queue *q = (SRSW_Queue*)malloc(sizeof(SRSW_Queue)); + Node *dummy = (Node*)malloc(sizeof(Node)); + + dummy->next = NULL; + + q->head = dummy; + q->tail = dummy; + + return q; +} + +void srsw_queue_enqueue(SRSW_Queue *q, int value) +{ + Node *n = (Node*)malloc(sizeof(Node)); + + if(!n) + printf("out of memory!!\n"); + n->value = value; + n->next = NULL; + + /* let the store settle here */ + + q->tail->next = n; + q->tail = n; +} + +int srsw_queue_dequeue(SRSW_Queue *q, int *value) +{ + Node *n = q->head; + Node *new_head = n->next; + + if(new_head == NULL) + return 0; + + *value = new_head->value; + q->head = new_head; + free(n); + + return 1; +} + +int srsw_queue_get_count(SRSW_Queue *q) +{ + Node *n = q->head; + int num = 0; + + /* the head node doesn't count; it is a dummy */ + while(n->next) + { + n = n->next; + num++; + } + + return num; +} + diff --git a/src/srsw_queue.h b/src/srsw_queue.h new file mode 100644 index 0000000..6c3c548 --- /dev/null +++ b/src/srsw_queue.h @@ -0,0 +1,15 @@ + +typedef struct Node { + int value; + struct Node *next; +}Node; + +typedef struct SRSW_Queue { + Node *head, *tail; +}SRSW_Queue; + +SRSW_Queue *srsw_queue_new(); +void srsw_queue_enqueue(SRSW_Queue *q, void *value); +int srsw_queue_dequeue(SRSW_Queue *q, void **value); +int srsw_queue_get_count(SRSW_Queue *q); + diff --git a/tests/.sconsign b/tests/.sconsign new file mode 100644 index 0000000..224851e --- /dev/null +++ b/tests/.sconsign @@ -0,0 +1,4 @@ +}q(UTestManagedFileContextq(cSCons.Sig +SConsignEntry +qoq}qUbsigqNsbU MezzoTest.cppq(hoq}q (U timestampq +JÁôÜAUcsigq U 87ea5c3198b550d29db960e9aef45f54q ubUTestManagedFileContext.cppq (hoq}q(U timestampqJÁôÜAUcsigqU 648f196fceb40bdeadd422ece70c5a4fqubU MezzoTest.oq(hoq}qUbsigqNsbU MezzoTestq(hoq}qhNsbUTestManagedFileContext.oq(hoq}qhNsbu. \ No newline at end of file diff --git a/tests/MezzoCacheDisplay.cpp b/tests/MezzoCacheDisplay.cpp new file mode 100644 index 0000000..ac433be --- /dev/null +++ b/tests/MezzoCacheDisplay.cpp @@ -0,0 +1,123 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + MezzoPlay.cpp + + Copyright (c) 2004 Dominic Mazzoni + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "Sequence.h" +#include "ManagedFileContext.h" +#include "DiskIO.h" +#include "BufferGroup.h" +#include "XMLLoadStore.h" + +#include + +extern "C" { +#include "srsw_queue.h" +} + +using namespace Mezzo; + +ManagedFileContext *gContext; +BlockedSequence *gSequence; + +void *DiskIOThreadFunc(void *arg) +{ + //gDiskIO->Run(); + while(1) { + ManagedFileContext::LoadBackgroundRequests(); + } + + return NULL; +} + +void StartDiskIOThread() +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, + DiskIOThreadFunc, NULL)) { + fprintf(stderr, "Fatal error: could not create Disk I/O thread!\n"); + exit(0); + } +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", + argv[0]); + return -1; + } + + XMLLoader loader(argv[1]); + loader.GetNextToken(); + + gContext = new ManagedFileContext(loader); + gSequence = new BlockedSequence(loader); + + StartDiskIOThread(); + + int numSamples = gSequence->GetLength(); + int totalWidth = 72; + double samplesPerPixel = numSamples / (double)totalWidth; + + int screenLeft = 0; + int screenWidth = 30; + int tick = 0; + + for(;;) { + struct timespec sleeptime; + sleeptime.tv_sec = 0; + sleeptime.tv_nsec = 100000000; /* 100 million nanosecs = 0.1 secs */ + + if ((rand()%20)==0) + screenLeft = (rand() % (totalWidth - screenWidth)); + + FloatBuffer min(0), max(0), rms(0); + Int16Buffer flag(0); + + int left = (int)(screenLeft*samplesPerPixel); + int len = (int)(screenWidth*samplesPerPixel); + + gSequence->GetWaveDisplay(min, max, rms, flag, + left, len, screenWidth, + samplesPerPixel); + + printf("%03d ", (tick%1000)); + tick++; + + int i; + for(i=0; i= screenLeft+screenWidth) + printf("."); + else if (flag[i-screenLeft]) + printf("X"); + else + printf("-"); + } + printf("\n"); + + nanosleep(&sleeptime, NULL); + } + + return 0; +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/tests/MezzoPlay.cpp b/tests/MezzoPlay.cpp new file mode 100644 index 0000000..54818af --- /dev/null +++ b/tests/MezzoPlay.cpp @@ -0,0 +1,240 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + MezzoPlay.cpp + + Copyright (c) 2004 Dominic Mazzoni + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "portaudio/pa_common/portaudio.h" + +#include "Sequence.h" +#include "ManagedFileContext.h" +#include "DiskIO.h" +#include "BufferGroup.h" +#include "XMLLoadStore.h" + +#include + +extern "C" { +#include "srsw_queue.h" +} + +using namespace Mezzo; + +const int kBufferSize = 256; +const int kNumBuffers = 1024; +const int kSampleRate = 44100; + +const int kMinBuffers = 32; /* Don't fill buffers unless we can do at + least this many */ +const int kMaxBuffers = 64; /* Maximum number of playback buffers + in the queue at any one time */ + +ManagedFileContext *gContext; +BlockedSequence *gSequence; +BufferGroup gBuffers(kBufferSize, kNumBuffers); +SRSW_Queue *gQueue = srsw_queue_new(); +int gDiskIOFinished = 0; +int gDiskIOPosition = 0; + +void *DiskIOThreadFunc(void *arg) +{ + gDiskIO->Run(); + + return NULL; +} + +void StartDiskIOThread() +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, + DiskIOThreadFunc, NULL)) { + fprintf(stderr, "Fatal error: could not create Disk I/O thread!\n"); + exit(0); + } +} + +void FillBuffers() +{ + int numBuffersInQueue = srsw_queue_get_count(gQueue); + int numSamplesLeft = gSequence->GetLength() - gDiskIOPosition; + int numBuffersLeft = (numSamplesLeft + (kBufferSize-1))/kBufferSize; + int maxBuffersThisRound = kMaxBuffers - numBuffersInQueue; + int numBuffersThisRound; + + numBuffersThisRound = numBuffersLeft; + if (numBuffersThisRound > maxBuffersThisRound) + numBuffersThisRound = maxBuffersThisRound; + + if (numBuffersThisRound >= kMinBuffers || + (numBuffersLeft > 0 && + numBuffersLeft < kMinBuffers && + numBuffersLeft <= maxBuffersThisRound)) { + printf("Filling %d buffers\n", numBuffersThisRound); + + int len = numBuffersThisRound * kBufferSize; + int getLen = len; + if (getLen + gDiskIOPosition > gSequence->GetLength()) + getLen = gSequence->GetLength() - gDiskIOPosition; + Buffer buffer = gSequence->Get(gDiskIOPosition, getLen); + if (getLen < len) + buffer = Buffer::Append(buffer, FloatBuffer(len-getLen)); + + int i; + for(i=0; i= gSequence->GetLength()) { + printf("Disk I/O Finished\n"); + gDiskIOFinished = 1; + } + } +} + +class MyDiskIOClass : public DiskIOCallable +{ + virtual void HandleDiskIOTasks() + { + FillBuffers(); + } +}; + +static int playCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + if (framesPerBuffer != (unsigned long)kBufferSize) { + fprintf(stderr, "Expected %d samples in callback, got %d\n", + (int)kBufferSize, (int)framesPerBuffer); + return 0; + } + + if (!outputBuffer) { + fprintf(stderr, "No output buffer in callback!\n"); + return 0; + } + + if (srsw_queue_get_count(gQueue) <= 0) { + memset(outputBuffer, 0, kBufferSize * sizeof(float)); + if (gDiskIOFinished) { + printf("Audio I/O exiting cleanly (path 2)\n"); + return 1; /* Finished */ + } + else { + fprintf(stderr, "playCallback starved!\n"); + return 0; + } + } + + float *floatBuffer; + srsw_queue_dequeue(gQueue, (void **)&floatBuffer); + memcpy(outputBuffer, floatBuffer, kBufferSize * sizeof(float)); + gBuffers.Recycle(floatBuffer); + + if (srsw_queue_get_count(gQueue) <= 0 && gDiskIOFinished) { + printf("Audio I/O exiting cleanly (path 1)\n"); + return 1; /* Finished */ + } + else + return 0; /* Not finished */ +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s \n", + argv[0]); + return -1; + } + + XMLLoader loader(argv[1]); + loader.GetNextToken(); + + gContext = new ManagedFileContext(loader); + gSequence = new BlockedSequence(loader); + + MyDiskIOClass *diskIOClass = new MyDiskIOClass(); + PortAudioStream *stream; + PaError err; + + gDiskIOFinished = 0; + gDiskIOPosition = 0; + + FillBuffers(); + + StartDiskIOThread(); + gDiskIO->RegisterCallable(diskIOClass, 1); + + err = Pa_Initialize(); + if( err != paNoError ) + goto error; + + /* Record some audio. -------------------------------------------- */ + err = Pa_OpenStream(&stream, + paNoDevice, + 0, + paFloat32, + NULL, + Pa_GetDefaultOutputDeviceID(), + 1, /* one channel out */ + paFloat32, + NULL, + kSampleRate, + kBufferSize, /* frames per buffer */ + 0, /* number of buffers, if zero use default */ + 0, //paDitherOff, /* flags */ + playCallback, + NULL ); + if( err != paNoError ) + goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) + goto error; + + while(!gDiskIOFinished) { + sleep(1); + } + + printf("Finished playing %d samples!\n", (int)gSequence->GetLength()); + + gDiskIO->UnregisterCallable(diskIOClass); + + Pa_StopStream( stream ); + + Pa_Terminate(); + + return 0; + + error: + Pa_Terminate(); + if (err) { + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + + return -1; +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/tests/MezzoRecord.cpp b/tests/MezzoRecord.cpp new file mode 100644 index 0000000..22989a2 --- /dev/null +++ b/tests/MezzoRecord.cpp @@ -0,0 +1,201 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + MezzoRecord.cpp + + Copyright (c) 2004 Dominic Mazzoni + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "portaudio/pa_common/portaudio.h" + +#include "Sequence.h" +#include "ManagedFileContext.h" +#include "DiskIO.h" +#include "BufferGroup.h" +#include "XMLLoadStore.h" + +#include + +extern "C" { +#include "srsw_queue.h" +} + +using namespace Mezzo; + +const int kBufferSize = 256; +const int kNumBuffers = 1024; +const int kSampleRate = 44100; +const int kTotalSamples = kSampleRate * 10; + +std::string gDirectoryName = "MezzoRecordedData"; +ManagedFileContext *gContext; +BlockedSequence *gSequence; +BufferGroup gBuffers(kBufferSize, kNumBuffers); +SRSW_Queue *gQueue = srsw_queue_new(); +int gAudioIOTotal; +int gDiskIOTotal; + +void *DiskIOThreadFunc(void *arg) +{ + gDiskIO->Run(); + + return NULL; +} + +void StartDiskIOThread() +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, + DiskIOThreadFunc, NULL)) { + fprintf(stderr, "Fatal error: could not create Disk I/O thread!\n"); + exit(0); + } +} + +class MyDiskIOClass : public DiskIOCallable +{ + virtual void HandleDiskIOTasks() + { + int numItems = srsw_queue_get_count(gQueue); + int numSamples = numItems * kBufferSize; + + if (numSamples > kSampleRate || + (numSamples > 0 && gAudioIOTotal >= kTotalSamples)) { + printf("%d samples of data to append to the Sequence\n", + numSamples); + + FloatBuffer buffer(numSamples); + int i; + for(i=0; iAppend(buffer); + } + } +}; + +static int recordCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + float *buffer; + + if (framesPerBuffer != (unsigned long)kBufferSize) { + fprintf(stderr, "Expected %d samples in callback, got %d\n", + (int)kBufferSize, (int)framesPerBuffer); + return 0; + } + + if (!inputBuffer) { + fprintf(stderr, "No input buffer in callback!\n"); + return 0; + } + + buffer = gBuffers.Allocate(); + memcpy(buffer, inputBuffer, kBufferSize * sizeof(float)); + + srsw_queue_enqueue(gQueue, (void *)buffer); + + gAudioIOTotal += kBufferSize; + if (gAudioIOTotal >= kTotalSamples) { + fprintf(stderr, "AudioIO finished\n"); + return 1; /* Finished */ + } + + return 0; /* Not finished */ +} + +int main(void) +{ + MyDiskIOClass *diskIOClass = new MyDiskIOClass(); + PortAudioStream *stream; + PaError err; + + gContext = new ManagedFileContext(gDirectoryName); + gSequence = new BlockedSequence(gContext, Buffer::FloatSample); + gAudioIOTotal = 0; + gDiskIOTotal = 0; + + StartDiskIOThread(); + gDiskIO->RegisterCallable(diskIOClass, 1); + + err = Pa_Initialize(); + if( err != paNoError ) + goto error; + + /* Record some audio. -------------------------------------------- */ + err = Pa_OpenStream(&stream, + Pa_GetDefaultInputDeviceID(), + 1, /* one channel in */ + paFloat32, + NULL, + paNoDevice, + 0, + paFloat32, + NULL, + kSampleRate, + kBufferSize, /* frames per buffer */ + 0, /* number of buffers, if zero use default */ + 0, //paDitherOff, /* flags */ + recordCallback, + NULL ); + if( err != paNoError ) + goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) + goto error; + + while(gSequence->GetLength() < kTotalSamples) { + sleep(1); + } + + printf("Finished recording %d samples!\n", (int)gSequence->GetLength()); + + gDiskIO->UnregisterCallable(diskIOClass); + + Pa_StopStream( stream ); + + Pa_Terminate(); + + if (1) { + XMLStorer storer(gDirectoryName + "/mezzo.xml"); + AttrDict attrs; + storer.StoreBeginNode("MezzoProject", attrs); + gContext->Store(storer, gDirectoryName, + gSequence->GetManagedFilesInUse()); + gSequence->Store(storer); + storer.StoreEndNode("MezzoProject"); + } + + return 0; + + error: + Pa_Terminate(); + if (err) { + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + + return -1; +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/tests/MezzoTest.cpp b/tests/MezzoTest.cpp new file mode 100644 index 0000000..f76e81a --- /dev/null +++ b/tests/MezzoTest.cpp @@ -0,0 +1,236 @@ +#include +#include +#include + +#include "Buffer.h" +#include "Sequence.h" +#include "ManagedFileContext.h" +#include "SeqMemBlock.h" +#include "Exceptions.h" +#include "XMLLoadStore.h" + +int compare(int step, + Mezzo::Sequence &seq1, Mezzo::BlockedSequence &seq2) +{ + long_sample_count len = seq1.GetLength(); + + printf("Step %d: Memory says %lld, Blocked says %lld\n", + step, len, seq2.GetLength()); + + if (seq2.GetLength() != len) { + printf("Lengths disagree: Memory says %lld, Blocked says %lld\n", + len, seq2.GetLength()); + return -1; + } + + Mezzo::Buffer b1 = seq1.Get(0, len); + Mezzo::Buffer b2 = seq2.Get(0, len); + Mezzo::Buffer diff = b2; + diff *= -1.0; + diff += b1; + Mezzo::FloatBuffer fdiff = diff.AsFloat(); + if (fdiff.GetMax() > 0) { + seq2.ConsistencyCheck(true); + printf("Error: seq1 and seq2 differ by %f\n", fdiff.GetMax()); + int j; + Mezzo::FloatBuffer f1 = b1.AsFloat(); + Mezzo::FloatBuffer f2 = b2.AsFloat(); + for(j=0; j<60000; j++) + if (f1[j] != f2[j]) { + printf("First error: sample %d/%lld, 1:%f 2:%f\n", + j, len, f1[j], f2[j]); + break; + } + + return -1; + } + + return 0; +} + +void usage() +{ + printf("testmain [save|load] \n"); +} + +int main(int argc, char **argv) +{ + enum { + SAVE, + LOAD + } mode; + + if(argc != 3) { + usage(); + exit(1); + } + + if(std::string("save") == argv[1]) + mode = SAVE; + else if(std::string("load") == argv[1]) + mode = LOAD; + else + { + usage(); + exit(1); + } + + std::string dir = argv[2]; + + try { + Mezzo::FloatBuffer f(50000); + + int i; + + for(i=0; i<50000; i++) + f[i] = i; + + if(mode == SAVE) { + Mezzo::ManagedFileContext context(dir); + Mezzo::BlockedSequence seq2(&context); + //Mezzo::BlockedSequence seq2(new SeqMemBlockContext); + + for(i=0; i<40; i++) + seq2.Append(f); + + Mezzo::XMLStorer storer(dir + "/test.xml"); + Mezzo::AttrDict attrs; + + storer.StoreBeginNode("testmain", attrs); // + context.Store(storer, dir, seq2.GetManagedFilesInUse()); // store context + seq2.Store(storer); // store sequence + storer.StoreEndNode("testmain"); // + + return 0; + } + else if (mode == LOAD) + { + Mezzo::XMLLoader loader(dir + "/test.xml"); + + loader.GetNextToken(); // + Mezzo::ManagedFileContext loadedContext(loader); // load context + Mezzo::BlockedSequence seq2(loader); // load sequence + loader.GetNextToken(); // + + Mezzo::MemorySequence seq1; + + Mezzo::FloatBuffer min(1000); + Mezzo::FloatBuffer max(1000); + Mezzo::FloatBuffer sumsq(1000); + float spp = seq2.GetLength() / 1000.0; + //seq2.GetWaveDisplay(min, max, sumsq, 0, seq2.GetLength(), 1000, spp); + + for(i=0; i<40; i++) + seq1.Append(f); + + for(i=0; i<100; i++) { + int len = seq1.GetLength(); + + if (compare(i, seq1, seq2)) { + printf("Comparison failed, exiting.\n"); + exit(0); + } + + if (len == 0) + break; + + int s1 = rand() % len; + int dlen = (len - s1 + 1); + if (dlen>100000) + dlen = 100000; + int l1 = rand() % dlen; + + seq1.Delete(s1, l1); + seq2.Delete(s1, l1); + + seq2.ConsistencyCheck(false); + } + + for(i=0; i<10; i++) { + seq1.Append(f); + seq2.Append(f); + } + + printf("Now doing cutting and pasting:\n"); + + for(i=0; i<100; i++) { + int len = seq1.GetLength(); + Mezzo::Sequence *c1, *c2; + + if (compare(i, seq1, seq2)) { + printf("Comparison failed, exiting.\n"); + exit(0); + } + + if (len == 0) + break; + + int s1 = rand() % len; + int dlen = (len - s1 + 1); + if (dlen>100000) + dlen = 100000; + int l1 = rand() % dlen; + + c1 = seq1.Cut(s1, l1); + c2 = seq2.Cut(s1, l1); + + if (compare(i, seq1, seq2)) { + printf("Comparison failed, exiting.\n"); + exit(0); + } + + int s2 = rand() % (len - l1 + 1); + + seq1.Paste(s2, c1); + seq2.Paste(s2, c2); + + delete c1; + delete c2; + + seq2.ConsistencyCheck(false); + } + + printf("Success!\n"); + printf("Final lengths: %lld %lld\n", + seq1.GetLength(), + seq2.GetLength()); + + seq2.ConsistencyCheck(true); + + printf("\n"); + printf("Now let's do something illegal just to test the " + "exception handling:\n"); + seq2.Get(0, seq2.GetLength() + 1); + } + } + catch (Mezzo::ClientException &e) { + printf("## Client exception at %s:\n## %s\n", + e.location.c_str(), + e.description.c_str()); + } + catch (Mezzo::InternalException &e) { + printf("## Internal exception at %s:\n## %s\n", + e.location.c_str(), + e.description.c_str()); + } + catch (Mezzo::UserException &e) { + printf("## User exception at %s:\n## %s\n", + e.location.c_str(), + e.description.c_str()); + } + catch (...) { + printf("Unknown exception!\n"); + } + + return 0; +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 + diff --git a/tests/TestManagedFileContext.cpp b/tests/TestManagedFileContext.cpp new file mode 100755 index 0000000..622dbf3 --- /dev/null +++ b/tests/TestManagedFileContext.cpp @@ -0,0 +1,118 @@ +/********************************************************************** + + Mezzo: A Cross-Platform Audio Editing Engine + + TestManagedFileContext.cpp + + Copyright (c) 2004 Joshua Haberman + + This program is free software and comes with no warranty; for more + information, see the file LICENSE.txt or visit + http://audacity.sourceforge.net/mezzo/license/ + +**********************************************************************/ + +#include "ManagedFileContext.h" +#include "platform/DiskFunctions.h" + +#include + +class TestManagedFileContext { + public: + TestManagedFileContext(std::string dir): + mContext(dir) + { + Mezzo::FloatBuffer buf(100000); + + // create a bunch of SeqBlock files + mSeqBlocks.push_back(mContext.NewSeqBlock(buf)); + + for(int i = 0; i < 100; i++) + { + if(rand() & 0x1) + mSeqBlocks.push_back(mContext.NewSeqBlock(buf)); + else + mSeqBlocks.push_back(mContext.GetSeqBlockRef(mSeqBlocks[rand()%mSeqBlocks.size()])); + } + } + + ~TestManagedFileContext() + { + FreeBlocks(); + } + + void MoveToNewDirectory(std::string newDir) + { + mContext.MoveToNewDirectory(newDir); + } + + void FreeBlocks() + { + for(unsigned int i = 0; i < mSeqBlocks.size(); i++) + mContext.ReleaseSeqBlockRef(mSeqBlocks[i]); + mSeqBlocks.clear(); + } + + private: + Mezzo::ManagedFileContext mContext; + std::vector mSeqBlocks; +}; + +int main(void) +{ + std::string dir = "/tmp/TestManagedFileContext-OwnsThisDir"; + std::string dir2 = "/tmp/TestManagedFileContext-OwnsThisDir2"; + + Mezzo::Platform::DeleteDirectory(dir); + Mezzo::Platform::DeleteDirectory(dir2); + + printf("Testing ManagedFileContext...\n"); + + printf(" Testing basic operation..."); + fflush(stdout); + { + TestManagedFileContext test(dir); + } + if(Mezzo::Platform::GetFilesInDir(dir) != 0) + { + printf("Error: not all files were cleaned up\n"); + exit(1); + } + printf("ok\n"); + + + printf(" Testing moving to a new directory..."); + fflush(stdout); + { + TestManagedFileContext test(dir); + int filesInOldDir = Mezzo::Platform::GetFilesInDir(dir); + test.MoveToNewDirectory(dir2); + + if(Mezzo::Platform::GetFilesInDir(dir) != 0) + { + printf("Error: files still exist in old directory\n"); + exit(1); + } + if(Mezzo::Platform::GetFilesInDir(dir2) != filesInOldDir) + { + printf("Files were not successfully moved\n"); + exit(1); + } + } + if(Mezzo::Platform::GetFilesInDir(dir2) != 0) + { + printf("Error: not all files were cleaned up\n"); + exit(1); + } + printf("ok\n"); +} + +// Indentation settings for Vim and Emacs. Please do not modify past this point. +// +// Local Variables: +// c-basic-offset: 3 +// indent-tabs-mode: nil +// End: +// +// vim: et sts=3 sw=3 +