]> Dogcows Code - chaz/yoink/commitdiff
testing new non-autotools build system
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 7 Jun 2010 16:33:23 +0000 (10:33 -0600)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 7 Jun 2010 16:33:23 +0000 (10:33 -0600)
395 files changed:
Makefile [new file with mode: 0644]
build/compile.sh [new file with mode: 0755]
build/config.guess [new file with mode: 0755]
build/config.sub [new file with mode: 0755]
build/functions.sh [new file with mode: 0755]
build/install.sh [new file with mode: 0755]
build/link.sh [new file with mode: 0755]
configure.ac
data/rules.mk [new file with mode: 0644]
data/yoinkrc
doc/rules.mk [new file with mode: 0644]
doc/yoink.6.in
src/Animation.cc
src/Animation.hh
src/Character.cc
src/GameLayer.cc
src/GameLayer.hh
src/Main.cc
src/Main.hh
src/Makefile.am
src/Scene.cc
src/Scene.hh
src/TitleLayer.cc
src/cml/cml.h [moved from src/moof/cml/cml.h with 100% similarity]
src/cml/constants.h [moved from src/moof/cml/constants.h with 100% similarity]
src/cml/core/cml_assert.h [moved from src/moof/cml/core/cml_assert.h with 100% similarity]
src/cml/core/cml_meta.h [moved from src/moof/cml/core/cml_meta.h with 100% similarity]
src/cml/core/common.h [moved from src/moof/cml/core/common.h with 100% similarity]
src/cml/core/dynamic_1D.h [moved from src/moof/cml/core/dynamic_1D.h with 100% similarity]
src/cml/core/dynamic_2D.h [moved from src/moof/cml/core/dynamic_2D.h with 100% similarity]
src/cml/core/external_1D.h [moved from src/moof/cml/core/external_1D.h with 100% similarity]
src/cml/core/external_2D.h [moved from src/moof/cml/core/external_2D.h with 100% similarity]
src/cml/core/fixed_1D.h [moved from src/moof/cml/core/fixed_1D.h with 100% similarity]
src/cml/core/fixed_2D.h [moved from src/moof/cml/core/fixed_2D.h with 100% similarity]
src/cml/core/fwd.h [moved from src/moof/cml/core/fwd.h with 100% similarity]
src/cml/core/meta/common.h [moved from src/moof/cml/core/meta/common.h with 100% similarity]
src/cml/core/meta/if.h [moved from src/moof/cml/core/meta/if.h with 100% similarity]
src/cml/core/meta/switch.h [moved from src/moof/cml/core/meta/switch.h with 100% similarity]
src/cml/defaults.h [moved from src/moof/cml/defaults.h with 100% similarity]
src/cml/dynamic.h [moved from src/moof/cml/dynamic.h with 100% similarity]
src/cml/et/array_promotions.h [moved from src/moof/cml/et/array_promotions.h with 100% similarity]
src/cml/et/scalar_ops.h [moved from src/moof/cml/et/scalar_ops.h with 100% similarity]
src/cml/et/scalar_promotions.h [moved from src/moof/cml/et/scalar_promotions.h with 100% similarity]
src/cml/et/size_checking.h [moved from src/moof/cml/et/size_checking.h with 100% similarity]
src/cml/et/tags.h [moved from src/moof/cml/et/tags.h with 100% similarity]
src/cml/et/traits.h [moved from src/moof/cml/et/traits.h with 100% similarity]
src/cml/external.h [moved from src/moof/cml/external.h with 100% similarity]
src/cml/fixed.h [moved from src/moof/cml/fixed.h with 100% similarity]
src/cml/mathlib/checking.h [moved from src/moof/cml/mathlib/checking.h with 100% similarity]
src/cml/mathlib/coord_conversion.h [moved from src/moof/cml/mathlib/coord_conversion.h with 100% similarity]
src/cml/mathlib/epsilon.h [moved from src/moof/cml/mathlib/epsilon.h with 100% similarity]
src/cml/mathlib/frustum.h [moved from src/moof/cml/mathlib/frustum.h with 100% similarity]
src/cml/mathlib/helper.h [moved from src/moof/cml/mathlib/helper.h with 100% similarity]
src/cml/mathlib/interpolation.h [moved from src/moof/cml/mathlib/interpolation.h with 100% similarity]
src/cml/mathlib/mathlib.h [moved from src/moof/cml/mathlib/mathlib.h with 100% similarity]
src/cml/mathlib/matrix_basis.h [moved from src/moof/cml/mathlib/matrix_basis.h with 100% similarity]
src/cml/mathlib/matrix_concat.h [moved from src/moof/cml/mathlib/matrix_concat.h with 100% similarity]
src/cml/mathlib/matrix_misc.h [moved from src/moof/cml/mathlib/matrix_misc.h with 100% similarity]
src/cml/mathlib/matrix_ortho.h [moved from src/moof/cml/mathlib/matrix_ortho.h with 100% similarity]
src/cml/mathlib/matrix_projection.h [moved from src/moof/cml/mathlib/matrix_projection.h with 100% similarity]
src/cml/mathlib/matrix_rotation.h [moved from src/moof/cml/mathlib/matrix_rotation.h with 100% similarity]
src/cml/mathlib/matrix_transform.h [moved from src/moof/cml/mathlib/matrix_transform.h with 100% similarity]
src/cml/mathlib/matrix_translation.h [moved from src/moof/cml/mathlib/matrix_translation.h with 100% similarity]
src/cml/mathlib/misc.h [moved from src/moof/cml/mathlib/misc.h with 100% similarity]
src/cml/mathlib/picking.h [moved from src/moof/cml/mathlib/picking.h with 100% similarity]
src/cml/mathlib/projection.h [moved from src/moof/cml/mathlib/projection.h with 100% similarity]
src/cml/mathlib/quaternion_basis.h [moved from src/moof/cml/mathlib/quaternion_basis.h with 100% similarity]
src/cml/mathlib/quaternion_rotation.h [moved from src/moof/cml/mathlib/quaternion_rotation.h with 100% similarity]
src/cml/mathlib/typedef.h [moved from src/moof/cml/mathlib/typedef.h with 100% similarity]
src/cml/mathlib/vector_angle.h [moved from src/moof/cml/mathlib/vector_angle.h with 100% similarity]
src/cml/mathlib/vector_misc.h [moved from src/moof/cml/mathlib/vector_misc.h with 100% similarity]
src/cml/mathlib/vector_ortho.h [moved from src/moof/cml/mathlib/vector_ortho.h with 100% similarity]
src/cml/mathlib/vector_transform.h [moved from src/moof/cml/mathlib/vector_transform.h with 100% similarity]
src/cml/matrix.h [moved from src/moof/cml/matrix.h with 100% similarity]
src/cml/matrix/class_ops.h [moved from src/moof/cml/matrix/class_ops.h with 100% similarity]
src/cml/matrix/determinant.h [moved from src/moof/cml/matrix/determinant.h with 100% similarity]
src/cml/matrix/dynamic.h [moved from src/moof/cml/matrix/dynamic.h with 100% similarity]
src/cml/matrix/external.h [moved from src/moof/cml/matrix/external.h with 100% similarity]
src/cml/matrix/fixed.h [moved from src/moof/cml/matrix/fixed.h with 100% similarity]
src/cml/matrix/inverse.h [moved from src/moof/cml/matrix/inverse.h with 100% similarity]
src/cml/matrix/lu.h [moved from src/moof/cml/matrix/lu.h with 100% similarity]
src/cml/matrix/matop_macros.h [moved from src/moof/cml/matrix/matop_macros.h with 100% similarity]
src/cml/matrix/matrix_comparison.h [moved from src/moof/cml/matrix/matrix_comparison.h with 100% similarity]
src/cml/matrix/matrix_expr.h [moved from src/moof/cml/matrix/matrix_expr.h with 100% similarity]
src/cml/matrix/matrix_functions.h [moved from src/moof/cml/matrix/matrix_functions.h with 100% similarity]
src/cml/matrix/matrix_mul.h [moved from src/moof/cml/matrix/matrix_mul.h with 100% similarity]
src/cml/matrix/matrix_ops.h [moved from src/moof/cml/matrix/matrix_ops.h with 100% similarity]
src/cml/matrix/matrix_print.h [moved from src/moof/cml/matrix/matrix_print.h with 100% similarity]
src/cml/matrix/matrix_promotions.h [moved from src/moof/cml/matrix/matrix_promotions.h with 100% similarity]
src/cml/matrix/matrix_rowcol.h [moved from src/moof/cml/matrix/matrix_rowcol.h with 100% similarity]
src/cml/matrix/matrix_traits.h [moved from src/moof/cml/matrix/matrix_traits.h with 100% similarity]
src/cml/matrix/matrix_transpose.h [moved from src/moof/cml/matrix/matrix_transpose.h with 100% similarity]
src/cml/matrix/matrix_unroller.h [moved from src/moof/cml/matrix/matrix_unroller.h with 100% similarity]
src/cml/matvec/matvec_mul.h [moved from src/moof/cml/matvec/matvec_mul.h with 100% similarity]
src/cml/matvec/matvec_promotions.h [moved from src/moof/cml/matvec/matvec_promotions.h with 100% similarity]
src/cml/quaternion.h [moved from src/moof/cml/quaternion.h with 100% similarity]
src/cml/quaternion/conjugate.h [moved from src/moof/cml/quaternion/conjugate.h with 100% similarity]
src/cml/quaternion/inverse.h [moved from src/moof/cml/quaternion/inverse.h with 100% similarity]
src/cml/quaternion/quaternion.h [moved from src/moof/cml/quaternion/quaternion.h with 100% similarity]
src/cml/quaternion/quaternion_comparison.h [moved from src/moof/cml/quaternion/quaternion_comparison.h with 100% similarity]
src/cml/quaternion/quaternion_dot.h [moved from src/moof/cml/quaternion/quaternion_dot.h with 100% similarity]
src/cml/quaternion/quaternion_expr.h [moved from src/moof/cml/quaternion/quaternion_expr.h with 100% similarity]
src/cml/quaternion/quaternion_functions.h [moved from src/moof/cml/quaternion/quaternion_functions.h with 100% similarity]
src/cml/quaternion/quaternion_mul.h [moved from src/moof/cml/quaternion/quaternion_mul.h with 100% similarity]
src/cml/quaternion/quaternion_ops.h [moved from src/moof/cml/quaternion/quaternion_ops.h with 100% similarity]
src/cml/quaternion/quaternion_print.h [moved from src/moof/cml/quaternion/quaternion_print.h with 100% similarity]
src/cml/quaternion/quaternion_promotions.h [moved from src/moof/cml/quaternion/quaternion_promotions.h with 100% similarity]
src/cml/quaternion/quaternion_traits.h [moved from src/moof/cml/quaternion/quaternion_traits.h with 100% similarity]
src/cml/quaternion/quatop_macros.h [moved from src/moof/cml/quaternion/quatop_macros.h with 100% similarity]
src/cml/util.h [moved from src/moof/cml/util.h with 100% similarity]
src/cml/vector.h [moved from src/moof/cml/vector.h with 100% similarity]
src/cml/vector/class_ops.h [moved from src/moof/cml/vector/class_ops.h with 100% similarity]
src/cml/vector/dynamic.h [moved from src/moof/cml/vector/dynamic.h with 100% similarity]
src/cml/vector/external.h [moved from src/moof/cml/vector/external.h with 100% similarity]
src/cml/vector/fixed.h [moved from src/moof/cml/vector/fixed.h with 100% similarity]
src/cml/vector/vecop_macros.h [moved from src/moof/cml/vector/vecop_macros.h with 100% similarity]
src/cml/vector/vector_comparison.h [moved from src/moof/cml/vector/vector_comparison.h with 100% similarity]
src/cml/vector/vector_expr.h [moved from src/moof/cml/vector/vector_expr.h with 100% similarity]
src/cml/vector/vector_functions.h [moved from src/moof/cml/vector/vector_functions.h with 100% similarity]
src/cml/vector/vector_ops.h [moved from src/moof/cml/vector/vector_ops.h with 100% similarity]
src/cml/vector/vector_print.h [moved from src/moof/cml/vector/vector_print.h with 100% similarity]
src/cml/vector/vector_products.h [moved from src/moof/cml/vector/vector_products.h with 100% similarity]
src/cml/vector/vector_promotions.h [moved from src/moof/cml/vector/vector_promotions.h with 100% similarity]
src/cml/vector/vector_traits.h [moved from src/moof/cml/vector/vector_traits.h with 100% similarity]
src/cml/vector/vector_unroller.h [moved from src/moof/cml/vector/vector_unroller.h with 100% similarity]
src/moof/camera.cc
src/moof/debug.hh [new file with mode: 0644]
src/moof/hash.hh
src/moof/image.cc
src/moof/image.hh
src/moof/interpolator.hh
src/moof/log.hh
src/moof/math.hh
src/moof/modal_dialog.hh
src/moof/packet.cc
src/moof/packet.hh
src/moof/resource.cc
src/moof/resource.hh
src/moof/rules.mk [new file with mode: 0644]
src/moof/socket.hh
src/moof/sound.cc
src/moof/sound.hh
src/moof/texture.cc
src/moof/texture.hh
src/moof/timer.cc
src/moof/timer.hh
src/moof/video.cc
src/rules.mk [new file with mode: 0644]
src/stlplus/README.txt [new file with mode: 0644]
src/stlplus/containers/containers.hpp [moved from src/moof/stlplus/containers.hpp with 100% similarity, mode: 0644]
src/stlplus/containers/containers_fixes.hpp [moved from src/moof/stlplus/containers_fixes.hpp with 79% similarity, mode: 0644]
src/stlplus/containers/copy_functors.hpp [new file with mode: 0644]
src/stlplus/containers/digraph.hpp [moved from src/moof/stlplus/digraph.hpp with 100% similarity, mode: 0644]
src/stlplus/containers/digraph.tpp [moved from src/moof/stlplus/digraph.tpp with 100% similarity, mode: 0644]
src/stlplus/containers/exceptions.hpp [moved from src/moof/stlplus/exceptions.hpp with 100% similarity, mode: 0644]
src/stlplus/containers/foursome.hpp [moved from src/moof/stlplus/foursome.hpp with 100% similarity, mode: 0644]
src/stlplus/containers/foursome.tpp [moved from src/moof/stlplus/foursome.tpp with 100% similarity, mode: 0644]
src/stlplus/containers/hash.hpp [moved from src/moof/stlplus/hash.hpp with 92% similarity, mode: 0644]
src/stlplus/containers/hash.tpp [moved from src/moof/stlplus/hash.tpp with 82% similarity, mode: 0644]
src/stlplus/containers/matrix.hpp [moved from src/moof/stlplus/matrix.hpp with 96% similarity, mode: 0644]
src/stlplus/containers/matrix.tpp [moved from src/moof/stlplus/matrix.tpp with 100% similarity, mode: 0644]
src/stlplus/containers/ntree.hpp [moved from src/moof/stlplus/ntree.hpp with 100% similarity, mode: 0644]
src/stlplus/containers/ntree.tpp [moved from src/moof/stlplus/ntree.tpp with 100% similarity, mode: 0644]
src/stlplus/containers/safe_iterator.hpp [moved from src/moof/stlplus/safe_iterator.hpp with 100% similarity, mode: 0644]
src/stlplus/containers/safe_iterator.tpp [moved from src/moof/stlplus/safe_iterator.tpp with 100% similarity, mode: 0644]
src/stlplus/containers/simple_ptr.hpp [new file with mode: 0644]
src/stlplus/containers/simple_ptr.tpp [new file with mode: 0644]
src/stlplus/containers/smart_ptr.hpp [moved from src/moof/stlplus/smart_ptr.hpp with 83% similarity, mode: 0644]
src/stlplus/containers/smart_ptr.tpp [moved from src/moof/stlplus/smart_ptr.tpp with 91% similarity, mode: 0644]
src/stlplus/containers/triple.hpp [moved from src/moof/stlplus/triple.hpp with 100% similarity, mode: 0644]
src/stlplus/containers/triple.tpp [moved from src/moof/stlplus/triple.tpp with 100% similarity, mode: 0644]
src/stlplus/messages/stlplus_messages.txt [new file with mode: 0644]
src/stlplus/persistence/persistence.hpp [new file with mode: 0644]
src/stlplus/persistence/persistence_fixes.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_basic.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_bitset.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_bitset.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_bool.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_bool.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_callback.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_callback.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_complex.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_complex.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_contexts.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_contexts.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_cstring.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_cstring.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_deque.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_deque.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_digraph.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_digraph.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_enum.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_enum.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_exceptions.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_exceptions.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_float.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_float.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_foursome.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_foursome.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_hash.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_hash.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_inf.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_inf.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_int.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_int.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_interface.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_interface.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_list.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_list.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_map.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_map.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_matrix.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_matrix.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_multimap.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_multimap.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_multiset.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_multiset.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_ntree.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_ntree.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_pair.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_pair.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_pointer.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_pointer.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_pointers.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_set.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_set.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_shortcuts.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_shortcuts.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_simple_ptr.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_simple_ptr.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_smart_ptr.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_smart_ptr.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_stl.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_stlplus.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_string.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_string.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_string.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_triple.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_triple.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_vector.cpp [new file with mode: 0644]
src/stlplus/persistence/persistent_vector.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_vector.tpp [new file with mode: 0644]
src/stlplus/persistence/persistent_xref.hpp [new file with mode: 0644]
src/stlplus/persistence/persistent_xref.tpp [new file with mode: 0644]
src/stlplus/portability/build.cpp [new file with mode: 0644]
src/stlplus/portability/build.hpp [new file with mode: 0644]
src/stlplus/portability/debug.cpp [new file with mode: 0644]
src/stlplus/portability/debug.hpp [new file with mode: 0644]
src/stlplus/portability/dprintf.cpp [new file with mode: 0644]
src/stlplus/portability/dprintf.hpp [new file with mode: 0644]
src/stlplus/portability/dynaload.cpp [new file with mode: 0644]
src/stlplus/portability/dynaload.hpp [new file with mode: 0644]
src/stlplus/portability/file_system.cpp [new file with mode: 0644]
src/stlplus/portability/file_system.hpp [new file with mode: 0644]
src/stlplus/portability/inf.cpp [new file with mode: 0644]
src/stlplus/portability/inf.hpp [new file with mode: 0644]
src/stlplus/portability/ip_sockets.cpp [new file with mode: 0644]
src/stlplus/portability/ip_sockets.hpp [new file with mode: 0644]
src/stlplus/portability/portability.hpp [new file with mode: 0644]
src/stlplus/portability/portability_exceptions.hpp [new file with mode: 0644]
src/stlplus/portability/portability_fixes.cpp [new file with mode: 0644]
src/stlplus/portability/portability_fixes.hpp [new file with mode: 0644]
src/stlplus/portability/subprocesses.cpp [new file with mode: 0644]
src/stlplus/portability/subprocesses.hpp [new file with mode: 0644]
src/stlplus/portability/tcp.hpp [new file with mode: 0644]
src/stlplus/portability/tcp_sockets.cpp [new file with mode: 0644]
src/stlplus/portability/tcp_sockets.hpp [new file with mode: 0644]
src/stlplus/portability/time.cpp [new file with mode: 0644]
src/stlplus/portability/time.hpp [new file with mode: 0644]
src/stlplus/portability/udp_sockets.cpp [new file with mode: 0644]
src/stlplus/portability/udp_sockets.hpp [new file with mode: 0644]
src/stlplus/portability/version.cpp [new file with mode: 0644]
src/stlplus/portability/version.hpp [new file with mode: 0644]
src/stlplus/portability/wildcard.cpp [new file with mode: 0644]
src/stlplus/portability/wildcard.hpp [new file with mode: 0644]
src/stlplus/rules.mk [new file with mode: 0644]
src/stlplus/strings/format_types.hpp [new file with mode: 0644]
src/stlplus/strings/print_address.cpp [new file with mode: 0644]
src/stlplus/strings/print_address.hpp [new file with mode: 0644]
src/stlplus/strings/print_basic.hpp [new file with mode: 0644]
src/stlplus/strings/print_bitset.hpp [new file with mode: 0644]
src/stlplus/strings/print_bitset.tpp [new file with mode: 0644]
src/stlplus/strings/print_bool.cpp [new file with mode: 0644]
src/stlplus/strings/print_bool.hpp [new file with mode: 0644]
src/stlplus/strings/print_cstring.cpp [new file with mode: 0644]
src/stlplus/strings/print_cstring.hpp [new file with mode: 0644]
src/stlplus/strings/print_digraph.hpp [new file with mode: 0644]
src/stlplus/strings/print_digraph.tpp [new file with mode: 0644]
src/stlplus/strings/print_float.cpp [new file with mode: 0644]
src/stlplus/strings/print_float.hpp [new file with mode: 0644]
src/stlplus/strings/print_foursome.hpp [new file with mode: 0644]
src/stlplus/strings/print_foursome.tpp [new file with mode: 0644]
src/stlplus/strings/print_hash.hpp [new file with mode: 0644]
src/stlplus/strings/print_hash.tpp [new file with mode: 0644]
src/stlplus/strings/print_inf.cpp [new file with mode: 0644]
src/stlplus/strings/print_inf.hpp [new file with mode: 0644]
src/stlplus/strings/print_int.cpp [new file with mode: 0644]
src/stlplus/strings/print_int.hpp [new file with mode: 0644]
src/stlplus/strings/print_list.hpp [new file with mode: 0644]
src/stlplus/strings/print_list.tpp [new file with mode: 0644]
src/stlplus/strings/print_map.hpp [new file with mode: 0644]
src/stlplus/strings/print_map.tpp [new file with mode: 0644]
src/stlplus/strings/print_matrix.hpp [new file with mode: 0644]
src/stlplus/strings/print_matrix.tpp [new file with mode: 0644]
src/stlplus/strings/print_ntree.hpp [new file with mode: 0644]
src/stlplus/strings/print_ntree.tpp [new file with mode: 0644]
src/stlplus/strings/print_pair.hpp [new file with mode: 0644]
src/stlplus/strings/print_pair.tpp [new file with mode: 0644]
src/stlplus/strings/print_pointer.hpp [new file with mode: 0644]
src/stlplus/strings/print_pointer.tpp [new file with mode: 0644]
src/stlplus/strings/print_sequence.hpp [new file with mode: 0644]
src/stlplus/strings/print_sequence.tpp [new file with mode: 0644]
src/stlplus/strings/print_set.hpp [new file with mode: 0644]
src/stlplus/strings/print_set.tpp [new file with mode: 0644]
src/stlplus/strings/print_simple_ptr.hpp [new file with mode: 0644]
src/stlplus/strings/print_simple_ptr.tpp [new file with mode: 0644]
src/stlplus/strings/print_smart_ptr.hpp [new file with mode: 0644]
src/stlplus/strings/print_smart_ptr.tpp [new file with mode: 0644]
src/stlplus/strings/print_stl.hpp [new file with mode: 0644]
src/stlplus/strings/print_stlplus.hpp [new file with mode: 0644]
src/stlplus/strings/print_string.cpp [new file with mode: 0644]
src/stlplus/strings/print_string.hpp [new file with mode: 0644]
src/stlplus/strings/print_triple.hpp [new file with mode: 0644]
src/stlplus/strings/print_triple.tpp [new file with mode: 0644]
src/stlplus/strings/print_vector.cpp [new file with mode: 0644]
src/stlplus/strings/print_vector.hpp [new file with mode: 0644]
src/stlplus/strings/print_vector.tpp [new file with mode: 0644]
src/stlplus/strings/string_address.cpp [new file with mode: 0644]
src/stlplus/strings/string_address.hpp [new file with mode: 0644]
src/stlplus/strings/string_basic.hpp [new file with mode: 0644]
src/stlplus/strings/string_bitset.hpp [new file with mode: 0644]
src/stlplus/strings/string_bitset.tpp [new file with mode: 0644]
src/stlplus/strings/string_bool.cpp [new file with mode: 0644]
src/stlplus/strings/string_bool.hpp [new file with mode: 0644]
src/stlplus/strings/string_cstring.cpp [new file with mode: 0644]
src/stlplus/strings/string_cstring.hpp [new file with mode: 0644]
src/stlplus/strings/string_digraph.hpp [new file with mode: 0644]
src/stlplus/strings/string_digraph.tpp [new file with mode: 0644]
src/stlplus/strings/string_float.cpp [new file with mode: 0644]
src/stlplus/strings/string_float.hpp [new file with mode: 0644]
src/stlplus/strings/string_foursome.hpp [new file with mode: 0644]
src/stlplus/strings/string_foursome.tpp [new file with mode: 0644]
src/stlplus/strings/string_hash.hpp [new file with mode: 0644]
src/stlplus/strings/string_hash.tpp [new file with mode: 0644]
src/stlplus/strings/string_inf.cpp [new file with mode: 0644]
src/stlplus/strings/string_inf.hpp [new file with mode: 0644]
src/stlplus/strings/string_int.cpp [new file with mode: 0644]
src/stlplus/strings/string_int.hpp [new file with mode: 0644]
src/stlplus/strings/string_list.hpp [new file with mode: 0644]
src/stlplus/strings/string_list.tpp [new file with mode: 0644]
src/stlplus/strings/string_map.hpp [new file with mode: 0644]
src/stlplus/strings/string_map.tpp [new file with mode: 0644]
src/stlplus/strings/string_matrix.hpp [new file with mode: 0644]
src/stlplus/strings/string_matrix.tpp [new file with mode: 0644]
src/stlplus/strings/string_ntree.hpp [new file with mode: 0644]
src/stlplus/strings/string_ntree.tpp [new file with mode: 0644]
src/stlplus/strings/string_pair.hpp [new file with mode: 0644]
src/stlplus/strings/string_pair.tpp [new file with mode: 0644]
src/stlplus/strings/string_pointer.hpp [new file with mode: 0644]
src/stlplus/strings/string_pointer.tpp [new file with mode: 0644]
src/stlplus/strings/string_sequence.hpp [new file with mode: 0644]
src/stlplus/strings/string_sequence.tpp [new file with mode: 0644]
src/stlplus/strings/string_set.hpp [new file with mode: 0644]
src/stlplus/strings/string_set.tpp [new file with mode: 0644]
src/stlplus/strings/string_simple_ptr.hpp [new file with mode: 0644]
src/stlplus/strings/string_simple_ptr.tpp [new file with mode: 0644]
src/stlplus/strings/string_smart_ptr.hpp [new file with mode: 0644]
src/stlplus/strings/string_smart_ptr.tpp [new file with mode: 0644]
src/stlplus/strings/string_stl.hpp [new file with mode: 0644]
src/stlplus/strings/string_stlplus.hpp [new file with mode: 0644]
src/stlplus/strings/string_string.cpp [new file with mode: 0644]
src/stlplus/strings/string_string.hpp [new file with mode: 0644]
src/stlplus/strings/string_triple.hpp [new file with mode: 0644]
src/stlplus/strings/string_triple.tpp [new file with mode: 0644]
src/stlplus/strings/string_utilities.cpp [new file with mode: 0644]
src/stlplus/strings/string_utilities.hpp [new file with mode: 0644]
src/stlplus/strings/string_vector.cpp [new file with mode: 0644]
src/stlplus/strings/string_vector.hpp [new file with mode: 0644]
src/stlplus/strings/string_vector.tpp [new file with mode: 0644]
src/stlplus/strings/strings.hpp [new file with mode: 0644]
src/stlplus/strings/strings_fixes.hpp [new file with mode: 0644]
src/stlplus/subsystems/cli_parser.cpp [new file with mode: 0644]
src/stlplus/subsystems/cli_parser.hpp [new file with mode: 0644]
src/stlplus/subsystems/ini_manager.cpp [new file with mode: 0644]
src/stlplus/subsystems/ini_manager.hpp [new file with mode: 0644]
src/stlplus/subsystems/library_manager.cpp [new file with mode: 0644]
src/stlplus/subsystems/library_manager.hpp [new file with mode: 0644]
src/stlplus/subsystems/message_handler.cpp [new file with mode: 0644]
src/stlplus/subsystems/message_handler.hpp [new file with mode: 0644]
src/stlplus/subsystems/subsystems.hpp [new file with mode: 0644]
src/stlplus/subsystems/subsystems_fixes.hpp [new file with mode: 0644]
src/stlplus/subsystems/timer.cpp [new file with mode: 0644]
src/stlplus/subsystems/timer.hpp [new file with mode: 0644]
src/yoink.rc

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..0ca002b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,149 @@
+
+#
+# Yoink
+# Use this file with make to compile and install Yoink.
+#
+# This makefile supports these targets:
+#   all, install, clean, cleandist, run, debug
+#
+# This build system incorporates the ideas written by Emile van Bergen:
+# http://www.xs4all.nl/~evbergen/nonrecursive-make.html
+#
+
+#
+# Define some options.
+#
+
+# Set this to `yes' to echo each build command.
+verbose := no
+
+
+#
+# Include the configuration file, config.mk.
+#
+
+have_config := $(wildcard config.mk)
+ifneq ($(strip $(have_config)),)
+include config.mk
+else
+no_config:
+       @echo "You must run the configure script before you can make anything."
+       @exit 1
+endif
+
+
+#
+# Some standard stuff.
+#
+
+.SUFFIXES:
+.SUFFIXES: .a .c .cc .cpp .o .rc
+
+all: targets
+
+
+#
+# Include the subdirectories--order is not important.
+#
+
+dir := src
+include $(dir)/rules.mk
+
+dir := data
+include $(dir)/rules.mk
+
+dir := doc
+include $(dir)/rules.mk
+
+
+#
+# Define some common rules.
+#
+
+CC_WRAPPER  = ./build/compile.sh $(CC)
+CXX_WRAPPER = ./build/compile.sh $(CXX)
+
+ifeq ($(verbose),no)
+DO_CC    = @echo "  CC    $@";
+DO_CXX   = @echo "  CXX   $@";
+DO_LD    = @echo "  LD    $@";
+DO_LDX   = @echo "  LD    $@";
+DO_CCLD  = @echo "  CCLD  $@";
+DO_CXXLD = @echo "  CXXLD $@";
+DO_AR    = @echo "  AR    $@";
+DO_RC    = @echo "  RC    $@";
+endif
+
+DO_CC    += $(CC_WRAPPER)  $(CFLAGS)   $(CF_TGT) -o $@ -c $<
+DO_CXX   += $(CXX_WRAPPER) $(CXXFLAGS) $(CF_TGT) -o $@ -c $<
+DO_LD    += $(CC_WRAPPER)  $(LDFLAGS)  $(LF_TGT) -o $@ $^ $(LL_TGT) $(LIBS)
+DO_LDX   += $(CXX_WRAPPER) $(LDFLAGS)  $(LF_TGT) -o $@ $^ $(LL_TGT) $(LIBS)
+DO_CCLD  += $(CC_WRAPPER)  $(CFLAGS)   $(CF_TGT) $(LDFLAGS) $(LF_TGT) -o $@ $< $(LL_TGT) $(LIBS)
+DO_CXXLD += $(CXX_WRAPPER) $(CXXFLAGS) $(CF_TGT) $(LDFLAGS) $(LF_TGT) -o $@ $< $(LL_TGT) $(LIBS)
+DO_AR    += $(AR) rcs $@ $^; $(RANLIB) $@
+DO_RC    += $(WINDRES)     $(DDEFINES) $(DF_TGT) -o $@ -i $<
+
+%.o: %.c
+       $(DO_CC)
+%.o: %.cc
+       $(DO_CXX)
+%.o: %.cpp
+       $(DO_CXX)
+%.o: %.rc
+       $(DO_RC)
+%: %.o
+       $(DO_LD)
+%: %.c
+       $(DO_CCLD)
+%: %.cc
+       $(DO_CXXLD)
+%: %.cpp
+       $(DO_CXXLD)
+%.a: %.o
+       $(DO_AR)
+
+
+#
+# Stuff.
+#
+
+#TGT_BIN := $(addsuffix $(EXEEXT),$(TGT_BIN))
+
+
+#
+# Define the phony targets.
+#
+
+.PHONY: targets
+targets: $(TGT_BIN) $(TGT_LIB)
+
+.PHONY: clean
+clean:
+ifeq ($(verbose),yes)
+       rm -f $(CLEAN)
+else
+       @echo "  CLEAN"; rm -f $(CLEAN)
+endif
+
+.PHONY: distclean
+distclean: clean
+ifeq ($(verbose),yes)
+       rm -f config.mk
+else
+       @rm -f config.mk
+endif
+
+.PHONY: install
+install: targets 
+       $(INSTALL) $(TGT_BIN)  -m 755 -d $(DESTDIR)$(bindir)
+       $(INSTALL) $(TGT_DATA) -m 644 -d $(DESTDIR)$(datadir)
+       $(INSTALL) $(TGT_LIB)  -m 750 -d $(DESTDIR)$(libdir)
+       $(INSTALL) $(TGT_MAN)  -m 644 -d $(DESTDIR)$(mandir)
+
+
+#
+# Prevent make from removing any build targets.
+#
+
+.SECONDARY:    $(CLEAN)
+
diff --git a/build/compile.sh b/build/compile.sh
new file mode 100755 (executable)
index 0000000..75aec0a
--- /dev/null
@@ -0,0 +1,130 @@
+#!/bin/sh
+#
+# CCDEPS-GCC (C) 2002 Emile van Bergen. Distribution of this file is allowed
+# under the conditions detailed in the GNU General Public License (GPL). See 
+# the file COPYING for more information.
+#
+# This script compiles and/or links one or more source or object files into a
+# object file or executable target, and outputs all extra dependencies found
+# while doing so in a file named target.d, which can be used by GNU Make.
+#
+# The script should be invoked the same way as your C compiler, that is,
+# specifying the target using a -o option and the source or object files as 
+# non-option arguments. It will generate dependencies in the form
+#
+# target target.d: dir/file1.c dir/file2.c header1.h header2.h
+# dir/file1.c dir/file2.c header1.h header2.h:
+#
+# This version is intended for GCC, which can do compilation and dependency
+# generation in one step. The name of the GCC version (default gcc) can be
+# overridden using the CC environment variable.
+#
+# CHANGELOG
+#
+# 2003/1/8: EvB: adapted for gcc 3.2, still handles 2.95 as well.
+#
+#   This was necessary because gcc 3.2 handles -MD differently than gcc 2.95:
+#   where the old version generated a .d file for each source, in the current
+#   directory, the new one does almost completely what this script intended to
+#   do: generate one .d file in the same directory and with the same file name
+#   as the target.
+#
+#   The only fixups 3.2's .d files still need are:
+#
+#   - changing the file name; gcc 3.2 strips the suffix of the target before 
+#     appending the .d, so targets x and x.o will both produce x.d, which is
+#     not what we want;
+#
+#   - adding the implicit dependencies as prerequisiteless targets, so that
+#     make will just consider the target out of date if one does not exist
+#     anymore; 
+#
+#   - adding the .d file as depending on the same prerequisites as our real
+#     target so that it will be considered out of date if one of the files
+#     mentioned in it are updated or missing.
+#
+#   Basically, this version does all that by simply including the file
+#   <strippedtarget>.d file in the list of .d files we look for. We may end
+#   up generating the same file name, but that already was handled correctly.
+#   Otherwise we perform the normal routine, so that we /know/ the targets will
+#   be correct, directories and all, regardless of variations in gcc behaviour.
+
+test -x "functions.sh" && . "functions.sh"
+test -x "build/functions.sh" && . "build/functions.sh"
+
+export CC=$1
+shift
+
+
+cmdline=""
+# After having passed the arguments, they have already been parsed once by
+# the shell, so they needed to be re-quoted.
+for arg in "$@"
+do
+       arg="$(quote_string "$arg")"
+       cmdline="$cmdline $arg"
+done
+
+
+while [ x"$1" != x ]
+do
+       case "$1" in
+               -o) tgt="$2" ; shift ;; # target specifier option
+               -x|-u|-b|-V) shift ;;   # options with arg after space
+               -*) ;;                  # standard options
+               *) fil="$fil $1" ;;     # source or object files
+       esac
+       shift
+done
+
+#if [ x"$CC" = x ]
+#then
+       #CC=gcc
+       #export CC
+#fi
+
+# If we're not processing any .c files (link only), run gcc as-is and we're done
+
+expr "$fil" : ".*\.c" >/dev/null || exec $CC $cmdline
+
+# Otherwise, run the gcc with the -MD option, which generates a .d file
+# in the current directory for each .c or .cc source file processed.
+#
+# These files are post-processed (replacing the incorrectly named target
+# with the real target specified with -o, and adding the .d file), concatenated
+# into one .d file that is named based on the target name, and put in the
+# correct directory. Further, all prerequisites are added as bare targets,
+# preventing errors when files are missing due to renaming or restructuring
+# headers, but causing the files dependent on them to be considered out of
+# date. (GNU Make feature).
+#
+# Makefiles must include the .d files like this: -include $(OBJS_$(d):.o=.d)
+# or, when compiling and linking in one step: -include $(TGTS_$(d):%=%.d)
+
+dep=$tgt.d
+rm -f $dep
+
+#echo $CC -MD $cmdline
+eval "$CC -MD $cmdline"
+res=$?
+
+dgcc3=`echo $tgt | sed -e 's/\.[^.]*$//'`.d
+dgcc=`echo $fil | sed -e 's/[^         ]*\.[^c]//' -e 's/\.cpp/\.d/g' -e 's/\.cc/\.d/g' -e 's/\.c/\.d/g' -e 's%.*/%%g'`
+
+for tf in $dgcc3 $dgcc
+do
+       if [ -f $tf ] && mv $tf $dep.tmp
+       then
+               sed -e "s%.*:%$tgt $dep:%" < $dep.tmp >> $dep
+               sed -e 's%^.*:%%' -e 's%^ *%%' -e 's% *\\$%%' -e 's%$%:%' \
+                       < $dep.tmp >> $dep
+               rm -f $dep.tmp
+               found=1
+       fi
+done
+
+[ x"$found" = x"1" ] && exit $res
+
+echo ERROR: $0: Cannot find any compiler-generated dependency files\!
+exit 1
+
diff --git a/build/config.guess b/build/config.guess
new file mode 100755 (executable)
index 0000000..bf48496
--- /dev/null
@@ -0,0 +1,1517 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+#   Free Software Foundation, Inc.
+
+timestamp='2009-12-30'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner.  Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free
+Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+       for c in cc gcc c89 c99 ; do
+         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "${UNAME_SYSTEM}" in
+Linux|GNU/*)
+       eval $set_cc_for_build
+       cat <<-EOF > $dummy.c
+       #include <features.h>
+       #ifdef __UCLIBC__
+       # ifdef __UCLIBC_CONFIG_VERSION__
+       LIBC=uclibc __UCLIBC_CONFIG_VERSION__
+       # else
+       LIBC=uclibc
+       # endif
+       #else
+       # ifdef __dietlibc__
+       LIBC=dietlibc
+       # else
+       LIBC=gnu
+       # endif
+       #endif
+       EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+       ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       sysctl="sysctl -n hw.machine_arch"
+       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       case "${UNAME_MACHINE_ARCH}" in
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           sh5el) machine=sh5le-unknown ;;
+           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE_ARCH}" in
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep -q __ELF__
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case "${UNAME_VERSION}" in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit ;;
+    *:OpenBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+       exit ;;
+    *:ekkoBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+       exit ;;
+    *:SolidBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+       exit ;;
+    macppc:MirBSD:*:*)
+       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    *:MirBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    alpha:OSF1:*:*)
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case "$ALPHA_CPU_TYPE" in
+           "EV4 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE="alpha" ;;
+           "EV5 (21164)")
+               UNAME_MACHINE="alphaev5" ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE="alphaev56" ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE="alphapca56" ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE="alphapca57" ;;
+           "EV6 (21264)")
+               UNAME_MACHINE="alphaev6" ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE="alphaev67" ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE="alphaev69" ;;
+           "EV7 (21364)")
+               UNAME_MACHINE="alphaev7" ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE="alphaev79" ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-morphos
+       exit ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit ;;
+    *:z/VM:*:*)
+       echo s390-ibm-zvmoe
+       exit ;;
+    *:OS400:*:*)
+        echo powerpc-ibm-os400
+       exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit ;;
+    arm:riscos:*:*|arm:RISCOS:*:*)
+       echo arm-unknown-riscos
+       exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit ;;
+    DRS?6000:unix:4.0:6*)
+       echo sparc-icl-nx6
+       exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) echo sparc-icl-nx7; exit ;;
+       esac ;;
+    s390x:SunOS:*:*)
+       echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+       echo i386-pc-auroraux${UNAME_RELEASE}
+       exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+       eval $set_cc_for_build
+       SUN_ARCH="i386"
+       # If there is a compiler, see if it is configured for 64-bit objects.
+       # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+       # This test works for both compilers.
+       if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+           if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+               (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+               grep IS_64BIT_ARCH >/dev/null
+           then
+               SUN_ARCH="x86_64"
+           fi
+       fi
+       echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit ;;
+    m68k:machten:*:*)
+       echo m68k-apple-machten${UNAME_RELEASE}
+       exit ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c &&
+         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`$dummy $dummyarg` &&
+           { echo "$SYSTEM_NAME"; exit; }
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit ;;
+    Motorola:*:4.3:PL8-*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+           else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+       fi
+       exit ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+       exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               eval $set_cc_for_build
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+               then
+                       echo "$SYSTEM_NAME"
+               else
+                       echo rs6000-ibm-aix3.2.5
+               fi
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit ;;
+    *:AIX:*:[456])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if [ -x /usr/bin/getconf ]; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+               fi
+               if [ "${HP_ARCH}" = "" ]; then
+                   eval $set_cc_for_build
+                   sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if [ ${HP_ARCH} = "hppa2.0w" ]
+       then
+           eval $set_cc_for_build
+
+           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+           # generating 64-bit code.  GNU and HP use different nomenclature:
+           #
+           # $ CC_FOR_BUILD=cc ./config.guess
+           # => hppa2.0w-hp-hpux11.23
+           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+           # => hppa64-hp-hpux11.23
+
+           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+               grep -q __LP64__
+           then
+               HP_ARCH="hppa2.0w"
+           else
+               HP_ARCH="hppa64"
+           fi
+       fi
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit ;;
+    3050*:HI-UX:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+               { echo "$SYSTEM_NAME"; exit; }
+       echo unknown-hitachi-hiuxwe2
+       exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    *:UNICOS/mp:*:*)
+       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit ;;
+    5000:UNIX_System_V:4.*:*)
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:FreeBSD:*:*)
+       case ${UNAME_MACHINE} in
+           pc98)
+               echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+           amd64)
+               echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+           *)
+               echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+       esac
+       exit ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit ;;
+    *:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit ;;
+    i*:windows32*:*)
+       # uname -m includes "-pc" on this system.
+       echo ${UNAME_MACHINE}-mingw32
+       exit ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit ;;
+    *:Interix*:*)
+       case ${UNAME_MACHINE} in
+           x86)
+               echo i586-pc-interix${UNAME_RELEASE}
+               exit ;;
+           authenticamd | genuineintel | EM64T)
+               echo x86_64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+           IA64)
+               echo ia64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+       esac ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+       echo i${UNAME_MACHINE}-pc-mks
+       exit ;;
+    8664:Windows_NT:*)
+       echo x86_64-pc-mks
+       exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i586-pc-interix
+       exit ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+       echo x86_64-unknown-cygwin
+       exit ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    *:GNU:*:*)
+       # the GNU system
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
+       exit ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+       objdump --private-headers /bin/sh | grep -q ld.so.1
+       if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    arm*:Linux:*:*)
+       eval $set_cc_for_build
+       if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_EABI__
+       then
+           echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       else
+           echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
+       fi
+       exit ;;
+    avr32*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    cris:Linux:*:*)
+       echo cris-axis-linux-${LIBC}
+       exit ;;
+    crisv32:Linux:*:*)
+       echo crisv32-axis-linux-${LIBC}
+       exit ;;
+    frv:Linux:*:*)
+       echo frv-unknown-linux-${LIBC}
+       exit ;;
+    i*86:Linux:*:*)
+       echo ${UNAME_MACHINE}-pc-linux-${LIBC}
+       exit ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    m32r*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef ${UNAME_MACHINE}
+       #undef ${UNAME_MACHINE}el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=${UNAME_MACHINE}el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=${UNAME_MACHINE}
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+       ;;
+    or32:Linux:*:*)
+       echo or32-unknown-linux-${LIBC}
+       exit ;;
+    padre:Linux:*:*)
+       echo sparc-unknown-linux-${LIBC}
+       exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-${LIBC}
+       exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
+         PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
+         *)    echo hppa-unknown-linux-${LIBC} ;;
+       esac
+       exit ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-${LIBC}
+       exit ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-${LIBC}
+       exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit ;;
+    sh64*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    vax:Linux:*:*)
+       echo ${UNAME_MACHINE}-dec-linux-${LIBC}
+       exit ;;
+    x86_64:Linux:*:*)
+       echo x86_64-unknown-linux-${LIBC}
+       exit ;;
+    xtensa*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+       exit ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit ;;
+    i*86:atheos:*:*)
+       echo ${UNAME_MACHINE}-unknown-atheos
+       exit ;;
+    i*86:syllable:*:*)
+       echo ${UNAME_MACHINE}-pc-syllable
+       exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit ;;
+    i*86:*:5:[678]*)
+       # UnixWare 7.x, OpenUNIX and OpenServer 6.
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i586.
+       # Note: whatever this is, it MUST be the same as what config.sub
+       # prints for the "djgpp" host, or else GDB configury will decide that
+       # this is a cross-build.
+       echo i586-pc-msdosdjgpp
+        exit ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       echo m68k-convergent-sysv
+       exit ;;
+    M680?0:D-NIX:5.3:*)
+       echo m68k-diab-dnix
+       exit ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+       OS_REL='.3'
+       test -r /etc/.relid \
+           && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+           && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+           && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+           && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit ;;
+    i*86:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo ${UNAME_MACHINE}-stratus-vos
+       exit ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit ;;
+    BePC:Haiku:*:*)    # Haiku running on Intel PC compatible.
+       echo i586-pc-haiku
+       exit ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-6:SUPER-UX:*:*)
+       echo sx6-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-7:SUPER-UX:*:*)
+       echo sx7-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8:SUPER-UX:*:*)
+       echo sx8-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8R:SUPER-UX:*:*)
+       echo sx8r-nec-superux${UNAME_RELEASE}
+       exit ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+       case $UNAME_PROCESSOR in
+           i386)
+               eval $set_cc_for_build
+               if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+                 if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+                     (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+                     grep IS_64BIT_ARCH >/dev/null
+                 then
+                     UNAME_PROCESSOR="x86_64"
+                 fi
+               fi ;;
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = "x86"; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       exit ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit ;;
+    NSE-?:NONSTOP_KERNEL:*:*)
+       echo nse-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+       exit ;;
+    SEI:*:*:SEIUX)
+        echo mips-sei-seiux${UNAME_RELEASE}
+       exit ;;
+    *:DragonFly:*:*)
+       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case "${UNAME_MACHINE}" in
+           A*) echo alpha-dec-vms ; exit ;;
+           I*) echo ia64-dec-vms ; exit ;;
+           V*) echo vax-dec-vms ; exit ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       echo i386-pc-xenix
+       exit ;;
+    i*86:skyos:*:*)
+       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+       exit ;;
+    i*86:rdos:*:*)
+       echo ${UNAME_MACHINE}-pc-rdos
+       exit ;;
+    i*86:AROS:*:*)
+       echo ${UNAME_MACHINE}-pc-aros
+       exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+       { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    c34*)
+       echo c34-convex-bsd
+       exit ;;
+    c38*)
+       echo c38-convex-bsd
+       exit ;;
+    c4*)
+       echo c4-convex-bsd
+       exit ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/build/config.sub b/build/config.sub
new file mode 100755 (executable)
index 0000000..4c0d959
--- /dev/null
@@ -0,0 +1,1732 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+#   Free Software Foundation, Inc.
+
+timestamp='2010-01-22'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted GNU ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free
+Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+  uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+  kopensolaris*-gnu* | \
+  storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis | -knuth | -cray | -microblaze)
+               os=
+               basic_machine=$1
+               ;;
+        -bluegene*)
+               os=-cnk
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco6)
+               os=-sco5v6
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5v6*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | am33_2.0 \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+       | bfin \
+       | c4x | clipper \
+       | d10v | d30v | dlx | dsp16xx | dvp \
+       | fido | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k | iq2000 \
+       | lm32 \
+       | m32c | m32r | m32rle | m68000 | m68k | m88k \
+       | maxq | mb | microblaze | mcore | mep | metag \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64octeon | mips64octeonel \
+       | mips64orion | mips64orionel \
+       | mips64r5900 | mips64r5900el \
+       | mips64vr | mips64vrel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mips64vr5900 | mips64vr5900el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64r2 | mipsisa64r2el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | moxie \
+       | mt \
+       | msp430 \
+       | nios | nios2 \
+       | ns16k | ns32k \
+       | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | rx \
+       | score \
+       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+       | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+       | spu | strongarm \
+       | tahoe | thumb | tic4x | tic80 | tron \
+       | ubicom32 \
+       | v850 | v850e \
+       | we32k \
+       | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+       | z8k | z80)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12 | picochip)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+       ms1)
+               basic_machine=mt-unknown
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* | avr32-* \
+       | bfin-* | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+       | clipper-* | craynv-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* | iq2000-* \
+       | lm32-* \
+       | m32c-* | m32r-* | m32rle-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64octeon-* | mips64octeonel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64r5900-* | mips64r5900el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mips64vr5900-* | mips64vr5900el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64r2-* | mipsisa64r2el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | mmix-* \
+       | mt-* \
+       | msp430-* \
+       | nios-* | nios2-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* | rx-* \
+       | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+       | sparclite-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+       | tahoe-* | thumb-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+       | tile-* | tilegx-* \
+       | tron-* \
+       | ubicom32-* \
+       | v850-* | v850e-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+       | xstormy16-* | xtensa*-* \
+       | ymp-* \
+       | z8k-* | z80-*)
+               ;;
+       # Recognize the basic CPU types without company name, with glob match.
+       xtensa*)
+               basic_machine=$basic_machine-unknown
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       abacus)
+               basic_machine=abacus-unknown
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amd64)
+               basic_machine=x86_64-pc
+               ;;
+       amd64-*)
+               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aros)
+               basic_machine=i386-pc
+               os=-aros
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       blackfin)
+               basic_machine=bfin-unknown
+               os=-linux
+               ;;
+       blackfin-*)
+               basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       bluegene*)
+               basic_machine=powerpc-ibm
+               os=-cnk
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+        cegcc)
+               basic_machine=arm-unknown
+               os=-cegcc
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       craynv)
+               basic_machine=craynv-cray
+               os=-unicosmp
+               ;;
+       cr16)
+               basic_machine=cr16-unknown
+               os=-elf
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       crisv32 | crisv32-* | etraxfs*)
+               basic_machine=crisv32-axis
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       crx)
+               basic_machine=crx-unknown
+               os=-elf
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dicos)
+               basic_machine=i686-pc
+               os=-dicos
+               ;;
+       djgpp)
+               basic_machine=i586-pc
+               os=-msdosdjgpp
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m68knommu)
+               basic_machine=m68k-unknown
+               os=-linux
+               ;;
+       m68knommu-*)
+               basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+        microblaze)
+               basic_machine=microblaze-xilinx
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       mingw32ce)
+               basic_machine=arm-unknown
+               os=-mingw32ce
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mipsEE* | ee | ps2)
+               basic_machine=mips64r5900el-scei
+               case $os in
+                   -linux*)
+                       ;;
+                   *)
+                       os=-elf
+                       ;;
+               esac
+               ;;
+       iop)
+               basic_machine=mipsel-scei
+               os=-irx
+               ;;
+       dvp)
+               basic_machine=dvp-scei
+               os=-elf
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       ms1-*)
+               basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       openrisc | openrisc-*)
+               basic_machine=or32-unknown
+               ;;
+       os400)
+               basic_machine=powerpc-ibm
+               os=-os400
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       parisc)
+               basic_machine=hppa-unknown
+               os=-linux
+               ;;
+       parisc-*)
+               basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+       pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pc98)
+               basic_machine=i386-pc
+               ;;
+       pc98-*)
+               basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2 | pentiumiii | pentium3)
+               basic_machine=i686-pc
+               ;;
+       pentium4)
+               basic_machine=i786-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium4-*)
+               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rdos)
+               basic_machine=i386-pc
+               os=-rdos
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sde)
+               basic_machine=mipsisa32-sde
+               os=-elf
+               ;;
+       sei)
+               basic_machine=mips-sei
+               os=-seiux
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sh5el)
+               basic_machine=sh5le-unknown
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tic55x | c55x*)
+               basic_machine=tic55x-unknown
+               os=-coff
+               ;;
+       tic6x | c6x*)
+               basic_machine=tic6x-unknown
+               os=-coff
+               ;;
+        # This must be matched before tile*.
+        tilegx*)
+               basic_machine=tilegx-unknown
+               os=-linux-gnu
+               ;;
+       tile*)
+               basic_machine=tile-unknown
+               os=-linux-gnu
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       tpf)
+               basic_machine=s390x-ibm
+               os=-tpf
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xbox)
+               basic_machine=i686-pc
+               os=-mingw32
+               ;;
+       xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       z80-*-coff)
+               basic_machine=z80-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       mmix)
+               basic_machine=mmix-knuth
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+               basic_machine=sh-unknown
+               ;;
+       sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+               basic_machine=sparc-sun
+               ;;
+       cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+        -auroraux)
+               os=-auroraux
+               ;;
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+             | -sym* | -kopensolaris* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* | -aros* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+             | -openbsd* | -solidbsd* \
+             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* | -cegcc* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+             | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -irx* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+             | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux-dietlibc)
+               os=-linux-dietlibc
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+        -os400*)
+               os=-os400
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -syllable*)
+               os=-syllable
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+        -tpf*)
+               os=-tpf
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -aros*)
+               os=-aros
+               ;;
+       -kaos*)
+               os=-kaos
+               ;;
+       -zvmoe)
+               os=-zvmoe
+               ;;
+       -dicos*)
+               os=-dicos
+               ;;
+        -nacl*)
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+        score-*)
+               os=-elf
+               ;;
+        spu-*)
+               os=-elf
+               ;;
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+        c4x-* | tic4x-*)
+               os=-coff
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+       pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+        mep-*)
+               os=-elf
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-haiku)
+               os=-haiku
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-knuth)
+               os=-mmixware
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+       *-gould)
+               os=-sysv
+               ;;
+       *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+       *-sgi)
+               os=-irix
+               ;;
+       *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -cnk*|-aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -os400*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -tpf*)
+                               vendor=ibm
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/build/functions.sh b/build/functions.sh
new file mode 100755 (executable)
index 0000000..c330d93
--- /dev/null
@@ -0,0 +1,93 @@
+
+
+# Print error message to stderr and exit.  The error message can either be
+# given as parameters or from stdin.
+die()
+{
+       echo -n "fatal: " >/dev/stderr
+       if [ "x$*" != x ]
+       then
+               echo $* >/dev/stderr
+       else
+               while read line;
+               do
+                       echo $line >/dev/stderr
+               done
+       fi
+       exit 1
+}
+
+# Parse an argument from an option in the form --option=argument.
+parse_argument()
+{
+       echo $(echo $2 | sed -e "s/--$1=\\(.*\\)$/\\1/")
+       return $?
+}
+
+# Insert all the escapes necessary to correctly quote a string for use in a
+# shell command.
+quote_string()
+{
+       for arg in "$@"
+       do
+               echo $1 | sed -e "s/'/\\'/g" \
+                                         -e 's/"/\\"/g' \
+                                         -e 's/|/\\|/g' \
+                                         -e 's/&/\\&/g' \
+                                         -e 's/;/\\;/g' \
+                                         -e 's/;/\\;/g' \
+                                         -e 's/(/\\(/g' \
+                                         -e 's/)/\\)/g' \
+                                         -e 's/</\\</g' \
+                                         -e 's/>/\\>/g' \
+                                         -e 's/ /\\ /g' \
+                                         -e "s/\t/\\\t/g" \
+                                         -e 's/\$/\\\$/g'
+       done
+}
+
+# Add a definition to the compiler flags.  The optional second parameter is
+# the unquoted value.
+define()
+{
+       if test "x$2" = x
+       then
+               defines="$defines -D$1"
+       else
+               defines="$defines -D$1=$2"
+       fi
+}
+
+# Add a definition to the compiler flags with a quoted value.
+define_string()
+{
+       arg=$(quote_string "$2")
+       define "$1" "\\\"$arg\\\""
+}
+
+undefine()
+{
+       for arg in "$@"
+       do
+               defines="$defines -U$arg"
+       done
+}
+
+is_defined()
+{
+       for arg in "$@"
+       do
+               echo "$defines" | grep -e "-D$arg" 2>&1 >/dev/null || return 1
+       done
+       return 0
+}
+
+is_undefined()
+{
+       for arg in "$@"
+       do
+               echo "$defines" | grep -e "-U$arg" 2>&1 >/dev/null || return 1
+       done
+       return 0
+}
+
diff --git a/build/install.sh b/build/install.sh
new file mode 100755 (executable)
index 0000000..be27f7f
--- /dev/null
@@ -0,0 +1,191 @@
+#!/bin/sh
+#
+# INSTALL (C) 2002 Emile van Bergen. Distribution of this file is allowed under
+# the conditions detailed in the GNU General Public License (GPL). See the file
+# COPYING for more information.
+#
+# This script installs zero or more files into a specified directory. If one or
+# more components of the destination path do not exist, they are created.  The
+# permissions of the destination file and the destination directory(s), if any
+# need to be created, can be specified separately. The user can also specify
+# that the operation must be skipped if the destination file or the destination
+# directory already exists. Source files are stripped of their directory
+# components before determining the destination name.
+#
+# It is intended to replace the /usr/bin/install command, which has no portable
+# subset of features that offers anything above /bin/cp. Each version is broken
+# in its own ways, the most annoying examples being that some can only install
+# a single file at a time and that some do not create destination directories
+# recursively. Hence this shell script, that is intended to be portable across
+# all POSIX-compliant /bin/sh shells. It does not assume a working 'mkdir -p'
+# command.
+#
+# Invocation:
+#
+# install arguments...
+#
+# Each argument is either an option, as specified below, a source file, or
+# a destination directory, with -d prepended. Each time a destination
+# directory is encountered, the source files leading up to it are installed,
+# and then all options are reset, allowing you to perform multiple install
+# operations with one command.
+#
+# Options:
+#
+#      -h      Show a brief usage message
+#      -v      Show what's being done
+#      -m      specify mode of destination files
+#      -md     specify mode of destination directories, if any are created
+#      -n      skip operation if destination file exists
+#      -nd     skip operation if destination directory exists
+#
+# Return values:
+#
+#      0       Successful completion
+#      >0      Error occurred
+#
+# Limitations:
+#
+# * Source files cannot start with a '-' or contain spaces
+# * Destination directories cannot start with a '-' or contain spaces
+# * If multiple different modes are desired for files in a single destination 
+#   directory, you must specify multiple installation sets (-d options), one 
+#   for each mode (eg. install -m 644 file1 file2 -d dir file3 -m 600 -d dir).
+# * The /bin/sh shell used to run this script must support user-defined 
+#   functions.
+# * The system must have mkdir, chmod, basename, tr, sed and cp available.
+#   If needed, basename and tr could be provided by sed, but I don't think
+#   that should be done by default as they are very common.
+#
+# Notes (not bugs, features. Really!):
+#
+# * If the destination directory already exists, its mode is not changed
+#   even if -md is specified; that mode is only used when creating new ones.
+# * If the destination file already exists but is overwritten because no -n
+#   was specified, the new mode, if specified, is applied as well.
+# * QNX-style paths starting with // are honored, as are .. path components.
+#   An initial .. works as expected, and a destination path a/b/../c creates
+#   a, a/b, a/c and installs the files in a/c.
+#
+# History
+#
+# 2002/09/13 - EvB - Created
+
+
+make_dir() {
+
+  dir="$1"
+  [ -n "$verbose" ] && echo "Creating directory $dir"
+
+  mkdir "$dir" || exit 1
+
+  if [ -n "$mode_dir" ] 
+  then
+    chmod "$mode_dir" "$dir" || exit 2
+  fi
+
+  return
+}
+
+
+make_dir_tree() {
+
+  root=`echo $1 | sed -e 's/[^/].*$//g'`
+  components=`echo $1 | tr / " "`
+
+  cumul=
+  for comp in $components
+  do
+    if [ -n "$cumul" ] 
+    then
+      cumul="$cumul/$comp"
+    else
+      cumul="$comp"
+    fi
+    [ "$comp" = "." ] || [ "$comp" = ".." ] || 
+      [ -d "$root$cumul" ] || make_dir "$root$cumul"
+  done
+
+  dest=$root$cumul
+}
+
+
+do_install() {
+
+  dest="$1"
+
+  if [ ! -d "$dest" ] 
+  then
+    make_dir_tree "$dest"
+  else
+    if [ -n "$new_dir" ]
+    then
+      echo "$me: Directory $dest already exists -- skipping"
+      return
+    fi
+  fi
+
+  for src_file in $src
+  do
+    file=`basename $src_file`
+    dest_file="$dest/$file"
+
+    if [ -n "$new" ] && [ -f $dest_file ]
+    then
+      echo "$me: File $dest_file already exists -- skipping"
+      continue
+    fi
+
+    [ -n "$verbose" ] && echo "Copying $src_file to $dest_file"
+    cp "$src_file" "$dest_file" || exit 3
+
+    if [ -n "$mode" ] 
+    then
+      chmod "$mode" "$dest_file" || exit 4
+    fi
+  done
+
+  return
+}
+
+
+init_opts() {
+# verbose=
+  mode=
+  mode_dir=
+  new=
+  new_dir=
+  src=
+}
+
+
+### Main entry point
+
+me=`basename $0`
+init_opts
+while [ -n "$1" ]
+do
+  case "$1" in
+
+    -v) verbose=1 ;;
+    
+    -m) mode="$2" ; shift ;;
+    -md) mode_dir="$2" ; shift ;;
+
+    -n) new=1 ;;
+    -nd) new_dir=1 ;;
+
+    -d) do_install "$2" ; init_opts ; shift ;;
+
+    -*)
+      echo Usage: $me [options] [file...] -d directory
+      exit 5
+      ;;
+
+    *) src="$src $1" ;;
+
+  esac
+  shift
+done
+
+exit 0
diff --git a/build/link.sh b/build/link.sh
new file mode 100755 (executable)
index 0000000..d3a739c
--- /dev/null
@@ -0,0 +1,160 @@
+#!/bin/sh
+
+#
+# Yoink
+# Run this script to link the executable with fewer direct dependencies.
+#
+# You shouldn't call this directly; instead, use the configure script's
+# --enable-link-sh option and run make normally.  This isn't enabled by
+# default because there is the potential for runtime linking problems on
+# some platforms.  If you have a newer version of GCC, you should prefer
+# the --as-needed linker flag over this method, though they both should
+# accomplish the same thing.
+#
+# This script was adapted from some public domain code written by Bram
+# Moolenaar for Vim.  The only input needed is the link command in the
+# variable LINK.  It is expected that the linker will return an error code
+# or this will not work.  The script caches the test results in the
+# `.link/link.sed' file; delete that file if you want to redetermine the
+# required direct dependencies.
+#
+
+
+# List here any libraries that are known to not be needed on some platform.
+libraries="\
+       atk-1.0 \
+       cairo \
+       fontconfig \
+       freetype \
+       gdk-x11-2.0 \
+       gio-2.0 \
+       glib-2.0 \
+       gmodule-2.0 \
+       ogg \
+       pango-1.0 \
+       pangocairo-1.0 \
+       pangoft2-1.0 \
+       pthread \
+       vorbis \
+       $THE_END"
+
+
+linkdir=".link"
+logfile="$linkdir/link.log"
+sedfile="$linkdir/link.sed"
+
+workdir=$(mktemp -d tmp.XXXXXXXX)
+cmdfile="$workdir/link.cmd"
+runfile="$workdir/link.run"
+
+tmpfile1="$workdir/link.tmp1"
+tmpfile2="$workdir/link.tmp2"
+tmpfile3="$workdir/link.tmp3"
+
+
+printlog()
+{
+       echo "link.sh: $@"
+}
+
+echo "$LINK " >$cmdfile
+exitcode=0
+
+
+if test -f $sedfile
+then
+       printlog "The file $sedfile exists, which is now going to be used."
+       printlog "If linking fails, try deleting the $sedfile file."
+       printlog "If that fails, try creating an empty $sedfile file."
+       printlog "If that fails, configure the package with --disable-link-sh."
+else
+       cat $cmdfile
+       if sh $cmdfile
+       then
+               mkdir -p $linkdir
+               touch $sedfile
+               cp $cmdfile $runfile
+               for libname in $libraries
+               do
+                       cont=yes
+                       while test -n "$cont"
+                       do
+                               if grep "l$libname " $runfile >/dev/null
+                               then
+                                       if test ! -f $tmpfile1
+                                       then
+                                               printlog "Full linking works; now the fun begins."
+                                               printlog "See $logfile for details."
+                                               rm -f $logfile
+                                       fi
+                                       echo "s/-l$libname  *//" >$tmpfile1
+                                       sed -f $sedfile <$cmdfile | sed -f $tmpfile1 >$runfile
+                                       # keep the last -lm; this is supposedly needed by HP-UX
+                                       if test $libname != "m" || grep "lm " $runfile >/dev/null
+                                       then
+                                               printlog "Trying to remove the $libname library..."
+                                               cat $runfile >>$logfile
+                                               if sh $runfile >>$logfile 2>&1
+                                               then
+                                                       printlog "We don't need the $libname library!"
+                                                       cat $tmpfile1 >>$sedfile
+                                                       continue
+                                               else
+                                                       printlog "We DO need the $libname library."
+                                               fi
+                                       fi
+                               fi
+                               cont=
+                               cp $cmdfile $runfile
+                       done
+               done
+               if test ! -f $tmpfile1
+               then
+                       printlog "Linked fine, no libraries can be removed."
+                       touch $tmpfile3
+               fi
+       else
+               exitcode=$?
+       fi
+fi
+
+
+if test -s $sedfile
+then
+       printlog "Using $sedfile file to remove a few libraries."
+       sed -f $sedfile <$cmdfile >$runfile
+       cat $runfile
+       if sh $runfile
+       then
+               exitcode=0
+               printlog "Linked fine with a few libraries removed."
+       else
+               exitcode=$?
+               printlog "Linking failed, making $sedfile empty and trying again."
+               mv -f $sedfile $tmpfile2
+               touch $sedfile
+       fi
+fi
+
+if test -f $sedfile -a ! -s $sedfile -a ! -f $tmpfile3
+then
+       printlog "Using unmodified link command."
+       cat $cmdfile
+       if sh $cmdfile
+       then
+               exitcode=0
+               printlog "Linked OK."
+       else
+               exitcode=$?
+               if test -f $tmpfile2
+               then
+                       printlog "Linking doesn't work at all, removing $sedfile."
+                       rm -f $sedfile
+               fi
+       fi
+fi
+
+
+rm -rf "$workdir"
+exit $exitcode
+
index d45b7566fccedba5db34cc4a2e463f2207b81100..a4f33cb40b21f52d65b956a13b305135c75305bd 100644 (file)
@@ -77,6 +77,12 @@ AC_ARG_ENABLE([threads],
                          [threads=$enableval],
                          [threads=no])
 
+AC_ARG_ENABLE([hotloading],
+                         [AS_HELP_STRING([--enable-hotloading],
+                                                         [monitor assets and reload them when they change])],
+                         [hotloading=$enableval],
+                         [hotloading=no])
+
 AC_ARG_WITH([gtk],
                        [AS_HELP_STRING([--with-gtk],
                                                        [use gtk2 modal dialogs])],
@@ -127,6 +133,12 @@ then
                          [Define to 1 if you want to use threads when applicable.])
 fi
 
+if test x$hotloading = xyes
+then
+       AC_DEFINE([USE_HOTLOADING], 1,
+                         [Define to 1 if you want to use hotloading assets.])
+fi
+
 if test x$gtk = xyes
 then
        AC_DEFINE([USE_GTK], 1,
@@ -216,6 +228,13 @@ AM_CONDITIONAL([HAVE_MAKENSIS], [test x$MAKENSIS != x])
 AC_MSG_NOTICE([Checks for libraries.])
 ####
 
+##### boost#####
+website="http://www.boost.org/"
+BOOST_BIND
+BOOST_FUNCTION
+BOOST_SMART_PTR
+BOOST_STRING_ALGO
+
 ##### SDL #####
 website="http://www.libsdl.org/"
 PKG_CHECK_MODULES([SDL], [sdl],
@@ -314,11 +333,6 @@ AC_HEADER_STDBOOL
 AC_HEADER_STDC
 AC_CHECK_HEADERS([arpa/inet.h byteswap.h fcntl.h stddef.h stdint.h stdlib.h string.h unistd.h])
 
-BOOST_SMART_PTR
-BOOST_STRING_ALGO
-BOOST_BIND
-BOOST_FUNCTION
-
 
 ####
 AC_MSG_NOTICE([Checks for types.])
diff --git a/data/rules.mk b/data/rules.mk
new file mode 100644 (file)
index 0000000..92d7529
--- /dev/null
@@ -0,0 +1,25 @@
+
+#########################
+sp             := $(sp).x
+dirstack_$(sp) := $(d)
+d              := $(dir)
+#########################
+
+#
+# Define rules and targets for data files.
+#
+
+TGTS_$(d) := $(DATA_FILES)
+
+# TODO: Also need to install yoink.desktop and the pixmap.
+
+
+TGT_DATA  := $(TGT_DATA) $(DATA_FILES)
+
+
+#######################
+-include $(DEPS_$(d))
+d  := $(dirstack_$(sp))
+sp := $(basename $(sp))
+#######################
+
index f6f362cbeedb0d88403317c000cd0293d70ef0bd..d26ac4d83481a0a22dfb08bf9a72eb853f4fd0ab 100644 (file)
@@ -1,6 +1,5 @@
 
 -- Example Yoink Configuration File
--- ex:ft=lua ts=4 sw=4 tw=75
 
 print "loading default settings..."
 
@@ -54,12 +53,9 @@ resizable            = true
 -- videomode to override the default resolution.  If the fullscreen option
 -- is false, videomode will determine the size of the window.
 
---videomode            = {800, 600}
-
--- Set this to make the cursor remain visible as you mouse over the video
--- output of the game.
-
-showcursor             = false
+if fullscreen == false then
+       videomode       = {800, 600}
+end
 
 -- Set this to use double-buffering to improve animation quality.  You
 -- really don't want to turn this off.
@@ -74,12 +70,15 @@ doublebuffer        = true
 
 swapcontrol            = true
 
--- Set the level of log detail that will be printed to the console.
+-- Set the level of log detail that will be output to the console.
 -- Possible values are:
 -- 0 print nothing
--- 1 errors only
+-- 1 output errors only
 -- 2 include warnings
 -- 3 print everything, including debug messages
 
 loglevel               = 2
 
+
+-- vi:ft=lua ts=4 sw=4 tw=75
+
diff --git a/doc/rules.mk b/doc/rules.mk
new file mode 100644 (file)
index 0000000..c654bbd
--- /dev/null
@@ -0,0 +1,23 @@
+
+#########################
+sp             := $(sp).x
+dirstack_$(sp) := $(d)
+d              := $(dir)
+#########################
+
+#
+# Define rules and targets for data files.
+#
+
+TGTS_$(d) := $(d)/yoink.6.in
+
+
+TGT_MAN  := $(TGT_MAN) $(DATA_FILES)
+
+
+#######################
+-include $(DEPS_$(d))
+d  := $(dirstack_$(sp))
+sp := $(basename $(sp))
+#######################
+
index 24787e8392f6da0ecd0d6274ca950c702a69967f..b730c6bed62b4b2eb23c2fd762c4f1009890e94e 100644 (file)
@@ -12,7 +12,7 @@ Yoink \- An alien-smashing action game.
 .B yoink [-hi] [OPTION=VALUE]...
 .SH DESCRIPTION
 Leap tall buildings!  Crush stupid robots beneath your feet!  Wield your
-extra-terrestrial powers in the defence of humanity, and send those alien
+extra-terrestrial powers in the defense of humanity, and send those alien
 invaders back from whence they came!
 
 You play the part of a flying alien heroine who must defend her home on
index ba34bc5d9f95cae0bc86fa0aba1242cf73cf4626..503f77a47c2eecf21a24a2986d082061358af829 100644 (file)
@@ -139,7 +139,7 @@ public:
                        moof::script script;
                        std::string path(name);
                        
-                       if (!Animation::find_path(path))
+                       if (!resource::find(path))
                        {
                                throw std::runtime_error("cannot find resource " + name);
                        }
@@ -306,14 +306,3 @@ unsigned Animation::getFrame() const
        return impl_->mFrameIndex;
 }
 
-
-/**
- * Specialized search location for animation files.  They can be found in
- * the "animations" subdirectory of any of the search directories.
- */
-
-bool Animation::find_path(std::string& name)
-{
-       return moof::resource::find_path(name, "animations/", "lua");
-}
-
index ba3860113ba180a69f5c0ae276bf455879958199..3875b45f4819dccb880d4df763fbd676a8f4a708 100644 (file)
@@ -38,9 +38,6 @@ typedef boost::shared_ptr<Animation> AnimationP;
 
 class Animation : public moof::resource
 {
-       class impl;
-       boost::shared_ptr<impl> impl_;
-
 public:
 
        Animation(const std::string& name);
@@ -56,7 +53,11 @@ public:
        void update(moof::scalar t, moof::scalar dt);
        unsigned getFrame() const;
 
-       static bool find_path(std::string& name);
+
+private:
+
+       class impl;
+       boost::shared_ptr<impl> impl_;
 };
 
 
index a857cbebfa6fffbb76facc3830c220d67df59b52..01b4753f0f2287372e23b527293a050ee4fd1117 100644 (file)
@@ -73,13 +73,13 @@ Character::Character(const std::string& name) :
 
        // forces
        state_.force = moof::vector2(0.0, 0.0);
-       //state_.forces.push_back(SpringForce(moof::vector2(5.0, 4.0)));
+       state_.forces.push_back(SpringForce(moof::vector2(5.0, 4.0)));
        state_.forces.push_back(ResistanceForce(2.0));
-       //state_.forces.push_back(moof::linear_state<2>::gravity_force(-9.8));
+       state_.forces.push_back(moof::linear_state<2>::gravity_force(-9.8));
 
        // starting position
        state_.position = moof::vector2(5.0, 5.0);
-       state_.momentum = moof::vector2(0.0, 0.0);
+       state_.momentum = moof::vector2(1.0, 0.0);
        state_.recalculate();
 
        prev_state_ = state_;
index 0498182a9888b22eaac118a55f26b16df2025f86..2518bdf7971c339739c018f7e8e87835cc6747b6 100644 (file)
@@ -30,7 +30,7 @@ void GameLayer::loadSceneLoader()
        moof::log::import(state_.script);
 
        std::string path("loader");
-       if (!Scene::find_path(path))
+       if (!moof::resource::find(path))
        {
                throw std::runtime_error("cannot find scene loader script");
        }
@@ -79,14 +79,18 @@ void GameLayer::advanceScene(moof::settings& settings)
 }
 
 
-GameLayer::GameLayer() :
-       mMusic("NightFusionIntro"),
-       mPunchSound("Thump")
+GameLayer::GameLayer()
 {
-       mMusic.loop(true);
-       mMusic.enqueue("NightFusionLoop");
+       moof::log_info("about to load sound resource...");
+       music_ = moof::resource::load("sounds/NightFusionIntro.ogg");
+       if (music_)
+       {
+               music_->loop(true);
+               music_->enqueue("NightFusionLoop");
+       }
+       else moof::log_error("music not loaded");
 
-       //mMusic.setPosition(moof::vector3(10.0, 5.0, 0.0));
+       //music_->position(moof::vector3(10.0, 5.0, 0.0));
 
        mThinkTimer.init(boost::bind(&GameLayer::thinkTimer, this),
                        0.1, moof::timer::repeat);
@@ -102,7 +106,7 @@ void GameLayer::did_add_to_view()
 {
        bool isMute = false;
        settings().get("nomusic", isMute);
-       if (!isMute) mMusic.play();
+       if (!isMute) music_->play();
 
        loadSceneLoader();
        advanceScene(settings());               // load the first scene
@@ -216,12 +220,12 @@ bool GameLayer::handle_event(const moof::event& event)
                        {
                                state_.heroine->animation.startSequence("Flattened");
                                moof::log_info("thump!");
-                               mPunchSound.play();
+                               //mPunchSound.play();
                                return true;
                        }
                        else if (event.key.keysym.sym == SDLK_m)
                        {
-                               mMusic.toggle();
+                               music_->toggle();
                                return true;
                        }
                        else if (event.key.keysym.sym == SDLK_PAGEUP)
@@ -278,7 +282,7 @@ void GameLayer::projection()
 
 void GameLayer::projection(moof::scalar width, moof::scalar height)
 {
-       state_.camera.projection(moof::rad(45.0),
+       state_.camera.projection(moof::rad(60.0),
                                                         width / height,
                                                         SCALAR(1.0), SCALAR(200.0));
 }
index c194e9f2935f87cec180c9f8f2c4960c4f188d85..37eb8e07874dda42593d09e0efe7a1de2c305bd4 100644 (file)
@@ -71,12 +71,14 @@ private:
 
        HudP                    mHud;
 
-       moof::sound_stream      mMusic;
-       moof::sound             mPunchSound;
+       //moof::sound_stream    mMusic;
+       //moof::sound           mPunchSound;
+       
+       moof::sound_stream_handle       music_;
 
        moof::ray2              mRay;
        moof::line2             mLine;
-       moof::circle            mCircle;
+       moof::circle    mCircle;
 
        moof::timer             mRayTimer;
        void rayTimer();
index bb6b35bc1fd30ecde0c4b3dfb06108a4269c76ef..2df3f1e93da9e96cf114f998df45b5e063c7d03e 100644 (file)
@@ -27,9 +27,6 @@
 #include "Main.hh"
 #include "TitleLayer.hh"
 
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
 #include "version.h"
 
 
@@ -128,7 +125,7 @@ std::string Main::getConfigPath()
        // 4. YOINKRC (environment)
 
        std::string path("yoinkrc");
-       moof::resource::find_path(path);
+       moof::resource::find(path);
 
 #if !defined(_WIN32)
        path += ":/etc/yoinkrc";
@@ -245,6 +242,10 @@ void Main::printInfo(int argc, char* argv[])
                          << "-"
 #endif
                          << "gtk "
+#ifndef USE_HOTLOADING
+                         << "-"
+#endif
+                         << "hotloading "
 #ifndef PROFILING_ENABLED
                          << "-"
 #endif
@@ -278,8 +279,111 @@ void goodbye()
 
 #include <moof/socket.hh>
 
+#include <fstream>
+
+class MyAsset
+{
+public:
+       MyAsset(const std::string& path)
+       {
+               moof::log_info("MyAsset loading:", path);
+
+               char buffer[1024];
+
+               std::ifstream stream(path.c_str());
+               stream.getline(buffer, sizeof(buffer));
+               str = buffer;
+               stream.close();
+
+               cool();
+       }
+
+       void cool()
+       {
+               moof::log_info("MyAsset COOL:", str);
+       }
+
+       void groovy()
+       {
+               moof::log_info("MyAsset GROOVY!!!!", str);
+       }
+
+       std::string str;
+};
+
+typedef moof::resource_handle<MyAsset> MyAsset_handle;
+
+class AnotherAsset
+{
+public:
+       AnotherAsset(const std::string& path, double d = 5.0)
+       {
+               moof::log_info("AnotherAsset loading:", path);
+               dude = d;
+       }
+
+
+       void cool()
+       {
+               moof::log_info("AnotherAsset cool", dude);
+       }
+
+       void groovy()
+       {
+               moof::log_info("AnotherAsset GROOVY!!!!", dude);
+       }
+
+       double dude;
+};
+
+
 int main(int argc, char* argv[])
 {
+       moof::resource::register_type<MyAsset>("mine");
+       //moof::resource::add_type<AnotherAsset>("k");
+       
+       //{
+       //moof::resource_ptr myAsset = moof::resource::load(assetName,
+                       //"prefix", "mine");
+
+       //MyAsset_handle aCopy = myAsset;
+
+       //MyAsset_handle copy2 = moof::resource::load(assetName, "asdfas", "mine");
+
+       ////if (myAsset->check<MyAsset>()) myAsset->get<AnotherAsset>()->cool();
+       //myAsset->get<MyAsset>()->cool();
+       ////myAsset->get<AnotherAsset>()->groovy();
+       
+       //aCopy.get()->cool();
+       //copy2.get()->cool();
+
+       //log_info("rsrc ptr:", moof::resource::load(assetName, "", "mine"));
+       //}
+       //log_info("rsrc ptr:", moof::resource::load(assetName, "", "k"));
+       //moof::resource::load(assetName, "", "mine")->get<MyAsset>()->cool();
+
+       ////if (myAsset) myAsset.get()->cool();
+       ////else moof::log_error("asset not obtained...");
+       
+       MyAsset_handle myAsset = moof::resource::load("/home/chaz/meh.mine");
+       MyAsset* asset = myAsset.get();
+       if (asset) asset->cool();
+       else moof::log_warning("no asset obtained!!");
+
+       moof::timer reloadTimer(
+                       boost::bind(&moof::resource::reload_as_needed),
+                       SCALAR(2.0),
+                       moof::timer::repeat);
+
+       //for (;;)
+       //{
+               //myAsset.get()->cool();
+               //moof::resource::reload_as_needed();
+               //sleep(1);
+       //}
+
+       //return 0;
+
        moof::resolver_task task("4950", "lappy");
        task.run();
 
@@ -344,7 +448,7 @@ int main(int argc, char* argv[])
 
 
        //return 0;
-
+       
 
        if (argc > 1)
        {
@@ -375,7 +479,7 @@ int main(int argc, char* argv[])
        try
        {
                std::string iconPath(PACKAGE".png");
-               moof::resource::find_path(iconPath);
+               moof::resource::find(iconPath);
                moof::image icon(iconPath);
                icon.set_as_icon();
 
index aa4a0d9c028409bf75c3210f5b897f22cdf1a311..0a3c616a685a4774a4cde93561ad0e4741a2b134 100644 (file)
@@ -13,7 +13,7 @@
 #define _MAIN_HH_
 
 /**
- * @file Main.hh
+ * \file Main.hh
  * This is where all the fun begins.
  */
 
index 61be61016885df1ad74557f77e2a17cf15b18503..7a680c8d61c4e0bcfabac435c66fa37e3a16c55d 100644 (file)
 #
 
 
+#
+# libstlplus.a
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+noinst_LIBRARIES = libstlplus.a
+
+libstlplus_a_SOURCES = \
+                                          stlplus/containers/containers.hpp \
+                                          stlplus/containers/containers_fixes.hpp \
+                                          stlplus/containers/copy_functors.hpp \
+                                          stlplus/containers/digraph.hpp \
+                                          stlplus/containers/digraph.tpp \
+                                          stlplus/containers/exceptions.hpp \
+                                          stlplus/containers/foursome.hpp \
+                                          stlplus/containers/foursome.tpp \
+                                          stlplus/containers/hash.hpp \
+                                          stlplus/containers/hash.tpp \
+                                          stlplus/containers/matrix.hpp \
+                                          stlplus/containers/matrix.tpp \
+                                          stlplus/containers/ntree.hpp \
+                                          stlplus/containers/ntree.tpp \
+                                          stlplus/containers/safe_iterator.hpp \
+                                          stlplus/containers/safe_iterator.tpp \
+                                          stlplus/containers/simple_ptr.hpp \
+                                          stlplus/containers/simple_ptr.tpp \
+                                          stlplus/containers/smart_ptr.hpp \
+                                          stlplus/containers/smart_ptr.tpp \
+                                          stlplus/containers/triple.hpp \
+                                          stlplus/containers/triple.tpp \
+                                          stlplus/persistence/persistence.hpp \
+                                          stlplus/persistence/persistence_fixes.hpp \
+                                          stlplus/persistence/persistent.hpp \
+                                          stlplus/persistence/persistent_basic.hpp \
+                                          stlplus/persistence/persistent_bitset.hpp \
+                                          stlplus/persistence/persistent_bitset.tpp \
+                                          stlplus/persistence/persistent_bool.cpp \
+                                          stlplus/persistence/persistent_bool.hpp \
+                                          stlplus/persistence/persistent_callback.hpp \
+                                          stlplus/persistence/persistent_callback.tpp \
+                                          stlplus/persistence/persistent_complex.hpp \
+                                          stlplus/persistence/persistent_complex.tpp \
+                                          stlplus/persistence/persistent_contexts.cpp \
+                                          stlplus/persistence/persistent_contexts.hpp \
+                                          stlplus/persistence/persistent_cstring.cpp \
+                                          stlplus/persistence/persistent_cstring.hpp \
+                                          stlplus/persistence/persistent_deque.hpp \
+                                          stlplus/persistence/persistent_deque.tpp \
+                                          stlplus/persistence/persistent_digraph.hpp \
+                                          stlplus/persistence/persistent_digraph.tpp \
+                                          stlplus/persistence/persistent_enum.hpp \
+                                          stlplus/persistence/persistent_enum.tpp \
+                                          stlplus/persistence/persistent_exceptions.cpp \
+                                          stlplus/persistence/persistent_exceptions.hpp \
+                                          stlplus/persistence/persistent_float.cpp \
+                                          stlplus/persistence/persistent_float.hpp \
+                                          stlplus/persistence/persistent_foursome.hpp \
+                                          stlplus/persistence/persistent_foursome.tpp \
+                                          stlplus/persistence/persistent_hash.hpp \
+                                          stlplus/persistence/persistent_hash.tpp \
+                                          stlplus/persistence/persistent_inf.cpp \
+                                          stlplus/persistence/persistent_inf.hpp \
+                                          stlplus/persistence/persistent_int.cpp \
+                                          stlplus/persistence/persistent_int.hpp \
+                                          stlplus/persistence/persistent_interface.hpp \
+                                          stlplus/persistence/persistent_interface.tpp \
+                                          stlplus/persistence/persistent_list.hpp \
+                                          stlplus/persistence/persistent_list.tpp \
+                                          stlplus/persistence/persistent_map.hpp \
+                                          stlplus/persistence/persistent_map.tpp \
+                                          stlplus/persistence/persistent_matrix.hpp \
+                                          stlplus/persistence/persistent_matrix.tpp \
+                                          stlplus/persistence/persistent_multimap.hpp \
+                                          stlplus/persistence/persistent_multimap.tpp \
+                                          stlplus/persistence/persistent_multiset.hpp \
+                                          stlplus/persistence/persistent_multiset.tpp \
+                                          stlplus/persistence/persistent_ntree.hpp \
+                                          stlplus/persistence/persistent_ntree.tpp \
+                                          stlplus/persistence/persistent_pair.hpp \
+                                          stlplus/persistence/persistent_pair.tpp \
+                                          stlplus/persistence/persistent_pointer.hpp \
+                                          stlplus/persistence/persistent_pointer.tpp \
+                                          stlplus/persistence/persistent_pointers.hpp \
+                                          stlplus/persistence/persistent_set.hpp \
+                                          stlplus/persistence/persistent_set.tpp \
+                                          stlplus/persistence/persistent_shortcuts.hpp \
+                                          stlplus/persistence/persistent_shortcuts.tpp \
+                                          stlplus/persistence/persistent_simple_ptr.hpp \
+                                          stlplus/persistence/persistent_simple_ptr.tpp \
+                                          stlplus/persistence/persistent_smart_ptr.hpp \
+                                          stlplus/persistence/persistent_smart_ptr.tpp \
+                                          stlplus/persistence/persistent_stl.hpp \
+                                          stlplus/persistence/persistent_stlplus.hpp \
+                                          stlplus/persistence/persistent_string.cpp \
+                                          stlplus/persistence/persistent_string.hpp \
+                                          stlplus/persistence/persistent_string.tpp \
+                                          stlplus/persistence/persistent_triple.hpp \
+                                          stlplus/persistence/persistent_triple.tpp \
+                                          stlplus/persistence/persistent_vector.cpp \
+                                          stlplus/persistence/persistent_vector.hpp \
+                                          stlplus/persistence/persistent_vector.tpp \
+                                          stlplus/persistence/persistent_xref.hpp \
+                                          stlplus/persistence/persistent_xref.tpp \
+                                          stlplus/portability/build.cpp \
+                                          stlplus/portability/build.hpp \
+                                          stlplus/portability/debug.cpp \
+                                          stlplus/portability/debug.hpp \
+                                          stlplus/portability/dprintf.cpp \
+                                          stlplus/portability/dprintf.hpp \
+                                          stlplus/portability/dynaload.cpp \
+                                          stlplus/portability/dynaload.hpp \
+                                          stlplus/portability/file_system.cpp \
+                                          stlplus/portability/file_system.hpp \
+                                          stlplus/portability/inf.cpp \
+                                          stlplus/portability/inf.hpp \
+                                          stlplus/portability/ip_sockets.cpp \
+                                          stlplus/portability/ip_sockets.hpp \
+                                          stlplus/portability/portability.hpp \
+                                          stlplus/portability/portability_exceptions.hpp \
+                                          stlplus/portability/portability_fixes.cpp \
+                                          stlplus/portability/portability_fixes.hpp \
+                                          stlplus/portability/subprocesses.cpp \
+                                          stlplus/portability/subprocesses.hpp \
+                                          stlplus/portability/tcp.hpp \
+                                          stlplus/portability/tcp_sockets.cpp \
+                                          stlplus/portability/tcp_sockets.hpp \
+                                          stlplus/portability/time.cpp \
+                                          stlplus/portability/time.hpp \
+                                          stlplus/portability/udp_sockets.cpp \
+                                          stlplus/portability/udp_sockets.hpp \
+                                          stlplus/portability/version.cpp \
+                                          stlplus/portability/version.hpp \
+                                          stlplus/portability/wildcard.cpp \
+                                          stlplus/portability/wildcard.hpp \
+                                          stlplus/strings/format_types.hpp \
+                                          stlplus/strings/print_address.cpp \
+                                          stlplus/strings/print_address.hpp \
+                                          stlplus/strings/print_basic.hpp \
+                                          stlplus/strings/print_bitset.hpp \
+                                          stlplus/strings/print_bitset.tpp \
+                                          stlplus/strings/print_bool.cpp \
+                                          stlplus/strings/print_bool.hpp \
+                                          stlplus/strings/print_cstring.cpp \
+                                          stlplus/strings/print_cstring.hpp \
+                                          stlplus/strings/print_digraph.hpp \
+                                          stlplus/strings/print_digraph.tpp \
+                                          stlplus/strings/print_float.cpp \
+                                          stlplus/strings/print_float.hpp \
+                                          stlplus/strings/print_foursome.hpp \
+                                          stlplus/strings/print_foursome.tpp \
+                                          stlplus/strings/print_hash.hpp \
+                                          stlplus/strings/print_hash.tpp \
+                                          stlplus/strings/print_inf.cpp \
+                                          stlplus/strings/print_inf.hpp \
+                                          stlplus/strings/print_int.cpp \
+                                          stlplus/strings/print_int.hpp \
+                                          stlplus/strings/print_list.hpp \
+                                          stlplus/strings/print_list.tpp \
+                                          stlplus/strings/print_map.hpp \
+                                          stlplus/strings/print_map.tpp \
+                                          stlplus/strings/print_matrix.hpp \
+                                          stlplus/strings/print_matrix.tpp \
+                                          stlplus/strings/print_ntree.hpp \
+                                          stlplus/strings/print_ntree.tpp \
+                                          stlplus/strings/print_pair.hpp \
+                                          stlplus/strings/print_pair.tpp \
+                                          stlplus/strings/print_pointer.hpp \
+                                          stlplus/strings/print_pointer.tpp \
+                                          stlplus/strings/print_sequence.hpp \
+                                          stlplus/strings/print_sequence.tpp \
+                                          stlplus/strings/print_set.hpp \
+                                          stlplus/strings/print_set.tpp \
+                                          stlplus/strings/print_simple_ptr.hpp \
+                                          stlplus/strings/print_simple_ptr.tpp \
+                                          stlplus/strings/print_smart_ptr.hpp \
+                                          stlplus/strings/print_smart_ptr.tpp \
+                                          stlplus/strings/print_stl.hpp \
+                                          stlplus/strings/print_stlplus.hpp \
+                                          stlplus/strings/print_string.cpp \
+                                          stlplus/strings/print_string.hpp \
+                                          stlplus/strings/print_triple.hpp \
+                                          stlplus/strings/print_triple.tpp \
+                                          stlplus/strings/print_vector.cpp \
+                                          stlplus/strings/print_vector.hpp \
+                                          stlplus/strings/print_vector.tpp \
+                                          stlplus/strings/string_address.cpp \
+                                          stlplus/strings/string_address.hpp \
+                                          stlplus/strings/string_basic.hpp \
+                                          stlplus/strings/string_bitset.hpp \
+                                          stlplus/strings/string_bitset.tpp \
+                                          stlplus/strings/string_bool.cpp \
+                                          stlplus/strings/string_bool.hpp \
+                                          stlplus/strings/string_cstring.cpp \
+                                          stlplus/strings/string_cstring.hpp \
+                                          stlplus/strings/string_digraph.hpp \
+                                          stlplus/strings/string_digraph.tpp \
+                                          stlplus/strings/string_float.cpp \
+                                          stlplus/strings/string_float.hpp \
+                                          stlplus/strings/string_foursome.hpp \
+                                          stlplus/strings/string_foursome.tpp \
+                                          stlplus/strings/string_hash.hpp \
+                                          stlplus/strings/string_hash.tpp \
+                                          stlplus/strings/string_inf.cpp \
+                                          stlplus/strings/string_inf.hpp \
+                                          stlplus/strings/string_int.cpp \
+                                          stlplus/strings/string_int.hpp \
+                                          stlplus/strings/string_list.hpp \
+                                          stlplus/strings/string_list.tpp \
+                                          stlplus/strings/string_map.hpp \
+                                          stlplus/strings/string_map.tpp \
+                                          stlplus/strings/string_matrix.hpp \
+                                          stlplus/strings/string_matrix.tpp \
+                                          stlplus/strings/string_ntree.hpp \
+                                          stlplus/strings/string_ntree.tpp \
+                                          stlplus/strings/string_pair.hpp \
+                                          stlplus/strings/string_pair.tpp \
+                                          stlplus/strings/string_pointer.hpp \
+                                          stlplus/strings/string_pointer.tpp \
+                                          stlplus/strings/string_sequence.hpp \
+                                          stlplus/strings/string_sequence.tpp \
+                                          stlplus/strings/string_set.hpp \
+                                          stlplus/strings/string_set.tpp \
+                                          stlplus/strings/string_simple_ptr.hpp \
+                                          stlplus/strings/string_simple_ptr.tpp \
+                                          stlplus/strings/string_smart_ptr.hpp \
+                                          stlplus/strings/string_smart_ptr.tpp \
+                                          stlplus/strings/string_stl.hpp \
+                                          stlplus/strings/string_stlplus.hpp \
+                                          stlplus/strings/string_string.cpp \
+                                          stlplus/strings/string_string.hpp \
+                                          stlplus/strings/string_triple.hpp \
+                                          stlplus/strings/string_triple.tpp \
+                                          stlplus/strings/string_utilities.cpp \
+                                          stlplus/strings/string_utilities.hpp \
+                                          stlplus/strings/string_vector.cpp \
+                                          stlplus/strings/string_vector.hpp \
+                                          stlplus/strings/string_vector.tpp \
+                                          stlplus/strings/strings.hpp \
+                                          stlplus/strings/strings_fixes.hpp \
+                                          stlplus/subsystems/cli_parser.cpp \
+                                          stlplus/subsystems/cli_parser.hpp \
+                                          stlplus/subsystems/ini_manager.cpp \
+                                          stlplus/subsystems/ini_manager.hpp \
+                                          stlplus/subsystems/library_manager.cpp \
+                                          stlplus/subsystems/library_manager.hpp \
+                                          stlplus/subsystems/message_handler.cpp \
+                                          stlplus/subsystems/message_handler.hpp \
+                                          stlplus/subsystems/subsystems.hpp \
+                                          stlplus/subsystems/subsystems_fixes.hpp \
+                                          stlplus/subsystems/timer.cpp \
+                                          stlplus/subsystems/timer.hpp \
+                                          $(ENDLIST)
+
+
 #
 # libmoof.a
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 noinst_LIBRARIES = libmoof.a
 
-libmoof_a_CPPFLAGS = -I$(top_srcdir)/src/moof
+#libmoof_a_CPPFLAGS = -I$(top_srcdir)/src/moof
 
 libmoof_a_SOURCES = \
                                        moof/aabb.cc \
@@ -77,7 +330,7 @@ libmoof_a_SOURCES = \
                                        moof/fastevents.h \
                                        $(ENDLIST)
 
-EXTRA_DIST = moof/cml moof/stlplus
+EXTRA_DIST = cml
 
 
 #
@@ -86,8 +339,8 @@ EXTRA_DIST = moof/cml moof/stlplus
 
 bin_PROGRAMS = yoink
 
-yoink_CPPFLAGS = -I$(top_srcdir)/src/moof
-yoink_LDADD = libmoof.a
+#yoink_CPPFLAGS = -I$(top_srcdir)/src/moof
+yoink_LDADD = libstlplus.a libmoof.a
 
 yoink_SOURCES = \
                                Animation.cc \
index 9510e3670577fff9a7385f343f34a6773a9a9392..2d8f12025d3c8fd119d99d86e58368f03793a876 100644 (file)
@@ -195,7 +195,7 @@ struct Scene::impl : public moof::manager<impl>
        moof::script::status load(moof::settings& settings, moof::script& script)
        {
                std::string path(name());
-               if (!Scene::find_path(path))
+               if (!resource::find(path))
                {
                        script.push("the scene file could not be found");
                        return moof::script::file_error;
@@ -574,8 +574,3 @@ bool Scene::checkForCollision(Character& character)
 }
 
 
-bool Scene::find_path(std::string& name)
-{
-       return moof::resource::find_path(name, "scenes/", "lua");
-}
-
index 9cebc3c5034660b4debaf470a9f56e6631b012ab..3c14b47e81564ee89c76a29e5631ec7d86e35845 100644 (file)
@@ -57,8 +57,6 @@ public:
        bool castRay(const moof::ray<2>& ray,
                        std::list<moof::ray<2>::contact>& hits) const;
        bool checkForCollision(Character& character);
-
-       static bool find_path(std::string& name);
 };
 
 
index 6bc70f3b6d7d7a251dab5bf3eef26100ac51ed9a..7a206c0b77a4c806b0111d76bc8eb820d3a33e7b 100644 (file)
@@ -17,7 +17,7 @@
 
 void TitleLayer::did_add_to_view()
 {
-       mFadeIn.init(0.0, 1.0, 0.1);
+       mFadeIn.init(0.0, 1.0, 0.15);
 
        //mGameLayer = GameLayer::alloc();
 }
similarity index 100%
rename from src/moof/cml/cml.h
rename to src/cml/cml.h
similarity index 100%
rename from src/moof/cml/constants.h
rename to src/cml/constants.h
similarity index 100%
rename from src/moof/cml/core/fwd.h
rename to src/cml/core/fwd.h
similarity index 100%
rename from src/moof/cml/defaults.h
rename to src/cml/defaults.h
similarity index 100%
rename from src/moof/cml/dynamic.h
rename to src/cml/dynamic.h
similarity index 100%
rename from src/moof/cml/et/tags.h
rename to src/cml/et/tags.h
similarity index 100%
rename from src/moof/cml/et/traits.h
rename to src/cml/et/traits.h
similarity index 100%
rename from src/moof/cml/external.h
rename to src/cml/external.h
similarity index 100%
rename from src/moof/cml/fixed.h
rename to src/cml/fixed.h
similarity index 100%
rename from src/moof/cml/matrix.h
rename to src/cml/matrix.h
similarity index 100%
rename from src/moof/cml/matrix/lu.h
rename to src/cml/matrix/lu.h
similarity index 100%
rename from src/moof/cml/util.h
rename to src/cml/util.h
similarity index 100%
rename from src/moof/cml/vector.h
rename to src/cml/vector.h
index 4a4e4bf71a2b6f711e100627d8d03221d9489f13..093b7d197d93658834b2c954aed5b38ec3f8b00b 100644 (file)
@@ -29,9 +29,8 @@ void camera::rotation(const quaternion& rotation)
 void camera::look_at(const vector3& point)
 {
        // FIXME: this doesn't work as expected
-       quaternion_rotation_aim_at(state_.orientation,
-                                                                       state_.position, point,
-                                                                       vector3(0.0, 1.0, 0.0));
+       quaternion_rotation_aim_at(state_.orientation, state_.position, point,
+                                                          vector3(0.0, 1.0, 0.0));
 }
 
 
diff --git a/src/moof/debug.hh b/src/moof/debug.hh
new file mode 100644 (file)
index 0000000..161a8af
--- /dev/null
@@ -0,0 +1,41 @@
+
+/*]  Copyright (c) 2009-2010, Charles McGarvey  [**************************
+**]  All rights reserved.
+*
+* vi:ts=4 sw=4 tw=75
+*
+* Distributable under the terms and conditions of the 2-clause BSD license;
+* see the file COPYING for a complete text of the license.
+*
+**************************************************************************/
+
+#ifndef _MOOF_DEBUG_HH_
+#define _MOOF_DEBUG_HH_
+
+/**
+ * \file debug.hh
+ * Debugging facilities.
+ */
+
+#include <cstdlib>             // exit
+
+#include <moof/log.hh>
+
+
+#undef ASSERT
+#if NDEBUG
+#define ASSERT(X)
+#else
+/**
+ * Macro which tests an assertion and issues a log_error() and exits if the
+ * assertion is false.
+ * \param X test to perform.
+ */
+#define ASSERT(X) if (!(X)) moof::log_error \
+       << "false assertion at " << __FILE__ << ":" << __LINE__ << ", " \
+       << #X, exit(1)
+#endif
+
+
+#endif // _MOOF_DEBUG_HH_
+
index 27cc650147ab98c81a90b4a6ff57d08dab4cab66..09639124da09d110b55cde03e73c8869d07e94f1 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <string>
 
-#include <moof/stlplus/hash.hpp>
+#include <stlplus/containers/hash.hpp>
 
 
 namespace moof {
index f2f0c09294654145b89e068ebc34a0384c8d8ccf..93ef6d6d0e8e7e7f00f9a3b5b0e074b33c42ba22 100644 (file)
@@ -73,7 +73,7 @@ public:
        {
                std::string path(name);
 
-               FILE* fp = image::open_file(path);
+               FILE* fp = resource::open_file(path);
                if (!fp) return;
 
                png_byte        signature[8];
@@ -287,16 +287,5 @@ void image::set_as_icon() const
 }
 
 
-bool image::find_path(std::string& name)
-{
-       return resource::find_path(name, "images/", "png");
-}
-
-FILE* image::open_file(std::string& name)
-{
-       return resource::open_file(name, "images/", "png");
-}
-
-
 } // namespace moof
 
index 97572708379ba76210422f5adc708c8c8175b65e..614e614459a0fd0c5279d02e87f1bc895945c850 100644 (file)
@@ -58,8 +58,6 @@ public:
 
        void set_as_icon() const;
 
-       static bool find_path(std::string& name);
-
 
 private:
 
index 937ca8a93f28ecda6d37eba8d258c0b6ff876a2d..0a7aa012b480747e69c7e3577c1c6f3c8546f9f8 100644 (file)
@@ -95,12 +95,11 @@ public:
         */
        void update(scalar t, scalar dt)
        {
+               prior_ = state_;
+
                if (!is_done_)
                {
                        alpha_ += dt * scale_;
-                       prior_ = state_;
-                       state_ = function_(a_, b_, alpha_);
-
                        if (alpha_ > 1.0)
                        {
                                switch (mode_)
@@ -135,6 +134,8 @@ public:
                                                break;
                                }
                        }
+
+                       state_ = function_(a_, b_, alpha_);
                }
        }
 
index 2679b37306694763e1b0886a904a660f549f5fff..f65d571e854b98b74c37978c3c11ca84adbb1af4 100644 (file)
 #include <iostream>
 
 
-#undef ASSERT
-#if NDEBUG
-#define ASSERT(X)
-#else
-/**
- * Macro which tests an assertion and issues a log_error() and exits if the
- * assertion is false.
- * \param X test to perform
- */
-#define ASSERT(X) if (!(X)) moof::log_error \
-       << "false assertion at " << __FILE__ << ":" << __LINE__ << ", " \
-       << #X, exit(1)
-#endif
-
-
 namespace moof {
 
 
index 9a0c8eb82c8ef28eaa789ff6a4da4183bb18e1ec..1c09d6ea4cc620d125cab4633becd37273f0574c 100644 (file)
@@ -21,7 +21,7 @@
 
 #include <SDL/SDL_opengl.h>
 
-#include <moof/cml/cml.h>
+#include <cml/cml.h>
 
 #if HAVE_CONFIG_H
 #include "config.h"
index 65bc61f79e54b286c3a511899888adaaa3ef2469..01dfc624795afe3d10c9e90fceca0f6663f62918 100644 (file)
@@ -143,7 +143,7 @@ struct modal_dialog
                gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
 
                std::string icon_path(PACKAGE".png");
-               if (resource::find_path(icon_path))
+               if (resource::find(icon_path))
                {
                        GdkPixbuf* iconPixbuf = gdk_pixbuf_new_from_file(icon_path.c_str(),
                                                                                                                         NULL);
@@ -183,7 +183,7 @@ struct modal_dialog
                dialog.setStandardButtons(QMessageBox::Close);
 
                std::string icon_path(PACKAGE".png");
-               if (resource::find_path(icon_path))
+               if (resource::find(icon_path))
                {
                        QIcon icon(icon_path.c_str());
                        dialog.setWindowIcon(icon);
index 0440b7df5749760861dfdfc4a5c913220258a450..4ae53114c998a28cae5d19db205f18faaf5d5a8f 100644 (file)
@@ -9,8 +9,6 @@
 *
 **************************************************************************/
 
-#include "../config.h"
-
 #include <algorithm>
 #if HAVE_BYTESWAP_H
 #include <byteswap.h>
index 03c519fc0cb41571283bb4d1af623326be8a05ec..55dd96c7a201989ab1bf7458a47edad34152881a 100644 (file)
@@ -84,7 +84,7 @@ public:
         * Write some bytes to the packet.
         * \param bytes The bytes.
         * \param size The number of bytes.
-        * return The number of bytes actually written.
+        * \return The number of bytes actually written.
         */
        size_t write(const void* bytes, size_t size);
 
@@ -140,7 +140,7 @@ public:
        /**
         * Get a pointer to an internal structure holding the serialized bytes
         * of the packet.
-        * return The pointer.
+        * \return The pointer.
         */
        const char* bytes() const
        {
index 3512878bb039f5cede9b164940c744eccadd664f..ac200f7831e380866665bc490a48ed416340a7e7 100644 (file)
 *
 **************************************************************************/
 
+#include <queue>
+
 #include <boost/algorithm/string.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <stlplus/portability/file_system.hpp>
 
-#include "log.hh"
+#include "hash.hh"
 #include "resource.hh"
 
+#if HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#if USE_HOTLOADING
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+#endif
+
+#ifndef BUF_SIZE
+#define BUF_SIZE 4096
+#endif
+
 
 namespace moof {
+       
 
+static std::vector<std::string> search_paths_;
+
+typedef boost::weak_ptr<resource> resource_weakptr;
+static hash<std::string,resource_weakptr,hash_function> resource_table_;
 
 // static member
-std::vector<std::string> resource::search_paths_;
+resource::type_lookup_ptr resource::type_lookup_;
+
+
+#if USE_HOTLOADING
+static hash<int,std::string,hash_function> monitor_lookup_;
+static int monitor_fd_ = inotify_init1(IN_NONBLOCK);
+#endif
+
+int resource::reload_as_needed()
+{
+       int num_resources = 0;
+
+#if USE_HOTLOADING
+       log_info("hotloading?");
+       char bytes[BUF_SIZE];
+       int num_bytes;
+       if (0 < (num_bytes = read(monitor_fd_, bytes, num_bytes)))
+       {
+               char* end = bytes + num_bytes;
+               char* byte = bytes;
+
+               log_warning("num_bytes:", num_bytes);
+               log_error("1");
+
+               while (byte < end)
+               {
+                       struct inotify_event* event = (struct inotify_event*)byte;
+
+                       if (event->mask & IN_IGNORED)
+                       {
+                               log_warning("watch", event->wd, "removed");
+                       }
+
+                       log_error("2");
+                       hash<int,std::string,hash_function>::iterator it;
+                       it = monitor_lookup_.find(event->wd);
+                       if (it != monitor_lookup_.end())
+                       {
+                               log_error("3");
+                               std::string path = (*it).second;
+                               monitor_lookup_.erase(it);
+                               resource::reload(path);
+                       }
+
+                       byte += sizeof(*event) + event->len;
+
+                       ++num_resources;
+               }
+       }
+#endif
+
+       return num_resources;
+}
+
+
+resource::~resource()
+{
+#if USE_HOTLOADING
+       inotify_rm_watch(monitor_fd_, wd_);
+#endif
+}
+
+
+resource_ptr resource::load(const std::string& path)
+{
+       std::string extension = stlplus::extension_part(path);
+
+       if (!find(path)) return resource_ptr();
+
+       hash<std::string,resource_weakptr,hash_function>::iterator it;
+       it = resource_table_.find(path);
+       if (it != resource_table_.end())
+       {
+               resource_weakptr rsrc = (*it).second;
+               resource_ptr locked = rsrc.lock();
+               if (locked) return locked;
+       }
+
+       std::map<std::string,loader_ptr>::iterator jt;
+       jt = type_lookup_->find(extension);
+       if (jt != type_lookup_->end())
+       {
+               resource_ptr rsrc((*jt).second->load(path));
+               rsrc->set_loader(path, (*jt).second);
+               resource_table_[path] = rsrc;
+
+#if USE_HOTLOADING
+               int wd = inotify_add_watch(monitor_fd_,
+                               path.c_str(), IN_MODIFY);
+               rsrc->set_watch_descriptor(wd);
+               monitor_lookup_[wd] = path;
+#endif
+
+               log_info("loaded", rsrc.get());
+               return rsrc;
+       }
+
+       return resource_ptr();
+}
+
+
+resource_ptr resource::reload(std::string& path)
+{
+       log_info("reloading...", path);
+       hash<std::string,resource_weakptr,hash_function>::iterator it;
+       it = resource_table_.find(path);
+       if (it != resource_table_.end())
+       {
+               resource_weakptr rsrc = (*it).second;
+               resource_ptr locked = rsrc.lock();
+               if (locked)
+               {
+                       locked->reload();
+                       return locked;
+               }
+       }
+
+       return load(path);
+}
+
+void resource::reload()
+{
+       log_info("reloaded", path_);
+
+       resource* resource = loader_->load(path_);
+       //*this = *resource;
+       resource_ = resource->resource_;
+       typeinfo_ = resource->typeinfo_;
+       unloader_ = resource->unloader_;
+
+#if USE_HOTLOADING
+       int wd = inotify_add_watch(monitor_fd_,
+                       path_.c_str(), IN_MODIFY);
+       set_watch_descriptor(wd);
+       monitor_lookup_[wd] = path_;
+#endif
+
+       delete resource;
+}
 
 
 void resource::add_search_paths(const std::string& paths)
@@ -43,7 +204,7 @@ void resource::add_search_paths(const std::vector<std::string>& pathList)
                if (*path.rbegin() != '/') path += '/';
 
 #if defined(_WIN32)
-               boost::replace_all(path, "/", "\\");
+               //boost::replace_all(path, "/", "\\");
 #endif
 
                search_paths_.push_back(path);
@@ -52,11 +213,9 @@ void resource::add_search_paths(const std::vector<std::string>& pathList)
 }
 
 
-bool resource::find_path(std::string& path,
-                                          const std::string& prefix,
-                                          const std::string& extension)
+bool resource::find(const std::string& path)
 {
-       FILE* file = open_file(path, prefix, extension);
+       FILE* file = open_file(path);
        if (file)
        {
                fclose(file);
@@ -66,60 +225,22 @@ bool resource::find_path(std::string& path,
        return false;
 }
 
-FILE* resource::open_file(std::string& path,
-                                                const std::string& prefix,
-                                                const std::string& extension,
-                                                const std::string& mode)
+FILE* resource::open_file(const std::string& path, const std::string& mode)
 {
 #if defined(_WIN32)
        // windows always has to be a little different
-       boost::replace_all(path, "/", "\\");
-       std::string temp_prefix(prefix);
-       boost::replace_all(temp_prefix, "/", "\\");
-       std::vector<std::string> preList;
-       boost::split(preList, temp_prefix, boost::is_any_of(":"));
-#else
-       std::vector<std::string> preList;
-       boost::split(preList, prefix, boost::is_any_of(":"));
+       //boost::replace_all(path, "/", "\\");
 #endif
 
-       std::vector<std::string> postList;
-       boost::split(postList, extension, boost::is_any_of(":"));
-
        std::vector<std::string>::iterator it;
        for (it = search_paths_.begin(); it != search_paths_.end(); ++it)
        {
-               std::vector<std::string>::iterator jt;
-               for (jt = preList.begin(); jt != preList.end(); ++jt)
-               {
-                       std::vector<std::string>::iterator kt;
-                       for (kt = postList.begin(); kt != postList.end(); ++kt)
-                       {
-                               std::string realPath(*it);
-                               realPath += *jt;
-                               realPath += path;
-                               realPath += ".";
-                               realPath += *kt;
-
-                               FILE* file = fopen(realPath.c_str(), mode.c_str());
-                               if (file)
-                               {
-                                       path = realPath;
-                                       return file;
-                               }
-                       }
-               }
-
                // check path relative to search path
-               std::string realPath(*it);
-               realPath += path;
+               std::string complete_path(*it);
+               complete_path += path;
 
-               FILE* file = fopen(realPath.c_str(), mode.c_str());
-               if (file)
-               {
-                       path = realPath;
-                       return file;
-               }
+               FILE* file = fopen(complete_path.c_str(), mode.c_str());
+               if (file) return file;
        }
 
        // last ditch effort; maybe it's already a path to a valid resource
index ed8a7d06f8fd9a8c420d9fab59c0c54de60a0658..0f797571c3e1266f493f148b098e06594b32cbc2 100644 (file)
  */
 
 #include <cstdio>
+#include <map>
+#include <stdexcept>
 #include <string>
 #include <vector>
 
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
+#include <moof/debug.hh>
+
+#if HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
 
 namespace moof {
 
 
+class resource;
+typedef boost::shared_ptr<resource> resource_ptr;
+
+
 /**
- * Generic resource class.  
+ * Generic resource class capable of containing any type of resource,
+ * providing a type-safe interface.
  */
-
 class resource
 {
 public:
 
-       virtual ~resource() {}
-
+       // FIXME: this won't be necessary once the existing code is modified to
+       // use the resource handles
+       resource() {}
 
        /**
         * Add a directory to search when looking for resource files.
@@ -53,33 +69,277 @@ public:
         * Get the path to a resource of a given name.
         * \param path The name of the resource to find.  Upon successful
         * return, this is changed to an absolute path to the resource.
-        * \param prefix A colon-separated list of subdirectories to search.
-        * \param extension A colon-separated list of possible extensions.
         * \return True if a path to a resource was found, false otherwise.
         */
-       static bool find_path(std::string& path,
-                                                 const std::string& prefix = "",
-                                                 const std::string& extension = "");
+       static bool find(const std::string& file);
 
        /**
         * Get the path to a resource of a given name and open it if a resource
         * was found.
         * \param path The name of the resource to find.  Upon successful
         * return, this is changed to an absolute path to the resource.
-        * \param prefix A colon-separated list of subdirectories to search.
-        * \param extension A colon-separated list of possible extensions.
         * \param mode The open mode.
         * \return The FILE* if the resource was found, 0 otherwise.
         */
-       static FILE* open_file(std::string& path,
-                                                  const std::string& prefix = "",
-                                                  const std::string& extension = "",
+       static FILE* open_file(const std::string& path,
                                                   const std::string& mode = "rb");
 
 
+       /**
+        * Register a type with the extension of files which this type can
+        * load.  If any type has already been registered with the given file
+        * extension, it will be replaced.
+        * \param extension The file extension.
+        */
+       template <class T>
+       static void register_type(const std::string& extension)
+       {
+               if (!type_lookup_) type_lookup_ = type_lookup_ptr(new type_lookup);
+               loader_ptr loader(new specific_loader<T>);
+               (*type_lookup_)[extension] = loader;
+       }
+
+       /**
+        * Unregister the type associated with a file extension.  Resources of
+        * this type will no longer be loadable, although resources which are
+        * already loaded will remain loaded.
+        * \param extension The file extension
+        */
+       static void unregister_type(const std::string& extension)
+       {
+               type_lookup_->erase(extension);
+       }
+
+
+       static resource_ptr load(const std::string& path);
+
+       static resource_ptr reload(std::string& path);
+
+
+       /**
+        * Construct a resource container.
+        * \param ptr A pointer to the underlying resource data.
+        */
+       template <class T>
+       explicit resource(T* ptr) :
+               resource_(ptr),
+               typeinfo_(const_cast<std::type_info*>(&typeid(T))),
+               unloader_(new specific_unloader<T>(ptr)) {}
+
+       /**
+        * Deconstruct a resource container.
+        */
+       virtual ~resource();
+
+
+       /**
+        * Reload the resource data.  This will cause the resource file to be
+        * reread, and the underlying resource data will change.
+        */
+       void reload();
+
+
+       /**
+        * Get whether or not the type of the underlying resource data matches
+        * an expected type.
+        * \return True if the types match, false otherwise.
+        */
+       template <class T>
+       bool check() const
+       {
+               return *typeinfo_ == typeid(T);
+       }
+
+       /**
+        * Get a pointer to the underlying resource data as long as the type of
+        * the resource data matches the expected type.
+        * \return The resource data, or null if there is a type mismatch.
+        */
+       template <class T>
+       T* get() const
+       {
+               if (check<T>()) return (T*)resource_;
+               return 0;
+       }
+
+
+       /**
+        * Reloads some resources which have been modified on disk since they
+        * were loaded.  Hotloading must have been enabled at compile-time.
+        * \return The number of resources reloaded.
+        */
+       static int reload_as_needed();
+
+
+private:
+
+       class loader
+       {
+       public:
+
+               virtual ~loader() {}
+
+               virtual resource* load(const std::string& path)
+               {
+                       return 0;
+               }
+       };
+
+       typedef boost::shared_ptr<loader> loader_ptr;
+
+       template <class T>
+       class specific_loader : public loader
+       {
+       public:
+
+               virtual resource* load(const std::string& path)
+               {
+                       return new resource(new T(path));
+               }
+       };
+
+
+       class unloader
+       {
+       public:
+
+               virtual ~unloader() {};
+       };
+
+       typedef boost::shared_ptr<unloader> unloader_ptr;
+
+       template <class T>
+       class specific_unloader : public unloader
+       {
+       public:
+
+               specific_unloader(T* object = 0) :
+                       object_(object) {}
+
+               virtual ~specific_unloader()
+               {
+                       log_warning("unloading resource of type ", typeid(T).name());
+                       delete object_;
+               }
+
+
+       private:
+
+               T* object_;
+       };
+
+
+       void set_loader(const std::string& path, loader_ptr loader)
+       {
+               path_ = path;
+               loader_ = loader;
+       }
+
+
+       void*                   resource_;
+       std::type_info* typeinfo_;
+       unloader_ptr    unloader_;
+
+       std::string             path_;
+       loader_ptr              loader_;
+
+       typedef std::map<std::string,loader_ptr> type_lookup;
+       typedef boost::shared_ptr<type_lookup> type_lookup_ptr;
+       static type_lookup_ptr type_lookup_;
+
+#if USE_HOTLOADING
+       int wd_;
+
+       void set_watch_descriptor(int wd)
+       {
+               wd_ = wd;
+       }
+#endif
+};
+
+
+/**
+ * The resource handle class provides a nicer way to work with resources.
+ * It allows you to work with a resource pointer as if you already know the
+ * type of the resource.
+ */
+template <class T>
+class resource_handle
+{
+public:
+
+       /**
+        * Construct a null resource handle.
+        */
+       resource_handle() {}
+
+       /**
+        * Construct a resource handle.
+        * \param ptr The resource pointer to reference.
+        */
+       resource_handle(resource_ptr ptr) :
+               resource_(ptr) {}
+
+
+       /**
+        * Get whether or not the handle is dereferenceable to the type of this
+        * handle.  A resource handle is dereferenceable if it is not a null
+        * handle and if its underlying resource is in fact the same type as is
+        * expected by the handle.
+        * \return True if the handle is dereferenceable, false otherwise.
+        */
+       operator bool () const
+       {
+               if (!resource_) return false;
+               return resource_->check<T>();
+       }
+
+
+       /**
+        * Get a pointer to the underlying resource.
+        * \return The pointer, or null if this handle is not dereferenceable.
+        */
+       T* get() const
+       {
+               if (!*this) return 0;
+               return resource_->get<T>();
+       }
+
+       /**
+        * Dereference the handle all the way to the underlying resource.
+        * \return A reference to the resource.
+        * \throws std::runtime_error If this is a null handle.
+        */
+       T& get_reference() const
+       {
+               if (!*this) throw std::runtime_error("dereference null handle");
+               return *(resource_->get<T>());
+       }
+
+
+       /**
+        * Same as get() for getting a pointer to the underlying resources.
+        * \return The pointer, or null if this handle is not dereferenceable.
+        */
+       T* operator -> () const
+       {
+               return get();
+       }
+
+       /**
+        * Same a get_reference() for dereferencing the handle.
+        * \return A reference to the resource.
+        * \throws std::runtime_error If this is a null handle.
+        */
+       T& operator * () const
+       {
+               return get_reference();
+       }
+
+
 private:
 
-       static std::vector<std::string> search_paths_;
+       resource_ptr resource_;
 };
 
 
diff --git a/src/moof/rules.mk b/src/moof/rules.mk
new file mode 100644 (file)
index 0000000..aa2c749
--- /dev/null
@@ -0,0 +1,54 @@
+
+#########################
+sp             := $(sp).x
+dirstack_$(sp) := $(d)
+d              := $(dir)
+#########################
+
+#
+# Define rules and targets for libmoof.
+#
+
+OBJS_$(d) := \
+             $(d)/ConvertUTF.o \
+             $(d)/aabb.o \
+             $(d)/backend.o \
+             $(d)/camera.o \
+             $(d)/dispatcher.o \
+             $(d)/fastevents.o \
+             $(d)/frustum.o \
+             $(d)/hash.o \
+             $(d)/image.o \
+             $(d)/log.o \
+             $(d)/packet.o \
+             $(d)/plane.o \
+             $(d)/resource.o \
+             $(d)/service.o \
+             $(d)/settings.o \
+             $(d)/sound.o \
+             $(d)/string.o \
+             $(d)/texture.o \
+             $(d)/timer.o \
+             $(d)/video.o \
+             $(d)/view.o \
+                        $(_END_)
+
+TGTS_$(d) := $(d)/libmoof.a
+DEPS_$(d) := $(OBJS_$(d):%=%.d)
+
+CLEAN     := $(CLEAN) $(OBJS_$(d)) $(DEPS_$(d)) $(TGTS_$(d))
+
+
+$(OBJS_$(d)): CF_TGT := -I$(d) -I$(d)/..
+$(OBJS_$(d)): $(d)/rules.mk
+
+$(TGTS_$(d)): $(OBJS_$(d))
+       $(DO_AR)
+
+
+#######################
+-include $(DEPS_$(d))
+d  := $(dirstack_$(sp))
+sp := $(basename $(sp))
+#######################
+
index f4d2c82609e433d95cc8cc6ce2cf6429b291e2d7..c592fdbfb097dd4488d2590a94c8ce267b9aadcf 100644 (file)
 #include <string>
 #include <vector>
 
-#if HAVE_FCNTL_H
 #include <fcntl.h>
-#else
-#error No alternative to fcntl implemented yet.
-#endif
 
 #if defined(_WIN32)
 #include <winsock2.h>
@@ -44,7 +40,7 @@
 #include <sys/types.h>
 #endif
 
-#include <moof/log.hh>
+#include <moof/debug.hh>
 #include <moof/packet.hh>
 #include <moof/thread.hh>
 
@@ -733,13 +729,13 @@ public:
         */
        void is_blocking(bool is_blocking)
        {
-#ifdef HAVE_FCNTL
+#if defined(_WIN32)
+               u_long value = is_blocking;
+               ioctlsocket(impl_.fd, FIONBIO, &value);
+#else
                int flags = fcntl(impl_.fd, F_GETFL);
                flags = is_blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
                fcntl(impl_.fd, F_SETFL, flags);
-#elif defined(_WIN32)
-               u_long value = is_blocking;
-               ioctlsocket(impl_.fd, FIONBIO, &value);
 #endif
        }
 
@@ -751,11 +747,12 @@ public:
         */
        bool is_blocking() const
        {
-#ifdef HAVE_FCNTL
+#if defined(_WIN32)
+               return true;
+#else
                int flags = fcntl(impl_.fd, F_GETFL);
                return !(flags & O_NONBLOCK);
 #endif
-               return true;
        }
 
 
index 7482de7eb9eb22f862041cd6e92a70b2bed14198..d5b67bd2d61d85de1d64d0439bc04bd259bcb990 100644 (file)
 namespace moof {
 
 
+class impl
+{
+public:
+       
+       impl()
+       {
+               //log_info("registering ogg resource handler");
+               resource::register_type<sound_stream>("ogg");
+       }
+
+       ~impl()
+       {
+               //log_info("unregistering ogg resource handler");
+               resource::unregister_type("ogg");
+       }
+};
+
+static impl impl;
+
+
 class sound::impl
 {
 public:
 
-       static ALenum getAudioFormat(const vorbis_info* audioInfo)
+       static ALenum get_audio_format(const vorbis_info* audioInfo)
        {
                if (audioInfo->channels == 1) return AL_FORMAT_MONO16;
                else                                              return AL_FORMAT_STEREO16;
@@ -67,27 +87,22 @@ public:
                }
 
 
-               void init(const std::string& name)
+               void init(const std::string& path)
                {
+                       log_info("initializing audio buffer...");
                        if (mOggStream.datasource)
                        {
                                ov_clear(&mOggStream);
                                mOggStream.datasource = 0;
                        }
 
-                       std::string path(name);
-                       if (!sound::find_path(path))
-                       {
-                               throw std::runtime_error("cannot find resource: " + name);
-                       }
-
                        if (ov_fopen((char*)path.c_str(), &mOggStream) < 0)
                        {
-                               throw std::runtime_error("problem reading audio: " + name);
+                               throw std::runtime_error("problem reading audio: " + path);
                        }
 
                        vorbis_info* vorbisInfo = ov_info(&mOggStream, -1);
-                       mFormat = getAudioFormat(vorbisInfo);
+                       mFormat = get_audio_format(vorbisInfo);
                        mFreq = vorbisInfo->rate;
                }
 
@@ -182,10 +197,11 @@ public:
                init();
        }
 
-       impl(const std::string& name)
+       impl(const std::string& path)
        {
+               log_info("sound::impl constructor");
                init();
-               enqueue(name);
+               enqueue(path);
        }
 
        void init()
@@ -357,7 +373,7 @@ public:
        }
 
 
-       void sample(const std::string& name)
+       void sample(const std::string& path)
        {
                stop();
                alSourcei(source_, AL_BUFFER, AL_NONE);
@@ -365,7 +381,7 @@ public:
                queue_.clear();
                is_loaded_ = false;
 
-               enqueue(name);
+               enqueue(path);
 
                while (!buffers_.empty())
                {
@@ -374,9 +390,9 @@ public:
                }
        }
 
-       void enqueue(const std::string& name)
+       void enqueue(const std::string& path)
        {
-               buffer_ptr buffer = buffer::instance(name);
+               buffer_ptr buffer = buffer::instance(path);
                queue_.push_back(buffer);
        }
 
@@ -469,19 +485,22 @@ ALCdevice*        sound::impl::al_device_ = 0;
 ALCcontext*    sound::impl::al_context_ = 0;
 
 
-sound::sound() :
-       // pass through
-       impl_(new sound::impl) {}
+//sound::sound() :
+       //// pass through
+       //impl_(new sound::impl) {}
 
-sound::sound(const std::string& name) :
+sound::sound(const std::string& path) :
        // pass through
-       impl_(new sound::impl(name)) {}
+       impl_(new sound::impl(path))
+{
+       log_info("sound constructor");
+}
 
 
-void sound::sample(const std::string& name)
+void sound::sample(const std::string& path)
 {
        // pass through
-       impl_->sample(name);
+       impl_->sample(path);
 }
 
 
@@ -559,7 +578,7 @@ void sound::listener_velocity(const vector3& velocity)
 }
 
 void sound::listener_orientation(const vector3& forward,
-                                                                  const vector3& up)
+                                                                const vector3& up)
 {
        float vec[6];
        vec[0] = float(forward[0]);
@@ -572,19 +591,13 @@ void sound::listener_orientation(const vector3& forward,
 }
 
 
-bool sound::find_path(std::string& name)
-{
-       return resource::find_path(name, "sounds/", "ogg");
-}
-
-
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-void sound_stream::enqueue(const std::string& name)
+void sound_stream::enqueue(const std::string& path)
 {
        // pass through
-       impl_->enqueue(name);
+       impl_->enqueue(path);
 }
 
 
index 5a60f0c1ecafffb143db1e9fef53f5704adc03b4..a0dd1033c1a1eb0f7979a62745537e12d8d36d08 100644 (file)
@@ -29,28 +29,22 @@ namespace moof {
 
 
 class sound;
-typedef boost::shared_ptr<sound> sound_ptr;
-
+typedef resource_handle<sound> sound_handle;
 class sound_stream;
-typedef boost::shared_ptr<sound_stream> sound_stream_ptr;
+typedef resource_handle<sound_stream> sound_stream_handle;
 
 
 class sound : public resource
 {
 public:
 
-       static sound_ptr alloc(const std::string& name)
-       {
-               return sound_ptr(new sound(name));
-       }
-
-       sound();
-       explicit sound(const std::string& name);
+       //sound();
+       explicit sound(const std::string& path);
 
        virtual ~sound() {}
 
        // this implicitly stops the sound if it is playing
-       void sample(const std::string& name);
+       void sample(const std::string& path);
 
        virtual void play();
        void stop();
@@ -70,8 +64,6 @@ public:
        static void listener_orientation(const vector3& forward,
                                                                         const vector3& up);
 
-       static bool find_path(std::string& name);
-
 protected:
 
        class impl;
@@ -83,16 +75,11 @@ class sound_stream : public sound
 {
 public:
 
-       static sound_stream_ptr alloc(const std::string& name)
-       {
-               return sound_stream_ptr(new sound_stream(name));
-       }
-
-       sound_stream();
-       explicit sound_stream(const std::string& name) :
-               sound(name) {}
+       //sound_stream();
+       explicit sound_stream(const std::string& path) :
+               sound(path) {}
 
-       void enqueue(const std::string& name);
+       void enqueue(const std::string& path);
 
        void play();
 };
index f8687f0c5f98b38110104225f5d47d357b97c43e..94d792dfc9d8ea295a152508d9c309700b1a5c93 100644 (file)
@@ -105,10 +105,9 @@ class texture::impl : public manager<impl>
 
 public:
 
-       /**
+       /*
         * Construction is initialization.
         */
-
        impl() :
                mMinFilter(GL_NEAREST),
                mMagFilter(GL_NEAREST),
@@ -138,7 +137,7 @@ public:
        {
                std::string path(name);
                
-               texture::find_path(path);
+               resource::find(path);
 
                mImage = image::alloc(path);
                if (!mImage->is_valid())
@@ -161,8 +160,7 @@ public:
                }
                else
                {
-                       log_info << "loading tiles from texture " << path
-                                       << std::endl;
+                       log_info("loading tiles from texture", path);
 
                        script::slot globals = script.globals();
                        globals.get(mTilesS, "tiles_s");
@@ -175,11 +173,10 @@ public:
        }
 
 
-       /**
+       /*
         * Upload the image to GL so that it will be accessible by a much more
         * manageable handle and hopefully reside in video memory.
         */
-
        void upload_to_gl()
        {
                if (mObject)
@@ -210,11 +207,10 @@ public:
        }
 
 
-       /**
+       /*
         * Sets some texture properties such as the filters and external
         * coordinate behavior.
         */
-
        void set_properties()
        {
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
@@ -399,11 +395,5 @@ bool texture::tile_coordinates(int index, scalar coords[8],
 }
 
 
-bool texture::find_path(std::string& name)
-{
-       return resource::find_path(name, "textures/", "png");
-}
-
-
 } // namespace moof
 
index 0ea195227c5b7e6de5764c1127ca8e0dd07fad7a..a53dcd1bb0580dfa977a9625f80997f8b36e9319 100644 (file)
@@ -94,9 +94,6 @@ public:
        bool tile_coordinates(int index, scalar coords[8], orientation what) const;
 
 
-       static bool find_path(std::string& name);
-
-
 private:
 
        class impl;
index 273625640ab4a78dedfda47a691981972a98d7cf..e31e49fdfb23a567ff857e46c4932e483d921fe3 100644 (file)
 
 #include <SDL/SDL.h>
 
-#include "log.hh"
+#include "debug.hh"
 #include "timer.hh"
 
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 
 namespace moof {
 
 
-scalar timer::gNextFire = std::numeric_limits<scalar>::max();
-std::map<unsigned,timer*> timer::gTimers;
+scalar timer::next_expiration_ = std::numeric_limits<scalar>::max();
+hash<unsigned,timer*,hash_function> timer::timers_;
 
 
 unsigned timer::new_identifier()
@@ -58,9 +54,9 @@ void timer::init(const function& function, scalar seconds, mode mode)
                }
 
                id_ = new_identifier();
-               gTimers.insert(std::pair<unsigned,timer*>(id_, this));
+               timers_.insert(std::pair<unsigned,timer*>(id_, this));
 
-               if (absolute_ < gNextFire) gNextFire = absolute_;
+               if (absolute_ < next_expiration_) next_expiration_ = absolute_;
        }
 }
 
@@ -74,10 +70,13 @@ void timer::invalidate()
 {
        if (mode_ != invalid)
        {
-               gTimers.erase(id_);
+               timers_.erase(id_);
                mode_ = invalid;
 
-               if (is_equal(absolute_, gNextFire)) gNextFire = find_next_expiration();
+               if (is_equal(absolute_, next_expiration_))
+               {
+                       next_expiration_ = find_next_expiration();
+               }
        }
 }
 
@@ -95,7 +94,10 @@ void timer::fire()
                if (is_equal(absolute_, t, 1.0)) absolute_ += interval_;
                else absolute_ = interval_ + t;
 
-               if (is_equal(absolute, gNextFire)) gNextFire = find_next_expiration();
+               if (is_equal(absolute, next_expiration_))
+               {
+                       next_expiration_ = find_next_expiration();
+               }
        }
        else
        {
@@ -106,16 +108,16 @@ void timer::fire()
 
 scalar timer::find_next_expiration()
 {
-       std::map<unsigned,timer*>::iterator it;
-       scalar nextFire = std::numeric_limits<scalar>::max();
+       scalar next_fire = std::numeric_limits<scalar>::max();
 
-       for (it = gTimers.begin(); it != gTimers.end(); ++it)
+       hash<unsigned,timer*,hash_function>::iterator it;
+       for (it = timers_.begin(); it != timers_.end(); ++it)
        {
                scalar absolute = (*it).second->absolute_;
-               if (absolute < nextFire) nextFire = absolute;
+               if (absolute < next_fire) next_fire = absolute;
        }
 
-       return nextFire;
+       return next_fire;
 }
 
 
@@ -142,11 +144,10 @@ void timer::fire_expired_timers()
 
 void timer::fire_expired_timers(scalar t)
 {
-       std::map<unsigned,timer*>::iterator it;
-
-       if (gNextFire > t) return;
+       if (next_expiration_ > t) return;
 
-       for (it = gTimers.begin(); it != gTimers.end(); ++it)
+       hash<unsigned,timer*,hash_function>::iterator it;
+       for (it = timers_.begin(); it != timers_.end(); ++it)
        {
                timer* timer = (*it).second;
                if (timer->is_expired()) timer->fire();
@@ -175,7 +176,7 @@ static time_t set_reference()
        return ts.tv_sec;
 }
 
-static const time_t reference = set_reference();
+static const time_t reference_ = set_reference();
 
 
 scalar timer::ticks()
@@ -185,7 +186,7 @@ scalar timer::ticks()
        int result = clock_gettime(CLOCK_MONOTONIC, &ts);
        ASSERT(result == 0 && "cannot access clock");
 
-       return scalar(ts.tv_sec - reference) +
+       return scalar(ts.tv_sec - reference_) +
                   scalar(ts.tv_nsec) / 1000000000.0;
 }
 
@@ -211,7 +212,7 @@ void timer::sleep(scalar seconds, mode mode)
 
 // If we don't have posix timers, we'll have to use a different timing
 // method.  SDL only promises centisecond accuracy, but that's better than
-// a kick in the pants.
+// a kick in the pants.  
 
 scalar timer::ticks()
 {
index d7a411bc48e82089891795db5602d2cfb42e4326..a86f445d4168a9e2b19073a88593cc4790d9e9a0 100644 (file)
  * Functions for measuring time in a friendly unit.
  */
 
-#include <map>
-
 #include <boost/bind.hpp>
 #include <boost/function.hpp>
 
+#include <moof/hash.hh>
 #include <moof/math.hh>
 
 
 namespace moof {
 
 
+/**
+ * A class to represent a timer for scheduled events.  The timer events
+ * will be executed on or sometime after their schedculed time.  The
+ * runloop associated with the current running view will make an attempt to
+ * fire any expired timers as close to their scheduled times as possible.
+ */
 class timer
 {
 public:
 
+       /**
+        * A type for the state of a timer.
+        */
        enum mode
        {
-               invalid         = -1,
-               normal          =  0,
-               absolute        =  1,
-               repeat          =  2
+               invalid         = -1,   /// Timer is not scheduled.
+               relative        =  0,   /// Timer is scheduled by a relative time.
+               absolute        =  1,   /// Timer is scheduled by an absolute time.
+               repeat          =  2    /// Timer is scheduled by a periodic time.
        };
 
+       /**
+        * Function protocol for a timer event handler.  A function matching
+        * this protocol will be called when the timer expires.  The function
+        * takes two parameters: the timer object that just expired, and the
+        * absolute time.
+        */
        typedef boost::function<void(timer&,scalar)> function;
 
 
+       /**
+        * Construct an invalid (uninitialized) timer.
+        */
        timer() :
-               mode_(invalid) {}
+               mode_(invalid),
+               absolute_(SCALAR(0.0)) {}
 
-       timer(const function& function, scalar seconds, mode mode = normal)
+       /**
+        * Construct a scheduled timer.
+        * \param function The event handler.
+        * \param seconds The number of seconds; the meaning of this depends on
+        * the mode of the timer.
+        * \param mode The timer mode.  If the mode is relative (default), the
+        * seconds parameter is the number of seconds from the current time to
+        * wait before expiring the timer.  If the mode is absolute, the
+        * seconds parameter is the number of seconds from some arbitrary,
+        * fixed time in the past.  If the mode is repeat, the seconds
+        * parameter is the number of seconds from now to wait before expiring
+        * the timer, at which time the timer will be rescheduled to expired
+        * again at that many seconds from the expiration time.  A repeating
+        * timer can be invalidated manually using invalidate().
+        */
+       timer(const function& function, scalar seconds, mode mode = relative)
        {
                init(function, seconds, mode);
        }
 
+       /**
+        * Deconstruct a timer.  This will automagically invalidate the timer,
+        * so it will not expire or fire an event.
+        */
        ~timer()
        {
                invalidate();
        }
 
-       void init(const function& function, scalar seconds, mode mode = normal);
 
+       /**
+        * Initialize a timer with a scheduled time.  If the timer is already
+        * scheduled, its prior schedule will be invalidated and replaced by
+        * this new schedule.
+        * \param function The event handler.
+        * \param seconds The number of seconds; the meaning of this depends on
+        * the mode of the timer.
+        * \param mode The timer mode.  If the mode is relative (default), the
+        * seconds parameter is the number of seconds from the current time to
+        * wait before expiring the timer.  If the mode is absolute, the
+        * seconds parameter is the number of seconds from some arbitrary,
+        * fixed time in the past.  If the mode is repeat, the seconds
+        * parameter is the number of seconds from now to wait before expiring
+        * the timer, at which time the timer will be rescheduled to expired
+        * again at that many seconds from the expiration time.  A repeating
+        * timer can be invalidated manually using invalidate().
+        */
+       void init(const function& function,
+                         scalar seconds,
+                         mode mode = relative);
+
+       
+       /**
+        * Get whether or not the timer is valid.  If a timer is valid, it is
+        * still scheduled to expired.  You can get the time remaining from
+        * seconds_remaining().
+        */
        bool is_valid() const;
+
+       /**
+        * Manually invalidated the timer, removing any schedule such that the
+        * timer will not expired and no event will be fired.
+        */
        void invalidate();
 
+
+       /**
+        * Manually fire the timer event.  Usually, the timer event will be
+        * fired when the timer expires, but this can be used to fire it
+        * prematurely.  If the timer is scheduled, it will be invalidated.  If
+        * the timer is already invalid (but is initialized with an event
+        * handler), the event will be fired and the timer will remain invalid.
+        */
        void fire();
 
+
+       /**
+        * Get the number of seconds remaining before the timer is scheduled to
+        * expired.  If the timer is invalid, the retured value will be
+        * negative.
+        * \return Seconds.
+        */
        scalar seconds_remaining() const;
+
+       /**
+        * Get whether or not the timer is expired.  A timer on a repeating
+        * schedule will never be expired since it will always have a scheduled
+        * expiration time in the future.  If the timer is expired but not
+        * invalid, the timer event has not yet fired; the timer will be
+        * invalidated when it does fire.
+        * \return True if the timer is expired, false otherwise.
+        */
        bool is_expired() const;
+
+       /**
+        * Get whether or not the timer is on a repeating schedule.
+        * \return True if the timer is repeating, false otherwise.
+        */
        bool is_repeating() const;
 
 
@@ -87,15 +184,29 @@ public:
         * \param seconds Number of seconds.
         * \param mode The timer mode.
         */
-       static void sleep(scalar seconds, mode mode = normal);
+       static void sleep(scalar seconds, mode mode = relative);
 
 
+       /**
+        * Get the absolute time when the next timer is scheduled to expire.
+        * \return Absolute time, in seconds.
+        */
        static scalar next_expiration()
        {
-               return gNextFire;
+               return next_expiration_;
        }
 
+
+       /**
+        * Fire any timers which are not yet invalidated but have an expiration
+        * time in the past.
+        */
        static void fire_expired_timers();
+
+       /**
+        * Fire any timers which are not yet invalidated but have an expiration
+        * time before a given absolute time.
+        */
        static void fire_expired_timers(scalar t);
 
 
@@ -110,8 +221,8 @@ private:
        scalar          interval_;
        unsigned        id_;
 
-       static scalar                                           gNextFire;
-       static std::map<unsigned,timer*>        gTimers;
+       static scalar                                                           next_expiration_;
+       static hash<unsigned,timer*,hash_function>      timers_;
 };
 
 
index 32278cc77ca25422e72595fffab04eeb692040b2..aa49b03c249b9d2927d0b43e52adfd5a8a8b357b 100644 (file)
@@ -321,7 +321,7 @@ video::attributes::attributes(const settings& settings)
        settings.get("fullscreen", is_fullscreen);
        settings.get("resizable", is_resizable);
        settings.get("showcursor", is_cursor_visible);
-       settings.get("grab", is_cursor_captured);
+       settings.get("capturecursor", is_cursor_captured);
 
        std::vector<int> dimensions;
        settings.get("videomode", dimensions);
@@ -337,12 +337,11 @@ video::attributes::attributes(const settings& settings)
 
                if (modes == (SDL_Rect**)0)
                {
-                       log_error("no native video mode");
+                       throw std::runtime_error("can't find appropriate video mode");
                }
                else if (modes == (SDL_Rect**)-1)
                {
-                       log_warning("any resolution allowed; "
-                                                  "choosing default 800x600");
+                       log_warning("any resolution allowed; choosing default 800x600");
                        mode[0] = 800;
                        mode[1] = 600;
                }
@@ -350,8 +349,8 @@ video::attributes::attributes(const settings& settings)
                {
                        mode[0] = (*modes)->w;
                        mode[1] = (*modes)->h;
-                       log_info << "choosing native resolution "
-                                       << mode[0] << "x" << mode[1] << std::endl;
+                       log_info << "choosing native resolution: "
+                                        << mode[0] << "x" << mode[1] << std::endl;
                }
        }
        if (dimensions.size() > 2) mode[2] = dimensions[2];
diff --git a/src/rules.mk b/src/rules.mk
new file mode 100644 (file)
index 0000000..e452ae4
--- /dev/null
@@ -0,0 +1,75 @@
+
+#########################
+sp             := $(sp).x
+dirstack_$(sp) := $(d)
+d              := $(dir)
+#########################
+
+#
+# Include the subdirectories--order is not important.
+#
+
+dir := $(d)/moof
+include $(dir)/rules.mk
+
+dir := $(d)/stlplus
+include $(dir)/rules.mk
+
+
+#
+# Define rules and targets for Yoink.
+#
+
+OBJS_$(d) := \
+             $(d)/Animation.o \
+             $(d)/Character.o \
+             $(d)/GameLayer.o \
+             $(d)/Heroine.o \
+             $(d)/Hud.o \
+             $(d)/Main.o \
+             $(d)/Scene.o \
+             $(d)/TilemapFont.o \
+             $(d)/TitleLayer.o \
+             $(d)/Typesetter.o \
+             $(d)/version.o \
+                        $(_END_)
+
+ifeq ($(HOST),win32)
+OBJS_$(d) += $(d)/yoink.o
+endif
+
+TGTS_$(d) := $(d)/yoink$(EXEEXT)
+DEPS_$(d) := $(OBJS_$(d):%=%.d)
+
+TGT_BIN   := $(TGT_BIN) $(TGTS_$(d))
+CLEAN     := $(CLEAN) $(OBJS_$(d)) $(DEPS_$(d)) $(TGTS_$(d))
+
+
+$(OBJS_$(d)): CF_TGT := -I$(d)
+$(OBJS_$(d)): $(d)/rules.mk
+
+$(TGTS_$(d)): $(OBJS_$(d)) $(d)/moof/libmoof.a $(d)/stlplus/libstlplus.a
+       $(DO_LDX)
+
+
+#
+# Define the run and debug targets.
+#
+
+YOINK_ENVIRONMENT = YOINK_DATADIR="./data"
+
+.PHONY: run
+run: $(TGTS_$(d))
+       @$(YOINK_ENVIRONMENT) $< $(YOINK_OPTS)
+
+.PHONY: debug
+debug: $(TGTS_$(d))
+       @$(YOINK_ENVIRONMENT) gdb $<
+
+
+#######################
+-include $(DEPS_$(d))
+d  := $(dirstack_$(sp))
+sp := $(basename $(sp))
+#######################
+
diff --git a/src/stlplus/README.txt b/src/stlplus/README.txt
new file mode 100644 (file)
index 0000000..dad518e
--- /dev/null
@@ -0,0 +1,19 @@
+This directory is for implementing STLplus as a library collection. The\r
+libraries are to be found in the directories:\r
+\r
+          containers\r
+          persistence\r
+          portability\r
+          strings\r
+          subsystems\r
+\r
+The documentation is in the 'docs' directory and starts with index.html.\r
+\r
+To build the STLplus3 library collection, use the project files supplied at\r
+this level - 'Makefile' for gcc, 'stlplus3.sln' for Visual Studio 9 (2008),\r
+'stlplus3.dsw' for Visual Studio 6 (and 7 or 8).\r
+\r
+The 'source' directory is provided with script files that allow the library\r
+collection to be merged into one large library - termed the monolithic build.\r
+See source/README.txt for instructions.\r
+\r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/containers.hpp
rename to src/stlplus/containers/containers.hpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 79%
rename from src/moof/stlplus/containers_fixes.hpp
rename to src/stlplus/containers/containers_fixes.hpp
index 618ef84..ad29f99
@@ -19,7 +19,6 @@
 #ifdef _MSC_VER\r
 // Microsoft Visual Studio\r
 // shut up the following irritating warnings\r
-//   4275 - VC6, exported class was derived from a class that was not exported\r
 //   4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)\r
 //   4305 - VC6, identifier type was converted to a smaller type\r
 //   4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)\r
 //   4355 - VC6, 'this' : used in base member initializer list\r
 //   4675 - VC7.1, "change" in function overload resolution _might_ have altered program\r
 //   4996 - VC8, 'xxxx' was declared deprecated\r
-#pragma warning(disable: 4275 4786 4305 4503 4309 4290 4800 4355 4675 4996)\r
+#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4355 4675 4996)\r
 #endif\r
 \r
 #ifdef __BORLANDC__\r
 // Borland\r
 // Shut up the following irritating warnings\r
-//   8008 - Condition is always true.\r
-//          Whenever the compiler encounters a constant comparison that (due to\r
-//          the nature of the value being compared) is always true or false, it\r
-//          issues this warning and evaluates the condition at compile time.\r
 //   8026 - Functions with exception specifications are not expanded inline\r
 //   8027 - Functions with xxx are not expanded inline\r
-//   8060 - Possibly incorrect assignment.\r
-//          This warning is generated when the compiler encounters an assignment\r
-//          operator as the main operator of a conditional expression (part of\r
-//          an if, while, or do-while statement). This is usually a\r
-//          typographical error for the equality operator.\r
-//   8066 - Unreachable code.\r
-//          A break, continue, goto, or return statement was not followed by a\r
-//          label or the end of a loop or function. The compiler checks while,\r
-//          do, and for loops with a constant test condition, and attempts to\r
-//          recognize loops that can't fall through.\r
-#pragma warn -8008\r
 #pragma warn -8026\r
 #pragma warn -8027\r
-#pragma warn -8060\r
-#pragma warn -8066\r
 #endif\r
 \r
 ////////////////////////////////////////////////////////////////////////////////\r
 //   - version 7 (.NET) (compiler v.13) requires a typename in a parameter specification but supports all\r
 //   - version 8 (2005) (compiler v.14) requires parameters and templates, supports all\r
 #ifdef _MSC_VER\r
-#if _MSC_VER < 1300\r
+#if _MSC_VER <= 1200\r
 #undef TYPENAME\r
 #define TYPENAME\r
 #endif\r
 #endif\r
 #endif\r
 \r
+////////////////////////////////////////////////////////////////////////////////\r
+// Member templates\r
+// e.g. a template function in a template class\r
+\r
+// Not all compilers support them - this fix can be used to disable member\r
+// templates for compilers that don't. Unfortunately that means that some\r
+// functionality will be missing for those compilers.\r
+\r
+#define STLPLUS_MEMBER_TEMPLATES\r
+\r
+// Visual Studio v6 (compiler version 12) does not support them\r
+#ifdef _MSC_VER\r
+#if _MSC_VER <= 1200\r
+#undef STLPLUS_MEMBER_TEMPLATES\r
+#endif\r
+#endif\r
+\r
 ////////////////////////////////////////////////////////////////////////////////\r
 #endif\r
diff --git a/src/stlplus/containers/copy_functors.hpp b/src/stlplus/containers/copy_functors.hpp
new file mode 100644 (file)
index 0000000..c82ee9f
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef STLPLUS_COPY_FUNCTORS\r
+#define STLPLUS_COPY_FUNCTORS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   The function constructor classes below are used by the smart_ptr and the\r
+//   simple_ptr classes. They provide three (well ok, two) copying mechanisms.\r
+//   These classes have been separated from the smart_ptr header by DJDM, as\r
+//   the simple_ptr classes now also use them.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // copy functors implementing the three possible copy semantics\r
+\r
+  // constructor_copy uses the copy constructor of the object - used for simple types\r
+\r
+  template <typename T>\r
+  class constructor_copy\r
+  {\r
+  public:\r
+    T* operator() (const T& from) throw()\r
+      {\r
+        return new T(from);\r
+      }\r
+  };\r
+\r
+  // clone_copy uses the clone method of the object - used for polymorphic types\r
+\r
+  template <typename T>\r
+  class clone_copy\r
+  {\r
+  public:\r
+    T* operator() (const T& from) throw()\r
+      {\r
+        return from.clone();\r
+      }\r
+  };\r
+\r
+  // no_copy throws an exception - used for types that cannot be copied\r
+\r
+  template <typename T>\r
+  class no_copy\r
+  {\r
+  public:\r
+    T* operator() (const T& from) throw(illegal_copy)\r
+      {\r
+        throw illegal_copy("no_copy functor called");\r
+        return 0;\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/digraph.hpp
rename to src/stlplus/containers/digraph.hpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/digraph.tpp
rename to src/stlplus/containers/digraph.tpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/exceptions.hpp
rename to src/stlplus/containers/exceptions.hpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/foursome.hpp
rename to src/stlplus/containers/foursome.hpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/foursome.tpp
rename to src/stlplus/containers/foursome.tpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 92%
rename from src/moof/stlplus/hash.hpp
rename to src/stlplus/containers/hash.hpp
index 05d3b7d..1962481
@@ -14,6 +14,7 @@
 #include "exceptions.hpp"\r
 #include "safe_iterator.hpp"\r
 #include <map>\r
+#include <iostream>\r
 \r
 namespace stlplus\r
 {\r
@@ -151,10 +152,14 @@ namespace stlplus
     iterator insert(const K& key);\r
 \r
     // remove a key/data pair from the hash table\r
-    bool erase(const K& key);\r
+    // as in map, this returns the number of elements erased\r
+    size_type erase(const K& key);\r
+    // remove an element from the hash table using an iterator\r
+    // as in map, returns an iterator to the next element\r
+    iterator erase(iterator it);\r
     // remove all elements from the hash table\r
     void erase(void);\r
-    // provide the std::map equivalent clear function\r
+    // map equivalent of above\r
     void clear(void);\r
 \r
     // find a key and return an iterator to it\r
@@ -176,6 +181,10 @@ namespace stlplus
     const_iterator end(void) const;\r
     iterator end(void);\r
 \r
+    // diagnostic report shows the number of items in each bin so can be used\r
+    // to diagnose effectiveness of hash functions\r
+    void debug_report(std::ostream&) const;\r
+\r
     // internals\r
   private:\r
     friend class hash_element<K,T,H,E>;\r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 82%
rename from src/moof/stlplus/hash.tpp
rename to src/stlplus/containers/hash.tpp
index bcb5bc5..9a1e4e9
@@ -6,6 +6,7 @@
 //   License:   BSD License, see ../docs/license.html\r
 \r
 ////////////////////////////////////////////////////////////////////////////////\r
+#include <iomanip>\r
 \r
 namespace stlplus\r
 {\r
@@ -22,13 +23,13 @@ namespace stlplus
     hash_element<K,T,H,E>* m_next;\r
     unsigned m_hash;\r
 \r
-    hash_element(const hash<K,T,H,E>* owner, const K& key, const T& data, unsigned hash) : \r
-      m_master(owner,this), m_value(key,data), m_next(0), m_hash(hash) \r
+    hash_element(const hash<K,T,H,E>* owner, const K& key, const T& data, unsigned hash) :\r
+      m_master(owner,this), m_value(key,data), m_next(0), m_hash(hash)\r
       {\r
       }\r
 \r
-    hash_element(const hash<K,T,H,E>* owner, const std::pair<const K,T>& value, unsigned hash) : \r
-      m_master(owner,this), m_value(value), m_next(0), m_hash(hash) \r
+    hash_element(const hash<K,T,H,E>* owner, const std::pair<const K,T>& value, unsigned hash) :\r
+      m_master(owner,this), m_value(value), m_next(0), m_hash(hash)\r
       {\r
       }\r
 \r
@@ -441,7 +442,7 @@ namespace stlplus
   // remove a key from the table - return true if the key was found and removed, false if it wasn't present\r
 \r
   template<typename K, typename T, class H, class E>\r
-  bool hash<K,T,H,E>::erase(const K& key)\r
+  unsigned hash<K,T,H,E>::erase(const K& key)\r
   {\r
     unsigned hash_value_full = H()(key);\r
     unsigned bin = hash_value_full % m_bins;\r
@@ -465,9 +466,43 @@ namespace stlplus
       delete current;\r
       // remember to maintain the size count\r
       m_size--;\r
-      return true;\r
+      return 1;\r
     }\r
-    return false;\r
+    return 0;\r
+  }\r
+\r
+  // remove an element from the hash table using an iterator (std::map equivalent)\r
+  template<typename K, typename T, class H, class E>\r
+  TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::erase(TYPENAME hash<K,T,H,E>::iterator it)\r
+  {\r
+    // work out what the next iterator is in order to return it later\r
+    TYPENAME hash<K,T,H,E>::iterator next(it);\r
+    ++next;\r
+    // we now need to find where this item is - made difficult by the use of\r
+    // single-linked lists which means I have to search through the bin from\r
+    // the top in order to unlink from the list.\r
+    unsigned hash_value_full = it.node()->m_hash;\r
+    unsigned bin = hash_value_full % m_bins;\r
+    // scan the list for this element\r
+    // need to keep a previous pointer because the lists are single-linked\r
+    hash_element<K,T,H,E>* previous = 0;\r
+    for (hash_element<K,T,H,E>* current = m_values[bin]; current; previous = current, current = current->m_next)\r
+    {\r
+      // direct test on the address of the element\r
+      if (current != it.node()) continue;\r
+      // found this iterator, so unhook the element from the list\r
+      if (previous)\r
+        previous->m_next = current->m_next;\r
+      else\r
+        m_values[bin] = current->m_next;\r
+      // destroy it\r
+      delete current;\r
+      current = 0;\r
+      // remember to maintain the size count\r
+      m_size--;\r
+      break;\r
+    }\r
+    return next;\r
   }\r
 \r
   template<typename K, typename T, class H, class E>\r
@@ -569,6 +604,54 @@ namespace stlplus
     return hash_iterator<K,T,H,E,std::pair<const K,T> >(this);\r
   }\r
 \r
+  template<typename K, typename T, class H, class E>\r
+  void hash<K,T,H,E>::debug_report(std::ostream& str) const\r
+  {\r
+    // calculate some stats first\r
+    unsigned occupied = 0;\r
+    unsigned min_in_bin = m_size;\r
+    unsigned max_in_bin = 0;\r
+    for (unsigned i = 0; i < m_bins; i++)\r
+    {\r
+      if (m_values[i]) occupied++;\r
+      unsigned count = 0;\r
+      for (hash_element<K,T,H,E>* item = m_values[i]; item; item = item->m_next) count++;\r
+      if (count > max_in_bin) max_in_bin = count;\r
+      if (count < min_in_bin) min_in_bin = count;\r
+    }\r
+    // now print the table\r
+    str << "------------------------------------------------------------------------" << std::endl;\r
+    str << "| size:     " << m_size << std::endl;\r
+    str << "| bins:     " << m_bins << std::endl;\r
+    str << "| loading:  " << loading() << " ";\r
+    if (m_rehash)\r
+      str << "auto-rehash at " << m_rehash << std::endl;\r
+    else\r
+      str << "manual rehash" << std::endl;\r
+    str << "| occupied: " << occupied \r
+        << std::fixed << " (" << (100.0*(float)occupied/(float)m_bins) << "%)" << std::scientific\r
+        << ", min = " << min_in_bin << ", max = " << max_in_bin << std::endl;\r
+    str << "|-----------------------------------------------------------------------" << std::endl;\r
+    str << "|  bin         0     1     2     3     4     5     6     7     8     9" << std::endl;\r
+    str << "|        ---------------------------------------------------------------";\r
+    for (unsigned j = 0; j < m_bins; j++)\r
+    {\r
+      if (j % 10 == 0)\r
+      {\r
+        str << std::endl;\r
+        str << "| " << std::setw(6) << std::right << (j/10*10) << std::left << " |";\r
+      }\r
+      unsigned count = 0;\r
+      for (hash_element<K,T,H,E>* item = m_values[j]; item; item = item->m_next) count++;\r
+      if (!count)\r
+        str << "     .";\r
+      else\r
+        str << std::setw(6) << std::right << count << std::left;\r
+    }\r
+    str << std::endl;\r
+    str << "------------------------------------------------------------------------" << std::endl;\r
+  }\r
+\r
   ////////////////////////////////////////////////////////////////////////////////\r
 \r
 } // end namespace stlplus\r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 96%
rename from src/moof/stlplus/matrix.hpp
rename to src/stlplus/containers/matrix.hpp
index fd0a512..b76a998
@@ -11,6 +11,7 @@
 \r
 ////////////////////////////////////////////////////////////////////////////////\r
 #include "containers_fixes.hpp"\r
+#include <stdexcept>\r
 \r
 namespace stlplus\r
 {\r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/matrix.tpp
rename to src/stlplus/containers/matrix.tpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/ntree.hpp
rename to src/stlplus/containers/ntree.hpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/ntree.tpp
rename to src/stlplus/containers/ntree.tpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/safe_iterator.hpp
rename to src/stlplus/containers/safe_iterator.hpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/safe_iterator.tpp
rename to src/stlplus/containers/safe_iterator.tpp
diff --git a/src/stlplus/containers/simple_ptr.hpp b/src/stlplus/containers/simple_ptr.hpp
new file mode 100644 (file)
index 0000000..08fff03
--- /dev/null
@@ -0,0 +1,264 @@
+#ifndef STLPLUS_SIMPLE_PTR\r
+#define STLPLUS_SIMPLE_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Daniel Milton, Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Daniel Milton, Andy Rushton 2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A smart pointer is a memory-managing pointer to an object. If you like, it\r
+//   is a zero-dimensional container.\r
+\r
+//   Assignment of smart pointers result in multiple aliases of the same object.\r
+//   The term alias is used to differentiate from conventional pointers because\r
+//   the semantics are different.\r
+\r
+//   Aliases can be turned into copies if the pointed-to class supports copying.\r
+\r
+//   These simple_ptr classes from DJDM have slightly different semantics than\r
+//   the smart_ptr classes of AJR. There are no cross-pointer side effects\r
+//   that occur when the pointer is cleared. The clear() function is effectively\r
+//   equivalent to the clear_unique() function of the smart_ptr. The only way\r
+//   that a "referenced" object will be deleted is if all simple_ptr's that\r
+//   reference the object are cleared (by deletion, manual clearing or reassignment).\r
+\r
+//   Also, the simple pointer cannot contain a reference to a shared null pointer\r
+//   (which occurs as a side-effect of clearing a multiply referenced object in\r
+//   the smart_ptr classes). Which means that if you have a null simple_ptr, then\r
+//   the assignment of any other null simple_ptr will NOT reassign the reference of\r
+//   any other simple_ptr. Hence, the simple_ptr class acts a little more like a\r
+//   normal pointer (with fewer side effects), with the added bonus of containment.\r
+\r
+//   Due to the way that the simple_ptr contains the data, it also allows the\r
+//   addition of various casting functions, while still keeping the managed data\r
+//   containment functionality of the underlying object. This means that you can\r
+//   have two simple_ptr's of different template types, both pointing to the same\r
+//   data (if the differing template types are derivatives of each other).\r
+\r
+//   The base class is simple_ptr_base which defines the common interface. Then\r
+//   there are three subclasses which have the same interface but different copy\r
+//   semantics:\r
+\r
+//   - simple_ptr        for simple types and classes which have copy constructors\r
+//   - simple_ptr_clone  for polymorphic class hierarchies which are copied using a clone method\r
+//   - simple_ptr_nocopy for any class that cannot or should not be copied\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+#include "copy_functors.hpp"\r
+#include <map>\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Base class\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename C>\r
+  class simple_ptr_base\r
+  {\r
+  public:\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // member type definitions\r
+\r
+    typedef T value_type;\r
+    typedef T& reference;\r
+    typedef const T& const_reference;\r
+    typedef C value_copy;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // constructors and destructors\r
+\r
+    // create a null pointer\r
+    simple_ptr_base(void);\r
+\r
+    // create a pointer containing a *copy* of the object using the template parameter C\r
+    // this copy is taken because the pointer class maintains a dynamically allocated object\r
+    // and the T& may not be (usually is not) dynamically allocated\r
+    explicit simple_ptr_base(const T& data) throw(illegal_copy);\r
+\r
+    // create a pointer containing a dynamically created object\r
+    // Note: the object must be allocated *by the user* with new\r
+    // constructor form - must be called in the form smart_ptr_base<type> x(new type(args))\r
+    explicit simple_ptr_base(T* data);\r
+\r
+    // copy constructor implements aliasing so no copy is made\r
+    // note that the copy constructor should NOT be explicit, as this breaks\r
+    // the returning of pointer objects from functions (at least within GCC 4.4)\r
+    simple_ptr_base(const simple_ptr_base<T,C>& r);\r
+\r
+    // assignment operator - required, else the output of GCC suffers segmentation faults\r
+    simple_ptr_base<T,C>& operator=(const simple_ptr_base<T,C>& r);\r
+\r
+    // destructor decrements the reference count and delete only when the last reference is destroyed\r
+    ~simple_ptr_base(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // logical tests to see if there is anything contained in the pointer since it can be null\r
+\r
+    // there are two forms:explicit and implicit\r
+    // implicit: if(!r) or if(r)\r
+    // explicit: if(r.null()) or if(r.present())\r
+    operator bool(void) const;\r
+    bool operator!(void) const;\r
+    bool present(void) const;\r
+    bool null(void) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // dereference operators and functions\r
+\r
+    // dereference the smart pointer to get the object - use in the form *p1\r
+    T& operator*(void) throw(null_dereference);\r
+    const T& operator*(void) const throw(null_dereference);\r
+\r
+    // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print()\r
+    T* operator->(void) throw(null_dereference);\r
+    const T* operator->(void) const throw(null_dereference);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // explicit function forms of the above assignment and dereference operators\r
+\r
+    // set the value - note that this does a copy using the C template parameter\r
+    void set_value(const T& data) throw(illegal_copy);\r
+    // get the value\r
+    T& value(void) throw(null_dereference);\r
+    const T& value(void) const throw(null_dereference);\r
+\r
+    // set the pointer\r
+    // deletes the previous pointer and adopts the passed pointer instead\r
+    // Note: the object must be allocated *by the user* with new\r
+    // Warning: it is very easy to break the memory management with this operation\r
+    void set(T* data = 0);\r
+    // get the pointer\r
+    T* pointer(void);\r
+    const T* pointer(void) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // functions to manage aliases\r
+\r
+    // make this an alias of the passed object\r
+    void alias(const simple_ptr_base<T,C>&);\r
+\r
+    // test whether two pointers point to the same object(known as aliasing the object)\r
+    // used in the form if(a.aliases(b))\r
+    bool aliases(const simple_ptr_base<T,C>&) const;\r
+\r
+    // find the number of aliases - used when you need to know whether an\r
+    // object is still referred to from elsewhere (rare!)\r
+    unsigned alias_count(void) const;\r
+\r
+    // clear the reference to the object, but only delete the object if there are no\r
+    // other references to that object. Hence, this does not affect other pointers\r
+    // that are pointing to the same object.\r
+    void clear(void);\r
+\r
+    // This is just an alias of the clear() function, provided for completeness of\r
+    // the interface when acting as a replacement for the smart_ptr classes\r
+    void clear_unique(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // functions that involve copying\r
+\r
+    // these functions use the copy functor passed as the template parameter C\r
+    // to copy the object with the right copy semantics. If the copy functor\r
+    // is no_copy, an exception will be thrown.\r
+\r
+    // make this pointer unique with respect to any other references to the same object\r
+    // if this pointer is already unique, it does nothing - otherwise it copies the object\r
+    void make_unique(void) throw(illegal_copy);\r
+\r
+    // make this pointer a unique copy of the parameter\r
+    // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2\r
+    void copy(const simple_ptr_base<T,C>&) throw(illegal_copy);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // functions that involve casting\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+\r
+    // dynamic cast of underlying pointer to a derived/parent\r
+    template<typename T2> simple_ptr_base<T2,C> dyn_cast(void) const;\r
+\r
+    // static cast of underlying pointer to a derived/parent\r
+    template<typename T2> simple_ptr_base<T2,C> stat_cast(void) const;\r
+\r
+    // cast of underlying pointer to a base - while keeping the same ref-counted object\r
+    template<typename T2> simple_ptr_base<T2,C> cast(void) const;\r
+\r
+#endif\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+\r
+  protected:\r
+    T* m_pointer;\r
+    unsigned* m_count;\r
+\r
+  public:\r
+    // internal use only - had to make them public because they need to be\r
+    // accessed by routines that could not be made friends\r
+    // can't have a handle due to the way the simple pointer stores it's data\r
+    // in separate counter and pointer objects\r
+    unsigned* _count(void) const;\r
+    T* _pointer(void) const;\r
+    void _make_alias(T* pointer, unsigned* count);\r
+\r
+  private:\r
+    void increment(void);\r
+    bool decrement(void);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // simple_ptr        for simple types and classes which have copy constructors\r
+\r
+  template <typename T>\r
+  class simple_ptr : public simple_ptr_base<T, constructor_copy<T> >\r
+  {\r
+  public:\r
+    simple_ptr(void) {}\r
+    explicit simple_ptr(const T& data) : simple_ptr_base<T, constructor_copy<T> >(data) {}\r
+    explicit simple_ptr(T* data) : simple_ptr_base<T, constructor_copy<T> >(data) {}\r
+    simple_ptr<T>& operator=(const T& data) {set_value(data); return *this;}\r
+    simple_ptr<T>& operator=(T* data) {set(data); return *this;}\r
+    ~simple_ptr(void) {}\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // smart_ptr_clone  for polymorphic class hierarchies which have a clone method\r
+\r
+  template <typename T>\r
+  class simple_ptr_clone : public simple_ptr_base<T, clone_copy<T> >\r
+  {\r
+  public:\r
+    simple_ptr_clone(void) {}\r
+    explicit simple_ptr_clone(const T& data) : simple_ptr_base<T, clone_copy<T> >(data) {}\r
+    explicit simple_ptr_clone(T* data) : simple_ptr_base<T, clone_copy<T> >(data) {}\r
+    simple_ptr_clone<T>& operator=(const T& data) {set_value(data); return *this;}\r
+    simple_ptr_clone<T>& operator=(T* data) {set(data); return *this;}\r
+    ~simple_ptr_clone(void) {}\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // smart_ptr_nocopy for any class that cannot or should not be copied\r
+\r
+  template <typename T>\r
+  class simple_ptr_nocopy : public simple_ptr_base<T, no_copy<T> >\r
+  {\r
+  public:\r
+    simple_ptr_nocopy(void) {}\r
+    explicit simple_ptr_nocopy(const T& data) : simple_ptr_base<T, no_copy<T> >(data) {}\r
+    explicit simple_ptr_nocopy(T* data) : simple_ptr_base<T, no_copy<T> >(data) {}\r
+    simple_ptr_nocopy<T>& operator=(const T& data) {set_value(data); return *this;}\r
+    simple_ptr_nocopy<T>& operator=(T* data) {set(data); return *this;}\r
+    ~simple_ptr_nocopy(void) {}\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "simple_ptr.tpp"\r
+#endif\r
diff --git a/src/stlplus/containers/simple_ptr.tpp b/src/stlplus/containers/simple_ptr.tpp
new file mode 100644 (file)
index 0000000..13f5596
--- /dev/null
@@ -0,0 +1,338 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Daniel Milton\r
+//   Copyright: (c) Daniel Milton           2002-2009\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // simple_ptr_base class\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // constructors, assignments and destructors\r
+\r
+  // create a null pointer\r
+  template <typename T, typename C>\r
+  simple_ptr_base<T,C>::simple_ptr_base(void) :\r
+    m_pointer(0),\r
+    m_count(new unsigned(1))\r
+  {\r
+  }\r
+\r
+  // create a pointer containing a *copy* of the object pointer\r
+  template <typename T, typename C>\r
+  simple_ptr_base<T,C>::simple_ptr_base(const T& data) throw(illegal_copy) :\r
+    m_pointer(C()(data)),\r
+    m_count(new unsigned(1))\r
+  {\r
+  }\r
+\r
+  // create a pointer containing a dynamically created object\r
+  // Note: the object must be allocated *by the user* with new\r
+  // constructor form - must be called in the form simple_ptr<type> x(new type(args))\r
+  template <typename T, typename C>\r
+  simple_ptr_base<T,C>::simple_ptr_base(T* data) :\r
+    m_pointer(data),\r
+    m_count(new unsigned(1))\r
+  {\r
+  }\r
+\r
+  // copy constructor implements counted referencing - no copy is made\r
+  template <typename T, typename C>\r
+  simple_ptr_base<T,C>::simple_ptr_base(const simple_ptr_base<T,C>& r) :\r
+    m_pointer(r.m_pointer),\r
+    m_count(r.m_count)\r
+  {\r
+    increment();\r
+  }\r
+\r
+  // assignment operator - required, else the output of GCC suffers segmentation faults\r
+  template <typename T, typename C>\r
+  simple_ptr_base<T,C>& simple_ptr_base<T,C>::operator=(const simple_ptr_base<T,C>& r)\r
+  {\r
+    alias(r);\r
+    return *this;\r
+  }\r
+\r
+  // destructor decrements the reference count and delete only when the last reference is destroyed\r
+  template <typename T, typename C>\r
+  simple_ptr_base<T,C>::~simple_ptr_base(void)\r
+  {\r
+    if(decrement()) \r
+    {\r
+      delete m_pointer;\r
+      delete m_count;\r
+    }\r
+  }\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // logical tests to see if there is anything contained in the pointer since it can be null\r
+\r
+  template <typename T, typename C>\r
+  bool simple_ptr_base<T,C>::null(void) const\r
+  {\r
+    return m_pointer==0;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  bool simple_ptr_base<T,C>::present(void) const\r
+  {\r
+    return m_pointer!=0;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  bool simple_ptr_base<T,C>::operator!(void) const\r
+  {\r
+    return m_pointer==0;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  simple_ptr_base<T,C>::operator bool(void) const\r
+  {\r
+    return m_pointer!=0;\r
+  }\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // dereference operators and functions\r
+\r
+  template <typename T, typename C>\r
+  T& simple_ptr_base<T,C>::operator*(void) throw(null_dereference)\r
+  {\r
+    if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*");\r
+    return *m_pointer;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  const T& simple_ptr_base<T,C>::operator*(void) const throw(null_dereference)\r
+  {\r
+    if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*");\r
+    return *m_pointer;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  T* simple_ptr_base<T,C>::operator->(void) throw(null_dereference)\r
+  {\r
+    if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->");\r
+    return m_pointer;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  const T* simple_ptr_base<T,C>::operator->(void) const throw(null_dereference)\r
+  {\r
+    if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->");\r
+    return m_pointer;\r
+  }\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // explicit function forms of the above assignment dereference operators\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::set_value(const T& data) throw(illegal_copy)\r
+  {\r
+    set(C()(data));\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  T& simple_ptr_base<T,C>::value(void) throw(null_dereference)\r
+  {\r
+    if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value");\r
+    return *m_pointer;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  const T& simple_ptr_base<T,C>::value(void) const throw(null_dereference)\r
+  {\r
+    if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value");\r
+    return *m_pointer;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::set(T* data)\r
+  {\r
+    unsigned& count = *m_count;\r
+    if (count<=1)\r
+      delete m_pointer;\r
+    else\r
+    {\r
+      --count;\r
+      m_count = new unsigned(1);\r
+    }\r
+    m_pointer = data;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  T* simple_ptr_base<T,C>::pointer(void)\r
+  {\r
+    return m_pointer;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  const T* simple_ptr_base<T,C>::pointer(void) const\r
+  {\r
+    return m_pointer;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // functions to manage counted referencing\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::increment(void)\r
+  {\r
+    ++(*m_count);\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  bool simple_ptr_base<T,C>::decrement(void)\r
+  {\r
+    unsigned& count = *m_count;\r
+    --count;\r
+    return count == 0;\r
+  }\r
+\r
+  // make this an alias of the passed object\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::alias(const simple_ptr_base<T,C>& r)\r
+  {\r
+    // make it alias-copy safe - this means that I don't try to do the\r
+    // assignment if r is either the same object or an alias of it\r
+    if (m_pointer==r.m_pointer) return;\r
+    if(decrement()) {\r
+      delete m_pointer;\r
+      delete m_count;\r
+    }\r
+    m_pointer = r.m_pointer;\r
+    m_count = r.m_count;\r
+    increment();\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  bool simple_ptr_base<T,C>::aliases(const simple_ptr_base<T,C>& r) const\r
+  {\r
+    return m_count == r.m_count;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  unsigned simple_ptr_base<T,C>::alias_count(void) const\r
+  {\r
+    return *m_count;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::clear(void)\r
+  {\r
+    set(0);\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::clear_unique(void)\r
+  {\r
+    set(0);    // no difference between clear and clear_unique with the simple_ptr\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::make_unique(void) throw(illegal_copy)\r
+  {\r
+    unsigned& count = *m_count;\r
+    if (count <= 1) return;\r
+    --count;\r
+    if (m_pointer) m_pointer = C()(*m_pointer);\r
+    m_count = new unsigned(1);\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::copy(const simple_ptr_base<T,C>& data) throw(illegal_copy)\r
+  {\r
+    alias(data);\r
+    make_unique();\r
+  }\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+\r
+  // dynamic cast of underlying pointer to a derived/parent\r
+  template <typename T, typename C>\r
+  template <typename T2>\r
+  simple_ptr_base<T2,C> simple_ptr_base<T,C>::dyn_cast(void) const\r
+  {\r
+    simple_ptr_base<T2,C> rtn;\r
+    rtn.m_pointer = dynamic_cast<T2*>(m_pointer);\r
+    if (rtn.m_pointer) {\r
+      delete rtn.m_count;\r
+      rtn.m_count = m_count;\r
+      rtn.increment();\r
+    }\r
+    return rtn;\r
+  }\r
+\r
+  // static cast of underlying pointer to a derived/parent\r
+  template <typename T, typename C>\r
+  template <typename T2>\r
+  simple_ptr_base<T2,C> simple_ptr_base<T,C>::stat_cast(void) const\r
+  {\r
+    simple_ptr_base<T2,C> rtn;\r
+    rtn.m_pointer = static_cast<T2*>(m_pointer);\r
+    if (rtn.m_pointer) {\r
+      delete rtn.m_count;\r
+      rtn.m_count = m_count;\r
+      rtn.increment();\r
+    }\r
+    return rtn;\r
+  }\r
+\r
+  // cast of underlying pointer to a base - while keeping the same ref-counted object\r
+  template <typename T, typename C>\r
+  template <typename T2>\r
+  simple_ptr_base<T2,C> simple_ptr_base<T,C>::cast(void) const\r
+  {\r
+    simple_ptr_base<T2,C> rtn;\r
+    rtn.m_pointer = (T2*)m_pointer;\r
+    if (rtn.m_pointer) {\r
+      delete rtn.m_count;\r
+      rtn.m_count = m_count;\r
+      rtn.increment();\r
+    }\r
+    return rtn;\r
+  }\r
+\r
+#endif\r
+\r
+  // internal function for distinguishing unique simple_ptr objects\r
+  // used for example in persistence routines\r
+\r
+  template <typename T, typename C>\r
+  unsigned* simple_ptr_base<T,C>::_count(void) const\r
+  {\r
+    return m_count;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  T* simple_ptr_base<T,C>::_pointer(void) const\r
+  {\r
+    return m_pointer;\r
+  }\r
+\r
+  template <typename T, typename C>\r
+  void simple_ptr_base<T,C>::_make_alias(T* pointer, unsigned* count)\r
+  {\r
+    // make it alias-copy safe - this means that I don't try to do the\r
+    // assignment if r is either the same object or an alias of it\r
+    if (m_count != count)\r
+    {\r
+      if(decrement())\r
+      {\r
+        delete m_pointer;\r
+        delete m_count;\r
+      }\r
+      m_pointer = pointer;\r
+      m_count = count;\r
+      increment();\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 83%
rename from src/moof/stlplus/smart_ptr.hpp
rename to src/stlplus/containers/smart_ptr.hpp
index d1395f5..2d37e2e
@@ -8,7 +8,7 @@
 //   License:   BSD License, see ../docs/license.html\r
 \r
 //   A smart pointer is a memory-managing pointer to an object. If you like, it\r
-//   is a zero-dimensional container. \r
+//   is a zero-dimensional container.\r
 \r
 //   Assignment of smart pointers result in multiple aliases of the same object.\r
 //   The term alias is used to differentiate from conventional pointers because\r
@@ -27,6 +27,7 @@
 ////////////////////////////////////////////////////////////////////////////////\r
 #include "containers_fixes.hpp"\r
 #include "exceptions.hpp"\r
+#include "copy_functors.hpp"\r
 #include <map>\r
 #include <string>\r
 \r
@@ -61,7 +62,7 @@ namespace stlplus
     smart_ptr_base(void);\r
 \r
     // create a pointer containing a *copy* of the object using the template parameter C\r
-    // this copy is taken because the pointer class maintains a dynamically allocated object \r
+    // this copy is taken because the pointer class maintains a dynamically allocated object\r
     // and the T& may not be (usually is not) dynamically allocated\r
     explicit smart_ptr_base(const T& data) throw(illegal_copy);\r
 \r
@@ -71,7 +72,12 @@ namespace stlplus
     explicit smart_ptr_base(T* data);\r
 \r
     // copy constructor implements aliasing so no copy is made\r
-    explicit smart_ptr_base(const smart_ptr_base<T,C>& r);\r
+    // note that the copy constructor should NOT be explicit, as this breaks\r
+    // the returning of pointer objects from functions (at least within GCC 4.4)\r
+    smart_ptr_base(const smart_ptr_base<T,C>& r);\r
+\r
+    // assignment operator - required, else the output of GCC suffers segmentation faults\r
+    smart_ptr_base<T,C>& operator=(const smart_ptr_base<T,C>& r);\r
 \r
     // destructor decrements the reference count and delete only when the last reference is destroyed\r
     ~smart_ptr_base(void);\r
@@ -159,48 +165,8 @@ namespace stlplus
   public:\r
     // internal use only - had to make them public because they need to be\r
     // accessed by routines that could not be made friends\r
-    void* handle(void) const;\r
-    void make_alias(void* handle);\r
-  };\r
-\r
-  ////////////////////////////////////////////////////////////////////////////////\r
-  // copy functors implementing the three possible copy semantics\r
-\r
-  // constructor_copy uses the copy constructor of the object - used for simple types\r
-\r
-  template <typename T>\r
-  class constructor_copy\r
-  {\r
-  public:\r
-    T* operator() (const T& from) throw()\r
-      {\r
-        return new T(from);\r
-      }\r
-  };\r
-\r
-  // clone_copy uses the clone method of the object - used for polymorphic types\r
-\r
-  template <typename T>\r
-  class clone_copy\r
-  {\r
-  public:\r
-    T* operator() (const T& from) throw()\r
-      {\r
-        return from.clone();\r
-      }\r
-  };\r
-\r
-  // no_copy throws an exception - used for types that cannot be copied\r
-\r
-  template <typename T>\r
-  class no_copy\r
-  {\r
-  public:\r
-    T* operator() (const T& from) throw(illegal_copy)\r
-      {\r
-        throw illegal_copy("no_copy functor called");\r
-        return 0;\r
-      }\r
+    smart_ptr_holder<T>* _handle(void) const;\r
+    void _make_alias(smart_ptr_holder<T>* handle);\r
   };\r
 \r
   ////////////////////////////////////////////////////////////////////////////////\r
@@ -214,7 +180,7 @@ namespace stlplus
     explicit smart_ptr(const T& data) : smart_ptr_base<T, constructor_copy<T> >(data) {}\r
     explicit smart_ptr(T* data) : smart_ptr_base<T, constructor_copy<T> >(data) {}\r
     smart_ptr<T>& operator=(const T& data) {set_value(data); return *this;}\r
-    smart_ptr<T>& operator=(const smart_ptr<T>& r) {alias(r); return *this;}\r
+    smart_ptr<T>& operator=(T* data) {set(data); return *this;}\r
     ~smart_ptr(void) {}\r
   };\r
 \r
@@ -229,7 +195,7 @@ namespace stlplus
     explicit smart_ptr_clone(const T& data) : smart_ptr_base<T, clone_copy<T> >(data) {}\r
     explicit smart_ptr_clone(T* data) : smart_ptr_base<T, clone_copy<T> >(data) {}\r
     smart_ptr_clone<T>& operator=(const T& data) {set_value(data); return *this;}\r
-    smart_ptr_clone<T>& operator=(const smart_ptr_clone<T>& r) {alias(r); return *this;}\r
+    smart_ptr_clone<T>& operator=(T* data) {set(data); return *this;}\r
     ~smart_ptr_clone(void) {}\r
   };\r
 \r
@@ -244,7 +210,7 @@ namespace stlplus
     explicit smart_ptr_nocopy(const T& data) : smart_ptr_base<T, no_copy<T> >(data) {}\r
     explicit smart_ptr_nocopy(T* data) : smart_ptr_base<T, no_copy<T> >(data) {}\r
     smart_ptr_nocopy<T>& operator=(const T& data) {set_value(data); return *this;}\r
-    smart_ptr_nocopy<T>& operator=(const smart_ptr_nocopy<T>& r) {alias(r); return *this;}\r
+    smart_ptr_nocopy<T>& operator=(T* data) {set(data); return *this;}\r
     ~smart_ptr_nocopy(void) {}\r
   };\r
 \r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 91%
rename from src/moof/stlplus/smart_ptr.tpp
rename to src/stlplus/containers/smart_ptr.tpp
index cb1b8bb..ce72da2
@@ -33,7 +33,7 @@ namespace stlplus
       }\r
 \r
   public:\r
-    smart_ptr_holder(T* p = 0) : \r
+    smart_ptr_holder(T* p = 0) :\r
       m_count(1), m_data(p)\r
       {\r
       }\r
@@ -139,6 +139,14 @@ namespace stlplus
     m_holder->increment();\r
   }\r
 \r
+       // assignment operator - required, else the output of GCC suffers segmentation faults\r
+  template <typename T, typename C>\r
+  smart_ptr_base<T,C>& smart_ptr_base<T,C>::operator=(const smart_ptr_base<T,C>& r) \r
+  {\r
+    alias(r);\r
+    return *this;\r
+  }\r
+\r
   // destructor decrements the reference count and delete only when the last reference is destroyed\r
   template <typename T, typename C>\r
   smart_ptr_base<T,C>::~smart_ptr_base(void)\r
@@ -253,14 +261,7 @@ namespace stlplus
   template <typename T, typename C>\r
   void smart_ptr_base<T,C>::alias(const smart_ptr_base<T,C>& r)\r
   {\r
-    // make it alias-copy safe - this means that I don't try to do the\r
-    // assignment if r is either the same object or an alias of it\r
-    //   if (m_holder == r.m_holder) return;\r
-    //   if (m_holder->decrement())\r
-    //     delete m_holder;\r
-    //   m_holder = r.m_holder;\r
-    //   m_holder->increment();\r
-    make_alias(r.m_holder);\r
+    _make_alias(r.m_holder);\r
   }\r
 \r
   template <typename T, typename C>\r
@@ -319,15 +320,16 @@ namespace stlplus
   // used for example in persistence routines\r
 \r
   template <typename T, typename C>\r
-  void* smart_ptr_base<T,C>::handle(void) const\r
+  smart_ptr_holder<T>* smart_ptr_base<T,C>::_handle(void) const\r
   {\r
     return m_holder;\r
   }\r
 \r
   template <typename T, typename C>\r
-  void smart_ptr_base<T,C>::make_alias(void* handle)\r
+  void smart_ptr_base<T,C>::_make_alias(smart_ptr_holder<T>* r_holder)\r
   {\r
-    smart_ptr_holder<T>* r_holder = (smart_ptr_holder<T>*)handle;\r
+    // make it alias-copy safe - this means that I don't try to do the\r
+    // assignment if r is either the same object or an alias of it\r
     if (m_holder != r_holder)\r
     {\r
       if (m_holder->decrement())\r
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/triple.hpp
rename to src/stlplus/containers/triple.hpp
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from src/moof/stlplus/triple.tpp
rename to src/stlplus/containers/triple.tpp
diff --git a/src/stlplus/messages/stlplus_messages.txt b/src/stlplus/messages/stlplus_messages.txt
new file mode 100644 (file)
index 0000000..548a999
--- /dev/null
@@ -0,0 +1,14 @@
+# messages required by the CLI parser (see cli_parser.hpp)\r
+CLI_VALUE_MISSING                option @0 requires a value - end of line was reached instead\r
+CLI_UNRECOGNISED_OPTION          @0 is not a recognised option for this command\r
+CLI_NO_VALUES                    argument @0 is invalid - this program doesn't take command-line arguments\r
+CLI_USAGE_PROGRAM                usage:\n\t@0 { arguments }\r
+CLI_USAGE_DEFINITIONS            arguments:\r
+CLI_USAGE_VALUES                 values:\r
+CLI_USAGE_VALUE_RESULT           \t@0 : from @1\r
+CLI_USAGE_SWITCH_RESULT          \t-@0 : from @1\r
+CLI_USAGE_OPTION_RESULT          \t-@0 @1 : from @2\r
+CLI_INI_HEADER                   configuration files:\r
+CLI_INI_FILE_PRESENT             \t@0\r
+CLI_INI_FILE_ABSENT              \t@0 (not found)\r
+CLI_INI_VARIABLE                 unknown variable "@0" found in Ini file\r
diff --git a/src/stlplus/persistence/persistence.hpp b/src/stlplus/persistence/persistence.hpp
new file mode 100644 (file)
index 0000000..6a47838
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef STLPLUS_PERSISTENCE\r
+#define STLPLUS_PERSISTENCE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Header that includes all the persistence routines in one go\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistent_contexts.hpp"\r
+#include "persistent_shortcuts.hpp"\r
+#include "persistent_basic.hpp"\r
+#include "persistent_pointers.hpp"\r
+#include "persistent_stl.hpp"\r
+#include "persistent_stlplus.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistence_fixes.hpp b/src/stlplus/persistence/persistence_fixes.hpp
new file mode 100644 (file)
index 0000000..d540ade
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef STLPLUS_PERSISTENCE_FIXES\r
+#define STLPLUS_PERSISTENCE_FIXES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Contains work arounds for OS or Compiler specific problems\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Unnecessary compiler warnings\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef _MSC_VER\r
+// Microsoft Visual Studio\r
+// shut up the following irritating warnings\r
+//   4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)\r
+//   4305 - VC6, identifier type was converted to a smaller type\r
+//   4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)\r
+//   4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it\r
+//   4290 - VC6, C++ exception specification ignored\r
+//   4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)\r
+//   4675 - VC7.1, "change" in function overload resolution _might_ have altered program\r
+//   4996 - VC8, 'xxxx' was declared deprecated\r
+#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996)\r
+#endif\r
+\r
+#ifdef __BORLANDC__\r
+// Borland\r
+// Shut up the following irritating warnings\r
+//   8026 - Functions with exception specifications are not expanded inline\r
+//   8027 - Functions with xxx are not expanded inline\r
+#pragma warn -8026\r
+#pragma warn -8027\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent.hpp b/src/stlplus/persistence/persistent.hpp
new file mode 100644 (file)
index 0000000..7ddfe4e
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef STLPLUS_PERSISTENT\r
+#define STLPLUS_PERSISTENT\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Interface class inherited by classes using the interface approach to polymorphism\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_exceptions.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  class dump_context;\r
+  class restore_context;\r
+\r
+  class persistent\r
+  {\r
+  public:\r
+    virtual void dump(dump_context&) const throw(persistent_dump_failed) = 0;\r
+    virtual void restore(restore_context&) throw(persistent_restore_failed) = 0;\r
+    virtual persistent* clone(void) const = 0;\r
+    virtual ~persistent(void) {}\r
+  };\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_basic.hpp b/src/stlplus/persistence/persistent_basic.hpp
new file mode 100644 (file)
index 0000000..2ffc30f
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef STLPLUS_PERSISTENT_BASIC\r
+#define STLPLUS_PERSISTENT_BASIC\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of basic types\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistent_bool.hpp"\r
+#include "persistent_cstring.hpp"\r
+#include "persistent_enum.hpp"\r
+#include "persistent_float.hpp"\r
+#include "persistent_int.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_bitset.hpp b/src/stlplus/persistence/persistent_bitset.hpp
new file mode 100644 (file)
index 0000000..3f684e4
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_PERSISTENT_BITSET\r
+#define STLPLUS_PERSISTENT_BITSET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL bitset\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <bitset>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<size_t N>\r
+  void dump_bitset(dump_context&, const std::bitset<N>& data) throw(persistent_dump_failed);\r
+\r
+  template<size_t N>\r
+  void restore_bitset(restore_context&, std::bitset<N>& data) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_bitset.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_bitset.tpp b/src/stlplus/persistence/persistent_bitset.tpp
new file mode 100644 (file)
index 0000000..e6b4d54
--- /dev/null
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // format: data msB first, packed into bytes with lowest index at the byte's lsb\r
+\r
+  // Note: the interface does not provide access to the internal storage and yet\r
+  // to be efficient the std::bitset must be packed as bytes. Thus I have to do it the\r
+  // hard way.\r
+\r
+  template<size_t N>\r
+  void dump_bitset(dump_context& context, const std::bitset<N>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    size_t bits = data.size();\r
+    size_t bytes = (bits+7)/8;\r
+    for (size_t B = bytes; B--; )\r
+    {\r
+      unsigned char ch = 0;\r
+      for (size_t b = 0; b < 8; b++)\r
+      {\r
+        size_t bit = B*8+b;\r
+        if (bit < bits && data.test(bit))\r
+          ch |= (0x01 << b);\r
+      }\r
+      dump_unsigned_char(context,ch);\r
+    }\r
+  }\r
+\r
+  template<size_t N>\r
+  void restore_bitset(restore_context& context, std::bitset<N>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    size_t bits = data.size();\r
+    size_t bytes = (bits+7)/8;\r
+    for (size_t B = bytes; B--; )\r
+    {\r
+      unsigned char ch = 0;\r
+      restore_unsigned_char(context,ch);\r
+      for (size_t b = 0; b < 8; b++)\r
+      {\r
+        size_t bit = B*8+b;\r
+        if (bit >= bits) break;\r
+        data.set(bit, ch & (0x01 << b) ? true : false);\r
+      }\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_bool.cpp b/src/stlplus/persistence/persistent_bool.cpp
new file mode 100644 (file)
index 0000000..dde1828
--- /dev/null
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_bool.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// bool is dumped and restored as an unsigned char\r
+void stlplus::dump_bool(stlplus::dump_context& context, const bool& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  context.put((unsigned char)data);\r
+}\r
+\r
+void stlplus::restore_bool(restore_context& context, bool& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  data = context.get() != 0;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/persistence/persistent_bool.hpp b/src/stlplus/persistence/persistent_bool.hpp
new file mode 100644 (file)
index 0000000..5fbe237
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef STLPLUS_PERSISTENT_BOOL\r
+#define STLPLUS_PERSISTENT_BOOL\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of bool\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void dump_bool(dump_context&, const bool& data) throw(persistent_dump_failed);\r
+  void restore_bool(restore_context&, bool& data) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_callback.hpp b/src/stlplus/persistence/persistent_callback.hpp
new file mode 100644 (file)
index 0000000..034f43e
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef STLPLUS_PERSISTENT_CALLBACK\r
+#define STLPLUS_PERSISTENT_CALLBACK\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence for pointers to polymorphic classes using the callback approach.\r
+\r
+//   This works on a set of classes. Each subclass has a set of callback\r
+//   (non-method) functions that enable create/dump/restore operations. Each\r
+//   subclass must be registered with the persistence dump/restore context so\r
+//   that the system knows how to handle it.\r
+\r
+//   This approach is suited to classes that cannot be modified to add\r
+//   persistence methods. See persistent_interface for a more C++-like way of\r
+//   handling polymorphism.\r
+\r
+//   Objects are always dumped/restored as pointers to the superclass T.\r
+\r
+//   Multiple pointers to the same object are handled in the same way as for\r
+//   simple pointers\r
+\r
+//   Only classes registered with the context can be dumped and restored as\r
+//   polymorphic types - see dump_context::register_callback and\r
+//   restore_context::register_callback. Attempting to use any unrecognised class\r
+//   will throw an exception.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T>\r
+  void dump_callback(dump_context&, const T* const data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_callback(restore_context&, T*& data)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_callback.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_callback.tpp b/src/stlplus/persistence/persistent_callback.tpp
new file mode 100644 (file)
index 0000000..0773dad
--- /dev/null
@@ -0,0 +1,100 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Polymorphous classes using the callback approach\r
+\r
+//   format: magic [ key data ]\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T>\r
+  void dump_callback(dump_context& context, const T* const data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    try\r
+    {\r
+      // register the address and get the magic key for it\r
+      std::pair<bool,unsigned> mapping = context.pointer_map(data);\r
+      dump_unsigned(context,mapping.second);\r
+      // if the address is null, then that is all that we need to do\r
+      // however, if it is non-null and this is the first sight of the address, dump the contents\r
+      if (data && !mapping.first)\r
+      {\r
+        // callback method - get the callback data and perform the dump\r
+        // this will throw persistent_illegal_type if not recognised, thus the try block\r
+        dump_context::callback_data callback = context.lookup_callback(typeid(*data));\r
+        // dump the magic key for the type\r
+        dump_unsigned(context, callback.first);\r
+        // now call the callback that dumps the subclass\r
+        callback.second(context,data);\r
+      }\r
+    }\r
+    catch (const persistent_illegal_type& except)\r
+    {\r
+      // convert this to a simpler dump failed exception\r
+      throw persistent_dump_failed(except.what());\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T>\r
+  void restore_callback(restore_context& context, T*& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    try\r
+    {\r
+      // first delete any previous object pointed to since the restore creates the object of the right subclass\r
+      if (data)\r
+      {\r
+        delete data;\r
+        data = 0;\r
+      }\r
+      // get the magic key\r
+      unsigned magic = 0;\r
+      restore_unsigned(context,magic);\r
+      // now lookup the magic key to see if this pointer has already been restored\r
+      // null pointers are always flagged as already restored\r
+      std::pair<bool,void*> address = context.pointer_map(magic);\r
+      if (address.first)\r
+      {\r
+        // seen before, so simply map it to the existing address\r
+        data = (T*)address.second;\r
+      }\r
+      else\r
+      {\r
+        // now restore the magic key that denotes the particular subclass\r
+        unsigned key = 0;\r
+        restore_unsigned(context, key);\r
+        // callback approach\r
+        // call the create callback to create an object of the right type\r
+        // then call the restore callback to get the contents\r
+        // this will throw persistent_illegal_type if not recognised - this is caught below\r
+        restore_context::callback_data callbacks = context.lookup_callback(key);\r
+        data = (T*)callbacks.first();\r
+        // add this pointer to the set of already seen objects\r
+        // note that the address is mapped before it is dumped so that self-referential structures dump correctly\r
+        context.pointer_add(magic,data);\r
+        callbacks.second(context,data);\r
+      }\r
+    }\r
+    catch (const persistent_illegal_type& exception)\r
+    {\r
+      // convert this to a simpler dump failed exception\r
+      throw persistent_restore_failed(exception.what());\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_complex.hpp b/src/stlplus/persistence/persistent_complex.hpp
new file mode 100644 (file)
index 0000000..4c02215
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_PERSISTENT_COMPLEX\r
+#define STLPLUS_PERSISTENT_COMPLEX\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Set of persistence routines for the STL classes\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <complex>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename D>\r
+  void dump_complex(dump_context&, const std::complex<T>& data, D dump_fn) throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename R>\r
+  void restore_complex(restore_context&, std::complex<T>& data, R restore_fn) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_complex.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_complex.tpp b/src/stlplus/persistence/persistent_complex.tpp
new file mode 100644 (file)
index 0000000..7db9bc4
--- /dev/null
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename D>\r
+  void dump_complex(dump_context& context, const std::complex<T>& data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_fn(context,data.real());\r
+    dump_fn(context,data.imag());\r
+  }\r
+\r
+  template<typename T, typename R>\r
+  void restore_complex(restore_context& context, std::complex<T>& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    T re, im;\r
+    restore_fn(context,re);\r
+    restore_fn(context,im);\r
+    data = std::complex<T>(re, im);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_contexts.cpp b/src/stlplus/persistence/persistent_contexts.cpp
new file mode 100644 (file)
index 0000000..008fa9f
--- /dev/null
@@ -0,0 +1,434 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author:    Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+// License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_contexts.hpp"\r
+#include "persistent.hpp"\r
+#include <map>\r
+#include <string>\r
+#include <stdio.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // File format version\r
+\r
+  // This relates to the layout of basic types - if I change the file layout of,\r
+  // say, int or vector, then this will change\r
+\r
+  // Early versions of the persistence routines did not have this - they are no longer supported\r
+  // - Change from version 1 to 2: changed the persistent representation of inf\r
+\r
+  unsigned char PersistentVersion = 2;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // avoid creating dependencies on other libraries\r
+\r
+  static std::string to_string(int number)\r
+  {\r
+    // use sprintf in a very controlled way that cannot overrun\r
+    char* buffer = new char[50];\r
+    sprintf(buffer, "%i", number);\r
+    std::string result = buffer;\r
+    delete buffer;\r
+    return result;\r
+  }\r
+\r
+  static bool little_endian(void)\r
+  {\r
+    // TODO - find a compile-time way of doing this\r
+    int sample = 1;\r
+    char* sample_bytes = (char*)&sample;\r
+    return sample_bytes[0] != 0;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // dump context classes\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class dump_context_body\r
+  {\r
+  public:\r
+    typedef std::map<const void*,unsigned> magic_map;\r
+    typedef std::map<std::string,dump_context::callback_data> callback_map;\r
+    typedef std::map<std::string,unsigned> interface_map;\r
+\r
+    unsigned m_max_key;\r
+    unsigned char m_version;\r
+    bool m_little_endian;\r
+    std::ostream* m_device;\r
+    magic_map m_pointers;\r
+    callback_map m_callbacks;\r
+    interface_map m_interfaces;\r
+\r
+    dump_context_body(std::ostream& device, unsigned char version) throw(persistent_dump_failed) : \r
+      m_max_key(0), m_version(version), m_little_endian(stlplus::little_endian()), m_device(&device)\r
+      {\r
+        // write the version number as a single byte\r
+        put(version);\r
+        // map a null pointer onto magic number zero\r
+        m_pointers[0] = 0;\r
+        // test whether the version number is supported\r
+        if (m_version != 1 && m_version != 2)\r
+          throw persistent_dump_failed(std::string("wrong version: ") + to_string(m_version));\r
+      }\r
+\r
+    void put(unsigned char data) throw(persistent_dump_failed)\r
+      {\r
+        if (!m_device->put(data))\r
+          throw persistent_dump_failed(std::string("output device error"));\r
+      }\r
+\r
+    const std::ostream& device(void) const\r
+      {\r
+        return *m_device;\r
+      }\r
+\r
+    unsigned char version(void) const\r
+      {\r
+        return m_version;\r
+      }\r
+\r
+    bool little_endian(void) const\r
+      {\r
+        return m_little_endian;\r
+      }\r
+\r
+    std::pair<bool,unsigned> pointer_map(const void* const pointer)\r
+      {\r
+        magic_map::iterator found = m_pointers.find(pointer);\r
+        if (found == m_pointers.end())\r
+        {\r
+          // add a new mapping\r
+          unsigned magic = m_pointers.size();\r
+          m_pointers[pointer] = magic;\r
+          return std::pair<bool,unsigned>(false,magic);\r
+        }\r
+        // return the old mapping\r
+        return std::pair<bool,unsigned>(true,found->second);\r
+      }\r
+\r
+    unsigned register_callback(const std::type_info& info, dump_context::dump_callback callback)\r
+      {\r
+        std::string key = info.name();\r
+        unsigned data = ++m_max_key;\r
+        m_callbacks[key] = std::make_pair(data,callback);\r
+        return data;\r
+      }\r
+\r
+    bool is_callback(const std::type_info& info) const\r
+      {\r
+        return m_callbacks.find(info.name()) != m_callbacks.end();\r
+      }\r
+\r
+    dump_context::callback_data lookup_callback(const std::type_info& info) const throw(persistent_illegal_type)\r
+      {\r
+        std::string key = info.name();\r
+        callback_map::const_iterator found = m_callbacks.find(key);\r
+        if (found == m_callbacks.end())\r
+          throw persistent_illegal_type(key);\r
+        return found->second;\r
+      }\r
+\r
+    unsigned register_interface(const std::type_info& info)\r
+      {\r
+        std::string key = info.name();\r
+        unsigned data = ++m_max_key;\r
+        m_interfaces[key] = data;\r
+        return data;\r
+      }\r
+\r
+    bool is_interface(const std::type_info& info) const\r
+      {\r
+        return m_interfaces.find(info.name()) != m_interfaces.end();\r
+      }\r
+\r
+    unsigned lookup_interface(const std::type_info& info) const throw(persistent_illegal_type)\r
+      {\r
+        std::string key = info.name();\r
+        interface_map::const_iterator found = m_interfaces.find(key);\r
+        if (found == m_interfaces.end())\r
+          throw persistent_illegal_type(key);\r
+        return found->second;\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  dump_context::dump_context(std::ostream& device, unsigned char version) throw(persistent_dump_failed) : m_body(0)\r
+  {\r
+    m_body = new dump_context_body(device,version);\r
+  }\r
+\r
+  dump_context::~dump_context(void)\r
+  {\r
+    delete m_body;\r
+  }\r
+\r
+  void dump_context::put(unsigned char data) throw(persistent_dump_failed)\r
+  {\r
+    m_body->put(data);\r
+  }\r
+\r
+  const std::ostream& dump_context::device(void) const\r
+  {\r
+    return m_body->device();\r
+  }\r
+\r
+  unsigned char dump_context::version(void) const\r
+  {\r
+    return m_body->version();\r
+  }\r
+\r
+  bool dump_context::little_endian(void) const\r
+  {\r
+    return m_body->little_endian();\r
+  }\r
+\r
+  std::pair<bool,unsigned> dump_context::pointer_map(const void* const pointer)\r
+  {\r
+    return m_body->pointer_map(pointer);\r
+  }\r
+\r
+  unsigned dump_context::register_callback(const std::type_info& info, dump_context::dump_callback callback)\r
+  {\r
+    return m_body->register_callback(info,callback);\r
+  }\r
+\r
+  bool dump_context::is_callback(const std::type_info& info) const\r
+  {\r
+    return m_body->is_callback(info);\r
+  }\r
+\r
+  dump_context::callback_data dump_context::lookup_callback(const std::type_info& info) const throw(persistent_illegal_type)\r
+  {\r
+    return m_body->lookup_callback(info);\r
+  }\r
+\r
+  unsigned dump_context::register_interface(const std::type_info& info)\r
+  {\r
+    return m_body->register_interface(info);\r
+  }\r
+\r
+  bool dump_context::is_interface(const std::type_info& info) const\r
+  {\r
+    return m_body->is_interface(info);\r
+  }\r
+\r
+  unsigned dump_context::lookup_interface(const std::type_info& info) const throw(persistent_illegal_type)\r
+  {\r
+    return m_body->lookup_interface(info);\r
+  }\r
+\r
+  void dump_context::register_all(dump_context::installer installer)\r
+  {\r
+    if (installer) installer(*this);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // restore context classes\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class restore_context_body\r
+  {\r
+  public:\r
+    typedef persistent* persistent_ptr;\r
+    typedef std::map<unsigned,void*> magic_map;\r
+    typedef std::map<unsigned,restore_context::callback_data> callback_map;\r
+    typedef std::map<unsigned,persistent_ptr> interface_map;\r
+\r
+    unsigned m_max_key;\r
+    unsigned char m_version;\r
+    bool m_little_endian;\r
+    std::istream* m_device;\r
+    magic_map m_pointers;\r
+    callback_map m_callbacks;\r
+    interface_map m_interfaces;\r
+\r
+    restore_context_body(std::istream& device) throw(persistent_restore_failed) : \r
+      m_max_key(0), m_little_endian(stlplus::little_endian()), m_device(&device)\r
+      {\r
+        // map a null pointer onto magic number zero\r
+        m_pointers[0] = 0;\r
+        // get the dump version and see if we support it\r
+        m_version = (unsigned char)get();\r
+        if (m_version != 1 && m_version != 2)\r
+          throw persistent_restore_failed(std::string("wrong version: ") + to_string(m_version));\r
+      }\r
+\r
+    ~restore_context_body(void)\r
+      {\r
+        // need to delete all interfaces\r
+        // I used to use smart_ptr_clone for storing them but I want to disconnect as many dependencies as possible\r
+        for (unsigned i = 0; i < m_interfaces.size(); i++)\r
+          delete m_interfaces[i];\r
+      }\r
+\r
+    const std::istream& device(void) const\r
+      {\r
+        return *m_device;\r
+      }\r
+\r
+    unsigned char version(void) const\r
+      {\r
+        return m_version;\r
+      }\r
+\r
+    bool little_endian(void) const\r
+      {\r
+        return m_little_endian;\r
+      }\r
+\r
+    int get(void) throw(persistent_restore_failed)\r
+      {\r
+        int result = m_device->get();\r
+        if (!m_device->good())\r
+          throw persistent_restore_failed(std::string("device error or premature end of file"));\r
+        return result;\r
+      }\r
+\r
+    std::pair<bool,void*> pointer_map(unsigned magic)\r
+      {\r
+        magic_map::iterator found = m_pointers.find(magic);\r
+        if (found == m_pointers.end())\r
+        {\r
+          // this magic number has never been seen before\r
+          return std::pair<bool,void*>(false,0);\r
+        }\r
+        return std::pair<bool,void*>(true,found->second);\r
+      }\r
+\r
+    void pointer_add(unsigned magic, void* new_pointer)\r
+      {\r
+        m_pointers[magic] = new_pointer;\r
+      }\r
+\r
+    unsigned register_callback(restore_context::create_callback create, restore_context::restore_callback restore)\r
+      {\r
+        unsigned key = ++m_max_key;\r
+        m_callbacks[key] = std::make_pair(create,restore);\r
+        return key;\r
+      }\r
+\r
+    bool is_callback(unsigned key) const\r
+      {\r
+        return m_callbacks.find(key) != m_callbacks.end();\r
+      }\r
+\r
+    restore_context::callback_data lookup_callback(unsigned key) const throw(persistent_illegal_type)\r
+      {\r
+        callback_map::const_iterator found = m_callbacks.find(key);\r
+        if (found == m_callbacks.end())\r
+          throw persistent_illegal_type(key);\r
+        return found->second;\r
+      }\r
+\r
+    unsigned register_interface(persistent* sample)\r
+      {\r
+        unsigned key = ++m_max_key;\r
+        m_interfaces[key] = sample;\r
+        return key;\r
+      }\r
+\r
+    bool is_interface(unsigned key) const\r
+      {\r
+        return m_interfaces.find(key) != m_interfaces.end();\r
+      }\r
+\r
+    persistent* lookup_interface(unsigned key) const throw(persistent_illegal_type)\r
+      {\r
+        interface_map::const_iterator found = m_interfaces.find(key);\r
+        if (found == m_interfaces.end())\r
+          throw persistent_illegal_type(key);\r
+        return found->second;\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  restore_context::restore_context(std::istream& device) throw(persistent_restore_failed) : \r
+    m_body(0)\r
+  {\r
+    m_body = new restore_context_body(device);\r
+  }\r
+\r
+  restore_context::~restore_context(void)\r
+  {\r
+    delete m_body;\r
+  }\r
+\r
+  const std::istream& restore_context::device(void) const\r
+  {\r
+    return m_body->device();\r
+  }\r
+\r
+  unsigned char restore_context::version(void) const\r
+  {\r
+    return m_body->version();\r
+  }\r
+\r
+  bool restore_context::little_endian(void) const\r
+  {\r
+    return m_body->little_endian();\r
+  }\r
+\r
+  int restore_context::get(void) throw(persistent_restore_failed)\r
+  {\r
+    return m_body->get();\r
+  }\r
+\r
+  std::pair<bool,void*> restore_context::pointer_map(unsigned magic)\r
+  {\r
+    return m_body->pointer_map(magic);\r
+  }\r
+\r
+  void restore_context::pointer_add(unsigned magic, void* new_pointer)\r
+  {\r
+    m_body->pointer_add(magic,new_pointer);\r
+  }\r
+\r
+  unsigned restore_context::register_callback(restore_context::create_callback create, restore_context::restore_callback restore)\r
+  {\r
+    return m_body->register_callback(create,restore);\r
+  }\r
+\r
+  bool restore_context::is_callback(unsigned key) const\r
+  {\r
+    return m_body->is_callback(key);\r
+  }\r
+\r
+  restore_context::callback_data restore_context::lookup_callback(unsigned key) const throw(persistent_illegal_type)\r
+  {\r
+    return m_body->lookup_callback(key);\r
+  }\r
+\r
+  unsigned restore_context::register_interface(persistent* sample)\r
+  {\r
+    return m_body->register_interface(sample);\r
+  }\r
+\r
+  bool restore_context::is_interface(unsigned key) const\r
+  {\r
+    return m_body->is_interface(key);\r
+  }\r
+\r
+  persistent* restore_context::lookup_interface(unsigned key) const throw(persistent_illegal_type)\r
+  {\r
+    return m_body->lookup_interface(key);\r
+  }\r
+\r
+  void restore_context::register_all(restore_context::installer installer)\r
+  {\r
+    if (installer) installer(*this);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/persistence/persistent_contexts.hpp b/src/stlplus/persistence/persistent_contexts.hpp
new file mode 100644 (file)
index 0000000..dfef17b
--- /dev/null
@@ -0,0 +1,156 @@
+#ifndef STLPLUS_PERSISTENT_CONTEXTS\r
+#define STLPLUS_PERSISTENT_CONTEXTS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Core context classes used to control the persistent dump/restore operations\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistence_fixes.hpp"\r
+#include "persistent.hpp"\r
+#include <iostream>\r
+#include <map>\r
+#include <typeinfo>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Internals\r
+\r
+  class dump_context_body;\r
+  class restore_context_body;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // The format version number currently supported\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  extern unsigned char PersistentVersion;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // dump_context controls the formatting of a persistent dump\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class dump_context\r
+  {\r
+    friend class persistent;\r
+  public:\r
+    //////////////////////////////////////////////////////////////////////////////\r
+\r
+    // device must be in binary mode\r
+    dump_context(std::ostream& device, unsigned char version = PersistentVersion) throw(persistent_dump_failed);\r
+    ~dump_context(void);\r
+\r
+    // low level output used to dump a byte\r
+    void put(unsigned char data) throw(persistent_dump_failed);\r
+\r
+    // access the device, for example to check the error status\r
+    const std::ostream& device(void) const;\r
+\r
+    // recover the version number of the dumped output\r
+    unsigned char version(void) const;\r
+\r
+    // test whether the current platform uses little-endian or big-endian addressing of bytes\r
+    // this is used in dump/restore of integers and is exported so that other routines can use it\r
+    bool little_endian(void) const;\r
+\r
+    // Assist functions for Pointers\r
+    // the return pair value is a flag saying whether this is a new pointer and the magic key to dump to file\r
+    std::pair<bool,unsigned> pointer_map(const void* const pointer);\r
+\r
+    // Assist functions for Polymorphous classes (i.e. subclasses) using callback approach\r
+    typedef void (*dump_callback)(dump_context&,const void*);\r
+    unsigned register_callback(const std::type_info& info, dump_callback);\r
+    bool is_callback(const std::type_info& info) const;\r
+    typedef std::pair<unsigned,dump_callback> callback_data;\r
+    callback_data lookup_callback(const std::type_info&) const throw(persistent_illegal_type);\r
+\r
+    // Assist functions for Polymorphous classes (i.e. subclasses) using interface approach\r
+    unsigned register_interface(const std::type_info& info);\r
+    bool is_interface(const std::type_info& info) const;\r
+    unsigned lookup_interface(const std::type_info&) const throw(persistent_illegal_type);\r
+\r
+    // Register all Polymorphous classes using either approach by calling an installer callback\r
+    typedef void (*installer)(dump_context&);\r
+    void register_all(installer);\r
+\r
+  private:\r
+    friend class dump_context_body;\r
+    dump_context_body* m_body;\r
+\r
+    // disallow copying by making assignment and copy constructor private\r
+    dump_context(const dump_context&);\r
+    dump_context& operator=(const dump_context&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // restore_context controls the reading of the persistent data during a restore\r
+\r
+  class restore_context\r
+  {\r
+    friend class persistent;\r
+  public:\r
+    //////////////////////////////////////////////////////////////////////////////\r
+\r
+    // device must be in binary mode\r
+    restore_context(std::istream& device) throw(persistent_restore_failed);\r
+    ~restore_context(void);\r
+\r
+    // low level input used to restore a byte\r
+    int get(void) throw(persistent_restore_failed);\r
+\r
+    // access the device, for example to check the error status\r
+    const std::istream& device(void) const;\r
+\r
+    // access the version number of the input being restored\r
+    unsigned char version(void) const;\r
+\r
+    // test whether the current platform uses little-endian or big-endian addressing of bytes\r
+    // this is used in dump/restore of integers\r
+    bool little_endian(void) const;\r
+\r
+    // Assist functions for Pointers\r
+    std::pair<bool,void*> pointer_map(unsigned magic);\r
+    void pointer_add(unsigned magic, void* new_pointer);\r
+\r
+    // Assist functions for Polymorphous classes using the callback approach\r
+    typedef void* (*create_callback)(void);\r
+    typedef void (*restore_callback)(restore_context&,void*);\r
+    unsigned register_callback(create_callback,restore_callback);\r
+    bool is_callback(unsigned) const;\r
+    typedef std::pair<create_callback, restore_callback> callback_data;\r
+    callback_data lookup_callback(unsigned) const throw(persistent_illegal_type);\r
+\r
+    // Assist functions for Polymorphous classes using the interface approach\r
+    unsigned register_interface(persistent*);\r
+    bool is_interface(unsigned) const;\r
+    persistent* lookup_interface(unsigned) const throw(persistent_illegal_type);\r
+\r
+    // Register all Polymorphous classes using either approach by calling an installer callback\r
+    typedef void (*installer)(restore_context&);\r
+    void register_all(installer);\r
+\r
+  private:\r
+    friend class restore_context_body;\r
+    restore_context_body* m_body;\r
+\r
+    typedef std::pair<unsigned,persistent*> interface_data;\r
+\r
+    // disallow copying by making assignment and copy constructor private\r
+    restore_context(const restore_context&);\r
+    restore_context& operator=(const restore_context&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_cstring.cpp b/src/stlplus/persistence/persistent_cstring.cpp
new file mode 100644 (file)
index 0000000..a829b9a
--- /dev/null
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistent_cstring.hpp"\r
+#include "persistent_int.hpp"\r
+#include <string.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Null-terminated char arrays\r
+// Format: address [ size data ]\r
+\r
+void stlplus::dump_cstring(stlplus::dump_context& context, const char* data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  // register the address and get the magic key for it\r
+  std::pair<bool,unsigned> mapping = context.pointer_map(data);\r
+  stlplus::dump_unsigned(context,mapping.second);\r
+  // if the address is null, then that is all that we need to do\r
+  // however, if it is non-null and this is the first sight of the address, dump the contents\r
+  if (data && !mapping.first)\r
+  {\r
+    unsigned size = strlen(data);\r
+    stlplus::dump_unsigned(context,size);\r
+    for (unsigned i = 0; i < size; i++)\r
+      stlplus::dump_char(context,data[i]);\r
+  }\r
+}\r
+\r
+void stlplus::restore_cstring(restore_context& context, char*& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  // destroy any previous contents\r
+  if (data)\r
+  {\r
+    delete[] data;\r
+    data = 0;\r
+  }\r
+  // get the magic key\r
+  unsigned magic = 0;\r
+  stlplus::restore_unsigned(context,magic);\r
+  // now lookup the magic key to see if this pointer has already been restored\r
+  // null pointers are always flagged as already restored\r
+  std::pair<bool,void*> address = context.pointer_map(magic);\r
+  if (!address.first)\r
+  {\r
+    // this pointer has never been seen before and is non-null\r
+    // restore the string\r
+    unsigned size = 0;\r
+    stlplus::restore_unsigned(context,size);\r
+    data = new char[size+1];\r
+    for (unsigned i = 0; i < size; i++)\r
+      stlplus::restore_char(context,data[i]);\r
+    data[size] = '\0';\r
+    // add this pointer to the set of already seen objects\r
+    context.pointer_add(magic,data);\r
+  }\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/persistence/persistent_cstring.hpp b/src/stlplus/persistence/persistent_cstring.hpp
new file mode 100644 (file)
index 0000000..8e3e94a
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef STLPLUS_PERSISTENT_CSTRING\r
+#define STLPLUS_PERSISTENT_CSTRING\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of C-style char* strings\r
+\r
+//   These are handled differently to other pointer types\r
+\r
+//   Warning! This means that pointers to char cannot be supported, since there\r
+//   is no type difference between a pointer to char and a C-style array of char.\r
+\r
+//   Warning! The restore deletes any old value of the data parameter and\r
+//   allocates a new char* which is (just) big enough and assigns it to the data\r
+//   field. This is because there is no way of knowing how long a char* is so\r
+//   the passed parameter is not safe to use. The allocation is done using\r
+//   standard new. If the data field is non-null on entry it will be deleted by\r
+//   standard delete. Best to make it null in the first place.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void dump_cstring(dump_context&, const char* data) throw(persistent_dump_failed);\r
+  void restore_cstring(restore_context&, char*& data) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_deque.hpp b/src/stlplus/persistence/persistent_deque.hpp
new file mode 100644 (file)
index 0000000..2a47180
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef STLPLUS_PERSISTENT_DEQUE\r
+#define STLPLUS_PERSISTENT_DEQUE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of the STL deque\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <deque>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename D>\r
+  void dump_deque(dump_context&, const std::deque<T>& data, D dump_fn) throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename R>\r
+  void restore_deque(restore_context&, std::deque<T>& data, R restore_fn) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_deque.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_deque.tpp b/src/stlplus/persistence/persistent_deque.tpp
new file mode 100644 (file)
index 0000000..26854ce
--- /dev/null
@@ -0,0 +1,41 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename D>\r
+  void dump_deque(dump_context& context, const std::deque<T>& data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (typename std::deque<T>::const_iterator i = data.begin(); i != data.end(); i++)\r
+      dump_fn(context,*i);\r
+  }\r
+\r
+  template<typename T, typename R>\r
+  void restore_deque(restore_context& context, std::deque<T>& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.clear();\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    for (unsigned i = 0; i < size; i++)\r
+    {\r
+      data.push_back(T());\r
+      restore_fn(context,data.back());\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_digraph.hpp b/src/stlplus/persistence/persistent_digraph.hpp
new file mode 100644 (file)
index 0000000..fcc57a0
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef STLPLUS_PERSISTENT_DIGRAPH\r
+#define STLPLUS_PERSISTENT_DIGRAPH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STLplus digraph\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "digraph.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // digraph\r
+\r
+  template<typename NT, typename AT, typename DN, typename DA>\r
+  void dump_digraph(dump_context&, const digraph<NT,AT>& data, DN dump_node, DA dump_arc)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename NT, typename AT, typename RN, typename RA>\r
+  void restore_digraph(restore_context&, digraph<NT,AT>& data, RN restore_node, RA restore_arc)\r
+    throw(persistent_restore_failed);\r
+\r
+  // node iterator\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void dump_digraph_iterator(dump_context& str, const digraph_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void restore_digraph_iterator(restore_context& str, digraph_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_restore_failed);\r
+\r
+  // arc iterator\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void dump_digraph_arc_iterator(dump_context& str, const digraph_arc_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void restore_digraph_arc_iterator(restore_context& str, digraph_arc_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_digraph.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_digraph.tpp b/src/stlplus/persistence/persistent_digraph.tpp
new file mode 100644 (file)
index 0000000..3ff94a5
--- /dev/null
@@ -0,0 +1,153 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+#include "persistent_xref.hpp"\r
+\r
+namespace stlplus\r
+{\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename NT, typename AT, typename DN, typename DA>\r
+  void dump_digraph(dump_context& context, const digraph<NT,AT>& data,\r
+                    DN dump_node, DA dump_arc)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // dump a magic key to the address of the graph for use in persistence of iterators\r
+    // and register it as a dumped address\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(&data);\r
+    if (mapping.first) throw persistent_dump_failed("digraph: already dumped this graph");\r
+    dump_unsigned(context,mapping.second);\r
+    // dump the nodes\r
+    dump_unsigned(context,data.size());\r
+    for (typename digraph<NT,AT>::const_iterator node = data.begin(); node != data.end(); node++)\r
+    {\r
+      // nodes are keyed by the magic key to the node address\r
+      // this key is then used in dumping the arc from/to pointers\r
+      std::pair<bool,unsigned> node_mapping = context.pointer_map(node.node());\r
+      if (node_mapping.first) throw persistent_dump_failed("digraph: already dumped this node");\r
+      dump_unsigned(context,node_mapping.second);\r
+      // finally, dump the node contents\r
+      dump_node(context,*node);\r
+    }\r
+    // dump the arcs\r
+    dump_unsigned(context,data.arc_size());\r
+    for (typename digraph<NT,AT>::const_arc_iterator arc = data.arc_begin(); arc != data.arc_end(); arc++)\r
+    {\r
+      // dump the magic key to the arc address\r
+      // this is used by iterator persistence too\r
+      std::pair<bool,unsigned> arc_mapping = context.pointer_map(arc.node());\r
+      if (arc_mapping.first) throw persistent_dump_failed("digraph: already dumped this arc");\r
+      dump_unsigned(context,arc_mapping.second);\r
+      // now dump the from/to pointers as cross-references\r
+      dump_xref(context,data.arc_from(arc).node());\r
+      dump_xref(context,data.arc_to(arc).node());\r
+      // now dump the arc's data\r
+      dump_arc(context,*arc);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename NT, typename AT, typename RN, typename RA>\r
+  void restore_digraph(restore_context& context, digraph<NT,AT>& data,\r
+                       RN restore_node, RA restore_arc)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.clear();\r
+    // restore the graph's magic key and map it onto the graph's address\r
+    // this is used in the persistence of iterators\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    context.pointer_add(magic,&data);\r
+    // restore the nodes\r
+    unsigned nodes = 0;\r
+    restore_unsigned(context, nodes);\r
+    for (unsigned n = 0; n < nodes; n++)\r
+    {\r
+      unsigned node_magic = 0;\r
+      restore_unsigned(context,node_magic);\r
+      // create a new node and map the magic key onto the new address\r
+      typename digraph<NT,AT>::iterator node = data.insert(NT());\r
+      context.pointer_add(node_magic,node.node());\r
+      // now restore the user's data\r
+      restore_node(context,*node);\r
+    }\r
+    // restore the arcs\r
+    unsigned arcs = 0;\r
+    restore_unsigned(context, arcs);\r
+    for (unsigned a = 0; a < arcs; a++)\r
+    {\r
+      unsigned arc_magic = 0;\r
+      restore_unsigned(context,arc_magic);\r
+      // restore the from and to cross-references\r
+      digraph_node<NT,AT>* from = 0;\r
+      digraph_node<NT,AT>* to = 0;\r
+      restore_xref(context,from);\r
+      restore_xref(context,to);\r
+      // create an arc with these from/to pointers\r
+      digraph_arc_iterator<NT,AT,AT&,AT*> arc = \r
+        data.arc_insert(digraph_iterator<NT,AT,NT&,NT*>(from), \r
+                        digraph_iterator<NT,AT,NT&,NT*>(to));\r
+      context.pointer_add(arc_magic,arc.node());\r
+      // restore the user data\r
+      restore_arc(context,*arc);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void dump_digraph_iterator(dump_context& context, \r
+                             const digraph_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_xref(context,data.owner());\r
+    dump_xref(context,data.node());\r
+  }\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void restore_digraph_iterator(restore_context& context, \r
+                                digraph_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    digraph<NT,AT>* owner = 0;\r
+    digraph_node<NT,AT>* node = 0;\r
+    restore_xref(context,owner);\r
+    restore_xref(context,node);\r
+    data = digraph_iterator<NT,AT,NRef,NPtr>(node);\r
+    data.assert_owner(owner);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void dump_digraph_arc_iterator(dump_context& context,\r
+                                 const digraph_arc_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_xref(context,data.owner());\r
+    dump_xref(context,data.node());\r
+  }\r
+\r
+  template<typename NT, typename AT, typename NRef, typename NPtr>\r
+  void restore_digraph_arc_iterator(restore_context& context, \r
+                                    digraph_arc_iterator<NT,AT,NRef,NPtr>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    digraph<NT,AT>* owner = 0;\r
+    digraph_arc<NT,AT>* arc = 0;\r
+    restore_xref(context,owner);\r
+    restore_xref(context,arc);\r
+    data = digraph_arc_iterator<NT,AT,NRef,NPtr>(arc);\r
+    data.assert_owner(owner);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_enum.hpp b/src/stlplus/persistence/persistent_enum.hpp
new file mode 100644 (file)
index 0000000..ddce1b4
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef STLPLUS_PERSISTENT_ENUM\r
+#define STLPLUS_PERSISTENT_ENUM\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of enumeration types\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T>\r
+  void dump_enum(dump_context&, const T& data) throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_enum(restore_context&, T& data) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_enum.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_enum.tpp b/src/stlplus/persistence/persistent_enum.tpp
new file mode 100644 (file)
index 0000000..9778688
--- /dev/null
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // enumeration types\r
+\r
+  template<typename T>\r
+  void dump_enum(dump_context& context, const T& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,(unsigned)data);\r
+  }\r
+\r
+  template<typename T>\r
+  void restore_enum(restore_context& context, T& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    unsigned value = 0;\r
+    restore_unsigned(context, value);\r
+    data = (T)value;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_exceptions.cpp b/src/stlplus/persistence/persistent_exceptions.cpp
new file mode 100644 (file)
index 0000000..d233826
--- /dev/null
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistent_exceptions.hpp"\r
+#include <stdio.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+static std::string to_string(int number)\r
+{\r
+  // use sprintf in a very controlled way that cannot overrun\r
+  char* buffer = new char[50];\r
+  sprintf(buffer, "%i", number);\r
+  std::string result = buffer;\r
+  delete buffer;\r
+  return result;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// exceptions\r
+\r
+stlplus::persistent_illegal_type::persistent_illegal_type(const std::string& type) throw() : \r
+  std::logic_error(std::string("illegal type: ") + type)\r
+{\r
+}\r
+\r
+stlplus::persistent_illegal_type::persistent_illegal_type(unsigned key) throw() : \r
+  std::logic_error(std::string("illegal key: ") + to_string((int)key))\r
+{\r
+}\r
+\r
+stlplus::persistent_illegal_type::~persistent_illegal_type(void) throw()\r
+{\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+stlplus::persistent_dump_failed::persistent_dump_failed(const std::string& message) throw() :\r
+  std::runtime_error(std::string("dump failed: ") + message)\r
+{\r
+}\r
+\r
+stlplus::persistent_dump_failed::~persistent_dump_failed(void) throw()\r
+{\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+stlplus::persistent_restore_failed::persistent_restore_failed(const std::string& message) throw() :\r
+  std::runtime_error(std::string("restore failed: ") + message)\r
+{\r
+}\r
+\r
+stlplus::persistent_restore_failed::~persistent_restore_failed(void) throw()\r
+{\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/persistence/persistent_exceptions.hpp b/src/stlplus/persistence/persistent_exceptions.hpp
new file mode 100644 (file)
index 0000000..2e2a861
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef STLPLUS_PERSISTENT_EXCEPTIONS\r
+#define STLPLUS_PERSISTENT_EXCEPTIONS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Exceptions thrown by persistence routines\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include <stdexcept>\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // exception thrown if you try to dump or restore an illegal polymorphic type\r
+  class persistent_illegal_type : public std::logic_error\r
+  {\r
+  public:\r
+    persistent_illegal_type(const std::string& type) throw();\r
+    persistent_illegal_type(unsigned key) throw();\r
+    ~persistent_illegal_type(void) throw();\r
+  };\r
+\r
+  // exception thrown if a dump fails for any reason - but typically because the output stream couldn't take the data\r
+  class persistent_dump_failed : public std::runtime_error\r
+  {\r
+  public:\r
+    persistent_dump_failed(const std::string& message) throw();\r
+    ~persistent_dump_failed(void) throw();\r
+  };\r
+\r
+  // exception thrown if you try to restore from an out of date or unrecognised byte stream\r
+  class persistent_restore_failed : public std::runtime_error\r
+  {\r
+  public:\r
+    persistent_restore_failed(const std::string& message) throw();\r
+    ~persistent_restore_failed(void) throw();\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_float.cpp b/src/stlplus/persistence/persistent_float.cpp
new file mode 100644 (file)
index 0000000..59a6678
--- /dev/null
@@ -0,0 +1,86 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_float.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Macro for mapping either endian data onto little-endian addressing to make\r
+// my life easier in writing this code! I think better in little-endian mode\r
+// so the macro does nothing in that mode but maps little-endian onto\r
+// big-endian addressing in big-endian mode\r
+// TODO - make this compile-time configurable\r
+\r
+#define INDEX(index) ((context.little_endian()) ? (index) : ((bytes) - (index) - 1))\r
+\r
+/////////////////////////////////////////////////////////////////////\r
+// floating point types\r
+// format: {size}{byte}*size\r
+// ordering is msB first\r
+\r
+// this uses a similar mechanism to integer dumps. However, it is not clear how\r
+// the big-endian and little-endian argument applies to multi-word data so\r
+// this may need reworking by splitting into words and then bytes.\r
+\r
+namespace stlplus\r
+{\r
+\r
+  static void dump_float(stlplus::dump_context& context, unsigned bytes, unsigned char* data)\r
+    throw(stlplus::persistent_dump_failed)\r
+  {\r
+    unsigned i = bytes;\r
+    // put the size\r
+    context.put((unsigned char)i);\r
+    // and put the bytes\r
+    while(i--)\r
+      context.put(data[INDEX(i)]);\r
+  }\r
+\r
+  static void restore_float(stlplus::restore_context& context, unsigned bytes, unsigned char* data)\r
+    throw(stlplus::persistent_restore_failed)\r
+  {\r
+    // get the dumped size from the file\r
+    unsigned dumped_bytes = (unsigned)context.get();\r
+    // get the bytes from the file\r
+    unsigned i = dumped_bytes;\r
+    while(i--)\r
+    {\r
+      int ch = context.get();\r
+      if (i < bytes)\r
+        data[INDEX(i)] = (unsigned char)ch;\r
+    }\r
+    // however, if the dumped size was different I don't know how to map the formats, so give an error\r
+    if (dumped_bytes != bytes)\r
+      throw stlplus::persistent_restore_failed(std::string("size mismatch"));\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // exported functions which simply call the low-level byte-dump and byte-restore routines above\r
+\r
+void stlplus::dump_float(stlplus::dump_context& context, const float& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  stlplus::dump_float(context, sizeof(float), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_float(restore_context& context, float& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  stlplus::restore_float(context, sizeof(float), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::dump_double(stlplus::dump_context& context, const double& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  stlplus::dump_float(context, sizeof(double), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_double(restore_context& context, double& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  stlplus::restore_float(context, sizeof(double), (unsigned char*)&data);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/persistence/persistent_float.hpp b/src/stlplus/persistence/persistent_float.hpp
new file mode 100644 (file)
index 0000000..4a01e30
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef STLPLUS_PERSISTENT_FLOAT\r
+#define STLPLUS_PERSISTENT_FLOAT\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of floating-point types\r
+\r
+//   Note: despite years and years of IEEE standardisation, not all\r
+//   architectures use IEEE-standard representations of floating-point numbers.\r
+//   Therefore a binary dump is not necessarily portable between platforms.\r
+//   Solving this is (currently) beyond the scope of the STLplus project.\r
+\r
+//   If you want to be strictly portable to all platforms, do not dump/restore float\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void dump_float(dump_context&, const float& data) throw(persistent_dump_failed);\r
+  void restore_float(restore_context&, float& data) throw(persistent_restore_failed);\r
+\r
+  void dump_double(dump_context&, const double& data) throw(persistent_dump_failed);\r
+  void restore_double(restore_context&, double& data) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_foursome.hpp b/src/stlplus/persistence/persistent_foursome.hpp
new file mode 100644 (file)
index 0000000..b326a85
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef STLPLUS_PERSISTENT_FOURSOME\r
+#define STLPLUS_PERSISTENT_FOURSOME\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL foursome\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "foursome.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename D1, typename D2, typename D3, typename D4>\r
+  void dump_foursome(dump_context&, const stlplus::foursome<T1,T2,T3,T4>& data, \r
+                     D1 dump_fn1, D2 dump_fn2, D3 dump_fn3, D4 dump_fn4)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename R1, typename R2, typename R3, typename R4>\r
+  void restore_foursome(restore_context&, stlplus::foursome<T1,T2,T3,T4>& data,\r
+                        R1 restore_fn1, R2 restore_fn2, R3 restore_fn3, R4 restore_fn4)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_foursome.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_foursome.tpp b/src/stlplus/persistence/persistent_foursome.tpp
new file mode 100644 (file)
index 0000000..cfcf6ae
--- /dev/null
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename D1, typename D2, typename D3, typename D4>\r
+  void dump_foursome(dump_context& context, const foursome<T1,T2,T3,T4>& data, \r
+                     D1 dump_fn1, D2 dump_fn2, D3 dump_fn3, D4 dump_fn4)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_fn1(context,data.first);\r
+    dump_fn2(context,data.second);\r
+    dump_fn3(context,data.third);\r
+    dump_fn4(context,data.fourth);\r
+  }\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename R1, typename R2, typename R3, typename R4>\r
+  void restore_foursome(restore_context& context, foursome<T1,T2,T3,T4>& data,\r
+                        R1 restore_fn1, R2 restore_fn2, R3 restore_fn3, R4 restore_fn4)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    restore_fn1(context,data.first);\r
+    restore_fn2(context,data.second);\r
+    restore_fn3(context,data.third);\r
+    restore_fn4(context,data.fourth);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_hash.hpp b/src/stlplus/persistence/persistent_hash.hpp
new file mode 100644 (file)
index 0000000..623f285
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PERSISTENT_HASH\r
+#define STLPLUS_PERSISTENT_HASH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence for STLplus hash\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "hash.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename H, typename E, typename DK, typename DT>\r
+  void dump_hash(dump_context&, const hash<K,T,H,E>& data, DK key_dump_fn, DT val_dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename K, typename T, typename H, typename E, typename RK, typename RT>\r
+  void restore_hash(restore_context&, hash<K,T,H,E>& data, RK key_restore_fn, RT val_restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_hash.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_hash.tpp b/src/stlplus/persistence/persistent_hash.tpp
new file mode 100644 (file)
index 0000000..9fe2dbc
--- /dev/null
@@ -0,0 +1,45 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename K, typename T, typename H, typename E, typename DK, typename DT>\r
+  void dump_hash(dump_context& context, const hash<K,T,H,E>& data, DK key_fn, DT val_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (typename hash<K,T,H,E>::const_iterator i = data.begin(); i != data.end(); i++)\r
+    {\r
+      key_fn(context,i->first);\r
+      val_fn(context,i->second);\r
+    }\r
+  }\r
+\r
+  template<typename K, typename T, typename H, typename E, typename RK, typename RT>\r
+  void restore_hash(restore_context& context, hash<K,T,H,E>& data, RK key_fn, RT val_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.erase();\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    for (unsigned j = 0; j < size; j++)\r
+    {\r
+      K key;\r
+      key_fn(context,key);\r
+      val_fn(context,data[key]);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_inf.cpp b/src/stlplus/persistence/persistent_inf.cpp
new file mode 100644 (file)
index 0000000..29637c9
--- /dev/null
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// can be excluded to break the dependency on the portability library\r
+#ifndef NO_STLPLUS_INF\r
+\r
+#include "persistent_int.hpp"\r
+#include "persistent_string.hpp"\r
+#include "persistent_inf.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+void stlplus::dump_inf(stlplus::dump_context& context, const stlplus::inf& data)\r
+  throw(stlplus::persistent_dump_failed)\r
+{\r
+  // don't support dumping of old versions\r
+  if (context.version() < 2)\r
+    throw stlplus::persistent_dump_failed(std::string("stlplus::inf::dump: wrong version"));\r
+  // just dump the string\r
+  stlplus::dump_string(context,data.get_bytes());\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+void stlplus::restore_inf(stlplus::restore_context& context, stlplus::inf& data)\r
+  throw(stlplus::persistent_restore_failed)\r
+{\r
+  if (context.version() < 1)\r
+    throw stlplus::persistent_restore_failed(std::string("stlplus::inf::restore: wrong version"));\r
+  if (context.version() == 1)\r
+  {\r
+    // old-style restore relies on the word size being the same - 32-bits - on all platforms\r
+    // this can be restored on such machines but is not portable to 64-bit machines\r
+    std::string value;\r
+    unsigned bits = 0;\r
+    stlplus::restore_unsigned(context,bits);\r
+    unsigned words = (bits+7)/32;\r
+    // inf was dumped msB first\r
+    for (unsigned i = words; i--; )\r
+    {\r
+      // restore a word\r
+      unsigned word = 0;\r
+      stlplus::restore_unsigned(context,word);\r
+      // now extract the bytes\r
+      unsigned char* byte_ptr = (unsigned char*)(&word);\r
+      for (unsigned b = 4; b--; )\r
+        value.insert(value.begin(),byte_ptr[context.little_endian() ? b : 3 - b]);\r
+    }\r
+    data.set_bytes(value);\r
+  }\r
+  else\r
+  {\r
+    // new-style dump just uses the string persistence\r
+    std::string value;\r
+    stlplus::restore_string(context,value);\r
+    data.set_bytes(value);\r
+  }\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_inf.hpp b/src/stlplus/persistence/persistent_inf.hpp
new file mode 100644 (file)
index 0000000..2b456d9
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef STLPLUS_PERSISTENT_INF\r
+#define STLPLUS_PERSISTENT_INF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//     Author:    Andy Rushton\r
+//     Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//     License:   BSD License, see ../docs/license.html\r
+  \r
+//     Persistence of stlplus infinite integer type - inf\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "inf.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void dump_inf(dump_context&, const inf& data) throw(persistent_dump_failed);\r
+  void restore_inf(restore_context&, inf& data) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_int.cpp b/src/stlplus/persistence/persistent_int.cpp
new file mode 100644 (file)
index 0000000..87d403a
--- /dev/null
@@ -0,0 +1,223 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Macro for mapping either endian data onto little-endian addressing to make\r
+// my life easier in writing this code! I think better in little-endian mode\r
+// so the macro does nothing in that mode but maps little-endian onto\r
+// big-endian addressing in big-endian mode\r
+// TODO - make this compile-time configurable so it's more efficient\r
+\r
+#define INDEX(index) ((context.little_endian()) ? (index) : ((bytes) - (index) - 1))\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Integer types\r
+// format: {size}{byte}*size\r
+// size can be zero!\r
+//\r
+// A major problem is that integer types may be different sizes on different\r
+// machines or even with different compilers on the same machine (though I\r
+// haven't come across that yet). Neither the C nor the C++ standards specify\r
+// the size of integer types. Dumping an int on one machine might dump 16\r
+// bits. Restoring it on another machine might try to restore 32 bits. These\r
+// functions must therefore handle different type sizes. It does this by\r
+// writing the size to the file as well as the data, so the restore can\r
+// therefore know how many bytes to restore independent of the type size.\r
+//\r
+// In fact, the standard does not even specify the size of char (true! And\r
+// mind-numbingly stupid...). However, to be able to do anything at all, I've\r
+// had to assume that a char is 1 byte.\r
+\r
+static void dump_unsigned(stlplus::dump_context& context, unsigned bytes, unsigned char* data)\r
+  throw(stlplus::persistent_dump_failed)\r
+{\r
+  // first skip zero bytes - this may reduce the data to zero bytes long\r
+  unsigned i = bytes;\r
+  while(i >= 1 && data[INDEX(i-1)] == 0)\r
+    i--;\r
+  // put the remaining size\r
+  context.put((unsigned char)i);\r
+  // and put the bytes\r
+  while(i--)\r
+    context.put(data[INDEX(i)]);\r
+}\r
+\r
+static void dump_signed(stlplus::dump_context& context, unsigned bytes, unsigned char* data)\r
+  throw(stlplus::persistent_dump_failed)\r
+{\r
+  // first skip all-zero or all-one bytes but only if doing so does not change the sign\r
+  unsigned i = bytes;\r
+  if (data[INDEX(i-1)] < 128)\r
+  {\r
+    // positive number so discard leading zeros but only if the following byte is positive\r
+    while(i >= 2 && data[INDEX(i-1)] == 0 && data[INDEX(i-2)] < 128)\r
+      i--;\r
+  }\r
+  else\r
+  {\r
+    // negative number so discard leading ones but only if the following byte is negative\r
+    while(i >= 2 && data[INDEX(i-1)] == 255 && data[INDEX(i-2)] >= 128)\r
+      i--;\r
+  }\r
+  // put the remaining size\r
+  context.put((unsigned char)i);\r
+  // and put the bytes\r
+  while(i--)\r
+    context.put(data[INDEX(i)]);\r
+}\r
+\r
+static void restore_unsigned(stlplus::restore_context& context, unsigned bytes, unsigned char* data)\r
+  throw(stlplus::persistent_restore_failed)\r
+{\r
+  // get the dumped size from the file\r
+  unsigned dumped_bytes = (unsigned)context.get();\r
+  // zero fill any empty space\r
+  unsigned i = bytes;\r
+  for (; i > dumped_bytes; i--)\r
+    data[INDEX(i-1)] = 0;\r
+  // restore the dumped bytes but discard any that don't fit\r
+  while(i--)\r
+  {\r
+    int ch = context.get();\r
+    if (i < bytes)\r
+      data[INDEX(i)] = (unsigned char)ch;\r
+    else\r
+      throw stlplus::persistent_restore_failed(std::string("integer overflow"));\r
+  }\r
+}\r
+\r
+static void restore_signed(stlplus::restore_context& context, unsigned bytes, unsigned char* data)\r
+  throw(stlplus::persistent_restore_failed)\r
+{\r
+  // get the dumped size from the file\r
+  unsigned dumped_bytes = (unsigned)context.get();\r
+  // restore the dumped bytes but discard any that don't fit\r
+  unsigned i = dumped_bytes;\r
+  while(i--)\r
+  {\r
+    int ch = context.get();\r
+    if (i < bytes)\r
+      data[INDEX(i)] = (unsigned char)ch;\r
+    else\r
+      throw stlplus::persistent_restore_failed(std::string("integer overflow"));\r
+  }\r
+  // sign extend if the dumped integer was smaller\r
+  if (dumped_bytes < bytes)\r
+  {\r
+    if (data[INDEX(dumped_bytes-1)] < 128)\r
+    {\r
+      // positive so zero fill\r
+      for (i = dumped_bytes; i < bytes; i++)\r
+        data[INDEX(i)] = 0;\r
+    }\r
+    else\r
+    {\r
+      // negative so one fill\r
+      for (i = dumped_bytes; i < bytes; i++)\r
+        data[INDEX(i)] = 0xff;\r
+    }\r
+  }\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// exported functions\r
+\r
+// char is dumped and restored as an unsigned char because the signedness of char is not defined and can vary\r
+void stlplus::dump_char(stlplus::dump_context& context, const char& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  context.put((unsigned char)data);\r
+}\r
+\r
+void stlplus::restore_char(restore_context& context, char& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  data = (char)(unsigned char)context.get();\r
+}\r
+\r
+void stlplus::dump_signed_char(stlplus::dump_context& context, const signed char& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  context.put((unsigned char)data);\r
+}\r
+\r
+void stlplus::restore_signed_char(restore_context& context, signed char& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  data = (signed char)(unsigned char)context.get();\r
+}\r
+\r
+void stlplus::dump_unsigned_char(stlplus::dump_context& context, const unsigned char& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  context.put((unsigned char)data);\r
+}\r
+\r
+void stlplus::restore_unsigned_char(restore_context& context, unsigned char& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  data = (signed char)(unsigned char)context.get();\r
+}\r
+\r
+void stlplus::dump_short(stlplus::dump_context& context, const short& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  ::dump_signed(context, sizeof(short), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_short(restore_context& context, short& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  ::restore_signed(context, sizeof(short),(unsigned char*)&data);\r
+}\r
+\r
+void stlplus::dump_unsigned_short(stlplus::dump_context& context, const unsigned short& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  ::dump_unsigned(context, sizeof(unsigned short), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_unsigned_short(restore_context& context, unsigned short& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  ::restore_unsigned(context, sizeof(unsigned short),(unsigned char*)&data);\r
+}\r
+\r
+void stlplus::dump_int(stlplus::dump_context& context, const int& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  ::dump_signed(context, sizeof(int), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_int(restore_context& context, int& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  ::restore_signed(context, sizeof(int),(unsigned char*)&data);\r
+}\r
+\r
+void stlplus::dump_unsigned(stlplus::dump_context& context, const unsigned& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  ::dump_unsigned(context, sizeof(unsigned), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_unsigned(restore_context& context, unsigned& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  ::restore_unsigned(context, sizeof(unsigned),(unsigned char*)&data);\r
+}\r
+\r
+void stlplus::dump_long(stlplus::dump_context& context, const long& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  ::dump_signed(context, sizeof(long), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_long(restore_context& context, long& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  ::restore_signed(context, sizeof(long),(unsigned char*)&data);\r
+}\r
+\r
+void stlplus::dump_unsigned_long(stlplus::dump_context& context, const unsigned long& data) throw(stlplus::persistent_dump_failed)\r
+{\r
+  ::dump_unsigned(context, sizeof(unsigned long), (unsigned char*)&data);\r
+}\r
+\r
+void stlplus::restore_unsigned_long(restore_context& context, unsigned long& data) throw(stlplus::persistent_restore_failed)\r
+{\r
+  ::restore_unsigned(context, sizeof(unsigned long),(unsigned char*)&data);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/persistence/persistent_int.hpp b/src/stlplus/persistence/persistent_int.hpp
new file mode 100644 (file)
index 0000000..404d1c5
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef STLPLUS_PERSISTENT_INT\r
+#define STLPLUS_PERSISTENT_INT\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of basic integer types\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void dump_char(dump_context&, const char& data) throw(persistent_dump_failed);\r
+  void restore_char(restore_context&, char& data) throw(persistent_restore_failed);\r
+\r
+  void dump_signed_char(dump_context&, const signed char& data) throw(persistent_dump_failed);\r
+  void restore_signed_char(restore_context&, signed char& data) throw(persistent_restore_failed);\r
+\r
+  void dump_unsigned_char(dump_context&, const unsigned char& data) throw(persistent_dump_failed);\r
+  void restore_unsigned_char(restore_context&, unsigned char& data) throw(persistent_restore_failed);\r
+\r
+  void dump_short(dump_context&, const short& data) throw(persistent_dump_failed);\r
+  void restore_short(restore_context&, short& data) throw(persistent_restore_failed);\r
+\r
+  void dump_unsigned_short(dump_context&, const unsigned short& data) throw(persistent_dump_failed);\r
+  void restore_unsigned_short(restore_context&, unsigned short& data) throw(persistent_restore_failed);\r
+\r
+  void dump_int(dump_context&, const int& data) throw(persistent_dump_failed);\r
+  void restore_int(restore_context&, int& data) throw(persistent_restore_failed);\r
+\r
+  void dump_unsigned(dump_context&, const unsigned& data) throw(persistent_dump_failed);\r
+  void restore_unsigned(restore_context&, unsigned& data) throw(persistent_restore_failed);\r
+\r
+  void dump_long(dump_context&, const long& data) throw(persistent_dump_failed);\r
+  void restore_long(restore_context&, long& data) throw(persistent_restore_failed);\r
+\r
+  void dump_unsigned_long(dump_context&, const unsigned long& data) throw(persistent_dump_failed);\r
+  void restore_unsigned_long(restore_context&, unsigned long& data) throw(persistent_restore_failed);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_interface.hpp b/src/stlplus/persistence/persistent_interface.hpp
new file mode 100644 (file)
index 0000000..366954c
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef STLPLUS_PERSISTENT_INTERFACE\r
+#define STLPLUS_PERSISTENT_INTERFACE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence for pointers to polymorphic classes using the interface approach.\r
+\r
+//   This works on a set of classes derived from a common superclass called\r
+//   persistent which is declared as an interface. Each subclass has a set of\r
+//   methods that enable clone/dump/restore operations. Each subclass must be\r
+//   registered with the persistence dump/restore context so that the system\r
+//   knows how to dump it.\r
+\r
+//   This approach is suited to classes that can be modified to add persistence\r
+//   methods. See persistent_callback for a non-invasive way of handling\r
+//   polymorphism.\r
+\r
+//   Objects are always dumped/restored as pointers to the superclass T.\r
+\r
+//   Multiple pointers to the same object are handled in the same way as for\r
+//   simple pointers\r
+\r
+//   Only classes registered with the context can be dumped and restored as\r
+//   polymorphic types - see dump_context::register_interface and\r
+//   restore_context::register_interface. Attempting to use any unrecognised class\r
+//   will throw an exception.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "persistent.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T>\r
+  void dump_interface(dump_context&, const T* const data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_interface(restore_context&, T*& data)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_interface.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_interface.tpp b/src/stlplus/persistence/persistent_interface.tpp
new file mode 100644 (file)
index 0000000..a684aa3
--- /dev/null
@@ -0,0 +1,99 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Polymorphous classes using the interface approach\r
+\r
+//   format: magic [ key data ]\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T>\r
+  void dump_interface(dump_context& context, const T* const data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    try\r
+    {\r
+      // register the address and get the magic key for it\r
+      std::pair<bool,unsigned> mapping = context.pointer_map(data);\r
+      dump_unsigned(context,mapping.second);\r
+      // if the address is null, then that is all that we need to do\r
+      // however, if it is non-null and this is the first sight of the address, dump the contents\r
+      if (data && !mapping.first)\r
+      {\r
+        // interface method\r
+        // the lookup just finds the magic key and the type has a dump method\r
+        // this will throw persistent_illegal_type if the type is not registered\r
+        unsigned key = context.lookup_interface(typeid(*data));\r
+        // dump the magic key for the type\r
+        dump_unsigned(context, key);\r
+        // now call the dump method defined by the interface\r
+        data->dump(context);\r
+      }\r
+    }\r
+    catch (const persistent_illegal_type& except)\r
+    {\r
+      // convert this to a simpler dump failed exception\r
+      throw persistent_dump_failed(except.what());\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T>\r
+  void restore_interface(restore_context& context, T*& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    try\r
+    {\r
+      // first delete any previous object pointed to since the restore creates the object of the right subclass\r
+      if (data)\r
+      {\r
+        delete data;\r
+        data = 0;\r
+      }\r
+      // get the magic key\r
+      unsigned magic = 0;\r
+      restore_unsigned(context,magic);\r
+      // now lookup the magic key to see if this pointer has already been restored\r
+      // null pointers are always flagged as already restored\r
+      std::pair<bool,void*> address = context.pointer_map(magic);\r
+      if (address.first)\r
+      {\r
+        // seen before, so simply map it to the existing address\r
+        data = (T*)address.second;\r
+      }\r
+      else\r
+      {\r
+        // now restore the magic key that denotes the particular subclass\r
+        unsigned key = 0;\r
+        restore_unsigned(context, key);\r
+        // interface approach\r
+        // first clone the sample object stored in the map - lookup_interface can throw persistent_illegal_type\r
+        data = (T*)(context.lookup_interface(key)->clone());\r
+        // add this pointer to the set of already seen objects\r
+        // do this before restoring the object so that self-referential structures restore correctly\r
+        context.pointer_add(magic,data);\r
+        // now restore the contents using the object's method\r
+        data->restore(context);\r
+      }\r
+    }\r
+    catch (const persistent_illegal_type& exception)\r
+    {\r
+      // convert this to a simpler dump failed exception\r
+      throw persistent_restore_failed(exception.what());\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_list.hpp b/src/stlplus/persistence/persistent_list.hpp
new file mode 100644 (file)
index 0000000..b95d767
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef STLPLUS_PERSISTENT_LIST\r
+#define STLPLUS_PERSISTENT_LIST\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL list\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <list>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename D>\r
+  void dump_list(dump_context&, const std::list<T>& data, D dump_fn) throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename R>\r
+  void restore_list(restore_context&, std::list<T>& data, R restore_fn) throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_list.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_list.tpp b/src/stlplus/persistence/persistent_list.tpp
new file mode 100644 (file)
index 0000000..aaf696f
--- /dev/null
@@ -0,0 +1,41 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename D>\r
+  void dump_list(dump_context& context, const std::list<T>& data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (typename std::list<T>::const_iterator i = data.begin(); i != data.end(); i++)\r
+      dump_fn(context,*i);\r
+  }\r
+\r
+  template<typename T, typename R>\r
+  void restore_list(restore_context& context, std::list<T>& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.clear();\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    for (unsigned i = 0; i < size; i++)\r
+    {\r
+      data.push_back(T());\r
+      restore_fn(context,data.back());\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_map.hpp b/src/stlplus/persistence/persistent_map.hpp
new file mode 100644 (file)
index 0000000..4f9449f
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PERSISTENT_MAP\r
+#define STLPLUS_PERSISTENT_MAP\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence for STL map\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <map>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename P, typename DK, typename DT>\r
+  void dump_map(dump_context&, const std::map<K,T,P>& data, DK key_dump_fn, DT val_dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename K, typename T, typename P, typename RK, typename RT>\r
+  void restore_map(restore_context&, std::map<K,T,P>& data, RK key_restore_fn, RT val_restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_map.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_map.tpp b/src/stlplus/persistence/persistent_map.tpp
new file mode 100644 (file)
index 0000000..b1af42c
--- /dev/null
@@ -0,0 +1,45 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename K, typename T, typename P, typename DK, typename DT>\r
+  void dump_map(dump_context& context, const std::map<K,T,P>& data, DK key_fn, DT val_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (typename std::map<K,T,P>::const_iterator i = data.begin(); i != data.end(); i++)\r
+    {\r
+      key_fn(context,i->first);\r
+      val_fn(context,i->second);\r
+    }\r
+  }\r
+\r
+  template<typename K, typename T, typename P, typename RK, typename RT>\r
+  void restore_map(restore_context& context, std::map<K,T,P>& data, RK key_fn, RT val_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.clear();\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    for (unsigned j = 0; j < size; j++)\r
+    {\r
+      K key;\r
+      key_fn(context,key);\r
+      val_fn(context,data[key]);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_matrix.hpp b/src/stlplus/persistence/persistent_matrix.hpp
new file mode 100644 (file)
index 0000000..2f727a2
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PERSISTENT_MATRIX\r
+#define STLPLUS_PERSISTENT_MATRIX\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STLplus matrix\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "matrix.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename DT>\r
+  void dump_matrix(dump_context&, const matrix<T>& data, DT dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename RT>\r
+  void restore_matrix(restore_context&, matrix<T>& data, RT restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_matrix.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_matrix.tpp b/src/stlplus/persistence/persistent_matrix.tpp
new file mode 100644 (file)
index 0000000..ac81f85
--- /dev/null
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename DT>\r
+  void dump_matrix(dump_context& context, const matrix<T>& data,\r
+                   DT dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    unsigned rows = data.rows();\r
+    unsigned cols = data.columns();\r
+    dump_unsigned(context, rows);\r
+    dump_unsigned(context, cols);\r
+    for (unsigned r = 0; r < rows; r++)\r
+      for (unsigned c = 0; c < cols; c++)\r
+        dump_fn(context, data(r,c));\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename RT>\r
+  void restore_matrix(restore_context& context, matrix<T>& data,\r
+                      RT restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    unsigned rows = 0;\r
+    restore_unsigned(context, rows);\r
+    unsigned cols = 0;\r
+    restore_unsigned(context, cols);\r
+    data.resize(rows,cols);\r
+    for (unsigned r = 0; r < rows; r++)\r
+      for (unsigned c = 0; c < cols; c++)\r
+        restore_fn(context, data(r,c));\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_multimap.hpp b/src/stlplus/persistence/persistent_multimap.hpp
new file mode 100644 (file)
index 0000000..0ad0716
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PERSISTENT_MULTIMAP\r
+#define STLPLUS_PERSISTENT_MULTIMAP\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL multimap\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <map>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename P, typename DK, typename DT>\r
+  void dump_multimap(dump_context&, const std::multimap<K,T,P>& data, DK key_dump_fn, DT val_dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename K, typename T, typename P, typename RK, typename RT>\r
+  void restore_multimap(restore_context&, std::multimap<K,T,P>& data, RK key_restore_fn, RT val_restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_multimap.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_multimap.tpp b/src/stlplus/persistence/persistent_multimap.tpp
new file mode 100644 (file)
index 0000000..5f96a3d
--- /dev/null
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename K, typename T, typename P, typename DK, typename DT>\r
+  void dump_multimap(dump_context& context, const std::multimap<K,T,P>& data, DK key_fn, DT val_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (typename std::multimap<K,T,P>::const_iterator i = data.begin(); i != data.end(); i++)\r
+    {\r
+      key_fn(context,i->first);\r
+      val_fn(context,i->second);\r
+    }\r
+  }\r
+\r
+  template<typename K, typename T, typename P, typename RK, typename RT>\r
+  void restore_multimap(restore_context& context, std::multimap<K,T,P>& data, RK key_fn, RT val_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.clear();\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    for (unsigned j = 0; j < size; j++)\r
+    {\r
+      K key;\r
+      key_fn(context,key);\r
+      typename std::multimap<K,T,P>::iterator v = data.insert(std::pair<K,T>(key,T()));\r
+      val_fn(context,v->second);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_multiset.hpp b/src/stlplus/persistence/persistent_multiset.hpp
new file mode 100644 (file)
index 0000000..d451a8e
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PERSISTENT_MULTISET\r
+#define STLPLUS_PERSISTENT_MULTISET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL multiset\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <set>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename P, typename D>\r
+  void dump_multiset(dump_context&, const std::multiset<K,P>& data, D dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename K, typename P, typename R>\r
+  void restore_multiset(restore_context&, std::multiset<K,P>& data, R restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_multiset.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_multiset.tpp b/src/stlplus/persistence/persistent_multiset.tpp
new file mode 100644 (file)
index 0000000..0a31353
--- /dev/null
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename K, typename P, typename D>\r
+  void dump_multiset(dump_context& context, const std::multiset<K,P>& data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (typename std::multiset<K,P>::const_iterator i = data.begin(); i != data.end(); i++)\r
+      dump_fn(context,*i);\r
+  }\r
+\r
+  template<typename K, typename P, typename R>\r
+  void restore_multiset(restore_context& context, std::multiset<K,P>& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.clear();\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    typename std::multiset<K,P>::iterator i = data.begin();\r
+    for (unsigned j = 0; j < size; j++)\r
+    {\r
+      K key;\r
+      restore_fn(context,key);\r
+      // inserting using an iterator is O(n) rather than O(n*log(n))\r
+      i = data.insert(i, key);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_ntree.hpp b/src/stlplus/persistence/persistent_ntree.hpp
new file mode 100644 (file)
index 0000000..635da50
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef STLPLUS_PERSISTENT_NTREE\r
+#define STLPLUS_PERSISTENT_NTREE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STLplus ntree\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "ntree.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // ntree\r
+\r
+  template<typename T, typename D>\r
+  void dump_ntree(dump_context&, const ntree<T>& data, D dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename R>\r
+  void restore_ntree(restore_context&, ntree<T>& data, R restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+  // iterator\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void dump_ntree_iterator(dump_context&, const ntree_iterator<T,TRef,TPtr>&)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void restore_ntree_iterator(restore_context&, ntree_iterator<T,TRef,TPtr>&)\r
+    throw(persistent_restore_failed);\r
+\r
+  // prefix iterator\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void dump_ntree_prefix_iterator(dump_context&, const ntree_prefix_iterator<T,TRef,TPtr>&)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void restore_ntree_prefix_iterator(restore_context&, ntree_prefix_iterator<T,TRef,TPtr>&)\r
+    throw(persistent_restore_failed);\r
+\r
+  // postfix iterator\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void dump_ntree_postfix_iterator(dump_context&, const ntree_postfix_iterator<T,TRef,TPtr>&)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void restore_ntree_postfix_iterator(restore_context&, ntree_postfix_iterator<T,TRef,TPtr>&)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_ntree.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_ntree.tpp b/src/stlplus/persistence/persistent_ntree.tpp
new file mode 100644 (file)
index 0000000..d50ba17
--- /dev/null
@@ -0,0 +1,174 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_bool.hpp"\r
+#include "persistent_int.hpp"\r
+#include "persistent_xref.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename D>\r
+  void dump_ntree_r(dump_context& context,\r
+                    const ntree<T>& tree, \r
+                    const TYPENAME ntree<T>::const_iterator& node,\r
+                    D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // the magic key of the ntree_node is dumped as well as the contents - this is used in iterator persistence\r
+    std::pair<bool,unsigned> node_mapping = context.pointer_map(node.node());\r
+    if (node_mapping.first) throw persistent_dump_failed("ntree: already dumped this node");\r
+    dump_unsigned(context,node_mapping.second);\r
+    // now dump the contents\r
+    dump_fn(context,*node);\r
+    // dump the number of children\r
+    unsigned children = tree.children(node);\r
+    dump_unsigned(context,children);\r
+    // recurse on the children\r
+    for (unsigned i = 0; i < children; i++)\r
+      dump_ntree_r<T,D>(context,tree,tree.child(node,i),dump_fn);\r
+  }\r
+\r
+  template<typename T, typename D>\r
+  void dump_ntree(dump_context& context,\r
+                  const ntree<T>& tree,\r
+                  D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // dump a magic key to the address of the tree for use in persistence of iterators\r
+    // and register it as a dumped address\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(&tree);\r
+    if (mapping.first) throw persistent_dump_failed("ntree: already dumped this tree");\r
+    dump_unsigned(context,mapping.second);\r
+    // now dump the tree contents - start with a flag to indicate whether the tree is empty\r
+    dump_bool(context, tree.empty());\r
+    // now recursively dump the contents\r
+    if (!tree.empty())\r
+      dump_ntree_r<T,D>(context,tree,tree.root(),dump_fn);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename R>\r
+  void restore_ntree_r(restore_context& context,\r
+                       ntree<T>& tree,\r
+                       const TYPENAME ntree<T>::iterator& node,\r
+                       R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    // restore the node magic key, check whether it has been used before and add it to the set of known addresses\r
+    unsigned node_magic = 0;\r
+    restore_unsigned(context,node_magic);\r
+    std::pair<bool,void*> node_mapping = context.pointer_map(node_magic);\r
+    if (node_mapping.first) throw persistent_restore_failed("ntree: restored this tree node already");\r
+    context.pointer_add(node_magic,node.node());\r
+    // now restore the node contents\r
+    restore_fn(context,*node);\r
+    // restore the number of children\r
+    unsigned children = 0;\r
+    restore_unsigned(context,children);\r
+    // recurse on each child\r
+    for (unsigned i = 0; i < children; i++)\r
+    {\r
+      typename ntree<T>::iterator child = tree.insert(node,i,T());\r
+      restore_ntree_r<T,R>(context,tree,child,restore_fn);\r
+    }\r
+  }\r
+\r
+  template<typename T, typename R>\r
+  void restore_ntree(restore_context& context,\r
+                     ntree<T>& tree,\r
+                     R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    tree.erase();\r
+    // restore the tree's magic key and map it onto the tree's address\r
+    // this is used in the persistence of iterators\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    context.pointer_add(magic,&tree);\r
+    // now restore the contents\r
+    bool empty = true;\r
+    restore_bool(context, empty);\r
+    if (!empty)\r
+    {\r
+      typename ntree<T>::iterator node = tree.insert(T());\r
+      restore_ntree_r<T,R>(context,tree,node,restore_fn);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void dump_ntree_iterator(dump_context& context,\r
+                           const ntree_iterator<T,TRef,TPtr>& data) \r
+    throw(persistent_dump_failed)\r
+  {\r
+    data.assert_valid();\r
+    dump_xref(context,data.owner());\r
+    dump_xref(context,data.node());\r
+  }\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void restore_ntree_iterator(restore_context& context,\r
+                              ntree_iterator<T,TRef,TPtr>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    const ntree<T>* owner = 0;\r
+    ntree_node<T>* node = 0;\r
+    restore_xref(context,owner);\r
+    restore_xref(context,node);\r
+    data = ntree_iterator<T,TRef,TPtr>(node->m_master);\r
+    data.assert_valid(owner);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void dump_ntree_prefix_iterator(dump_context& context,\r
+                                  const ntree_prefix_iterator<T,TRef,TPtr>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_ntree_iterator(context,data.iterator());\r
+  }\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void restore_ntree_prefix_iterator(restore_context& context,\r
+                                     ntree_prefix_iterator<T,TRef,TPtr>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    ntree_iterator<T,TRef,TPtr> iterator;\r
+    restore_ntree_iterator(context,iterator);\r
+    data = ntree_prefix_iterator<T,TRef,TPtr>(iterator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void dump_ntree_postfix_iterator(dump_context& context,\r
+                                   const ntree_postfix_iterator<T,TRef,TPtr>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_ntree_iterator(context,data.iterator());\r
+  }\r
+\r
+  template<typename T, typename TRef, typename TPtr>\r
+  void restore_ntree_postfix_iterator(restore_context& context,\r
+                                      ntree_postfix_iterator<T,TRef,TPtr>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    ntree_iterator<T,TRef,TPtr> iterator;\r
+    restore_ntree_iterator(context,iterator);\r
+    data = ntree_postfix_iterator<T,TRef,TPtr>(iterator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_pair.hpp b/src/stlplus/persistence/persistent_pair.hpp
new file mode 100644 (file)
index 0000000..7d23107
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PERSISTENT_PAIR\r
+#define STLPLUS_PERSISTENT_PAIR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL pair\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <map>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename V1, typename V2, typename D1, typename D2>\r
+  void dump_pair(dump_context&, const std::pair<V1,V2>& data, D1 dump_fn1, D2 dump_fn2)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename V1, typename V2, typename R1, typename R2>\r
+  void restore_pair(restore_context&, std::pair<V1,V2>& data, R1 restore_fn1, R2 restore_fn2)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_pair.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_pair.tpp b/src/stlplus/persistence/persistent_pair.tpp
new file mode 100644 (file)
index 0000000..aa118cf
--- /dev/null
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_pair.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename K, typename T, typename D1, typename D2>\r
+  void dump_pair(dump_context& context, const std::pair<K,T>& data, D1 dump_fn1, D2 dump_fn2)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_fn1(context,data.first);\r
+    dump_fn2(context,data.second);\r
+  }\r
+\r
+  template<typename K, typename T, typename R1, typename R2>\r
+  void restore_pair(restore_context& context, std::pair<K,T>& data, R1 restore_fn1, R2 restore_fn2)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    restore_fn1(context,data.first);\r
+    restore_fn2(context,data.second);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_pointer.hpp b/src/stlplus/persistence/persistent_pointer.hpp
new file mode 100644 (file)
index 0000000..02d0951
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef STLPLUS_PERSISTENT_POINTER\r
+#define STLPLUS_PERSISTENT_POINTER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence for pointers to persistent objects\r
+\r
+//   Warning! The pointer must be a dynamically-allocated type, since the\r
+//   implementation uses new/delete\r
+\r
+//   Multiple pointers to the same object *will* be restored as multiple pointers\r
+//   to the same object. The object is dumped only the first time it is\r
+//   encountered along with a "magic key". Subsequent pointers to the same object\r
+//   cause only the magic key to be dumped. On restore, the object is only\r
+//   restored once and the magic keys are matched up so that the other pointers\r
+//   now point to the restored object.\r
+\r
+//   Supports null pointers too! If the data field to restore is null and the\r
+//   file format non-null, allocates a new T(). If the data field is non-null and\r
+//   the file format is null, deletes it and sets it null\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename D>\r
+  void dump_pointer(dump_context&, const T* const data, D dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename R>\r
+  void restore_pointer(restore_context&, T*& data, R restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_pointer.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_pointer.tpp b/src/stlplus/persistence/persistent_pointer.tpp
new file mode 100644 (file)
index 0000000..c760b17
--- /dev/null
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   format: magic_key [ data ]\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename D>\r
+  void dump_pointer(dump_context& context, const T* const data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // register the address and get the magic key for it\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data);\r
+    dump_unsigned(context,mapping.second);\r
+    // if the address is null, then that is all that we need to do\r
+    // however, if it is non-null and this is the first sight of the address, dump the contents\r
+    // note that the address is mapped before it is dumped so that self-referential structures dump correctly\r
+    if (data && !mapping.first)\r
+      dump_fn(context,*data);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename R>\r
+  void restore_pointer(restore_context& context, T*& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    if (data)\r
+    {\r
+      delete data;\r
+      data = 0;\r
+    }\r
+    // get the magic key\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    // now lookup the magic key to see if this pointer has already been restored\r
+    // null pointers are always flagged as already restored\r
+    std::pair<bool,void*> address = context.pointer_map(magic);\r
+    if (address.first)\r
+    {\r
+      // seen before, so simply assign the old address\r
+      data = (T*)address.second;\r
+    }\r
+    else\r
+    {\r
+      // this pointer has never been seen before and is non-null\r
+      data = new T();\r
+      // add this pointer to the set of already seen objects\r
+      // do this before restoring the object so that self-referential structures restore correctly\r
+      context.pointer_add(magic,data);\r
+      // now restore it\r
+      restore_fn(context,*data);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_pointers.hpp b/src/stlplus/persistence/persistent_pointers.hpp
new file mode 100644 (file)
index 0000000..7e84759
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef STLPLUS_PERSISTENT_POINTERS\r
+#define STLPLUS_PERSISTENT_POINTERS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of all pointer types\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistent_pointer.hpp"\r
+#include "persistent_xref.hpp"\r
+#include "persistent_callback.hpp"\r
+#include "persistent_interface.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_set.hpp b/src/stlplus/persistence/persistent_set.hpp
new file mode 100644 (file)
index 0000000..a2f8c7a
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PERSISTENT_SET\r
+#define STLPLUS_PERSISTENT_SET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Set of persistence routines for the STL classes\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <set>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename P, typename D>\r
+  void dump_set(dump_context&, const std::set<K,P>& data, D dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename K, typename P, typename R>\r
+  void restore_set(restore_context&, std::set<K,P>& data, R restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_set.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_set.tpp b/src/stlplus/persistence/persistent_set.tpp
new file mode 100644 (file)
index 0000000..007d068
--- /dev/null
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename K, typename P, typename D>\r
+  void dump_set(dump_context& context, const std::set<K,P>& data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (typename std::set<K,P>::const_iterator i = data.begin(); i != data.end(); i++)\r
+      dump_fn(context,*i);\r
+  }\r
+\r
+  template<typename K, typename P, typename R>\r
+  void restore_set(restore_context& context, std::set<K,P>& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.clear();\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    typename std::set<K,P>::iterator i = data.begin();\r
+    for (unsigned j = 0; j < size; j++)\r
+    {\r
+      K key;\r
+      restore_fn(context,key);\r
+      // inserting using an iterator is O(n) rather than O(n*log(n))\r
+      i = data.insert(i, key);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_shortcuts.hpp b/src/stlplus/persistence/persistent_shortcuts.hpp
new file mode 100644 (file)
index 0000000..beb54d0
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef STLPLUS_PERSISTENT_SHORTCUTS\r
+#define STLPLUS_PERSISTENT_SHORTCUTS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Short-cut functions for dumping and restoring to common targets. These do\r
+//   the whole dump operation in a single function call.\r
+\r
+//   They take as their second template argument a dump or restore functor which\r
+//   is then called to perform the dump/restore operation.\r
+\r
+//   They use an installer callback function to install any polymorphic type\r
+//   handlers required prior to performing the dump/restore. If there are no\r
+//   polymorphic types used in the data structure, then the callback can be set\r
+//   to null (i.e. 0).\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // arbitrary IOStream device\r
+  // must be in binary mode\r
+\r
+  template<typename T, class D>\r
+  void dump_to_device(const T& source, std::ostream& result, D dump_fn, dump_context::installer installer)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, class R>\r
+  void restore_from_device(std::istream& source, T& result, R restore_fn, restore_context::installer installer)\r
+    throw(persistent_restore_failed);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // string IO device\r
+\r
+  template<typename T, class D>\r
+  void dump_to_string(const T& source, std::string& result, D dump_fn, dump_context::installer installer)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, class R>\r
+  void restore_from_string(const std::string& source, T& result, R restore_fn, restore_context::installer installer)\r
+    throw(persistent_restore_failed);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // file IO device\r
+\r
+  template<typename T, class D>\r
+  void dump_to_file(const T& source, const std::string& filename, D dump_fn, dump_context::installer installer)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, class R>\r
+  void restore_from_file(const std::string& filename, T& result, R restore_fn, restore_context::installer installer)\r
+    throw(persistent_restore_failed);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_shortcuts.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_shortcuts.tpp b/src/stlplus/persistence/persistent_shortcuts.tpp
new file mode 100644 (file)
index 0000000..9b5aac5
--- /dev/null
@@ -0,0 +1,80 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include <sstream>\r
+#include <fstream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, class D>\r
+  void dump_to_device(const T& source, std::ostream& result, D dump_fn, \r
+                      dump_context::installer installer)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_context context(result);\r
+    context.register_all(installer);\r
+    dump_fn(context, source);\r
+  }\r
+\r
+  template<typename T, class R>\r
+  void restore_from_device(std::istream& source, T& result, R restore_fn,\r
+                           restore_context::installer installer)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    restore_context context(source);\r
+    context.register_all(installer);\r
+    restore_fn(context, result);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, class D>\r
+  void dump_to_string(const T& source, std::string& result, D dump_fn, \r
+                      dump_context::installer installer)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    std::ostringstream output(std::ios_base::out | std::ios_base::binary);\r
+    dump_to_device<T,D>(source, output, dump_fn, installer);\r
+    result = output.str();\r
+  }\r
+\r
+  template<typename T, class R>\r
+  void restore_from_string(const std::string& source, T& result, R restore_fn, \r
+                           restore_context::installer installer)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    std::istringstream input(source, std::ios_base::in | std::ios_base::binary);\r
+    restore_from_device<T,R>(input, result, restore_fn, installer);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, class D>\r
+  void dump_to_file(const T& source, const std::string& filename, D dump_fn,\r
+                    dump_context::installer installer)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    std::ofstream output(filename.c_str(), std::ios_base::out | std::ios_base::binary);\r
+    dump_to_device<T,D>(source, output, dump_fn, installer);\r
+  }\r
+\r
+  template<typename T, class R>\r
+  void restore_from_file(const std::string& filename, T& result, R restore_fn,\r
+                         restore_context::installer installer)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    std::ifstream input(filename.c_str(), std::ios_base::in | std::ios_base::binary);\r
+    restore_from_device<T,R>(input, result, restore_fn, installer);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_simple_ptr.hpp b/src/stlplus/persistence/persistent_simple_ptr.hpp
new file mode 100644 (file)
index 0000000..ccf4260
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef STLPLUS_PERSISTENT_SIMPLE_PTR\r
+#define STLPLUS_PERSISTENT_SIMPLE_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STLplus simple_ptr\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "simple_ptr.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // simple_ptr - uses dump/restore_pointer on the contents\r
+\r
+  template<typename T, typename DE>\r
+  void dump_simple_ptr(dump_context&, const simple_ptr<T>& data, DE dump_element)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename RE>\r
+  void restore_simple_ptr(restore_context&, simple_ptr<T>& data, RE restore_element)\r
+    throw(persistent_restore_failed);\r
+\r
+  // simple_ptr_clone using the polymorphic callback approach - uses dump/restore_callback on the contents\r
+\r
+  template<typename T>\r
+  void dump_simple_ptr_clone_callback(dump_context&, const simple_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_simple_ptr_clone_callback(restore_context&, simple_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed);\r
+\r
+  // simple_ptr_clone using the interface approach - uses dump/restore_interface on the contents\r
+\r
+  template<typename T>\r
+  void dump_simple_ptr_clone_interface(dump_context&, const simple_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_simple_ptr_clone_interface(restore_context&, simple_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed);\r
+\r
+  // simple_ptr_nocopy is not made persistent because if it is uncopyable, it must be undumpable\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_simple_ptr.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_simple_ptr.tpp b/src/stlplus/persistence/persistent_simple_ptr.tpp
new file mode 100644 (file)
index 0000000..36f88bd
--- /dev/null
@@ -0,0 +1,144 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+#include "persistent_pointer.hpp"\r
+#include "persistent_callback.hpp"\r
+#include "persistent_interface.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename  T, typename DE>\r
+  void dump_simple_ptr(dump_context& context, const simple_ptr<T>& data,\r
+                      DE dump_element)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // Many smart pointers can point to the same object.\r
+    // I could have used the address of the object to differentiate, \r
+    // but that would not have differentiated between different null smart pointers\r
+    // so I use the address of the count to differentiate between different objects.\r
+    // get a magic key for the substructure - this also returns a flag saying whether its been seen before\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data._count());\r
+    // dump the magic key for the count\r
+    dump_unsigned(context,mapping.second);\r
+    // dump the contents always - this is because I need to rely on the pointer routines dumping a second magic key\r
+    // use the existing routines for ordinary pointers to dump the contents\r
+    dump_pointer(context,data._pointer(),dump_element);\r
+  }\r
+\r
+  template<typename T, typename RE>\r
+  void restore_simple_ptr(restore_context& context, simple_ptr<T>& data,\r
+                         RE restore_element)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    // get the old counter magic key\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    // lookup this magic number to see if we have seen this already\r
+    std::pair<bool,void*> mapping = context.pointer_map(magic);\r
+    if (mapping.first)\r
+    {\r
+      // this holder has already been restored\r
+      // now restore the object and rely on the pointer routines to return the existing object\r
+      T* value = 0;\r
+      restore_pointer(context,value,restore_element);\r
+      // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it\r
+      data._make_alias(value, (unsigned*)mapping.second);\r
+    }\r
+    else\r
+    {\r
+      // this is the first contact with this holder\r
+      // make sure this smart pointer is unique to prevent side-effects\r
+      data.clear_unique();\r
+      // map the magic key onto this structure's holder\r
+      // do this before restoring the object so that self-referential structures restore correctly\r
+      context.pointer_add(magic,data._count());\r
+      // now restore the object\r
+      T* value = 0;\r
+      restore_pointer(context,value,restore_element);\r
+      // and add it to the pointer\r
+      data.set(value);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // simple_ptr_clone using callbacks\r
+\r
+  template<typename T>\r
+  void dump_simple_ptr_clone_callback(dump_context& context, const simple_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data._count());\r
+    dump_unsigned(context,mapping.second);\r
+    dump_callback(context,data._pointer());\r
+  }\r
+\r
+  template<typename T>\r
+  void restore_simple_ptr_clone_callback(restore_context& context, simple_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    std::pair<bool,void*> mapping = context.pointer_map(magic);\r
+    if (mapping.first)\r
+    {\r
+      T* value = 0;\r
+      restore_callback(context,value);\r
+      data._make_alias(value, (unsigned*)mapping.second);\r
+    }\r
+    else\r
+    {\r
+      data.clear_unique();\r
+      context.pointer_add(magic,data._count());\r
+      T* value = 0;\r
+      restore_callback(context,value);\r
+      data.set(value);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // simple_ptr_clone using interface\r
+\r
+  template<typename T>\r
+  void dump_simple_ptr_clone_interface(dump_context& context, const simple_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data._count());\r
+    dump_unsigned(context,mapping.second);\r
+    dump_interface(context,data._pointer());\r
+  }\r
+\r
+  template<typename T>\r
+  void restore_simple_ptr_clone_interface(restore_context& context, simple_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    std::pair<bool,void*> mapping = context.pointer_map(magic);\r
+    if (mapping.first)\r
+    {\r
+      T* value = 0;\r
+      restore_interface(context,value);\r
+      data._make_alias(value, (unsigned*)mapping.second);\r
+    }\r
+    else\r
+    {\r
+      data.clear_unique();\r
+      context.pointer_add(magic,data._count());\r
+      T* value = 0;\r
+      restore_interface(context,value);\r
+      data.set(value);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_smart_ptr.hpp b/src/stlplus/persistence/persistent_smart_ptr.hpp
new file mode 100644 (file)
index 0000000..5f37725
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef STLPLUS_PERSISTENT_SMART_PTR\r
+#define STLPLUS_PERSISTENT_SMART_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STLplus smart_ptr\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "smart_ptr.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // smart_ptr - uses dump/restore_pointer on the contents\r
+\r
+  template<typename T, typename DE>\r
+  void dump_smart_ptr(dump_context&, const smart_ptr<T>& data, DE dump_element)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename RE>\r
+  void restore_smart_ptr(restore_context&, smart_ptr<T>& data, RE restore_element)\r
+    throw(persistent_restore_failed);\r
+\r
+  // smart_ptr_clone using the polymorphic callback approach - uses dump/restore_callback on the contents\r
+\r
+  template<typename T>\r
+  void dump_smart_ptr_clone_callback(dump_context&, const smart_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_smart_ptr_clone_callback(restore_context&, smart_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed);\r
+\r
+  // smart_ptr_clone using the interface approach - uses dump/restore_interface on the contents\r
+\r
+  template<typename T>\r
+  void dump_smart_ptr_clone_interface(dump_context&, const smart_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_smart_ptr_clone_interface(restore_context&, smart_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed);\r
+\r
+  // smart_ptr_nocopy is not made persistent because if it is uncopyable, it must be undumpable\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_smart_ptr.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_smart_ptr.tpp b/src/stlplus/persistence/persistent_smart_ptr.tpp
new file mode 100644 (file)
index 0000000..2ce5315
--- /dev/null
@@ -0,0 +1,176 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+#include "persistent_pointer.hpp"\r
+#include "persistent_callback.hpp"\r
+#include "persistent_interface.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename  T, typename DE>\r
+  void dump_smart_ptr(dump_context& context, const smart_ptr<T>& data,\r
+                      DE dump_element)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases\r
+    // Many smart pointers can point to the same object.\r
+    // I could have used the address of the object to differentiate, \r
+    // but that would not have differentiated between different null smart pointers\r
+    // so I use the address of the substructure to differentiate between different objects.\r
+    // get a magic key for the substructure - this also returns a flag saying whether its been seen before\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data._handle());\r
+    // dump the magic key\r
+    dump_unsigned(context,mapping.second);\r
+    // dump the contents but only if this is the first time this object has been seen\r
+    // use the existing routines for ordinary pointers to dump the contents\r
+    if (!mapping.first)\r
+      dump_pointer(context,data.pointer(),dump_element);\r
+  }\r
+\r
+  template<typename T, typename RE>\r
+  void restore_smart_ptr(restore_context& context, smart_ptr<T>& data,\r
+                         RE restore_element)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    // get the old substructure magic key\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    // lookup this magic number to see if we have seen this already\r
+    std::pair<bool,void*> mapping = context.pointer_map(magic);\r
+    if (mapping.first)\r
+    {\r
+      // this holder has already been restored\r
+      // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it\r
+      data._make_alias((smart_ptr_holder<T>*)mapping.second);\r
+    }\r
+    else\r
+    {\r
+      // this is the first contact with this holder\r
+      // make sure this smart pointer is unique to prevent side-effects\r
+      data.clear_unique();\r
+      // map the magic key onto this structure's holder\r
+      // do this before restoring the object so that self-referential structures restore correctly\r
+      context.pointer_add(magic,data._handle());\r
+      // now restore the object\r
+      T* value = 0;\r
+      restore_pointer(context,value,restore_element);\r
+      data.set(value);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // smart_ptr_clone using callbacks\r
+\r
+  template<typename T>\r
+  void dump_smart_ptr_clone_callback(dump_context& context, const smart_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases\r
+    // Many smart pointers can point to the same object.\r
+    // I could have used the address of the object to differentiate, \r
+    // but that would not have differentiated between different null smart pointers\r
+    // so I use the address of the substructure to differentiate between different objects.\r
+    // get a magic key for the substructure - this also returns a flag saying whether its been seen before\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data._handle());\r
+    // dump the magic key\r
+    dump_unsigned(context,mapping.second);\r
+    // dump the contents but only if this is the first time this object has been seen\r
+    // use the existing routines for ordinary pointers to dump the contents\r
+    if (!mapping.first)\r
+      dump_callback(context,data.pointer());\r
+  }\r
+\r
+  template<typename T>\r
+  void restore_smart_ptr_clone_callback(restore_context& context, smart_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    // get the old substructure magic key\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    // lookup this magic number to see if we have seen this already\r
+    std::pair<bool,void*> mapping = context.pointer_map(magic);\r
+    if (mapping.first)\r
+    {\r
+      // this holder has already been restored\r
+      // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it\r
+      data._make_alias((smart_ptr_holder<T>*)mapping.second);\r
+    }\r
+    else\r
+    {\r
+      // this is the first contact with this holder\r
+      // make sure this smart pointer is unique to prevent side-effects\r
+      data.clear_unique();\r
+      // map the magic key onto this structure's holder\r
+      // do this before restoring the object so that self-referential structures restore correctly\r
+      context.pointer_add(magic,data._handle());\r
+      // now restore the object\r
+      T* value = 0;\r
+      restore_callback(context,value);\r
+      data.set(value);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // smart_ptr_clone using interface\r
+\r
+  template<typename T>\r
+  void dump_smart_ptr_clone_interface(dump_context& context, const smart_ptr_clone<T>& data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases\r
+    // Many smart pointers can point to the same object.\r
+    // I could have used the address of the object to differentiate, \r
+    // but that would not have differentiated between different null smart pointers\r
+    // so I use the address of the substructure to differentiate between different objects.\r
+    // get a magic key for the substructure - this also returns a flag saying whether its been seen before\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data._handle());\r
+    // dump the magic key\r
+    dump_unsigned(context,mapping.second);\r
+    // dump the contents but only if this is the first time this object has been seen\r
+    // use the existing routines for ordinary pointers to dump the contents\r
+    if (!mapping.first)\r
+      dump_interface(context,data.pointer());\r
+  }\r
+\r
+  template<typename T>\r
+  void restore_smart_ptr_clone_interface(restore_context& context, smart_ptr_clone<T>& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    // get the old substructure magic key\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    // lookup this magic number to see if we have seen this already\r
+    std::pair<bool,void*> mapping = context.pointer_map(magic);\r
+    if (mapping.first)\r
+    {\r
+      // this holder has already been restored\r
+      // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it\r
+      data._make_alias((smart_ptr_holder<T>*)mapping.second);\r
+    }\r
+    else\r
+    {\r
+      // this is the first contact with this holder\r
+      // make sure this smart pointer is unique to prevent side-effects\r
+      data.clear_unique();\r
+      // map the magic key onto this structure's holder\r
+      // do this before restoring the object so that self-referential structures restore correctly\r
+      context.pointer_add(magic,data._handle());\r
+      // now restore the object\r
+      T* value = 0;\r
+      restore_interface(context,value);\r
+      data.set(value);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_stl.hpp b/src/stlplus/persistence/persistent_stl.hpp
new file mode 100644 (file)
index 0000000..bc2f32b
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef STLPLUS_PERSISTENT_STL\r
+#define STLPLUS_PERSISTENT_STL\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Set of persistence routines for the STL classes\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "persistent_bitset.hpp"\r
+#include "persistent_complex.hpp"\r
+#include "persistent_deque.hpp"\r
+#include "persistent_list.hpp"\r
+#include "persistent_map.hpp"\r
+#include "persistent_multimap.hpp"\r
+#include "persistent_multiset.hpp"\r
+#include "persistent_pair.hpp"\r
+#include "persistent_set.hpp"\r
+#include "persistent_string.hpp"\r
+#include "persistent_vector.hpp"\r
+\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_stlplus.hpp b/src/stlplus/persistence/persistent_stlplus.hpp
new file mode 100644 (file)
index 0000000..a3cb476
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef STLPLUS_PERSISTENT_STLPLUS\r
+#define STLPLUS_PERSISTENT_STLPLUS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Set of persistence routines for the STLplus classes\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// can be excluded to break the dependency on the containers library\r
+#ifndef NO_STLPLUS_CONTAINERS\r
+#include "persistent_digraph.hpp"\r
+#include "persistent_foursome.hpp"\r
+#include "persistent_hash.hpp"\r
+#include "persistent_matrix.hpp"\r
+#include "persistent_ntree.hpp"\r
+#include "persistent_smart_ptr.hpp"\r
+#include "persistent_triple.hpp"\r
+#endif\r
+\r
+// can be excluded to break the dependency on the portability library\r
+#ifndef NO_STLPLUS_INF\r
+#include "persistent_inf.hpp"\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_string.cpp b/src/stlplus/persistence/persistent_string.cpp
new file mode 100644 (file)
index 0000000..80940d1
--- /dev/null
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_string.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+void stlplus::dump_string(stlplus::dump_context& context, const std::string& data)\r
+  throw(stlplus::persistent_dump_failed)\r
+{\r
+  stlplus::dump_basic_string(context, data, stlplus::dump_char);\r
+}\r
+\r
+void stlplus::restore_string(stlplus::restore_context& context, std::string& data)\r
+  throw(stlplus::persistent_restore_failed)\r
+{\r
+  stlplus::restore_basic_string(context, data, stlplus::restore_char);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/persistence/persistent_string.hpp b/src/stlplus/persistence/persistent_string.hpp
new file mode 100644 (file)
index 0000000..ff065c7
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef STLPLUS_PERSISTENT_STRING\r
+#define STLPLUS_PERSISTENT_STRING\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence for STL strings\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // basic_string\r
+\r
+  template<typename charT, typename traits, typename allocator, typename D>\r
+  void dump_basic_string(dump_context&, const std::basic_string<charT,traits,allocator>& data, D dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename charT, typename traits, typename allocator, typename R>\r
+  void restore_basic_string(restore_context&, std::basic_string<charT,traits,allocator>& data, R restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+  // string\r
+\r
+  void dump_string(dump_context&, const std::string& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  void restore_string(restore_context&, std::string& data) \r
+    throw(persistent_restore_failed);\r
+\r
+\r
+  // Note: persistence of wstring not supported because it is too weakly defined and messy\r
+  //       decide on a byte-wide encoding of wide strings (e.g. UTF8) and use the string persistence on that\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_string.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_string.tpp b/src/stlplus/persistence/persistent_string.tpp
new file mode 100644 (file)
index 0000000..c78f7e8
--- /dev/null
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // STL strings\r
+\r
+  template<typename charT, typename traits, typename allocator, typename D>\r
+  void dump_basic_string(dump_context& context, const std::basic_string<charT,traits,allocator>& data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    unsigned size = data.size();\r
+    dump_unsigned(context, size);\r
+    for (unsigned i = 0; i < size; i++)\r
+    {\r
+      charT ch = data[i];\r
+      dump_fn(context,ch);\r
+    }\r
+  }\r
+\r
+  template<typename charT, typename traits, typename allocator, typename R>\r
+  void restore_basic_string(restore_context& context, std::basic_string<charT,traits,allocator>& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    data.erase();\r
+    unsigned size = 0;\r
+    restore_unsigned(context, size);\r
+    for (unsigned i = 0; i < size; i++)\r
+    {\r
+      charT ch;\r
+      restore_fn(context,ch);\r
+      data += ch;\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_triple.hpp b/src/stlplus/persistence/persistent_triple.hpp
new file mode 100644 (file)
index 0000000..d9e30d6
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef STLPLUS_PERSISTENT_TRIPLE\r
+#define STLPLUS_PERSISTENT_TRIPLE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL triple\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include "triple.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename D1, typename D2, typename D3>\r
+  void dump_triple(dump_context&, const stlplus::triple<T1,T2,T3>& data, \r
+                   D1 dump_fn1, D2 dump_fn2, D3 dump_fn3)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T1, typename T2, typename T3, typename R1, typename R2, typename R3>\r
+  void restore_triple(restore_context&, stlplus::triple<T1,T2,T3>& data,\r
+                      R1 restore_fn1, R2 restore_fn2, R3 restore_fn3)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_triple.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_triple.tpp b/src/stlplus/persistence/persistent_triple.tpp
new file mode 100644 (file)
index 0000000..9c3c978
--- /dev/null
@@ -0,0 +1,37 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T1, typename T2, typename T3, typename D1, typename D2, typename D3>\r
+  void dump_triple(dump_context& context, const triple<T1,T2,T3>& data, \r
+                   D1 dump_fn1, D2 dump_fn2, D3 dump_fn3)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_fn1(context,data.first);\r
+    dump_fn2(context,data.second);\r
+    dump_fn3(context,data.third);\r
+  }\r
+\r
+  template<typename T1, typename T2, typename T3, typename R1, typename R2, typename R3>\r
+  void restore_triple(restore_context& context, triple<T1,T2,T3>& data,\r
+                      R1 restore_fn1, R2 restore_fn2, R3 restore_fn3)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    restore_fn1(context,data.first);\r
+    restore_fn2(context,data.second);\r
+    restore_fn3(context,data.third);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_vector.cpp b/src/stlplus/persistence/persistent_vector.cpp
new file mode 100644 (file)
index 0000000..b983d37
--- /dev/null
@@ -0,0 +1,56 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_vector.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// specialisation for a vector of bool which has a different implementation to a vector of anything else\r
+\r
+void stlplus::dump_vector_bool(stlplus::dump_context& context, const std::vector<bool>& data)\r
+  throw(stlplus::persistent_dump_failed)\r
+{\r
+  stlplus::dump_unsigned(context,data.size());\r
+  unsigned size = data.size();\r
+  unsigned bytes = ((size + 7) / 8);\r
+  for (unsigned b = 0; b < bytes; b++)\r
+  {\r
+    unsigned char byte = 0;\r
+    unsigned char mask = 1;\r
+    for (unsigned e = 0; e < 8; e++)\r
+    {\r
+      unsigned i = b*8 + e;\r
+      if (i >= size) break;\r
+      if (data[i]) byte |= mask;\r
+      mask <<= 1;\r
+    }\r
+    context.put(byte);\r
+  }\r
+}\r
+\r
+void stlplus::restore_vector_bool(stlplus::restore_context& context, std::vector<bool>& data)\r
+  throw(stlplus::persistent_restore_failed)\r
+{\r
+  unsigned size = 0;\r
+  stlplus::restore_unsigned(context,size);\r
+  data.resize(size);\r
+  unsigned bytes = ((size + 7) / 8);\r
+  for (unsigned b = 0; b < bytes; b++)\r
+  {\r
+    unsigned char byte = context.get();\r
+    unsigned char mask = 1;\r
+    for (unsigned e = 0; e < 8; e++)\r
+    {\r
+      unsigned i = b*8 + e;\r
+      if (i >= size) break;\r
+      data[i] = ((byte & mask) != 0);\r
+      mask <<= 1;\r
+    }\r
+  }\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/persistence/persistent_vector.hpp b/src/stlplus/persistence/persistent_vector.hpp
new file mode 100644 (file)
index 0000000..fd108aa
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef STLPLUS_PERSISTENT_VECTOR\r
+#define STLPLUS_PERSISTENT_VECTOR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence of STL vector\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+#include <vector>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // vector\r
+\r
+  template<typename T, typename D>\r
+  void dump_vector(dump_context&, const std::vector<T>& data, D dump_fn)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T, typename R>\r
+  void restore_vector(restore_context&, std::vector<T>& data, R restore_fn)\r
+    throw(persistent_restore_failed);\r
+\r
+  // specialism for vector<bool>\r
+\r
+  void dump_vector_bool(dump_context&, const std::vector<bool>& data)\r
+    throw(persistent_dump_failed);\r
+\r
+  void restore_vector_bool(restore_context&, std::vector<bool>& data)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_vector.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_vector.tpp b/src/stlplus/persistence/persistent_vector.tpp
new file mode 100644 (file)
index 0000000..4ad69b6
--- /dev/null
@@ -0,0 +1,39 @@
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename D>\r
+  void dump_vector(dump_context& context, const std::vector<T>& data, D dump_fn)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    dump_unsigned(context,data.size());\r
+    for (unsigned i = 0; i < data.size(); i++)\r
+      dump_fn(context,data[i]);\r
+  }\r
+\r
+  template<typename T, typename R>\r
+  void restore_vector(restore_context& context, std::vector<T>& data, R restore_fn)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    unsigned size = 0;\r
+    restore_unsigned(context,size);\r
+    data.resize(size);\r
+    for (unsigned i = 0; i < size; i++)\r
+      restore_fn(context,data[i]);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/persistence/persistent_xref.hpp b/src/stlplus/persistence/persistent_xref.hpp
new file mode 100644 (file)
index 0000000..c41e28d
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef STLPLUS_PERSISTENT_XREF\r
+#define STLPLUS_PERSISTENT_XREF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Persistence for cross-references to persistent objects\r
+\r
+//   A cross-reference is a pointer to an object that has definitely been dumped\r
+//   already by one of dump_pointer, dump_interface or dump_callback, i.e. by\r
+//   one of the dump routines for pointers to objects.\r
+\r
+//   These are typically used in data structures as back-pointers or pointers\r
+//   between nodes.\r
+\r
+//   For example, you may have a tree with cross links. Dump the tree as the\r
+//   primary data structure first, then dump the cross links as cross-references\r
+//   afterwards. The whole tree must be dumped before any cross-references to\r
+//   ensure that all cross-references are known to the persistence system.\r
+\r
+//   These functions will throw an exception if the cross-reference points to\r
+//   something not dumped before.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistence_fixes.hpp"\r
+#include "persistent_contexts.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T>\r
+  void dump_xref(dump_context&, const T* const data)\r
+    throw(persistent_dump_failed);\r
+\r
+  template<typename T>\r
+  void restore_xref(restore_context&, T*& data)\r
+    throw(persistent_restore_failed);\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_xref.tpp"\r
+#endif\r
diff --git a/src/stlplus/persistence/persistent_xref.tpp b/src/stlplus/persistence/persistent_xref.tpp
new file mode 100644 (file)
index 0000000..e12639d
--- /dev/null
@@ -0,0 +1,51 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   format: magic_key [ data ]\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "persistent_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T>\r
+  void dump_xref(dump_context& context, const T* const data)\r
+    throw(persistent_dump_failed)\r
+  {\r
+    // register the address and get the magic key for it\r
+    std::pair<bool,unsigned> mapping = context.pointer_map(data);\r
+    // if this is the first view of this pointer, simply throw an exception\r
+    if (!mapping.first) throw persistent_dump_failed("tried to dump a cross-reference not seen before");\r
+    // otherwise, just dump the magic key\r
+    dump_unsigned(context,mapping.second);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////>\r
+\r
+  template<typename T>\r
+  void restore_xref(restore_context& context, T*& data)\r
+    throw(persistent_restore_failed)\r
+  {\r
+    // Note: I do not try to delete the old data because this is a cross-reference\r
+    // get the magic key\r
+    unsigned magic = 0;\r
+    restore_unsigned(context,magic);\r
+    // now lookup the magic key to see if this pointer has already been restored\r
+    // null pointers are always flagged as already restored\r
+    std::pair<bool,void*> address = context.pointer_map(magic);\r
+    // if this is the first view of this pointer, simply throw an exception\r
+    if (!address.first) throw persistent_restore_failed("tried to restore a cross-reference not seen before");\r
+    // seen before, so simply assign the old address\r
+    data = (T*)address.second;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/build.cpp b/src/stlplus/portability/build.cpp
new file mode 100644 (file)
index 0000000..6a9cd64
--- /dev/null
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "build.hpp"\r
+#include "version.hpp"\r
+#include "dprintf.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // report the platform-specific details of this build\r
+\r
+  std::string build(void)\r
+  {\r
+    ////////////////////////////////////////////////////////////////////////////////\r
+    // work out the platform\r
+\r
+#ifdef _WIN32\r
+    std::string platform("Windows");\r
+#else\r
+    // at present there are no variations between different Unix platforms so\r
+    // they all map onto the generic Unix platform\r
+    std::string platform("Generic Unix");\r
+#endif\r
+\r
+    ////////////////////////////////////////////////////////////////////////////////\r
+    // work out the compiler\r
+\r
+#if defined __GNUC__\r
+    std::string compiler(dformat("gcc v%s",__VERSION__));\r
+#elif defined _MSC_VER\r
+    std::string compiler(dformat("MSVC v%0.2f",((float)_MSC_VER)/100.0));\r
+#elif defined __BORLANDC__\r
+    std::string compiler(dformat("Borland v%d.%d",__BORLANDC__/256,__BORLANDC__/16%16));\r
+#else\r
+    std::string compiler("unknown compiler");\r
+#endif\r
+\r
+    ////////////////////////////////////////////////////////////////////////////////\r
+    // work out the kind of build\r
+    // there are two variants - debug and release\r
+\r
+#ifndef NDEBUG\r
+    std::string variant("debug");\r
+#else\r
+    std::string variant("release");\r
+#endif\r
+\r
+    return std::string("STLplus v") + version() + ", " + platform + ", " + compiler + ", " + variant;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/build.hpp b/src/stlplus/portability/build.hpp
new file mode 100644 (file)
index 0000000..b105f19
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_BUILD\r
+#define STLPLUS_BUILD\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Provides a printable representation of the build characteristics in the form:\r
+\r
+//     version, platform, compiler, variant\r
+\r
+//   Where\r
+//     version is the version of STLplus\r
+//     platform is the target operating system\r
+//     compiler is the compilation system and version that the function was compiled with\r
+//     variant is the kind of build - debug or release\r
+\r
+//   Example:\r
+//     STLplus version 3.0, Generic Unix, gcc v3.4, debug\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  std::string build(void);\r
+\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/portability/debug.cpp b/src/stlplus/portability/debug.cpp
new file mode 100644 (file)
index 0000000..562518e
--- /dev/null
@@ -0,0 +1,187 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "debug.hpp"\r
+#include "dprintf.hpp"\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  static std::string format(const char* file, int line, const char* function, const char* message)\r
+  {\r
+    return dformat("%s:%d:%s: assertion failed: %s",\r
+                   (file ? file : ""),\r
+                   line,\r
+                   (function ? function : "") ,\r
+                   (message ? message : ""));\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  assert_failed::assert_failed(const char* file, int line, const char* function, const char* message)\r
+    throw() : \r
+    std::logic_error(format(file, line, function, message))\r
+  {\r
+  }\r
+\r
+  assert_failed::~assert_failed(void) throw()\r
+  {\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  static unsigned _debug_depth = 0;\r
+  static bool _debug_global = false;\r
+  static bool _debug_set = false;\r
+  static bool _debug_recurse = false;\r
+  static bool _debug_read = false;\r
+  static char* _debug_match = 0;\r
+  static const debug_trace* debug_last = 0;\r
+\r
+  void debug_global(const char* file, int line, const char* function, bool state)\r
+  {\r
+    _debug_global = state;\r
+    fprintf(stderr, "%s:%i:[%i]%s ", file, line, _debug_depth, function ? function : "");\r
+    fprintf(stderr, "debug global : %s\n", _debug_global ? "on" : "off");\r
+  }\r
+\r
+  void debug_assert_fail(const char* file, int line, const char* function, const char* test) \r
+    throw(assert_failed)\r
+  {\r
+    fprintf(stderr, "%s:%i:[%i]%s: assertion failed: %s\n", \r
+            file, line, _debug_depth, function ? function : "", test);\r
+    if (debug_last) debug_last->stackdump();\r
+    throw assert_failed(file, line, function, test);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  debug_trace::debug_trace(const char* f, int l, const char* fn) :\r
+    m_file(f), m_line(l), m_function(fn ? fn : ""), \r
+    m_depth(0), m_last(debug_last), m_dbg(false), m_old(false)\r
+  {\r
+    if (!_debug_read)\r
+    {\r
+      _debug_match = getenv("DEBUG");\r
+      _debug_recurse = getenv("DEBUG_LOCAL") == 0;\r
+      _debug_read = true;\r
+    }\r
+    m_dbg = _debug_set || (_debug_match && (!_debug_match[0] || (strcmp(_debug_match, m_file) == 0)));\r
+    m_old = _debug_set;\r
+    if (m_dbg && _debug_recurse)\r
+      _debug_set = true;\r
+    m_depth = ++_debug_depth;\r
+    debug_last = this;\r
+    if (debug()) report(std::string("entering ") + (m_function ? m_function : ""));\r
+  }\r
+\r
+  debug_trace::~debug_trace(void)\r
+  {\r
+    if (debug()) report("leaving");\r
+    --_debug_depth;\r
+    _debug_set = m_old;\r
+    debug_last = m_last;\r
+  }\r
+\r
+  const char* debug_trace::file(void) const\r
+  {\r
+    return m_file;\r
+  }\r
+\r
+  int debug_trace::line(void) const\r
+  {\r
+    return m_line;\r
+  }\r
+\r
+  bool debug_trace::debug(void) const\r
+  {\r
+    return m_dbg || _debug_global;\r
+  }\r
+\r
+  void debug_trace::debug_on(int l, bool recurse)\r
+  {\r
+    m_dbg = true;\r
+    m_old = _debug_set;\r
+    if (recurse)\r
+      _debug_set = true;\r
+    report(l, std::string("debug on") + (recurse ? " recursive" : ""));\r
+  }\r
+\r
+  void debug_trace::debug_off(int l)\r
+  {\r
+    if (debug()) report(l, std::string("debug off"));\r
+    m_dbg = false;\r
+    _debug_set = m_old;\r
+  }\r
+\r
+  void debug_trace::prefix(int l) const\r
+  {\r
+    fprintf(stderr, "%s:%i:[%i]%s ", m_file, l, m_depth, m_function ? m_function : "");\r
+  }\r
+\r
+  void debug_trace::do_report(int l, const std::string& message) const\r
+  {\r
+    prefix(l);\r
+    fprintf(stderr, "%s\n", message.c_str());\r
+    fflush(stderr);\r
+  }\r
+\r
+  void debug_trace::do_report(const std::string& message) const\r
+  {\r
+    do_report(m_line, message);\r
+  }\r
+\r
+  void debug_trace::report(int l, const std::string& message) const\r
+  {\r
+    do_report(l, message);\r
+  }\r
+\r
+  void debug_trace::report(const std::string& message) const\r
+  {\r
+    report(m_line, message);\r
+  }\r
+\r
+  void debug_trace::error(int l, const std::string& message) const\r
+  {\r
+    do_report(l, "ERROR: " + message);\r
+  }\r
+\r
+  void debug_trace::error(const std::string& message) const\r
+  {\r
+    error(m_line, message);\r
+  }\r
+\r
+  void debug_trace::stackdump(int l, const std::string& message) const\r
+  {\r
+    do_report(l, message);\r
+    stackdump();\r
+  }\r
+\r
+  void debug_trace::stackdump(const std::string& message) const\r
+  {\r
+    stackdump(m_line, message);\r
+  }\r
+\r
+  void debug_trace::stackdump(void) const\r
+  {\r
+    for (const debug_trace* item = this; item; item = item->m_last)\r
+      item->do_report("...called from here");\r
+  }\r
+\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/debug.hpp b/src/stlplus/portability/debug.hpp
new file mode 100644 (file)
index 0000000..a7aafd6
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef STLPLUS_DEBUG\r
+#define STLPLUS_DEBUG\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Set of simple debug utilities, all of which are switched off by the\r
+//   NDEBUG compiler directive\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include <stdexcept>\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problem with missing __FUNCTION__ macro\r
+////////////////////////////////////////////////////////////////////////////////\r
+// this macro is used in debugging but was missing in Visual Studio prior to version 7\r
+// it also has a different name in Borland\r
+\r
+#if defined(_MSC_VER) && (_MSC_VER < 1300)\r
+#define __FUNCTION__ 0\r
+#endif\r
+\r
+#ifdef __BORLANDC__\r
+#define __FUNCTION__ __FUNC__\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Exception thrown if an assertion fails\r
+\r
+namespace stlplus\r
+{\r
+\r
+  class assert_failed : public std::logic_error\r
+  {\r
+  public:\r
+    assert_failed(const char* file, int line, const char* function, const char* message) throw();\r
+    ~assert_failed(void) throw();\r
+  };\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // The macros used in debugging\r
+\r
+#ifndef NDEBUG\r
+\r
+#define DEBUG_TRACE stlplus::debug_trace stlplus_debug_trace(__FILE__,__LINE__,__FUNCTION__)\r
+#define IF_DEBUG(stmts) {if (stlplus_debug_trace.debug()){stlplus_debug_trace.prefix(__LINE__);stmts;}}\r
+#define DEBUG_REPORT(str) IF_DEBUG(stlplus_debug_trace.report(__LINE__,str))\r
+#define DEBUG_ERROR(str) stlplus_debug_trace.error(__LINE__,str)\r
+#define DEBUG_STACKDUMP(str) stlplus_debug_trace.stackdump(__LINE__,str)\r
+#define DEBUG_ON stlplus_debug_trace.debug_on(__LINE__,true)\r
+#define DEBUG_ON_LOCAL stlplus_debug_trace.debug_on(__LINE__,false)\r
+#define DEBUG_ON_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,true)\r
+#define DEBUG_OFF_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,false)\r
+#define DEBUG_OFF stlplus_debug_trace.debug_off(__LINE__)\r
+#define DEBUG_ASSERT(test) if (!(test))stlplus::debug_assert_fail(__FILE__,__LINE__,__FUNCTION__,#test)\r
+\r
+#else\r
+\r
+#define DEBUG_TRACE\r
+#define IF_DEBUG(stmts)\r
+#define DEBUG_REPORT(str)\r
+#define DEBUG_ERROR(str)\r
+#define DEBUG_STACKDUMP(str)\r
+#define DEBUG_ON\r
+#define DEBUG_ON_LOCAL\r
+#define DEBUG_ON_GLOBAL\r
+#define DEBUG_OFF_GLOBAL\r
+#define DEBUG_OFF\r
+#define DEBUG_ASSERT(test)\r
+\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// infrastructure - don't use directly\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void debug_global(const char* file, int line, const char* function, bool state = true);\r
+  void debug_assert_fail(const char* file, int line, const char* function, const char* test) throw(assert_failed);\r
+\r
+  class debug_trace\r
+  {\r
+  public:\r
+    debug_trace(const char* f, int l, const char* fn);\r
+    ~debug_trace(void);\r
+    const char* file(void) const;\r
+    int line(void) const;\r
+    bool debug(void) const;\r
+    void debug_on(int l, bool recurse);\r
+    void debug_off(int l);\r
+    void prefix(int l) const;\r
+    void report(int l, const std::string& message) const;\r
+    void report(const std::string& message) const;\r
+    void error(int l, const std::string& message) const;\r
+    void error(const std::string& message) const;\r
+    void stackdump(int l, const std::string& message) const;\r
+    void stackdump(const std::string& message) const;\r
+    void stackdump(void) const;\r
+\r
+  private:\r
+    const char* m_file;\r
+    int m_line;\r
+    const char* m_function;\r
+    unsigned m_depth;\r
+    const debug_trace* m_last;\r
+    bool m_dbg;\r
+    bool m_old;\r
+    void do_report(int l, const std::string& message) const;\r
+    void do_report(const std::string& message) const;\r
+\r
+    // make this class uncopyable\r
+    debug_trace(const debug_trace&);\r
+    debug_trace& operator = (const debug_trace&);\r
+  };\r
+\r
+} // end namespace stlplus\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/portability/dprintf.cpp b/src/stlplus/portability/dprintf.cpp
new file mode 100644 (file)
index 0000000..f03217e
--- /dev/null
@@ -0,0 +1,92 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Notes:\r
+\r
+//   Feb 2007: Rewritten in terms of platform-specific fixes to the\r
+//   buffer-overflow problem. Using native functions for this has the added\r
+//   benefit of giving access to other features of the C-runtime such as Unicode\r
+//   support.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "dprintf.hpp"\r
+#include <stdio.h>\r
+#include <limits.h>\r
+#include <float.h>\r
+#include <ctype.h>\r
+#include <stdlib.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  int vdprintf(std::string& formatted, const char* format, va_list args)\r
+  {\r
+#ifdef MSWINDOWS\r
+    int length = 0;\r
+    char* buffer = 0;\r
+    for(int buffer_length = 256; ; buffer_length*=2)\r
+    {\r
+      buffer = (char*)malloc(buffer_length);\r
+      if (!buffer) return -1;\r
+      length = _vsnprintf(buffer, buffer_length-1, format, args);\r
+      if (length >= 0)\r
+      {\r
+        buffer[length] = 0;\r
+        formatted += std::string(buffer);\r
+        free(buffer);\r
+        break;\r
+      }\r
+      free(buffer);\r
+    }\r
+    return length;\r
+#else\r
+    char* buffer = 0;\r
+    int length = vasprintf(&buffer, format, args);\r
+    if (!buffer) return -1;\r
+    if (length >= 0)\r
+      formatted += std::string(buffer);\r
+    free(buffer);\r
+    return length;\r
+#endif\r
+  }\r
+\r
+  int dprintf(std::string& formatted, const char* format, ...)\r
+  {\r
+    va_list args;\r
+    va_start(args, format);\r
+    int result = vdprintf(formatted, format, args);\r
+    va_end(args);\r
+    return result;\r
+  }\r
+\r
+  std::string vdformat(const char* format, va_list args) throw(std::invalid_argument)\r
+  {\r
+    std::string formatted;\r
+    int length = vdprintf(formatted, format, args);\r
+    if (length < 0) throw std::invalid_argument("dprintf");\r
+    return formatted;\r
+  }\r
+\r
+  std::string dformat(const char* format, ...) throw(std::invalid_argument)\r
+  {\r
+    std::string formatted;\r
+    va_list args;\r
+    va_start(args, format);\r
+    int length = vdprintf(formatted, format, args);\r
+    va_end(args);\r
+    if (length < 0) throw std::invalid_argument("dprintf");\r
+    return formatted;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/dprintf.hpp b/src/stlplus/portability/dprintf.hpp
new file mode 100644 (file)
index 0000000..f7de78b
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef STLPLUS_DPRINTF\r
+#define STLPLUS_DPRINTF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Provides an sprintf-like function acting on STL strings. The 'd' in dprintf\r
+//   stands for "dynamic" in that the string is a dynamic string whereas a char*\r
+//   buffer would be static (in size that is, not static in C terms).\r
+\r
+//   The obvious solution to the problem of in-memory formatted output is to use\r
+//   sprintf(), but this is a potentially dangerous operation since it will quite\r
+//   happily charge off the end of the string it is printing to and thereby\r
+//   corrupt memory. This kind of buffer-overflow vulnerability is the source of\r
+//   most security failures exploited by virus-writers. It means that sprintf\r
+//   should *never* be used and should be made obsolete.\r
+\r
+//   In any case, using arbitrary-sized fixed-length buffers is not part of any\r
+//   quality-orientated design philosophy.\r
+\r
+//   Most operating systems now have a safe version of sprintf, but this is\r
+//   non-standard. The functions in this file are platform-independent interfaces\r
+//   to the underlying safe implementation.\r
+\r
+//   I would like to make this set of functions obsolete too, since I believe the\r
+//   C runtime should be deprecated in favour of C++ runtime which uses dynamic\r
+//   strings and can handle exceptions. However, there is as yet no C++\r
+//   equivalent functionality to some of the string-handling available through\r
+//   the printf-like functions, so it has to stay for now.\r
+\r
+//     int dprintf (std::string& buffer, const char* format, ...);\r
+\r
+//       Formats the message by appending to the std::string buffer according to\r
+//       the formatting codes in the format string. The return int is the number\r
+//       of characters generated by this call, i.e. the increase in the length of\r
+//       the std::string.\r
+\r
+//     int vdprintf (std::string& buffer, const char* format, va_list args);\r
+\r
+//       As above, but using a pre-initialised va_args argument list. Useful for\r
+//       nesting dprintf calls within variable argument functions.\r
+\r
+//     std::string dformat (const char* format, ...);\r
+\r
+//       Similar to dprintf() above, except the result is formatted into a new\r
+//       std::string which is returned by the function. Very useful for inline\r
+//       calls within an iostream expression.\r
+\r
+//       e.g.    cout << "Total: " << dformat("%6i",t) << endl;\r
+\r
+//     std::string vdformat (const char* format, va_list);\r
+  \r
+//       As above, but using a pre-initialised va_args argument list. Useful for nesting\r
+//       dformat calls within variable argument functions.\r
+\r
+//   The format string supports the following format codes as in the C runtime library:\r
+\r
+//     % [ flags ] [ field ] [ . precision ] [ modifier ] [ conversion ]\r
+\r
+//     flags:\r
+//       -    - left justified\r
+//       +    - print sign for +ve numbers\r
+//       ' '  - leading space where + sign would be\r
+//       0    - leading zeros to width of field\r
+//       #    - alternate format\r
+\r
+//     field:\r
+//       a numeric argument specifying the field width - default = 0\r
+//       * means take the next va_arg as the field width - if negative then left justify\r
+\r
+//     precision:\r
+//       a numeric argument the meaning of which depends on the conversion -\r
+//       - %s - max characters from a string - default = strlen()\r
+//       - %e, %f - decimal places to be displayed - default = 6\r
+//       - %g - significant digits to be displayed - default = 6\r
+//       - all integer conversions - minimum digits to display - default = 0\r
+//       * means take the next va_arg as the field width - if negative then left justify\r
+\r
+//     modifier:\r
+//       h    - short or unsigned short\r
+//       l    - long or unsigned long\r
+//       L    - long double\r
+\r
+//     conversions:\r
+//       d, i - short/int/long as decimal\r
+//       u    - short/int/long as unsigned decimal\r
+//       o    - short/int/long as unsigned octal - # adds leading 0\r
+//       x, X - short/int/long as unsigned hexadecimal - # adds leading 0x\r
+//       c    - char\r
+//       s    - char*\r
+//       f    - double/long double as fixed point\r
+//       e, E - double/long double as floating point\r
+//       g, G - double/long double as fixed point/floating point depending on value\r
+//       p    - void* as unsigned hexadecimal\r
+//       %    - literal %\r
+//       n    - int* as recipient of length of formatted string so far\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+#include <stdarg.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // format by appending to a string and return the increase in length\r
+  // if there is an error, return a negative number and leave the string unchanged\r
+  int dprintf (std::string& formatted, const char* format, ...);\r
+  int vdprintf (std::string& formatted, const char* format, va_list args);\r
+\r
+  // format into a new string and return the result\r
+  // if there is an error, throw an exception\r
+  std::string dformat (const char* format, ...) throw(std::invalid_argument);\r
+  std::string vdformat (const char* format, va_list) throw(std::invalid_argument);\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/dynaload.cpp b/src/stlplus/portability/dynaload.cpp
new file mode 100644 (file)
index 0000000..cdd1848
--- /dev/null
@@ -0,0 +1,184 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "dynaload.hpp"\r
+\r
+#ifdef MSWINDOWS\r
+#include <windows.h>\r
+#else\r
+#include <dlfcn.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+\r
+static std::string last_error(void)\r
+{\r
+  // get the last error code - if none, return the empty string\r
+  DWORD err = GetLastError();\r
+  if (err == 0) return std::string();\r
+  // get the system message for this error code\r
+  char* message;\r
+  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                0,\r
+                err,\r
+                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+                (LPTSTR)&message,\r
+                0,0);\r
+  std::string result = message;\r
+  LocalFree(message);\r
+  // the error message is for some perverse reason newline terminated - remove this\r
+  if (result[result.size()-1] == '\n')\r
+    result.erase(result.end()-1);\r
+  if (result[result.size()-1] == '\r')\r
+    result.erase(result.end()-1);\r
+  return result;\r
+}\r
+\r
+#else\r
+\r
+static std::string last_error(void)\r
+{\r
+  return std::string(dlerror());\r
+}\r
+\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // library management\r
+\r
+  // construct the object but do not load\r
+  dynaload::dynaload(void) : m_handle(0) \r
+  {\r
+  }\r
+\r
+  // construct and load\r
+  dynaload::dynaload(const std::string& library) : m_handle(0)\r
+  {\r
+    load(library);\r
+  }\r
+\r
+  // destroy and unload if loaded\r
+  dynaload::~dynaload(void)\r
+  {\r
+    unload();\r
+  }\r
+\r
+  // load the library - return success or fail\r
+  bool dynaload::load(const std::string& library)\r
+  {\r
+#ifdef MSWINDOWS\r
+    m_handle = (void*)LoadLibrary(library.c_str());\r
+#elif defined(CYGWIN)\r
+    m_handle = dlopen(library.c_str(),RTLD_NOW);\r
+#else\r
+    std::string full_library = std::string("lib") + library + std::string(".so");\r
+    m_handle = dlopen(full_library.c_str(),RTLD_NOW);\r
+#endif\r
+    if (!m_handle)\r
+    {\r
+      m_error = load_error;\r
+      m_text = last_error();\r
+    }\r
+    return loaded();\r
+  }\r
+\r
+  // unload the library if loaded\r
+  bool dynaload::unload(void)\r
+  {\r
+    if (!loaded()) return false;\r
+#ifdef MSWINDOWS\r
+    int status = FreeLibrary((HINSTANCE)m_handle) ? 0 : 1;\r
+#else\r
+    int status = dlclose(m_handle);\r
+#endif\r
+    if (status != 0)\r
+    {\r
+      m_error = unload_error;\r
+      m_text = last_error();\r
+    }\r
+    return status == 0;\r
+  }\r
+\r
+  // test whether the library is loaded\r
+  bool dynaload::loaded(void) const\r
+  {\r
+    return m_handle != 0;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // symbol management\r
+\r
+  // test whether a function is exported by the library\r
+  // does not set the error flag if fails\r
+  bool dynaload::present(const std::string& name)\r
+  {\r
+    if (!loaded()) return false;\r
+#ifdef MSWINDOWS\r
+    void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str());\r
+#else\r
+    void* fn = dlsym(m_handle,name.c_str());\r
+#endif\r
+    return fn != 0;\r
+  }\r
+\r
+  // get the function as a generic pointer\r
+  void* dynaload::symbol(const std::string& name)\r
+  {\r
+    if (!loaded()) return 0;\r
+#ifdef MSWINDOWS\r
+    void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str());\r
+#else\r
+    void* fn = dlsym(m_handle,name.c_str());\r
+#endif\r
+    if (!fn)\r
+    {\r
+      m_error = symbol_error;\r
+      m_text = last_error();\r
+    }\r
+    return fn;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // error management\r
+\r
+  // test whether there has been an error\r
+  bool dynaload::error(void) const\r
+  {\r
+    return m_error != no_error;\r
+  }\r
+\r
+  // clear an error once it has been handled (or ignored)\r
+  void dynaload::clear_error(void)\r
+  {\r
+    m_error = no_error;\r
+    m_text = std::string();\r
+  }\r
+\r
+  // get the type of the error as indicated by the enum error_t\r
+  dynaload::error_t dynaload::error_type(void) const\r
+  {\r
+    return m_error;\r
+  }\r
+\r
+  // get the text of the error as provided by the OS\r
+  std::string dynaload::error_text(void) const\r
+  {\r
+    return m_text;\r
+  }\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/dynaload.hpp b/src/stlplus/portability/dynaload.hpp
new file mode 100644 (file)
index 0000000..dafaf24
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef STLPLUS_DYNALOAD\r
+#define STLPLUS_DYNALOAD\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A portable interface to the dynamic loader - i.e. the system for loading\r
+//   dynamic libraries or shared libraries during the running of a program,\r
+//   rather than by linking\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // dynaload class manages a dynamic loadable library and unloads it on destruction\r
+\r
+  class dynaload\r
+  {\r
+  public:\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // library management\r
+\r
+    // construct the object but do not load\r
+    dynaload(void);\r
+\r
+    // construct and load\r
+    dynaload(const std::string& library);\r
+\r
+    // destroy and unload if loaded\r
+    ~dynaload(void);\r
+\r
+    // load the library - return success or fail\r
+    bool load(const std::string& library);\r
+\r
+    // unload the library if loaded\r
+    bool unload(void);\r
+\r
+    // test whether the library is loaded\r
+    bool loaded(void) const;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // symbol management\r
+\r
+    // test whether a function is exported by the library\r
+    bool present(const std::string& name);\r
+\r
+    // get the function as a generic pointer\r
+    void* symbol(const std::string& name);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error management\r
+\r
+    // enum values to indicate type of error\r
+    enum error_t {no_error, load_error, unload_error, symbol_error};\r
+\r
+    // test whether there has been an error\r
+    bool error(void) const;\r
+\r
+    // clear an error once it has been handled (or ignored)\r
+    void clear_error(void);\r
+\r
+    // get the type of the error as indicated by the enum error_t\r
+    error_t error_type(void) const;\r
+\r
+    // get the text of the error as provided by the OS\r
+    std::string error_text(void) const;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+\r
+  private:\r
+    void* m_handle;\r
+    error_t m_error;\r
+    std::string m_text;\r
+  };\r
+\r
+}\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/file_system.cpp b/src/stlplus/portability/file_system.cpp
new file mode 100644 (file)
index 0000000..983d984
--- /dev/null
@@ -0,0 +1,1058 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   This is a portable interface to the file system.\r
+\r
+//   The idea is that you write all file system access code using these functions,\r
+//   which are ported to all platforms that we are interested in. Therefore your\r
+//   code is inherently portable.\r
+\r
+//   Native Windows version: switched on by macro _WIN32 which is defined by VC++/Borland/Mingw compilers\r
+//   Unix/Gnu version:   default variant, no compiler directives are required but _WIN32 must be absent\r
+//   Cygwin/Gnu version: as Unix version but with additional support for Windows drive letters\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "file_system.hpp"\r
+#include "wildcard.hpp"\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <time.h>\r
+#include <algorithm>\r
+#include <ctype.h>\r
+\r
+#ifdef MSWINDOWS\r
+#include <windows.h>\r
+#include <dos.h>\r
+#include <direct.h>\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <sys/stat.h>\r
+#include <sys/types.h>\r
+#else\r
+#include <dirent.h>\r
+#include <fcntl.h>\r
+#include <sys/param.h>\r
+#include <unistd.h>\r
+#include <sys/stat.h>\r
+#include <sys/types.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// definitions of separators\r
+\r
+#ifdef MSWINDOWS\r
+  static const char* separator_set = "\\/";\r
+  static const char preferred_separator = '\\';\r
+#else\r
+  static const char* separator_set = "/";\r
+  static const char preferred_separator = '/';\r
+#endif\r
+\r
+  static bool is_separator (char ch)\r
+  {\r
+    for (int i = 0; separator_set[i]; i++)\r
+    {\r
+      if (separator_set[i] == ch)\r
+        return true;\r
+    }\r
+    return false;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  static std::string lowercase(const std::string& val)\r
+  {\r
+    std::string text = val;\r
+    for (unsigned i = 0; i < text.size(); i++)\r
+      text[i] = tolower(text[i]);\r
+    return text;\r
+  }\r
+\r
+#endif\r
+\r
+  bool path_compare(const std::string& l, const std::string& r)\r
+  {\r
+#ifdef MSWINDOWS\r
+    return lowercase(l) == lowercase(r);\r
+#else\r
+    return l == r;\r
+#endif\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Internal data structure used to hold the different parts of a filespec\r
+\r
+  class file_specification\r
+  {\r
+  private:\r
+    bool m_relative;                 // true = relative, false = absolute\r
+    std::string m_drive;             // drive - drive letter (e.g. "c:") or the path for an UNC (e.g. "\\somewhere")\r
+                                     //         empty if not known or on Unix\r
+    std::vector<std::string> m_path; // the subdirectory path to follow from the drive\r
+    std::string m_filename;          // the filename\r
+  public:\r
+    file_specification(void) : m_relative(false) {}\r
+    ~file_specification(void) {}\r
+\r
+    bool initialise_folder(const std::string& spec);\r
+    bool initialise_file(const std::string& spec);\r
+    bool simplify(void);\r
+    bool make_absolute(const std::string& root = folder_current_full());\r
+    bool make_absolute(const file_specification& root);\r
+    bool make_relative(const std::string& root = folder_current_full());\r
+    bool make_relative(const file_specification& root);\r
+    bool relative(void) const {return m_relative;}\r
+    bool absolute(void) const {return !relative();}\r
+    void set_relative(void) {m_relative = true;}\r
+    void set_absolute(void) {m_relative = false;}\r
+\r
+    const std::string& drive(void) const {return m_drive;}\r
+    std::string& drive(void) {return m_drive;}\r
+    void set_drive(const std::string& drive) {m_drive = drive;}\r
+\r
+    const std::vector<std::string>& path(void) const {return m_path;}\r
+    std::vector<std::string>& path(void) {return m_path;}\r
+    void set_path(const std::vector<std::string>& path) {m_path = path;}\r
+\r
+    void add_subpath(const std::string& subpath) {m_path.push_back(subpath);}\r
+    unsigned subpath_size(void) const {return m_path.size();}\r
+    const std::string& subpath_element(unsigned i) const {return m_path[i];}\r
+    void subpath_erase(unsigned i) {m_path.erase(m_path.begin()+i);}\r
+\r
+    const std::string& file(void) const {return m_filename;}\r
+    std::string& file(void) {return m_filename;}\r
+    void set_file(const std::string& file) {m_filename = file;}\r
+\r
+    std::string image(void) const;\r
+  };\r
+\r
+  bool file_specification::initialise_folder(const std::string& folder_spec)\r
+  {\r
+    std::string spec = folder_spec;\r
+    m_relative = true;\r
+    m_drive.erase();\r
+    m_path.clear();\r
+    m_filename.erase();\r
+    unsigned i = 0;\r
+#ifdef MSWINDOWS\r
+    // first split off the drive letter or UNC prefix on Windows\r
+    if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')\r
+    {\r
+      // found a drive letter\r
+      i = 2;\r
+      m_drive = spec.substr(0, 2);\r
+      m_relative = false;\r
+      // if there is a drive but no path or a relative path, get the current\r
+      // path for this drive and prepend it to the path\r
+      if (i == spec.size() || !is_separator(spec[i]))\r
+      {\r
+        // getdcwd requires the drive number (1..26) not the letter (A..Z)\r
+        char path [MAX_PATH+1];\r
+        int drivenum = toupper(m_drive[0]) - 'A' + 1;\r
+        if (_getdcwd(drivenum, path, MAX_PATH+1))\r
+        {\r
+          // the path includes the drive so we have the drive info twice\r
+          // need to prepend this absolute path to the spec such that any remaining relative path is still retained\r
+          if (!is_separator(path[strlen(path)-1])) spec.insert(2, 1, preferred_separator);\r
+          spec.insert(2, path+2);\r
+        }\r
+        else\r
+        {\r
+          // non-existent drive - fill in just the root directory\r
+          spec.insert(2, 1, preferred_separator);\r
+        }\r
+      }\r
+    }\r
+    else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))\r
+    {\r
+      // found an UNC prefix\r
+      i = 2;\r
+      // find the end of the prefix by scanning for the next seperator or the end of the spec\r
+      while (i < spec.size() && !is_separator(spec[i])) i++;\r
+      m_drive = spec.substr(0, i);\r
+      m_relative = false;\r
+    }\r
+#endif\r
+#ifdef CYGWIN\r
+    // first split off the drive letter or UNC prefix on Windows - the Cygwin environment supports these too\r
+    if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')\r
+    {\r
+      // found a drive letter\r
+      i = 2;\r
+      m_drive = spec.substr(0, 2);\r
+      m_relative = false;\r
+      // if there is a drive but no path or a relative path, get the current\r
+      // path for this drive and prepend it to the path\r
+      if (i == spec.size() || !is_separator(spec[i]))\r
+      {\r
+        // non-existent drive - fill in just the root directory\r
+        spec.insert(2, 1, preferred_separator);\r
+      }\r
+    }\r
+    else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))\r
+    {\r
+      // found an UNC prefix\r
+      i = 2;\r
+      // find the end of the prefix by scanning for the next seperator or the end of the spec\r
+      while (i < spec.size() && !is_separator(spec[i])) i++;\r
+      m_drive = spec.substr(0, i);\r
+      m_relative = false;\r
+    }\r
+#endif\r
+    // check whether the path is absolute or relative and discard the leading / if absolute\r
+    if (i < spec.size() && is_separator(spec[i]))\r
+    {\r
+      m_relative = false;\r
+      i++;\r
+#ifdef MSWINDOWS\r
+      // if there's no drive, fill it in on Windows since absolute paths must have a drive\r
+      if (m_drive.empty())\r
+      {\r
+        m_drive += (char)(_getdrive() - 1 + 'A');\r
+        m_drive += ':';\r
+      }\r
+#endif\r
+    }\r
+    // now extract the path elements - note that a trailing / is not significant since /a/b/c/ === /a/b/c\r
+    // also note that the leading / has been discarded - all paths are relative\r
+    // if absolute() is set, then paths are relative to the drive, else they are relative to the current path\r
+    unsigned start = i;\r
+    while(i <= spec.size())\r
+    {\r
+      if (i == spec.size())\r
+      {\r
+        // path element terminated by the end of the string\r
+        // discard this element if it is zero length because that represents the trailing /\r
+        if (i != start)\r
+          m_path.push_back(spec.substr(start, i-start));\r
+      }\r
+      else if (is_separator(spec[i]))\r
+      {\r
+        // path element terminated by a separator\r
+        m_path.push_back(spec.substr(start, i-start));\r
+        start = i+1;\r
+      }\r
+      i++;\r
+    }\r
+    // TODO - some error handling?\r
+    return true;\r
+  }\r
+\r
+  bool file_specification::initialise_file(const std::string& spec)\r
+  {\r
+    m_filename.erase();\r
+    // remove last element as the file and then treat the rest as a folder\r
+    unsigned i = spec.size();\r
+    while (--i)\r
+    {\r
+      if (is_separator(spec[i]))\r
+        break;\r
+#ifdef MSWINDOWS\r
+      // on windoze you can say a:fred.txt so the colon separates the path from the filename\r
+      else if (i == 1 && spec[i] == ':')\r
+        break;\r
+#endif\r
+    }\r
+    bool result = initialise_folder(spec.substr(0,i+1));\r
+    m_filename = spec.substr(i+1,spec.size()-i-1);\r
+    // TODO - some error handling?\r
+    return result;\r
+  }\r
+\r
+  bool file_specification::simplify(void)\r
+  {\r
+    // simplify the path by removing unnecessary . and .. entries - Note that zero-length entries are treated like .\r
+    for (unsigned i = 0; i < m_path.size(); )\r
+    {\r
+      if (m_path[i].empty() || m_path[i].compare(".") == 0)\r
+      {\r
+        // found . or null\r
+        // these both mean do nothing - so simply delete this element\r
+        m_path.erase(m_path.begin()+i);\r
+      }\r
+      else if (m_path[i].compare("..") == 0)\r
+      {\r
+        // found ..\r
+        if (i == 0 && !m_relative)\r
+        {\r
+          // up from the root does nothing so can be deleted\r
+          m_path.erase(m_path.begin()+i);\r
+          i++;\r
+        }\r
+        else if (i == 0 || m_path[i-1].compare("..") == 0)\r
+        {\r
+          // the first element of a relative path or the previous element is .. then keep it\r
+          i++;\r
+        }\r
+        else\r
+        {\r
+          // otherwise delete this element and the previous one\r
+          m_path.erase(m_path.begin()+i);\r
+          m_path.erase(m_path.begin()+i-1);\r
+          i--;\r
+        }\r
+      }\r
+      // keep all other elements\r
+      else\r
+        i++;\r
+    }\r
+    // TODO - error checking?\r
+    return true;\r
+  }\r
+\r
+  bool file_specification::make_absolute(const std::string& root)\r
+  {\r
+    // test whether already an absolute path in which case there's nothing to do\r
+    if (absolute()) return true;\r
+    // now simply call the other version of make_absolute\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root);\r
+    return make_absolute(rootspec);\r
+  }\r
+\r
+  bool file_specification::make_absolute(const file_specification& rootspec)\r
+  {\r
+    // test whether already an absolute path in which case there's nothing to do\r
+    if (absolute()) return true;\r
+    // initialise the result with the root and make the root absolute\r
+    file_specification result = rootspec;\r
+    result.make_absolute();\r
+    // now append this's relative path and filename to the root's absolute path\r
+    for (unsigned i = 0; i < subpath_size(); i++)\r
+      result.add_subpath(subpath_element(i));\r
+    result.set_file(file());\r
+    // now the result is the absolute path, so transfer it to this\r
+    *this = result;\r
+    // and simplify to get rid of any unwanted .. or . elements\r
+    simplify();\r
+    return true;\r
+  }\r
+\r
+  bool file_specification::make_relative(const std::string& root)\r
+  {\r
+    // test whether already an relative path in which case there's nothing to do\r
+    if (relative()) return true;\r
+    // now simply call the other version of make_relative\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root);\r
+    return make_relative(rootspec);\r
+  }\r
+\r
+  bool file_specification::make_relative(const file_specification& rootspec)\r
+  {\r
+    // test whether already an relative path in which case there's nothing to do\r
+    if (relative()) return true;\r
+    // initialise the result with the root and make the root absolute\r
+    file_specification absolute_root = rootspec;\r
+    absolute_root.make_absolute();\r
+    // now compare elements of the absolute root with elements of this to find the common path\r
+    // if the drives are different, no conversion can take place and the result must be absolute, else clear the drive\r
+    if (!path_compare(drive(), absolute_root.drive())) return true;\r
+    set_drive("");\r
+    // first remove leading elements that are identical to the corresponding element in root\r
+    unsigned i = 0;\r
+    while(subpath_size() > 0 && \r
+          i < absolute_root.subpath_size() && \r
+          path_compare(subpath_element(0), absolute_root.subpath_element(i)))\r
+    {\r
+      subpath_erase(0);\r
+      i++;\r
+    }\r
+    // now add a .. prefix for every element in root that is different from this\r
+    while (i < absolute_root.subpath_size())\r
+    {\r
+      m_path.insert(m_path.begin(), "..");\r
+      i++;\r
+    }\r
+    set_relative();\r
+    return true;\r
+  }\r
+\r
+  std::string file_specification::image(void) const\r
+  {\r
+    std::string result = m_drive;\r
+    if (absolute())\r
+      result += preferred_separator;\r
+    if (!m_path.empty())\r
+    {\r
+      for (unsigned i = 0; i < m_path.size(); i++)\r
+      {\r
+        if (i != 0) result += std::string(1,preferred_separator);\r
+        result += m_path[i];\r
+      }\r
+    }\r
+    else if (relative())\r
+      result += '.';\r
+    // add a trailing / to the last directory element\r
+    if (result.empty() || !is_separator(result[result.size()-1]))\r
+      result += preferred_separator;\r
+    if (!m_filename.empty())\r
+      result += m_filename;\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// classifying functions\r
+\r
+#ifdef MSWINDOWS\r
+// file type tests are not defined for some reason on Windows despite them providing the stat() function!\r
+#define R_OK 4\r
+#define W_OK 2\r
+#endif\r
+\r
+  bool is_present (const std::string& thing)\r
+  {\r
+    // strip off any trailing separator because that will cause the stat function to fail\r
+    std::string path = thing;\r
+    if (!path.empty() && is_separator(path[path.size()-1]))\r
+      path.erase(path.size()-1,1);\r
+    // now test if this thing exists using the built-in stat function\r
+    struct stat buf;\r
+    return stat(path.c_str(), &buf) == 0;\r
+  }\r
+\r
+  bool is_folder (const std::string& thing)\r
+  {\r
+    // strip off any trailing separator because that will cause the stat function to fail\r
+    std::string path = thing;\r
+    if (!path.empty() && is_separator(path[path.size()-1]))\r
+      path.erase(path.size()-1,1);\r
+    // now test if this thing exists using the built-in stat function and if so, is it a folder\r
+    struct stat buf;\r
+    if (!(stat(path.c_str(), &buf) == 0)) {return false;}\r
+    return (buf.st_mode & S_IFDIR) != 0;\r
+  }\r
+\r
+  bool is_file (const std::string& thing)\r
+  {\r
+    // strip off any trailing separator because that will cause the stat function to fail\r
+    std::string path = thing;\r
+    if (!path.empty() && is_separator(path[path.size()-1]))\r
+      path.erase(path.size()-1,1);\r
+    // now test if this thing exists using the built-in stat function and if so, is it a file\r
+    struct stat buf;\r
+    if (!(stat(path.c_str(), &buf) == 0)) {return false;}\r
+    return (buf.st_mode & S_IFREG) != 0;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// file functions\r
+\r
+  bool file_exists (const std::string& filespec)\r
+  {\r
+    return is_file(filespec);\r
+  }\r
+\r
+  bool file_readable (const std::string& filespec)\r
+  {\r
+    // a file is readable if it exists and can be read\r
+    if (!file_exists(filespec)) return false;\r
+    return access(filespec.c_str(),R_OK)==0;\r
+  }\r
+\r
+  bool file_writable (const std::string& filespec)\r
+  {\r
+    // a file is writable if it exists as a file and is writable or if it doesn't exist but could be created and would be writable\r
+    if (is_present(filespec))\r
+    {\r
+      if (!is_file(filespec)) return false;\r
+      return access(filespec.c_str(),W_OK)==0;\r
+    }\r
+    std::string dir = folder_part(filespec);\r
+    if (dir.empty()) dir = ".";\r
+    return folder_writable(dir);\r
+  }\r
+\r
+  size_t file_size (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_size;\r
+  }\r
+\r
+  bool file_delete (const std::string& filespec)\r
+  {\r
+    if (!is_file(filespec)) return false;\r
+    return remove(filespec.c_str())==0;\r
+  }\r
+\r
+  bool file_rename (const std::string& old_filespec, const std::string& new_filespec)\r
+  {\r
+    if (!is_file(old_filespec)) return false;\r
+    return rename(old_filespec.c_str(), new_filespec.c_str())==0;\r
+  }\r
+\r
+  bool file_copy (const std::string& old_filespec, const std::string& new_filespec)\r
+  {\r
+    if (!is_file(old_filespec)) return false;\r
+    // do an exact copy - to do this, use binary mode\r
+    bool result = true;\r
+    FILE* old_file = fopen(old_filespec.c_str(),"rb");\r
+    FILE* new_file = fopen(new_filespec.c_str(),"wb");\r
+    if (!old_file)\r
+      result = false;\r
+    else if (!new_file)\r
+      result = false;\r
+    else\r
+    {\r
+      for (int byte = getc(old_file); byte != EOF; byte = getc(old_file))\r
+        putc(byte,new_file);\r
+    }\r
+    if (old_file) fclose(old_file);\r
+    if (new_file) fclose(new_file);\r
+    return result;\r
+  }\r
+\r
+  bool file_move (const std::string& old_filespec, const std::string& new_filespec)\r
+  {\r
+    // try to move the file by renaming - if that fails then do a copy and delete the original\r
+    if (file_rename(old_filespec, new_filespec))\r
+      return true;\r
+    if (!file_copy(old_filespec, new_filespec))\r
+      return false;\r
+    // I'm not sure what to do if the delete fails - is that an error?\r
+    // I've made it an error and then delete the copy so that the original state is recovered\r
+    if (file_delete(old_filespec))\r
+      return true;\r
+    file_delete(new_filespec);\r
+    return false;\r
+  }\r
+\r
+  time_t file_created (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_ctime;\r
+  }\r
+\r
+  time_t file_modified (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_mtime;\r
+  }\r
+\r
+  time_t file_accessed (const std::string& filespec)\r
+  {\r
+    struct stat buf;\r
+    if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+    return buf.st_atime;\r
+  }\r
+\r
+  std::string create_filespec (const std::string& directory, const std::string& filename)\r
+  {\r
+    std::string result = directory;\r
+    // if directory is empty then no directory part will be added\r
+    // add trailing slash if the directory was specified and does not have a trailing slash\r
+    if (!result.empty() && !is_separator(result[result.size()-1]))\r
+      result += preferred_separator;\r
+    // if filename is null or empty, nothing will be added so the path is then a directory path\r
+    result += filename;\r
+    return result;\r
+  }\r
+\r
+  std::string create_filespec (const std::string& directory, const std::string& basename, const std::string& extension)\r
+  {\r
+    return create_filespec(directory, create_filename(basename, extension));\r
+  }\r
+\r
+  std::string create_filename(const std::string& basename, const std::string& extension)\r
+  {\r
+    std::string name = basename;\r
+    // extension is optional - so the dot is also optional\r
+    if (!extension.empty())\r
+    {\r
+      if (extension[0] != '.') name += '.';\r
+      name += extension;\r
+    }\r
+    return name;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// folder functions\r
+\r
+  bool folder_create (const std::string& directory)\r
+  {\r
+#ifdef MSWINDOWS\r
+    return mkdir(directory.c_str()) == 0;\r
+#else\r
+    return mkdir(directory.c_str(), 0777) == 0;\r
+#endif\r
+  }\r
+\r
+  bool folder_exists (const std::string& directory)\r
+  {\r
+    return is_folder(directory);\r
+  }\r
+\r
+  bool folder_readable (const std::string& directory)\r
+  {\r
+    // a folder is readable if it exists and has read access\r
+    std::string dir = directory;\r
+    if (dir.empty()) dir = ".";\r
+    if (!folder_exists(dir)) return false;\r
+    return access(dir.c_str(),R_OK)==0;\r
+  }\r
+\r
+  bool folder_writable (const std::string& directory)\r
+  {\r
+    // a folder is writable if it exists and has write access\r
+    std::string dir = directory;\r
+    if (dir.empty()) dir = ".";\r
+    if (!folder_exists(dir)) return false;\r
+    return access(dir.c_str(),W_OK)==0;\r
+  }\r
+\r
+  bool folder_delete (const std::string& directory, bool recurse)\r
+  {\r
+    std::string dir = directory;\r
+    if (dir.empty()) dir = ".";\r
+    if (!folder_exists(dir)) return false;\r
+    bool result = true;\r
+    // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself\r
+    if (recurse)\r
+    {\r
+      std::vector<std::string> subdirectories = folder_subdirectories(dir);\r
+      for (std::vector<std::string>::size_type d = 0; d < subdirectories.size(); ++d)\r
+        if (!folder_delete(folder_down(dir,subdirectories[d]),true)) \r
+          result = false;\r
+      std::vector<std::string> files = folder_files(dir);\r
+      for (std::vector<std::string>::size_type f = 0; f < files.size(); ++f)\r
+        if (!file_delete(create_filespec(dir, files[f]))) \r
+          result = false;\r
+    }\r
+    if (rmdir(dir.c_str())!=0) result = false;\r
+    return result;\r
+  }\r
+\r
+  bool folder_rename (const std::string& old_directory, const std::string& new_directory)\r
+  {\r
+    if (!folder_exists(old_directory)) return false;\r
+    return rename(old_directory.c_str(), new_directory.c_str())==0;\r
+  }\r
+\r
+  bool folder_empty(const std::string& directory)\r
+  {\r
+    std::string dir = directory.empty() ? std::string(".") : directory;\r
+    bool result = true;\r
+#ifdef MSWINDOWS\r
+    std::string wildcard = create_filespec(dir, "*.*");\r
+    long handle = -1;\r
+    _finddata_t fileinfo;\r
+    for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))\r
+    {\r
+      std::string strentry = fileinfo.name;\r
+      if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+      {\r
+        result = false;\r
+        break;\r
+      }\r
+    }\r
+    _findclose(handle);\r
+#else\r
+    DIR* d = opendir(dir.c_str());\r
+    if (d)\r
+    {\r
+      for (dirent* entry = readdir(d); entry; entry = readdir(d))\r
+      {\r
+        std::string strentry = entry->d_name;\r
+        if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+        {\r
+          result = false;\r
+          break;\r
+        }\r
+      }\r
+      closedir(d);\r
+    }\r
+#endif\r
+    return result;\r
+  }\r
+\r
+  bool folder_set_current(const std::string& folder)\r
+  {\r
+    if (!folder_exists(folder))\r
+      return false;\r
+#ifdef MSWINDOWS\r
+    // Windose implementation - this returns non-zero for success\r
+    return (SetCurrentDirectoryA(folder.c_str()) != 0);\r
+#else\r
+    // Unix implementation - this returns zero for success\r
+    return (chdir(folder.c_str()) == 0);\r
+#endif\r
+  }\r
+\r
+  std::string folder_current (void)\r
+  {\r
+    return ".";\r
+  }\r
+\r
+  std::string folder_current_full(void)\r
+  {\r
+    // It's not clear from the documentation whether the buffer for a path should be one byte longer\r
+    // than the maximum path length to allow for the null termination, so I have made it so anyway\r
+#ifdef MSWINDOWS\r
+    char abspath [MAX_PATH+1];\r
+    return std::string(_fullpath(abspath, ".", MAX_PATH+1));\r
+#else\r
+    char pathname [MAXPATHLEN+1];\r
+    getcwd(pathname,MAXPATHLEN+1);\r
+    return std::string(pathname);\r
+#endif\r
+  }\r
+\r
+  std::string folder_down (const std::string& directory, const std::string& subdirectory)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(directory);\r
+    spec.add_subpath(subdirectory);\r
+    return spec.image();\r
+  }\r
+\r
+  std::string folder_up (const std::string& directory, unsigned levels)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(directory);\r
+    for (unsigned i = 0; i < levels; i++)\r
+      spec.add_subpath("..");\r
+    spec.simplify();\r
+    return spec.image();\r
+  }\r
+\r
+  std::vector<std::string> folder_subdirectories (const std::string& directory)\r
+  {\r
+    return folder_wildcard(directory, "*", true, false);\r
+  }\r
+\r
+  std::vector<std::string> folder_files (const std::string& directory)\r
+  {\r
+    return folder_wildcard(directory, "*", false, true);\r
+  }\r
+\r
+  std::vector<std::string> folder_all(const std::string& directory)\r
+  {\r
+    return folder_wildcard(directory, "*", true, true);\r
+  }\r
+\r
+  std::vector<std::string> folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files)\r
+  {\r
+    std::string dir = directory.empty() ? std::string(".") : directory;\r
+    std::vector<std::string> results;\r
+#ifdef MSWINDOWS\r
+    std::string wildcard = create_filespec(dir, wild);\r
+    long handle = -1;\r
+    _finddata_t fileinfo;\r
+    for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))\r
+    {\r
+      std::string strentry = fileinfo.name;\r
+      if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+        if ((subdirs && (fileinfo.attrib & _A_SUBDIR)) || (files && !(fileinfo.attrib & _A_SUBDIR)))\r
+          results.push_back(strentry);\r
+    }\r
+    _findclose(handle);\r
+#else\r
+    DIR* d = opendir(dir.c_str());\r
+    if (d)\r
+    {\r
+      for (dirent* entry = readdir(d); entry; entry = readdir(d))\r
+      {\r
+        std::string strentry = entry->d_name;\r
+        if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+        {\r
+          std::string subpath = create_filespec(dir, strentry);\r
+          if (((subdirs && is_folder(subpath)) || (files && is_file(subpath))) && (wildcard(wild, strentry)))\r
+            results.push_back(strentry);\r
+        }\r
+      }\r
+      closedir(d);\r
+    }\r
+#endif\r
+    return results;\r
+  }\r
+\r
+  std::string folder_home (void)\r
+  {\r
+    if (getenv("HOME"))\r
+      return std::string(getenv("HOME"));\r
+#ifdef MSWINDOWS\r
+    if (getenv("HOMEDRIVE") || getenv("HOMEPATH"))\r
+      return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));\r
+    return "C:\\";\r
+#else\r
+    if (getenv("USER"))\r
+      return folder_down("/home", std::string(getenv("USER")));\r
+    if (getenv("USERNAME"))\r
+      return folder_down("/home", std::string(getenv("USERNAME")));\r
+    return "";\r
+#endif\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// path functions convert between full and relative paths\r
+\r
+  bool is_full_path(const std::string& path)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    return spec.absolute();\r
+  }\r
+\r
+  bool is_relative_path(const std::string& path)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    return spec.relative();\r
+  }\r
+\r
+  static std::string full_path(const std::string& root, const std::string& path)\r
+  {\r
+    // convert path to a full path using root as the start point for relative paths\r
+    // decompose the path and test whether it is already an absolute path, in which case just return it\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    if (spec.absolute()) return spec.image();\r
+    // okay, so the path is relative after all, so we need to combine it with the root path\r
+    // decompose the root path and check whether it is relative\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root.empty() ? std::string(".") : root);\r
+    if (rootspec.relative())\r
+      rootspec.make_absolute();\r
+    // Now do the conversion of the path relative to the root\r
+    spec.make_absolute(rootspec);\r
+    return spec.image();\r
+  }\r
+\r
+  static std::string relative_path(const std::string& root, const std::string& path)\r
+  {\r
+    // convert path to a relative path, using the root path as its starting point\r
+    // first convert both paths to full paths relative to CWD\r
+    file_specification rootspec;\r
+    rootspec.initialise_folder(root.empty() ? std::string(".") : root);\r
+    if (rootspec.relative())\r
+      rootspec.make_absolute();\r
+    file_specification spec;\r
+    spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+    if (spec.relative())\r
+      spec.make_absolute();\r
+    // now make path spec relative to the root spec\r
+    spec.make_relative(rootspec);\r
+    return spec.image();\r
+  }\r
+\r
+  std::string folder_to_path (const std::string& path, const std::string& directory)\r
+  {\r
+    return full_path(path, directory);\r
+  }\r
+\r
+  std::string filespec_to_path (const std::string& path, const std::string& spec)\r
+  {\r
+    return create_filespec(folder_to_path(path, folder_part(spec)),filename_part(spec));\r
+  }\r
+\r
+  std::string folder_to_path(const std::string& folder)\r
+  {\r
+    return folder_to_path(folder_current(), folder);\r
+  }\r
+\r
+  std::string filespec_to_path(const std::string& filespec)\r
+  {\r
+    return filespec_to_path(folder_current(), filespec);\r
+  }\r
+\r
+  std::string folder_to_relative_path(const std::string& root, const std::string& folder)\r
+  {\r
+    return relative_path(root, folder);\r
+  }\r
+\r
+  std::string filespec_to_relative_path(const std::string& root, const std::string& spec)\r
+  {\r
+    return create_filespec(folder_to_relative_path(root, folder_part(spec)),filename_part(spec));\r
+  }\r
+\r
+  std::string folder_to_relative_path(const std::string& folder)\r
+  {\r
+    return folder_to_relative_path(folder_current(), folder);\r
+  }\r
+\r
+  std::string filespec_to_relative_path(const std::string& filespec)\r
+  {\r
+    return filespec_to_relative_path(folder_current(), filespec);\r
+  }\r
+\r
+  std::string folder_append_separator(const std::string& folder)\r
+  {\r
+    std::string result = folder;\r
+    if (result.empty() || !is_separator(result[result.size()-1]))\r
+      result += preferred_separator;\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string basename_part (const std::string& spec)\r
+  {\r
+    std::string fname = filename_part(spec);\r
+    // scan back through filename until a '.' is found and remove suffix\r
+    // the whole filename is the basename if there is no '.'\r
+    std::string::size_type i = fname.find_last_of('.');\r
+    // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension\r
+    if (i != 0 && i != std::string::npos)\r
+      fname.erase(i, fname.size()-i);\r
+    return fname;\r
+  }\r
+\r
+  std::string filename_part (const std::string& spec)\r
+  {\r
+    // scan back through filename until a preferred_separator is found and remove prefix;\r
+    // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename\r
+    unsigned i = spec.size();\r
+    while (i--)\r
+    {\r
+      if (is_separator(spec[i]))\r
+        return spec.substr(i+1,spec.size()-i-1);\r
+    }\r
+    return spec;\r
+  }\r
+\r
+  std::string extension_part (const std::string& spec)\r
+  {\r
+    std::string fname = filename_part(spec);\r
+    // scan back through filename until a '.' is found and remove prefix;\r
+    std::string::size_type i = fname.find_last_of('.');\r
+    // observe Unix convention that a dot at the start of a filename is part of the name, not the extension;\r
+    if (i != 0 && i != std::string::npos)\r
+      fname.erase(0, i+1);\r
+    else\r
+      fname.erase();\r
+    return fname;\r
+  }\r
+\r
+  std::string folder_part (const std::string& spec)\r
+  {\r
+    // scan back through filename until a separator is found and remove prefix\r
+    // if there is no separator, remove the whole\r
+    unsigned i = spec.size();\r
+    while (i--)\r
+    {\r
+      if (is_separator(spec[i]))\r
+        return spec.substr(0,i);\r
+    }\r
+    return std::string();\r
+  }\r
+\r
+  std::vector<std::string> filespec_elements(const std::string& filespec)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_file(filespec);\r
+    std::vector<std::string> result = spec.path();\r
+    if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());\r
+    if (!spec.file().empty()) result.push_back(spec.file());\r
+    return result;\r
+  }\r
+\r
+  std::vector<std::string> folder_elements(const std::string& folder)\r
+  {\r
+    file_specification spec;\r
+    spec.initialise_folder(folder);\r
+    std::vector<std::string> result = spec.path();\r
+    if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// mimic the command lookup used by the shell\r
+\r
+// Windows looks at the following locations:\r
+// 1) application root\r
+// 2) current directory\r
+// 3) 32-bit system directory\r
+// 4) 16-bit system directory\r
+// 5) windows system directory\r
+// 6) %path%\r
+// currently only (2) and (6) has been implemented although many system folders are on the path anyway\r
+// also implement the implied .exe extension on commands with no path (see CreateProcess documentation)\r
+// TODO - PATHEXT handling to find non-exe executables\r
+\r
+  std::string path_lookup (const std::string& command)\r
+  {\r
+    std::string path = std::string(".") + PATH_SPLITTER + getenv("PATH");\r
+    return lookup(command, path);\r
+  }\r
+\r
+  std::string lookup (const std::string& command, const std::string& path, const std::string& splitter)\r
+  {\r
+    // first check whether the command is already a path and check whether it exists\r
+    if (!folder_part(command).empty())\r
+    {\r
+      if (file_exists(command))\r
+        return command;\r
+    }\r
+    else\r
+    {\r
+      // command is just a name - so do path lookup\r
+      // split the path into its elements\r
+      std::vector<std::string> paths;\r
+      if (!path.empty())\r
+      {\r
+        for(std::string::size_type offset = 0;;)\r
+        {\r
+          std::string::size_type found = path.find(splitter, offset);\r
+          if (found != std::string::npos)\r
+          {\r
+            paths.push_back(path.substr(offset, found-offset));\r
+            offset = found + splitter.size();\r
+          }\r
+          else\r
+          {\r
+            paths.push_back(path.substr(offset, path.size()-offset));\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      // now lookup each path to see if it its the matching one\r
+      for (unsigned i = 0; i < paths.size(); i++)\r
+      {\r
+        std::string spec = create_filespec(paths[i], command);\r
+        if (file_exists(spec))\r
+        {\r
+          return spec;\r
+        }\r
+      }\r
+    }\r
+#ifdef MSWINDOWS\r
+    // if there is no extension, try recursing on each possible extension\r
+    // TODO iterate through PATHEXT\r
+    if (extension_part(command).empty())\r
+      return lookup(create_filespec(folder_part(command), basename_part(command), "exe"), path, splitter);\r
+#endif\r
+    // if path lookup failed, return empty string to indicate error\r
+    return std::string();\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string install_path(const std::string& argv0)\r
+  {\r
+    std::string bin_directory = folder_part(argv0);\r
+    if (bin_directory.empty())\r
+    {\r
+      // do path lookup to find the executable path\r
+      bin_directory = folder_part(path_lookup(argv0));\r
+    }\r
+    return bin_directory;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/file_system.hpp b/src/stlplus/portability/file_system.hpp
new file mode 100644 (file)
index 0000000..374e86d
--- /dev/null
@@ -0,0 +1,201 @@
+#ifndef STLPLUS_FILE_SYSTEM\r
+#define STLPLUS_FILE_SYSTEM\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Simplified access to the File system\r
+\r
+//   All file system access and filename manipulation should be done\r
+//   with this package. Then it is only necessary to port this package\r
+//   to port all file handling.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <vector>\r
+#include <time.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // implement string comparison of paths - Unix is case-sensitive, Windows is case-insensitive\r
+\r
+  bool path_compare(const std::string& l, const std::string& r);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // classifying functions\r
+\r
+  // test for whether there's something (i.e. folder or file) with this name\r
+  bool is_present(const std::string& thing);\r
+  // test for whether there's something present and its a folder\r
+  bool is_folder(const std::string& thing);\r
+  // test for whether there's something present and its a file\r
+  bool is_file(const std::string& thing);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // file functions\r
+\r
+  // tests whether there's a file of this name\r
+  bool file_exists(const std::string& filespec);\r
+  // tests whether the file is readable - i.e. exists and has read mode set\r
+  bool file_readable(const std::string& filespec);\r
+  // tests whether file is writable - either it exists and is writable or doesn't exist but is in a writable folder\r
+  bool file_writable(const std::string& filespec);\r
+  // the size of the file in bytes - 0 if doesn't exist\r
+  size_t file_size(const std::string& filespec);\r
+  // delete the file - returns true if the delete succeeded\r
+  bool file_delete(const std::string& filespec);\r
+  // rename the file - returns true if the rename succeeded\r
+  bool file_rename (const std::string& old_filespec, const std::string& new_filespec);\r
+  // make an exact copy of the file - returns true if it succeeded\r
+  bool file_copy (const std::string& old_filespec, const std::string& new_filespec);\r
+  // move the file - tries to rename, if that fails, tries to copy - returns true if either of these succeeded\r
+  bool file_move (const std::string& old_filespec, const std::string& new_filespec);\r
+\r
+  // get the file's time stamps as a time_t - see the stlplus time.hpp package\r
+\r
+  // time the file was originally created\r
+  time_t file_created(const std::string& filespec);\r
+  // time the file was last modified\r
+  time_t file_modified(const std::string& filespec);\r
+  // time the file was accessed\r
+  time_t file_accessed(const std::string& filespec);\r
+\r
+  // platform-specific string handling to combine a directory and filename into a path\r
+\r
+  // combine a folder with a filename (basename.extension)\r
+  std::string create_filespec(const std::string& folder, const std::string& filename);\r
+  // combine a folder, a basename and an extension - extension does not need the .\r
+  std::string create_filespec(const std::string& folder, const std::string& basename, const std::string& extension);\r
+  // combine a basename and an extension - extension does not need the .\r
+  std::string create_filename(const std::string& basename, const std::string& extension);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // folder functions\r
+\r
+  // craete a folder - returns true if successful\r
+  bool folder_create(const std::string& folder);\r
+  // tests for whether the folder exists, i.e. there is something of that name and its a folder\r
+  bool folder_exists(const std::string& folder);\r
+  // test whether the folder contents are readable\r
+  bool folder_readable(const std::string& folder);\r
+  // tests whether the folder can be written to - for example to create a new file\r
+  bool folder_writable(const std::string& folder);\r
+  // delete the folder, optionally deleting the contents first - only succeeds if everything could be deleted\r
+  bool folder_delete(const std::string& folder, bool recurse = false);\r
+  // rename the folder - this probably only works within a disk/partition\r
+  bool folder_rename (const std::string& old_directory, const std::string& new_directory);\r
+  // test whether the folder is empty (of files)\r
+  bool folder_empty(const std::string& folder);\r
+\r
+  // set the current folder\r
+  bool folder_set_current(const std::string& folder);\r
+\r
+  // platform-specific string handling to retrieve some special locations\r
+  // these do not check whether the folder exists, they just process strings\r
+\r
+  // get the current folder\r
+  std::string folder_current(void);\r
+  // get the current folder as a full path\r
+  std::string folder_current_full(void);\r
+  // get the home folder - $HOME or %HOMEDRIVE%%HOMEPATH%\r
+  std::string folder_home(void);\r
+  // go down a level in the folder hierarchy\r
+  std::string folder_down(const std::string& folder, const std::string& subfolder);\r
+  // go up a level in the folder hierarchy\r
+  std::string folder_up(const std::string& folder, unsigned levels = 1);\r
+\r
+  // get folder contents\r
+\r
+  // the set of all subdirectories\r
+  std::vector<std::string> folder_subdirectories(const std::string& folder);\r
+  // the set of all files\r
+  std::vector<std::string> folder_files(const std::string& folder);\r
+  // the set of all folders and files\r
+  std::vector<std::string> folder_all(const std::string& folder);\r
+  // the set of all folder contents matching a wildcard string\r
+  // if folders is true, include folders; if files is true, include files\r
+  std::vector<std::string> folder_wildcard(const std::string& folder,\r
+                                           const std::string& wildcard,\r
+                                           bool folders = true,\r
+                                           bool files = true);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // path functions\r
+\r
+  // string manipulations of paths\r
+\r
+  // test whether a string represents a full path or a relative one\r
+  bool is_full_path(const std::string& path);\r
+  bool is_relative_path(const std::string& path);\r
+\r
+  // convert to a full path relative to the root path\r
+  std::string folder_to_path(const std::string& root, const std::string& folder);\r
+  std::string filespec_to_path(const std::string& root, const std::string& filespec);\r
+\r
+  // convert to a full path relative to the current working directory\r
+  std::string folder_to_path(const std::string& folder);\r
+  std::string filespec_to_path(const std::string& filespec);\r
+\r
+  // convert to a relative path relative to the root path\r
+  std::string folder_to_relative_path(const std::string& root, const std::string& folder);\r
+  std::string filespec_to_relative_path(const std::string& root, const std::string& filespec);\r
+\r
+  // convert to a relative path relative to the current working directory\r
+  std::string folder_to_relative_path(const std::string& folder);\r
+  std::string filespec_to_relative_path(const std::string& filespec);\r
+\r
+  // append a folder separator to the path to make it absolutely clear that it is a folder\r
+  std::string folder_append_separator(const std::string& folder);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // access functions split a filespec into its elements\r
+\r
+  // get the basename - that is, the name of the file without folder or extension parts\r
+  std::string basename_part(const std::string& filespec);\r
+  // get the filename - that is, the name of the file without folder part but with extension\r
+  std::string filename_part(const std::string& filespec);\r
+  // get the extension - that is the part of the filename after the . (and excluding the .)\r
+  std::string extension_part(const std::string& filespec);\r
+  // get the folder part - that is the filespec with the filename removed\r
+  std::string folder_part(const std::string& filespec);\r
+\r
+  // split a path into a vector of elements - i.e. split at the folder separator\r
+  std::vector<std::string> folder_elements(const std::string& folder);\r
+  std::vector<std::string> filespec_elements(const std::string& filespec);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Path lookup functions\r
+\r
+#ifdef MSWINDOWS\r
+#define PATH_SPLITTER ";"\r
+#else\r
+#define PATH_SPLITTER ":"\r
+#endif\r
+\r
+  // The lookup normally carried out by the shell to find a command in a\r
+  // directory in the PATH. Give this function the name of a command and it\r
+  // will return the full path. It returns an empty string on failure.\r
+  std::string path_lookup (const std::string& command);\r
+\r
+  // Generalised form of the above, takes a second argument\r
+  // - the list to search. This can be used to do other path lookups,\r
+  // such as LD_LIBRARY_PATH. The third argument specifies the splitter -\r
+  // the default value of PATH_SPLITTER is appropriate for environment variables.\r
+  std::string lookup (const std::string& file, const std::string& path, const std::string& splitter = PATH_SPLITTER);\r
+\r
+  // utility function for finding the folder that contains the current executable\r
+  // the argument is the argv[0] parameter passed to main\r
+  std::string install_path(const std::string& argv0);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/inf.cpp b/src/stlplus/portability/inf.cpp
new file mode 100644 (file)
index 0000000..80b0475
--- /dev/null
@@ -0,0 +1,1482 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   The integer is represented as a sequence of bytes. They are stored such that\r
+//   element 0 is the lsB, which makes sense when seen as an integer offset but\r
+//   is counter-intuitive when you think that a string is usually read from left\r
+//   to right, 0 to size-1, in which case the lsB is on the *left*.\r
+\r
+//   This solution is compatible with 32-bit and 64-bit machines with either\r
+//   little-endian or big-endian representations of integers.\r
+\r
+//   Problem: I'm using std::string, which is an array of char. However, char is\r
+//   not well-defined - it could be signed or unsigned.\r
+\r
+//   In fact, there's no requirement for a char to even be one byte - it can be\r
+//   any size of one byte or more. However, it's just impossible to make any\r
+//   progress with that naffness (thanks to the C non-standardisation committee)\r
+//   and the practice is that char on every platform/compiler I've ever come\r
+//   across is that char = byte.\r
+\r
+//   The algorithms here use unsigned char to represent bit-patterns so I have to\r
+//   be careful to type-cast from char to unsigned char a lot. I use a typedef to\r
+//   make life easier.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "inf.hpp"\r
+#include <ctype.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // choose a sensible C type for a byte\r
+\r
+  typedef unsigned char byte;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // local functions\r
+\r
+  // removes leading bytes that don't contribute to the value to create the minimum string representation\r
+  static void reduce_string(std::string& data)\r
+  {\r
+    while(data.size() > 1 && \r
+          ((byte(data[data.size()-1]) == byte(0) && byte(data[data.size()-2]) < byte(128)) ||\r
+           (byte(data[data.size()-1]) == byte(255) && byte(data[data.size()-2]) >= byte(128))))\r
+    {\r
+      data.erase(data.end()-1);\r
+    }\r
+  }\r
+\r
+  // generic implementations of type conversions from integer type to internal representation\r
+  // data: integer value for conversion\r
+  // result: internal representation\r
+\r
+  template <typename T>\r
+  static void convert_from_signed(const T& data, std::string& result)\r
+  {\r
+    result.erase();\r
+    bool lsb_first = little_endian();\r
+    byte* address = (byte*)&data;\r
+    for (size_t i = 0; i < sizeof(T); i++)\r
+    {\r
+      size_t offset = (lsb_first ? i : (sizeof(T) - i - 1));\r
+      result.append(1,address[offset]);\r
+    }\r
+    reduce_string(result);\r
+  }\r
+\r
+  template <typename T>\r
+  static void convert_from_unsigned(const T& data, std::string& result)\r
+  {\r
+    result.erase();\r
+    bool lsb_first = little_endian();\r
+    byte* address = (byte*)&data;\r
+    for (size_t i = 0; i < sizeof(T); i++)\r
+    {\r
+      size_t offset = (lsb_first ? i : (sizeof(T) - i - 1));\r
+      result.append(1,address[offset]);\r
+    }\r
+    // inf is signed - so there is a possible extra sign bit to add\r
+    result.append(1,std::string::value_type(0));\r
+    reduce_string(result);\r
+  }\r
+\r
+  // generic implementations of type conversions from internal representation to an integer type\r
+  // data : string representation of integer\r
+  // result: integer result of conversion\r
+  // return: flag indicating success - false = overflow\r
+\r
+  template <class T>\r
+  bool convert_to_signed(const std::string& data, T& result)\r
+  {\r
+    bool lsb_first = little_endian();\r
+    byte* address = (byte*)&result;\r
+    for (size_t i = 0; i < sizeof(T); i++)\r
+    {\r
+      size_t offset = lsb_first ? i : (sizeof(T) - i - 1);\r
+      if (i < data.size())\r
+        address[offset] = byte(data[i]);\r
+      else if (data.empty() || (byte(data[data.size()-1]) < byte(128)))\r
+        address[offset] = byte(0);\r
+      else\r
+        address[offset] = byte(255);\r
+    }\r
+    return data.size() <= sizeof(T);\r
+  }\r
+\r
+  template <class T>\r
+  bool convert_to_unsigned(const std::string& data, T& result)\r
+  {\r
+    bool lsb_first = little_endian();\r
+    byte* address = (byte*)&result;\r
+    for (size_t i = 0; i < sizeof(T); i++)\r
+    {\r
+      size_t offset = lsb_first ? i : (sizeof(T) - i - 1);\r
+      if (i < data.size())\r
+        address[offset] = byte(data[i]);\r
+      else\r
+        address[offset] = byte(0);\r
+    }\r
+    return data.size() <= sizeof(T);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions to string\r
+\r
+  static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz";\r
+  static int from_char [] = \r
+  {\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\r
+  };\r
+\r
+  static void convert_to_string(const stlplus::inf& data, std::string& result, unsigned radix = 10)\r
+    throw(std::invalid_argument)\r
+  {\r
+    // only support the C-style radixes plus 0b for binary\r
+    if (radix != 2 && radix != 8 && radix != 10 && radix != 16)\r
+      throw std::invalid_argument("invalid radix value");\r
+    inf local_i = data;\r
+    // untangle all the options\r
+    bool binary = radix == 2;\r
+    bool octal = radix == 8;\r
+    bool hex = radix == 16;\r
+    // the C representations for binary, octal and hex use 2's-complement representation\r
+    // all other represenations use sign-magnitude\r
+    if (hex || octal || binary)\r
+    {\r
+      // bit-pattern representation\r
+      // this is the binary representation optionally shown in octal or hex\r
+      // first generate the binary by masking the bits\r
+      for (unsigned j = local_i.bits(); j--; )\r
+        result += (local_i.bit(j) ? '1' : '0');\r
+      // the result is now the full width of the type - e.g. int will give a 32-bit result\r
+      // now interpret this as either binary, octal or hex and add the prefix\r
+      if (binary)\r
+      {\r
+        // trim down to the smallest string that preserves the value\r
+        while (true)\r
+        {\r
+          // do not trim to less than 1 bit (sign only)\r
+          if (result.size() <= 1) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0b");\r
+      }\r
+      else if (octal)\r
+      {\r
+        // the result is currently binary\r
+        // trim down to the smallest string that preserves the value\r
+        while (true)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier\r
+        while (result.size() % 3 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to octal\r
+        std::string octal_result;\r
+        for (unsigned i = 0; i < result.size()/3; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*3] == '0')\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '0';\r
+              else\r
+                octal_result += '1';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '2';\r
+              else\r
+                octal_result += '3';\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '4';\r
+              else\r
+                octal_result += '5';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '6';\r
+              else\r
+                octal_result += '7';\r
+            }\r
+          }\r
+        }\r
+        result = octal_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0");\r
+      }\r
+      else\r
+      {\r
+        // similar to octal\r
+        while (true)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        // pad to a multiple of 4 characters\r
+        while (result.size() % 4 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to hex\r
+        std::string hex_result;\r
+        for (unsigned i = 0; i < result.size()/4; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*4] == '0')\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '0';\r
+                else\r
+                  hex_result += '1';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '2';\r
+                else\r
+                  hex_result += '3';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '4';\r
+                else\r
+                  hex_result += '5';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '6';\r
+                else\r
+                  hex_result += '7';\r
+              }\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '8';\r
+                else\r
+                  hex_result += '9';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'a';\r
+                else\r
+                  hex_result += 'b';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'c';\r
+                else\r
+                  hex_result += 'd';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'e';\r
+                else\r
+                  hex_result += 'f';\r
+              }\r
+            }\r
+          }\r
+        }\r
+        result = hex_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0x");\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // convert to sign-magnitude\r
+      // the representation is:\r
+      // [sign]magnitude\r
+      bool negative = local_i.negative();\r
+      local_i.abs();\r
+      // create a representation of the magnitude by successive division\r
+      inf inf_radix(radix);\r
+      do\r
+      {\r
+        std::pair<inf,inf> divided = local_i.divide(inf_radix);\r
+        unsigned remainder = divided.second.to_unsigned();\r
+        char digit = to_char[remainder];\r
+        result.insert((std::string::size_type)0, 1, digit);\r
+        local_i = divided.first;\r
+      }\r
+      while(!local_i.zero());\r
+      // add the prefixes\r
+      // add a sign only for negative values\r
+      if (negative)\r
+        result.insert((std::string::size_type)0, 1, '-');\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions FROM string\r
+\r
+  void convert_from_string(const std::string& str, inf& result, unsigned radix = 10) throw(std::invalid_argument)\r
+  {\r
+    result = 0;\r
+    // only support the C-style radixes plus 0b for binary\r
+    // a radix of 0 means deduce the radix from the input - assume 10\r
+    if (radix != 0 && radix != 2 && radix != 8 && radix != 10 && radix != 16)\r
+      throw std::invalid_argument("invalid radix value");\r
+    unsigned i = 0;\r
+    // the radix passed as a parameter is just the default - it can be\r
+    // overridden by the C prefix\r
+    // Note: a leading zero is the C-style prefix for octal - I only make this\r
+    // override the default when the default radix is not specified\r
+    // first check for a C-style prefix\r
+    bool c_style = false;\r
+    if (i < str.size() && str[i] == '0')\r
+    {\r
+      // binary or hex\r
+      if (i+1 < str.size() && tolower(str[i+1]) == 'x')\r
+      {\r
+        c_style = true;\r
+        radix = 16;\r
+        i += 2;\r
+      }\r
+      else if (i+1 < str.size() && tolower(str[i+1]) == 'b')\r
+      {\r
+        c_style = true;\r
+        radix = 2;\r
+        i += 2;\r
+      }\r
+      else if (radix == 0)\r
+      {\r
+        c_style = true;\r
+        radix = 8;\r
+        i += 1;\r
+      }\r
+    }\r
+    if (radix == 0)\r
+      radix = 10;\r
+    if (c_style)\r
+    {\r
+      // the C style formats are bit patterns not integer values - these need\r
+      // to be sign-extended to get the right value\r
+      std::string binary;\r
+      if (radix == 2)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += '0';\r
+            break;\r
+          case '1':\r
+            binary += '1';\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid binary character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      else if (radix == 8)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += "000";\r
+            break;\r
+          case '1':\r
+            binary += "001";\r
+            break;\r
+          case '2':\r
+            binary += "010";\r
+            break;\r
+          case '3':\r
+            binary += "011";\r
+            break;\r
+          case '4':\r
+            binary += "100";\r
+            break;\r
+          case '5':\r
+            binary += "101";\r
+            break;\r
+          case '6':\r
+            binary += "110";\r
+            break;\r
+          case '7':\r
+            binary += "111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid octal character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      else\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(tolower(str[j]))\r
+          {\r
+          case '0':\r
+            binary += "0000";\r
+            break;\r
+          case '1':\r
+            binary += "0001";\r
+            break;\r
+          case '2':\r
+            binary += "0010";\r
+            break;\r
+          case '3':\r
+            binary += "0011";\r
+            break;\r
+          case '4':\r
+            binary += "0100";\r
+            break;\r
+          case '5':\r
+            binary += "0101";\r
+            break;\r
+          case '6':\r
+            binary += "0110";\r
+            break;\r
+          case '7':\r
+            binary += "0111";\r
+            break;\r
+          case '8':\r
+            binary += "1000";\r
+            break;\r
+          case '9':\r
+            binary += "1001";\r
+            break;\r
+          case 'a':\r
+            binary += "1010";\r
+            break;\r
+          case 'b':\r
+            binary += "1011";\r
+            break;\r
+          case 'c':\r
+            binary += "1100";\r
+            break;\r
+          case 'd':\r
+            binary += "1101";\r
+            break;\r
+          case 'e':\r
+            binary += "1110";\r
+            break;\r
+          case 'f':\r
+            binary += "1111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid hex character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      // now convert the value\r
+      result.resize(binary.size());\r
+      for (unsigned j = 0; j < binary.size(); j++)\r
+        result.preset(binary.size() - j - 1, binary[j] == '1');\r
+    }\r
+    else\r
+    {\r
+      // sign-magnitude representation\r
+      // now scan for a sign and find whether this is a negative number\r
+      bool negative = false;\r
+      if (i < str.size())\r
+      {\r
+        switch (str[i])\r
+        {\r
+        case '-':\r
+          negative = true;\r
+          i++;\r
+          break;\r
+        case '+':\r
+          i++;\r
+          break;\r
+        }\r
+      }\r
+      for (; i < str.size(); i++)\r
+      {\r
+        result *= inf(radix);\r
+        unsigned char ascii = (unsigned char)str[i];\r
+        int ch = from_char[ascii] ;\r
+        if (ch == -1)\r
+          throw std::invalid_argument("invalid decimal character in string " + str);\r
+        result += inf(ch);\r
+      }\r
+      if (negative)\r
+        result.negate();\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // constructors - mostly implemented in terms of the assignment operators\r
+\r
+  inf::inf(void)\r
+  {\r
+    // void constructor initialises to zero - represented as a single-byte value containing zero\r
+    m_data.append(1,std::string::value_type(0));\r
+  }\r
+\r
+  inf::inf(short r)\r
+  {\r
+    operator=(r);\r
+  }\r
+\r
+  inf::inf(unsigned short r)\r
+  {\r
+    operator=(r);\r
+  }\r
+\r
+  inf::inf(int r)\r
+  {\r
+    operator=(r);\r
+  }\r
+\r
+  inf::inf(unsigned r)\r
+  {\r
+    operator=(r);\r
+  }\r
+\r
+  inf::inf(long r)\r
+  {\r
+    operator=(r);\r
+  }\r
+\r
+  inf::inf(unsigned long r)\r
+  {\r
+    operator=(r);\r
+  }\r
+\r
+  inf::inf (const std::string& r) throw(std::invalid_argument)\r
+  {\r
+    operator=(r);\r
+  }\r
+\r
+  inf::inf(const inf& r)\r
+  {\r
+#ifdef __BORLANDC__\r
+    // work round bug in Borland compiler - copy constructor fails if string\r
+    // contains null characters, so do my own copy\r
+    for (unsigned i = 0; i < r.m_data.size(); i++)\r
+      m_data += r.m_data[i];\r
+#else\r
+    m_data = r.m_data;\r
+#endif\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  inf::~inf(void)\r
+  {\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // assignments convert from iteger types to internal representation\r
+\r
+  inf& inf::operator = (short r)\r
+  {\r
+    convert_from_signed(r, m_data);\r
+    return *this;\r
+  }\r
+\r
+  inf& inf::operator = (unsigned short r)\r
+  {\r
+    convert_from_unsigned(r, m_data);\r
+    return *this;\r
+  }\r
+\r
+  inf& inf::operator = (int r)\r
+  {\r
+    convert_from_signed(r, m_data);\r
+    return *this;\r
+  }\r
+\r
+  inf& inf::operator = (unsigned r)\r
+  {\r
+    convert_from_unsigned(r, m_data);\r
+    return *this;\r
+  }\r
+\r
+  inf& inf::operator = (long r)\r
+  {\r
+    convert_from_signed(r, m_data);\r
+    return *this;\r
+  }\r
+\r
+  inf& inf::operator = (unsigned long r)\r
+  {\r
+    convert_from_unsigned(r, m_data);\r
+    return *this;\r
+  }\r
+\r
+  inf& inf::operator = (const std::string& r) throw(std::invalid_argument)\r
+  {\r
+    convert_from_string(r, *this);\r
+    return *this;\r
+  }\r
+\r
+  inf& inf::operator = (const inf& r)\r
+  {\r
+    m_data = r.m_data;\r
+    return *this;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  short inf::to_short(bool truncate) const throw(std::overflow_error)\r
+  {\r
+    short result = 0;\r
+    if (!convert_to_signed(m_data, result))\r
+      if (!truncate)\r
+        throw std::overflow_error("stlplus::inf::to_short");\r
+    return result;\r
+  }\r
+\r
+  unsigned short inf::to_unsigned_short(bool truncate) const throw(std::overflow_error)\r
+  {\r
+    unsigned short result = 0;\r
+    if (!convert_to_unsigned(m_data, result))\r
+      if (!truncate)\r
+        throw std::overflow_error("stlplus::inf::to_unsigned_short");\r
+    return result;\r
+  }\r
+\r
+  int inf::to_int(bool truncate) const throw(std::overflow_error)\r
+  {\r
+    int result = 0;\r
+    if (!convert_to_signed(m_data, result))\r
+      if (!truncate)\r
+        throw std::overflow_error("stlplus::inf::to_int");\r
+    return result;\r
+  }\r
+\r
+  unsigned inf::to_unsigned(bool truncate) const throw(std::overflow_error)\r
+  {\r
+    unsigned result = 0;\r
+    if (!convert_to_unsigned(m_data, result))\r
+      if (!truncate)\r
+        throw std::overflow_error("stlplus::inf::to_unsigned");\r
+    return result;\r
+  }\r
+\r
+  long inf::to_long(bool truncate) const throw(std::overflow_error)\r
+  {\r
+    long result = 0;\r
+    if (!convert_to_signed(m_data, result))\r
+      if (!truncate)\r
+        throw std::overflow_error("stlplus::inf::to_long");\r
+    return result;\r
+  }\r
+\r
+  unsigned long inf::to_unsigned_long(bool truncate) const throw(std::overflow_error)\r
+  {\r
+    unsigned long result = 0;\r
+    if (!convert_to_unsigned(m_data, result))\r
+      if (!truncate)\r
+        throw std::overflow_error("stlplus::inf::to_unsigned_long");\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // resize the inf regardless of the data\r
+\r
+  void inf::resize(unsigned bits)\r
+  {\r
+    if (bits == 0) bits = 1;\r
+    unsigned bytes = (bits+7)/8;\r
+    byte extend = negative() ? byte(255) : byte (0);\r
+    while(bytes > m_data.size())\r
+      m_data.append(1,extend);\r
+  }\r
+\r
+  // reduce the bit count to the minimum needed to preserve the value\r
+\r
+  void inf::reduce(void)\r
+  {\r
+    reduce_string(m_data);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // the number of significant bits in the number\r
+\r
+  unsigned inf::bits (void) const\r
+  {\r
+    // The number of significant bits in the integer value - this is the number\r
+    // of indexable bits less any redundant sign bits at the msb\r
+    // This does not assume that the inf has been reduced to its minimum form\r
+    unsigned result = indexable_bits();\r
+    bool sign = bit(result-1);\r
+    while (result > 1 && (sign == bit(result-2)))\r
+      result--;\r
+    return result;\r
+  }\r
+\r
+  unsigned inf::size(void) const\r
+  {\r
+    return bits();\r
+  }\r
+\r
+  unsigned inf::indexable_bits (void) const\r
+  {\r
+    return 8 * unsigned(m_data.size());\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // bitwise operations\r
+\r
+  bool inf::bit (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    if (index >= indexable_bits())\r
+      throw std::out_of_range(std::string("stlplus::inf::bit"));\r
+    // first split the offset into byte offset and bit offset\r
+    unsigned byte_offset = index/8;\r
+    unsigned bit_offset = index%8;\r
+    return (byte(m_data[byte_offset]) & (byte(1) << bit_offset)) != 0;\r
+  }\r
+\r
+  bool inf::operator [] (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    return bit(index);\r
+  }\r
+\r
+  void inf::set (unsigned index)  throw(std::out_of_range)\r
+  {\r
+    if (index >= indexable_bits())\r
+      throw std::out_of_range(std::string("stlplus::inf::set"));\r
+    // first split the offset into byte offset and bit offset\r
+    unsigned byte_offset = index/8;\r
+    unsigned bit_offset = index%8;\r
+    m_data[byte_offset] |= (byte(1) << bit_offset);\r
+  }\r
+\r
+  void inf::clear (unsigned index)  throw(std::out_of_range)\r
+  {\r
+    if (index >= indexable_bits())\r
+      throw std::out_of_range(std::string("stlplus::inf::clear"));\r
+    // first split the offset into byte offset and bit offset\r
+    unsigned byte_offset = index/8;\r
+    unsigned bit_offset = index%8;\r
+    m_data[byte_offset] &= (~(byte(1) << bit_offset));\r
+  }\r
+\r
+  void inf::preset (unsigned index, bool value)  throw(std::out_of_range)\r
+  {\r
+    if (value)\r
+      set(index);\r
+    else\r
+      clear(index);\r
+  }\r
+\r
+  inf inf::slice(unsigned low, unsigned high) const throw(std::out_of_range)\r
+  {\r
+    if (low >= indexable_bits())\r
+      throw std::out_of_range(std::string("stlplus::inf::slice: low index"));\r
+    if (high >= indexable_bits())\r
+      throw std::out_of_range(std::string("stlplus::inf::slice: high index"));\r
+    inf result;\r
+    if (high >= low)\r
+    {\r
+      // create a result the right size and filled with sign bits\r
+      std::string::size_type result_size = (high-low+1+7)/8;\r
+      result.m_data.erase();\r
+      byte extend = bit(high) ? byte(255) : byte (0);\r
+      while (result.m_data.size() < result_size)\r
+        result.m_data.append(1,extend);\r
+      // now set the relevant bits\r
+      for (unsigned i = low; i <= high; i++)\r
+        result.preset(i-low, bit(i));\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // testing operations\r
+\r
+  bool inf::negative (void) const\r
+  {\r
+    return bit(indexable_bits()-1);\r
+  }\r
+\r
+  bool inf::natural (void) const\r
+  {\r
+    return !negative();\r
+  }\r
+\r
+  bool inf::positive (void) const\r
+  {\r
+    return natural() && !zero();\r
+  }\r
+\r
+  bool inf::zero (void) const\r
+  {\r
+    for (std::string::size_type i = 0; i < m_data.size(); i++)\r
+      if (m_data[i] != 0)\r
+        return false;\r
+    return true;\r
+  }\r
+\r
+  bool inf::non_zero (void) const\r
+  {\r
+    return !zero();\r
+  }\r
+\r
+  bool inf::operator ! (void) const\r
+  {\r
+    return zero();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // comparison operators\r
+\r
+  bool inf::operator == (const inf& r) const\r
+  {\r
+    // Two infs are equal if they are numerically equal, even if they are\r
+    // different sizes (i.e. they could be non-reduced values).\r
+    // This makes life a little more complicated than if I could assume that values were reduced.\r
+    byte l_extend = negative() ? byte(255) : byte (0);\r
+    byte r_extend = r.negative() ? byte(255) : byte (0);\r
+    std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+    for (std::string::size_type i = bytes; i--; )\r
+    {\r
+      byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+      byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+      if (l_byte != r_byte)\r
+        return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+  bool inf::operator != (const inf& r) const\r
+  {\r
+    return !operator==(r);\r
+  }\r
+\r
+  bool inf::operator < (const inf& r) const\r
+  {\r
+    // This could be implemented in terms of subtraction. However, it can be\r
+    // simplified since there is no need to calculate the accurate difference,\r
+    // just the direction of the difference. I compare from msB down and as\r
+    // soon as a byte difference is found, that defines the ordering. The\r
+    // problem is that in 2's-complement, all negative values are greater than\r
+    // all natural values if you just do a straight unsigned comparison. I\r
+    // handle this by doing a preliminary test for different signs.\r
+\r
+    // For example, a 3-bit signed type has the coding:\r
+    // 000 = 0\r
+    // ...\r
+    // 011 = 3\r
+    // 100 = -4\r
+    // ...\r
+    // 111 = -1\r
+\r
+    // So, for natural values, the ordering of the integer values is the\r
+    // ordering of the bit patterns. Similarly, for negative values, the\r
+    // ordering of the integer values is the ordering of the bit patterns\r
+    // However, the bit patterns for the negative values are *greater than*\r
+    // the natural values. This is a side-effect of the naffness of\r
+    // 2's-complement representation\r
+\r
+    // first handle the case of comparing two values with different signs\r
+    bool l_sign = negative();\r
+    bool r_sign = r.negative();\r
+    if (l_sign != r_sign)\r
+    {\r
+      // one argument must be negative and the other natural\r
+      // the left is less if it is the negative one\r
+      return l_sign;\r
+    }\r
+    // the arguments are the same sign\r
+    // so the ordering is a simple unsigned byte-by-byte comparison\r
+    // However, this is complicated by the possibility that the values could be different lengths\r
+    byte l_extend = l_sign ? byte(255) : byte (0);\r
+    byte r_extend = r_sign ? byte(255) : byte (0);\r
+    std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+    for (std::string::size_type i = bytes; i--; )\r
+    {\r
+      byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+      byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+      if (l_byte != r_byte)\r
+        return l_byte < r_byte;\r
+    }\r
+    // if I get here, the two are equal, so that is not less-than\r
+    return false;\r
+  }\r
+\r
+  bool inf::operator <= (const inf& r) const\r
+  {\r
+    return !(r < *this);\r
+  }\r
+\r
+  bool inf::operator > (const inf& r) const\r
+  {\r
+    return r < *this;\r
+  }\r
+\r
+  bool inf::operator >= (const inf& r) const\r
+  {\r
+    return !(*this < r);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // logical operators\r
+\r
+  inf& inf::invert (void)\r
+  {\r
+    for (std::string::size_type i = 0; i < m_data.size(); i++)\r
+      m_data[i] = ~m_data[i];\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator ~ (void) const\r
+  {\r
+    inf result(*this);\r
+    result.invert();\r
+    return result;\r
+  }\r
+\r
+  inf& inf::operator &= (const inf& r)\r
+  {\r
+    // bitwise AND is extended to the length of the largest argument\r
+    byte l_extend = negative() ? byte(255) : byte (0);\r
+    byte r_extend = r.negative() ? byte(255) : byte (0);\r
+    std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+    for (std::string::size_type i = 0; i < bytes; i++)\r
+    {\r
+      byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+      byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+      byte result = l_byte & r_byte;\r
+      if (i < m_data.size())\r
+        m_data[i] = result;\r
+      else\r
+        m_data.append(1,result);\r
+    }\r
+    // now reduce the result\r
+    reduce();\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator & (const inf& r) const\r
+  {\r
+    inf result(*this);\r
+    result &= r;\r
+    return result;\r
+  }\r
+\r
+  inf& inf::operator |= (const inf& r)\r
+  {\r
+    // bitwise OR is extended to the length of the largest argument\r
+    byte l_extend = negative() ? byte(255) : byte (0);\r
+    byte r_extend = r.negative() ? byte(255) : byte (0);\r
+    std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+    for (std::string::size_type i = 0; i < bytes; i++)\r
+    {\r
+      byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+      byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+      byte result = l_byte | r_byte;\r
+      if (i < m_data.size())\r
+        m_data[i] = result;\r
+      else\r
+        m_data.append(1,result);\r
+    }\r
+    // now reduce the result\r
+    reduce();\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator | (const inf& r) const\r
+  {\r
+    inf result(*this);\r
+    result |= r;\r
+    return result;\r
+  }\r
+\r
+  inf& inf::operator ^= (const inf& r)\r
+  {\r
+    // bitwise XOR is extended to the length of the largest argument\r
+    byte l_extend = negative() ? byte(255) : byte (0);\r
+    byte r_extend = r.negative() ? byte(255) : byte (0);\r
+    std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+    for (std::string::size_type i = 0; i < bytes; i++)\r
+    {\r
+      byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+      byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+      byte result = l_byte ^ r_byte;\r
+      if (i < m_data.size())\r
+        m_data[i] = result;\r
+      else\r
+        m_data.append(1,result);\r
+    }\r
+    // now reduce the result\r
+    reduce();\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator ^ (const inf& r) const\r
+  {\r
+    inf result(*this);\r
+    result ^= r;\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // shift operators all preserve the value by increasing the word size\r
+\r
+  inf& inf::operator <<= (unsigned shift)\r
+  {\r
+    // left shift is a shift towards the msb, with 0s being shifted in at the lsb\r
+    // split this into a byte shift followed by a bit shift\r
+\r
+    // first expand the value to be big enough for the result\r
+    std::string::size_type new_size = (indexable_bits() + shift + 7) / 8;\r
+    byte extend = negative() ? byte(255) : byte (0);\r
+    while (m_data.size() < new_size)\r
+      m_data.append(1,extend);\r
+    // now do the byte shift\r
+    unsigned byte_shift = shift/8;\r
+    if (byte_shift > 0)\r
+    {\r
+      for (std::string::size_type b = new_size; b--; )\r
+        m_data[b] = (b >= byte_shift) ? m_data[b-byte_shift] : byte(0);\r
+    }\r
+    // and finally the bit shift\r
+    unsigned bit_shift = shift%8;\r
+    if (bit_shift > 0)\r
+    {\r
+      for (std::string::size_type b = new_size; b--; )\r
+      {\r
+        byte current = byte(m_data[b]);\r
+        byte previous = b > 0 ? m_data[b-1] : byte(0);\r
+        m_data[b] = (current << bit_shift) | (previous >> (8 - bit_shift));\r
+      }\r
+    }\r
+    // now reduce the result\r
+    reduce();\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator << (unsigned shift) const\r
+  {\r
+    inf result(*this);\r
+    result <<= shift;\r
+    return result;\r
+  }\r
+\r
+  inf& inf::operator >>= (unsigned shift)\r
+  {\r
+    // right shift is a shift towards the lsb, with sign bits being shifted in at the msb\r
+    // split this into a byte shift followed by a bit shift\r
+\r
+    // a byte of sign bits\r
+    byte extend = negative() ? byte(255) : byte (0);\r
+    // do the byte shift\r
+    unsigned byte_shift = shift/8;\r
+    if (byte_shift > 0)\r
+    {\r
+      for (std::string::size_type b = 0; b < m_data.size(); b++)\r
+        m_data[b] = (b + byte_shift < m_data.size()) ? m_data[b+byte_shift] : extend;\r
+    }\r
+    // and finally the bit shift\r
+    unsigned bit_shift = shift%8;\r
+    if (bit_shift > 0)\r
+    {\r
+      for (std::string::size_type b = 0; b < m_data.size(); b++)\r
+      {\r
+        byte current = byte(m_data[b]);\r
+        byte next = ((b+1) < m_data.size()) ? m_data[b+1] : extend;\r
+        byte shifted = (current >> bit_shift) | (next << (8 - bit_shift));\r
+        m_data[b] = shifted;\r
+      }\r
+    }\r
+    // now reduce the result\r
+    reduce();\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator >> (unsigned shift) const\r
+  {\r
+    inf result(*this);\r
+    result >>= shift;\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // negation operators\r
+\r
+  inf& inf::negate (void)\r
+  {\r
+    // do 2's-complement negation\r
+    // equivalent to inversion plus one\r
+    invert();\r
+    operator += (inf(1));\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator - (void) const\r
+  {\r
+    inf result(*this);\r
+    result.negate();\r
+    return result;\r
+  }\r
+\r
+  inf& inf::abs(void)\r
+  {\r
+    if (negative()) negate();\r
+    return *this;\r
+  }\r
+\r
+  inf abs(const inf& i)\r
+  {\r
+    inf result = i;\r
+    result.abs();\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // addition operators\r
+\r
+  inf& inf::operator += (const inf& r)\r
+  {\r
+    // do 2's-complement addition\r
+    // Note that the addition can give a result that is larger than either argument\r
+    byte carry = 0;\r
+    std::string::size_type max_size = maximum(m_data.size(),r.m_data.size());\r
+    byte l_extend = negative() ? byte(255) : byte (0);\r
+    byte r_extend = r.negative() ? byte(255) : byte (0);\r
+    for (std::string::size_type i = 0; i < max_size; i++)\r
+    {\r
+      byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+      byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+      // calculate the addition in a type that is bigger than a byte in order to catch the carry-out\r
+      unsigned short result = ((unsigned short)(l_byte)) + ((unsigned short)(r_byte)) + carry;\r
+      // now truncate the result to get the lsB\r
+      if (i < m_data.size())\r
+        m_data[i] = byte(result);\r
+      else\r
+        m_data.append(1,byte(result));\r
+      // and capture the carry out by grabbing the second byte of the result\r
+      carry = byte(result >> 8);\r
+    }\r
+    // if the result overflowed or underflowed, add an extra byte to catch it\r
+    unsigned short result = ((unsigned short)(l_extend)) + ((unsigned short)(r_extend)) + carry;\r
+    if (byte(result) != (negative() ? byte(255) : byte(0)))\r
+      m_data.append(1,byte(result));\r
+    // now reduce the result\r
+    reduce();\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator + (const inf& r) const\r
+  {\r
+    inf result(*this);\r
+    result += r;\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // subtraction operators\r
+\r
+  inf& inf::operator -= (const inf& r)\r
+  {\r
+    // subtraction is defined in terms of negation and addition\r
+    inf negated = -r;\r
+    operator += (negated);\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator - (const inf& r) const\r
+  {\r
+    inf result(*this);\r
+    result -= r;\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // multiplication operators\r
+\r
+  inf& inf::operator *= (const inf& r)\r
+  {\r
+    // 2's complement multiplication\r
+    // one day I'll do a more efficient version than this based on the underlying representation\r
+    inf left(*this);\r
+    inf right = r;\r
+    // make the right value natural but preserve its sign for later\r
+    bool right_negative = right.negative();\r
+    right.abs();\r
+    // implemented as a series of conditional additions\r
+    operator = (0);\r
+    //  left.resize(right.bits() + left.bits() - 1);\r
+    left <<= right.bits()-1;\r
+    for (unsigned i = right.bits(); i--; )\r
+    {\r
+      if (right[i]) \r
+        operator += (left);\r
+      left >>= 1;\r
+    }\r
+    if (right_negative)\r
+      negate();\r
+    // now reduce the result\r
+    reduce();\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator * (const inf& r) const\r
+  {\r
+    inf result(*this);\r
+    result *= r;\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // division and remainder operators\r
+\r
+  std::pair<inf,inf> inf::divide(const inf& right) const throw(divide_by_zero)\r
+  {\r
+    if (right.zero())\r
+      throw divide_by_zero("stlplus::inf::divide");\r
+    inf numerator(*this);\r
+    inf denominator = right;\r
+    // make the numerator natural but preserve the sign for later\r
+    bool numerator_negative = numerator.negative();\r
+    numerator.abs();\r
+    // same with the denominator\r
+    bool denominator_negative = denominator.negative();\r
+    denominator.abs();\r
+    // the quotient and remainder will form the result\r
+    // start with the quotiont zero and the remainder equal to the whole of the\r
+    // numerator, then do trial subtraction from this\r
+    inf quotient;\r
+    inf remainder = numerator;\r
+    // there's nothing more to do if the numerator is smaller than the denominator\r
+    // but otherwise do the division\r
+    if (numerator.bits() >= denominator.bits())\r
+    {\r
+      // make the quotient big enough to take the result\r
+      quotient.resize(numerator.bits());\r
+      // start with the numerator shifted to the far left\r
+      unsigned shift = numerator.bits() - denominator.bits();\r
+      denominator <<= shift;\r
+      // do the division by repeated subtraction, \r
+      for (unsigned i = shift+1; i--; )\r
+      {\r
+        if (remainder >= denominator)\r
+        {\r
+          remainder -= denominator;\r
+          quotient.set(i);\r
+        }\r
+        denominator >>= 1;\r
+      }\r
+    }\r
+    // now adjust the signs \r
+    // x/(-y) == (-x)/y == -(x/y)\r
+    if (numerator_negative != denominator_negative)\r
+      quotient.negate();\r
+    quotient.reduce();\r
+    // x%(-y) == x%y and (-x)%y == -(x%y)\r
+    if (numerator_negative)\r
+      remainder.negate();\r
+    remainder.reduce();\r
+    return std::pair<inf,inf>(quotient,remainder);\r
+  }\r
+\r
+  inf& inf::operator /= (const inf& r) throw(divide_by_zero)\r
+  {\r
+    std::pair<inf,inf> result = divide(r);\r
+    operator=(result.first);\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator / (const inf& r) const throw(divide_by_zero)\r
+  {\r
+    std::pair<inf,inf> result = divide(r);\r
+    return result.first;\r
+  }\r
+\r
+  inf& inf::operator %= (const inf& r) throw(divide_by_zero)\r
+  {\r
+    std::pair<inf,inf> result = divide(r);\r
+    operator=(result.second);\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator % (const inf& r) const throw(divide_by_zero)\r
+  {\r
+    std::pair<inf,inf> result = divide(r);\r
+    return result.second;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // prefix (void) and postfix (int) operators\r
+\r
+  inf& inf::operator ++ (void)\r
+  {\r
+    operator += (inf(1));\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator ++ (int)\r
+  {\r
+    inf old(*this);\r
+    operator += (inf(1));\r
+    return old;\r
+  }\r
+\r
+  inf& inf::operator -- (void)\r
+  {\r
+    operator -= (inf(1));\r
+    return *this;\r
+  }\r
+\r
+  inf inf::operator -- (int)\r
+  {\r
+    inf old(*this);\r
+    operator -= (inf(1));\r
+    return old;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // string representation and I/O routines\r
+\r
+  std::string inf::to_string(unsigned radix) const\r
+    throw(std::invalid_argument)\r
+  {\r
+    std::string result;\r
+    convert_to_string(*this, result, radix);\r
+    return result;\r
+  }\r
+\r
+  inf& inf::from_string(const std::string& value, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    convert_from_string(value, *this, radix);\r
+    return *this;\r
+  }\r
+\r
+  std::ostream& operator << (std::ostream& str, const inf& i)\r
+  {\r
+    try\r
+    {\r
+      // get radix\r
+      unsigned radix = 10;\r
+      if (str.flags() & std::ios_base::oct)\r
+        radix = 8;\r
+      if (str.flags() & std::ios_base::hex)\r
+        radix = 16;\r
+      // the field width is handled by iostream, so I don't need to handle it as well\r
+      // generate the string representation then print it\r
+      str << i.to_string(radix);\r
+    }\r
+    catch(const std::invalid_argument)\r
+    {\r
+      str.setstate(std::ios_base::badbit);\r
+    }\r
+    return str;\r
+  }\r
+\r
+  std::istream& operator >> (std::istream& str, inf& i)\r
+  {\r
+    try\r
+    {\r
+      // get radix\r
+      unsigned radix = 10;\r
+      if (str.flags() & std::ios_base::oct)\r
+        radix = 8;\r
+      if (str.flags() & std::ios_base::hex)\r
+        radix = 16;\r
+      // now get the string image of the value\r
+      std::string image;\r
+      str >> image;\r
+      // and convert to inf\r
+      i.from_string(image, radix);\r
+    }\r
+    catch(const std::invalid_argument)\r
+    {\r
+      str.setstate(std::ios_base::badbit);\r
+    }\r
+    return str;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // diagnostic dump\r
+  // just convert to hex\r
+\r
+  std::string inf::image_debug(void) const\r
+  {\r
+    // create this dump in the human-readable form, i.e. msB to the left\r
+    std::string result = "0x";\r
+    for (std::string::size_type i = m_data.size(); i--; )\r
+    {\r
+      byte current = m_data[i];\r
+      byte msB = (current & byte(0xf0)) >> 4;\r
+      result += to_char[msB];\r
+      byte lsB = (current & byte(0x0f));\r
+      result += to_char[lsB];\r
+    }\r
+    return result;\r
+  }\r
+\r
+  const std::string& inf::get_bytes(void) const\r
+  {\r
+    return m_data;\r
+  }\r
+\r
+  void inf::set_bytes(const std::string& data)\r
+  {\r
+    m_data = data;\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/inf.hpp b/src/stlplus/portability/inf.hpp
new file mode 100644 (file)
index 0000000..f28541a
--- /dev/null
@@ -0,0 +1,228 @@
+#ifndef STLPLUS_INF\r
+#define STLPLUS_INF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   An infinite-precision integer class. This allows calculations on large\r
+//   integers to be performed without overflow.\r
+\r
+//   this class can throw the following exceptions:\r
+//     std::out_of_range\r
+//     std::overflow_error\r
+//     std::invalid_argument\r
+//     stlplus::divide_by_zero    // why doesn't std have this?\r
+//   all of these are derivations of the baseclass:\r
+//     std::logic_error\r
+//   So you can catch all of them by catching the baseclass\r
+\r
+//   Warning: inf was never intended to be fast, it is just for programs which\r
+//   need a bit of infinite-precision integer arithmetic. For high-performance\r
+//   processing, use the Gnu Multi-Precision (GMP) library. The inf type is just\r
+//   easier to integrate and is already ported to all platforms and compilers\r
+//   that STLplus is ported to.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include "portability_exceptions.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class inf\r
+  {\r
+  public:\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // constructors and assignments initialise the inf\r
+\r
+    // the void constructor initialises to zero, the others initialise to the\r
+    // value of the C integer type or the text value contained in the string\r
+\r
+    inf(void);\r
+    explicit inf(short);\r
+    explicit inf(unsigned short);\r
+    explicit inf(int);\r
+    explicit inf(unsigned);\r
+    explicit inf(long);\r
+    explicit inf(unsigned long);\r
+    explicit inf(const std::string&) throw(std::invalid_argument);\r
+    inf(const inf&);\r
+\r
+    ~inf(void);\r
+\r
+    // assignments with equivalent behaviour to the constructors\r
+\r
+    inf& operator = (short);\r
+    inf& operator = (unsigned short);\r
+    inf& operator = (int);\r
+    inf& operator = (unsigned);\r
+    inf& operator = (long);\r
+    inf& operator = (unsigned long);\r
+    inf& operator = (const std::string&) throw(std::invalid_argument);\r
+    inf& operator = (const inf&);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // conversions back to the C types\r
+    // truncate: controls the behaviour when the value is too long for the result\r
+    //           true: truncate the value\r
+    //           false: throw an exception\r
+\r
+    short to_short(bool truncate = true) const throw(std::overflow_error);\r
+    unsigned short to_unsigned_short(bool truncate = true) const throw(std::overflow_error);\r
+\r
+    int to_int(bool truncate = true) const throw(std::overflow_error);\r
+    unsigned to_unsigned(bool truncate = true) const throw(std::overflow_error);\r
+\r
+    long to_long(bool truncate = true) const throw(std::overflow_error);\r
+    unsigned long to_unsigned_long(bool truncate = true) const throw(std::overflow_error);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // bitwise manipulation\r
+\r
+    void resize(unsigned bits);\r
+    void reduce(void);\r
+\r
+    // the number of significant bits in the value\r
+    unsigned bits (void) const;\r
+    unsigned size (void) const;\r
+\r
+    // the number of bits that can be accessed by the bit() method (=bits() rounded up to the next byte)\r
+    unsigned indexable_bits(void) const;\r
+\r
+    bool bit (unsigned index) const throw(std::out_of_range);\r
+    bool operator [] (unsigned index) const throw(std::out_of_range);\r
+\r
+    void set (unsigned index) throw(std::out_of_range);\r
+    void clear (unsigned index) throw(std::out_of_range);\r
+    void preset (unsigned index, bool value) throw(std::out_of_range);\r
+\r
+    inf slice(unsigned low, unsigned high) const throw(std::out_of_range);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // tests for common values or ranges\r
+\r
+    bool negative (void) const;\r
+    bool natural (void) const;\r
+    bool positive (void) const;\r
+    bool zero (void) const;\r
+    bool non_zero (void) const;\r
+\r
+    // tests used in if(i) and if(!i)\r
+//  operator bool (void) const;\r
+    bool operator ! (void) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // comparisons\r
+\r
+    bool operator == (const inf&) const;\r
+    bool operator != (const inf&) const;\r
+    bool operator < (const inf&) const;\r
+    bool operator <= (const inf&) const;\r
+    bool operator > (const inf&) const;\r
+    bool operator >= (const inf&) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // bitwise logic operations\r
+\r
+    inf& invert (void);\r
+    inf operator ~ (void) const;\r
+\r
+    inf& operator &= (const inf&);\r
+    inf operator & (const inf&) const;\r
+\r
+    inf& operator |= (const inf&);\r
+    inf operator | (const inf&) const;\r
+\r
+    inf& operator ^= (const inf&);\r
+    inf operator ^ (const inf&) const;\r
+\r
+    inf& operator <<= (unsigned shift);\r
+    inf operator << (unsigned shift) const;\r
+\r
+    inf& operator >>= (unsigned shift);\r
+    inf operator >> (unsigned shift) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // arithmetic operations\r
+\r
+    inf& negate (void);\r
+    inf operator - (void) const;\r
+\r
+    inf& abs(void);\r
+    friend inf abs(const inf&);\r
+\r
+    inf& operator += (const inf&);\r
+    inf operator + (const inf&) const;\r
+\r
+    inf& operator -= (const inf&);\r
+    inf operator - (const inf&) const;\r
+\r
+    inf& operator *= (const inf&);\r
+    inf operator * (const inf&) const;\r
+\r
+    inf& operator /= (const inf&) throw(divide_by_zero);\r
+    inf operator / (const inf&) const throw(divide_by_zero);\r
+\r
+    inf& operator %= (const inf&) throw(divide_by_zero);\r
+    inf operator % (const inf&) const throw(divide_by_zero);\r
+\r
+    // combined division operator - returns the result pair(quotient,remainder) in one go\r
+    std::pair<inf,inf> divide(const inf&) const throw(divide_by_zero);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // pre- and post- increment and decrement\r
+\r
+    inf& operator ++ (void);\r
+    inf operator ++ (int);\r
+    inf& operator -- (void);\r
+    inf operator -- (int);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // string representation and I/O\r
+\r
+    std::string image_debug(void) const;\r
+\r
+    // conversion to a string representation\r
+    // radix must be 10, 2, 8 or 16\r
+    std::string to_string(unsigned radix = 10) const\r
+      throw(std::invalid_argument);\r
+\r
+    // conversion from a string\r
+    // radix == 0 - radix is deduced from the input - assumed 10 unless number is prefixed by 0b, 0 or 0x\r
+    // however, you can specify the radix to be 10, 2, 8 or 16 to force that interpretation\r
+    inf& from_string(const std::string&, unsigned radix = 0)\r
+      throw(std::invalid_argument);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+  private:\r
+    std::string m_data;\r
+  public:\r
+    const std::string& get_bytes(void) const;\r
+    void set_bytes(const std::string&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // redefine friends for gcc v4.1\r
+\r
+  inf abs(const inf&);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::ostream& operator << (std::ostream&, const inf&);\r
+  std::istream& operator >> (std::istream&, inf&);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/ip_sockets.cpp b/src/stlplus/portability/ip_sockets.cpp
new file mode 100644 (file)
index 0000000..6dc1fad
--- /dev/null
@@ -0,0 +1,961 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author:    Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+//            (c) Andy Rushton           2004-2009\r
+// License:   BSD License, see ../docs/license.html\r
+\r
+// Contains all the platform-specific socket handling used by the TCP and UDP classes\r
+\r
+// TODO - any conversion required to support IPv6\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "ip_sockets.hpp"\r
+#include "dprintf.hpp"\r
+#include <string.h>\r
+\r
+#ifdef MSWINDOWS\r
+// Windoze-specific includes\r
+#include <winsock2.h>\r
+#define ERRNO WSAGetLastError()\r
+#define HERRNO WSAGetLastError()\r
+#define IOCTL ioctlsocket\r
+#define CLOSE closesocket\r
+#define SHUT_RDWR SD_BOTH\r
+#define EINPROGRESS WSAEINPROGRESS\r
+#define EWOULDBLOCK WSAEWOULDBLOCK\r
+#define ECONNRESET WSAECONNRESET\r
+#define SOCKLEN_T int\r
+#else\r
+// Generic Unix includes\r
+// fix for older versions of Darwin?\r
+#define _BSD_SOCKLEN_T_ int\r
+#include <sys/types.h>\r
+#include <sys/socket.h>\r
+#include <sys/ioctl.h>\r
+#include <sys/time.h>\r
+#include <netinet/in.h>\r
+#include <errno.h>\r
+#include <netdb.h>\r
+#include <unistd.h>\r
+#define INVALID_SOCKET -1\r
+#define ERRNO errno\r
+#define HERRNO h_errno\r
+#define SOCKET int\r
+#define SOCKET_ERROR -1\r
+#define IOCTL ::ioctl\r
+#define CLOSE ::close\r
+#define SOCKLEN_T socklen_t\r
+#ifdef SOLARIS\r
+// Sun put some definitions in a different place\r
+#include <sys/filio.h>\r
+#endif\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Utilities\r
+\r
+  // get an operating-system error message given an error code\r
+  static std::string error_string(int error)\r
+  {\r
+    std::string result = "error " + dformat("%d",error);\r
+#ifdef MSWINDOWS\r
+    char* message = 0;\r
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                  0,\r
+                  error,\r
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // "User default language"\r
+                  (LPTSTR)&message,\r
+                  0,0);\r
+    if (message) \r
+    {\r
+      result = message;\r
+      LocalFree(message);\r
+    }\r
+    // the error message is for some perverse reason newline terminated - remove this\r
+    if (result[result.size()-1] == '\n')\r
+      result.erase(result.end()-1);\r
+    if (result[result.size()-1] == '\r')\r
+      result.erase(result.end()-1);\r
+#else\r
+    char* message = strerror(error);\r
+    if (message && message[0])\r
+      result = message;\r
+#endif\r
+    return result;\r
+  }\r
+\r
+  // convert address:port into a sockaddr\r
+  static void convert_address(unsigned long address, unsigned short port, sockaddr& sa)\r
+  {\r
+    sa.sa_family = AF_INET;\r
+    unsigned short network_port = htons(port);\r
+    memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));\r
+    unsigned long network_address = htonl(address);\r
+    memcpy(&sa.sa_data[2], &network_address, sizeof(network_address));\r
+  }\r
+\r
+//   // convert host:port into a sockaddr\r
+//   static void convert_host(hostent& host, unsigned short port, sockaddr& sa)\r
+//   {\r
+//     sa.sa_family = host.h_addrtype;\r
+//     unsigned short network_port = htons(port);\r
+//     memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));\r
+//     memcpy(&sa.sa_data[2], host.h_addr, host.h_length);\r
+//   }\r
+\r
+  // convert sockaddr to address:port\r
+  static void convert_sockaddr(const sockaddr& sa, unsigned long& address, unsigned short& port)\r
+  {\r
+    unsigned short network_port = 0;\r
+    memcpy(&network_port, &sa.sa_data[0], sizeof(network_port));\r
+    port = ntohs(network_port);\r
+    unsigned long network_address = 0;\r
+    memcpy(&network_address, &sa.sa_data[2], sizeof(network_address));\r
+    address = ntohl(network_address);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Initialisation\r
+  // Windows requires that Winsock is initialised before use and closed after\r
+  // These routines initialise once on first use and close on the destruction of the last object using it\r
+  // on non-windows platforms, I still increment/decrement the sockets count variable for diagnostic purposes\r
+\r
+  static int sockets_count = 0;\r
+\r
+  static int sockets_init(void)\r
+  {\r
+    int error = 0;\r
+    if (sockets_count++ == 0)\r
+    {\r
+#ifdef MSWINDOWS\r
+      WSAData winsock_info;\r
+      // request Winsock 2.0 or higher\r
+      error = WSAStartup(MAKEWORD(2,0),&winsock_info);\r
+#endif\r
+    }\r
+    return error;\r
+  }\r
+\r
+  static int sockets_close(void)\r
+  {\r
+    int error = 0;\r
+    if (--sockets_count == 0)\r
+    {\r
+#ifdef MSWINDOWS\r
+      if (WSACleanup() == SOCKET_ERROR)\r
+        error = ERRNO;\r
+#endif\r
+    }\r
+    return error;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Socket Implementation - common code to manipulate a TCP socket\r
+\r
+  class IP_socket_internals\r
+  {\r
+  private:\r
+    IP_socket_type m_type;\r
+    SOCKET m_socket;\r
+    unsigned long m_remote_address;\r
+    unsigned short m_remote_port;\r
+    mutable int m_error;\r
+    mutable std::string m_message;\r
+    unsigned m_count;\r
+\r
+    // disable copying of the internals\r
+    IP_socket_internals(const IP_socket_internals&);\r
+    IP_socket_internals& operator=(const IP_socket_internals&);\r
+\r
+  public:\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // PIMPL alias counting \r
+\r
+    void increment(void)\r
+      {\r
+        ++m_count;\r
+      }\r
+\r
+    bool decrement(void)\r
+      {\r
+        --m_count;\r
+        return m_count == 0;\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // constructors/destructors\r
+\r
+    // construct an invalid socket\r
+    IP_socket_internals(void) : m_type(undefined_socket_type), m_socket(INVALID_SOCKET), m_error(0), m_count(1)\r
+      {\r
+        set_error(sockets_init());\r
+      }\r
+\r
+    // close on destroy\r
+    ~IP_socket_internals(void)\r
+      {\r
+        close();\r
+        set_error(sockets_close());\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation, connection\r
+\r
+    bool initialised(void) const\r
+      {\r
+        return m_socket != INVALID_SOCKET;\r
+      }\r
+\r
+    // attach this object to a pre-opened socket\r
+    bool set(SOCKET socket, unsigned long remote_address, unsigned short remote_port)\r
+      {\r
+        if (initialised()) close();\r
+        clear_error();\r
+        m_socket = socket;\r
+        m_remote_address = remote_address;\r
+        m_remote_port = remote_port;\r
+        return true;\r
+      }\r
+\r
+    // create a raw socket attached to this object\r
+    bool initialise(IP_socket_type type)\r
+      {\r
+        if (initialised()) close();\r
+        clear_error();\r
+        if ((type != TCP) && (type != UDP))\r
+        {\r
+          set_error(-1, "Illegal socket type");\r
+          return false;\r
+        }\r
+        // create an anonymous socket\r
+        m_socket = ::socket(AF_INET, ((type == TCP) ? SOCK_STREAM : SOCK_DGRAM), 0);\r
+        if (m_socket == INVALID_SOCKET)\r
+        {\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        // record the type on success only\r
+        m_type = type;\r
+        // set the socket into non-blocking mode\r
+        unsigned long nonblocking = 1;\r
+        if (IOCTL(m_socket, FIONBIO, &nonblocking) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        return true;\r
+      }\r
+    \r
+    // function for performing IP lookup (i.e. gethostbyname)\r
+    // could be standalone but making it a member means that it can use the socket's error handler\r
+    // - remote_address: IP name or number\r
+    // - returns the IP address as a number - zero if there's an error\r
+    unsigned long ip_lookup(const std::string& remote_address)\r
+      {\r
+        unsigned long result = 0;\r
+        // Lookup the IP address to convert it into a host record\r
+        // this DOES lookup IP address names as well (not according to MS help !!)\r
+        // TODO - convert this to use ::getaddrinfo - ::gethostbyname is deprecated\r
+        hostent* host_info = ::gethostbyname(remote_address.c_str());\r
+        if (!host_info)\r
+        {\r
+          set_error(HERRNO);\r
+          return 0;\r
+        }\r
+        // extract the address from the host info\r
+        unsigned long network_address = 0;\r
+        memcpy(&network_address, host_info->h_addr, host_info->h_length);\r
+        result = ntohl(network_address);\r
+        return result;\r
+      }\r
+\r
+    // tests whether a socket is ready for communication\r
+    bool select(bool readable, bool writeable, unsigned wait)\r
+      {\r
+        if (!initialised()) return false;\r
+        // set up the readable set\r
+        fd_set readable_set;\r
+        fd_set* readable_set_ptr = 0;\r
+        if (readable)\r
+        {\r
+          FD_ZERO(&readable_set);\r
+          FD_SET(m_socket,&readable_set);\r
+          readable_set_ptr = &readable_set;\r
+        }\r
+        // set up the writeable set\r
+        fd_set writeable_set;\r
+        fd_set* writeable_set_ptr = 0;\r
+        if (writeable)\r
+        {\r
+          FD_ZERO(&writeable_set);\r
+          FD_SET(m_socket,&writeable_set);\r
+          writeable_set_ptr = &writeable_set;\r
+        }\r
+        // TODO - check the error set and lookup the error?\r
+        fd_set* error_set_ptr = 0;\r
+        // set up the timout value\r
+        // Note: a null pointer implements a blocking select\r
+        //       a pointer to a zero value implements a zero-wait poll\r
+        //       a pointer to a positive value implements a poll with a timeout\r
+        // I currently only implement polling with timeout which may be zero  - no blocking\r
+        timeval timeout;\r
+        timeval* timeout_ptr = 0;\r
+        timeout.tv_sec = wait/1000000;\r
+        timeout.tv_usec = wait%1000000;\r
+        timeout_ptr = &timeout;\r
+        // now test the socket\r
+        int select_result = ::select(m_socket+1, readable_set_ptr, writeable_set_ptr, error_set_ptr, timeout_ptr);\r
+        switch(select_result)\r
+        {\r
+        case SOCKET_ERROR:\r
+          // select failed with an error - trap the error\r
+          set_error(ERRNO);\r
+          return false;\r
+        case 0:\r
+          // timeout exceeded without a connection appearing\r
+          return false;\r
+        default:\r
+          // at least one connection is pending\r
+          // TODO - do we need to do the extra socket options checking on Posix?\r
+          // TODO - does this connect in any way to the error_set above?\r
+          return true;\r
+        }\r
+      }\r
+\r
+    // bind the socket to a port so that it can receive from specific address\r
+    bool bind(unsigned long remote_address, unsigned short local_port)\r
+      {\r
+        if (!initialised()) return false;\r
+        // name the socket and bind it to a port - this is a requirement for a server\r
+        sockaddr server;\r
+        convert_address(INADDR_ANY, local_port, server);\r
+        if (::bind(m_socket, &server, sizeof(server)) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        return true;\r
+      }\r
+    \r
+    // bind the socket to a port so that it can receive from any address\r
+    bool bind_any(unsigned short local_port)\r
+      {\r
+        return bind(INADDR_ANY, local_port);\r
+      }\r
+\r
+    // set this socket up to be a listening port\r
+    // must have been bound to a local port already\r
+    // - length of backlog queue to manage - may be zero\r
+    // - returns success status\r
+    bool listen(unsigned short queue)\r
+      {\r
+        if (!initialised()) return false;\r
+        // set the port to listen for incoming connections\r
+        if (::listen(m_socket, (int)queue) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        return true;\r
+      }\r
+\r
+    // test whether there's an incoming connection on the socket\r
+    // only applicable if it has been set up as a listening port\r
+    bool accept_ready(unsigned wait)\r
+      {\r
+        // the test for a connection being ready is the same as the test for whether the socket is readable\r
+        // see documentation for select\r
+        return select(true, false, wait);\r
+      }\r
+\r
+    // accept a connection on the socket\r
+    // only applicable if it has been set up as a listening port\r
+    // - returns socket filled in with the accepted connection's details - or with the error fields set\r
+    IP_socket accept(void)\r
+      {\r
+        if (!initialised()) return IP_socket();\r
+        IP_socket result;\r
+        // accept the connection, at the same time getting the address of the connecting client\r
+        sockaddr saddress;\r
+        SOCKLEN_T saddress_length = sizeof(saddress);\r
+        SOCKET socket = ::accept(m_socket, &saddress, &saddress_length);\r
+        if (socket == INVALID_SOCKET)\r
+        {\r
+          // only set the result socket with an error\r
+          result.m_impl->set_error(ERRNO);\r
+          return result;\r
+        }\r
+        // extract the contents of the address\r
+        unsigned long remote_address = 0;\r
+        unsigned short remote_port = 0;\r
+        convert_sockaddr(saddress, remote_address, remote_port);\r
+        result.m_impl->set(socket, remote_address, remote_port);\r
+        return result;\r
+      }\r
+\r
+    // client connect to a server\r
+    // - remote_address: IP number of remote address to connect to\r
+    // - remote_port: port to connect to\r
+    bool connect(unsigned long remote_address, unsigned short remote_port)\r
+      {\r
+        if (!initialised()) return false;\r
+        // fill in the connection data structure\r
+        sockaddr connect_data;\r
+        convert_address(remote_address, remote_port, connect_data);\r
+        // connect binds the socket to a local address\r
+        // if connectionless it simply sets the default remote address\r
+        // if connectioned it makes the connection\r
+        if (::connect(m_socket, &connect_data, sizeof(connect_data)) == SOCKET_ERROR)\r
+        {\r
+          // the socket is non-blocking, so connect will almost certainly fail with EINPROGRESS which is not an error\r
+          // only catch real errors\r
+          int error = ERRNO;\r
+          if (error != EINPROGRESS && error != EWOULDBLOCK)\r
+          {\r
+            set_error(error);\r
+            return false;\r
+          }\r
+        }\r
+        // extract the remote connection details for local storage\r
+        convert_sockaddr(connect_data, m_remote_address, m_remote_port);\r
+        return true;\r
+      }\r
+\r
+    // test whether a socket is connected and ready to communicate\r
+    bool connected(unsigned wait)\r
+      {\r
+        if (!initialised()) return false;\r
+        // Linux and Windows docs say test with select for whether socket is\r
+        // writable. However, a problem has been reported with Linux whereby\r
+        // the OS will report a socket as writable when it isn't\r
+        // first use the select method\r
+        if (!select(false, true, wait))\r
+          return false;\r
+#ifdef MSWINDOWS\r
+        // Windows needs no further processing - select method works\r
+        return true;\r
+#else\r
+        // Posix version needs further checking using the socket options\r
+        // DJDM: socket has returned EINPROGRESS on the first attempt at connection\r
+        // it has also returned that it can be written to\r
+        // we must now ask it if it has actually connected - using getsockopt\r
+        int error = 0;\r
+        socklen_t serror = sizeof(int);\r
+        if (::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &error, &serror)==0)\r
+          // handle the error value - one of them means that the socket has connected\r
+          if (!error || error == EISCONN)\r
+            return true;\r
+        return false;\r
+#endif\r
+      }\r
+\r
+    bool close(void)\r
+      {\r
+        bool result = true;\r
+        if (initialised())\r
+        {\r
+          if (shutdown(m_socket,SHUT_RDWR) == SOCKET_ERROR)\r
+          {\r
+            set_error(ERRNO);\r
+            result = false;\r
+          }\r
+          if (CLOSE(m_socket) == SOCKET_ERROR)\r
+          {\r
+            set_error(ERRNO);\r
+            result = false;\r
+          }\r
+        }\r
+        m_socket = INVALID_SOCKET;\r
+        m_remote_address = 0;\r
+        m_remote_port = 0;\r
+        return result;\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // sending/receiving\r
+\r
+    bool send_ready(unsigned wait)\r
+      {\r
+        // determines whether the socket is ready to send by testing whether it is writable\r
+        return select(false, true, wait);\r
+      }\r
+\r
+    bool send (std::string& data)\r
+      {\r
+        if (!initialised()) return false;\r
+        // send the data - this will never block but may not send all the data\r
+        int bytes = ::send(m_socket, data.c_str(), data.size(), 0);\r
+        if (bytes == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent\r
+        data.erase(0,bytes);\r
+        return true;\r
+      }\r
+\r
+    bool send_packet(std::string& data, unsigned long address = 0, unsigned short port = 0)\r
+      {\r
+        if (!initialised()) return false;\r
+        // if no address specified, rely on the socket having been connected (can I test this?)\r
+        // so use the standard send, otherwise use the sendto function\r
+        int bytes = 0;\r
+        if (!address)\r
+        {\r
+          bytes = ::send(m_socket, data.c_str(), data.size(), 0);\r
+        }\r
+        else\r
+        {\r
+          sockaddr saddress;\r
+          convert_address(address, port, saddress);\r
+          bytes = ::sendto(m_socket, data.c_str(), data.size(), 0, &saddress, sizeof(saddress));\r
+        }\r
+        if (bytes == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent\r
+        data.erase(0,bytes);\r
+        return true;\r
+      }\r
+\r
+    bool receive_ready(unsigned wait)\r
+      {\r
+        // determines whether the socket is ready to receive by testing whether it is readable\r
+        return select(true, false, wait);\r
+      }\r
+\r
+    bool receive (std::string& data)\r
+      {\r
+        if (!initialised()) return false;\r
+        // determine how much data is available to read\r
+        unsigned long bytes = 0;\r
+        if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // get the data up to the amount claimed to be present - this is non-blocking\r
+        char* buffer = new char[bytes+1];\r
+        int read = ::recv(m_socket, buffer, bytes, 0);\r
+        if (read == SOCKET_ERROR)\r
+        {\r
+          delete[] buffer;\r
+          set_error(ERRNO);\r
+          close();\r
+          return false;\r
+        }\r
+        if (read == 0)\r
+        {\r
+          // TODO - check whether this is an appropriate conditon to close the socket\r
+          close();\r
+        }\r
+        else\r
+        {\r
+          // this is binary data so copy the bytes including nulls\r
+          data.append(buffer,read);\r
+        }\r
+        delete[] buffer;\r
+        return true;\r
+      }\r
+\r
+    bool receive_packet(std::string& data, unsigned long& address, unsigned short& port)\r
+      {\r
+        if (!initialised()) return false;\r
+        // determine how much data is available to read\r
+        unsigned long bytes = 0;\r
+        if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)\r
+        {\r
+          set_error(ERRNO);\r
+          return false;\r
+        }\r
+        // get the data up to the amount claimed to be present - this is non-blocking\r
+        // also get the sender's details\r
+        char* buffer = new char[bytes+1];\r
+        sockaddr saddress;\r
+        SOCKLEN_T saddress_length = sizeof(saddress);\r
+        int read = ::recvfrom(m_socket, buffer, bytes, 0, &saddress, &saddress_length);\r
+        if (read == SOCKET_ERROR)\r
+        {\r
+          // UDP connection reset means that a previous sent failed to deliver cos the address was unknown\r
+          // this is NOT an error with the sending server socket, which IS still usable\r
+          int error = ERRNO;\r
+          if (error != ECONNRESET)\r
+          {\r
+            delete[] buffer;\r
+            set_error(error);\r
+            close();\r
+            return false;\r
+          }\r
+        }\r
+        // this is binary data so copy the bytes including nulls\r
+        data.append(buffer,read);\r
+        // also retrieve the sender's details\r
+        convert_sockaddr(saddress, address, port);\r
+        delete[] buffer;\r
+        return true;\r
+      }\r
+\r
+    bool receive_packet(std::string& data)\r
+      {\r
+        // call the above and then discard the address details\r
+        unsigned long address = 0;\r
+        unsigned short port = 0;\r
+        return receive_packet(data, address, port);\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // informational\r
+\r
+    IP_socket_type type(void) const\r
+      {\r
+        return m_type;\r
+      }\r
+\r
+    unsigned short local_port(void) const\r
+      {\r
+        if (!initialised()) return 0;\r
+        sockaddr saddress;\r
+        SOCKLEN_T saddress_length = sizeof(saddress);\r
+        if (::getsockname(m_socket, &saddress, &saddress_length) != 0)\r
+        {\r
+          set_error(ERRNO);\r
+          return 0;\r
+        }\r
+        unsigned long address = 0;\r
+        unsigned short port = 0;\r
+        convert_sockaddr(saddress, address, port);\r
+        return port;\r
+      }\r
+\r
+    unsigned long remote_address(void) const\r
+      {\r
+        return m_remote_address;\r
+      }\r
+\r
+    unsigned short remote_port(void) const\r
+      {\r
+        return m_remote_port;\r
+      }\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+\r
+    void set_error (int error, const char* message = 0) const\r
+      {\r
+        if (error != 0)\r
+        {\r
+          m_error = error;\r
+          if (message && (message[0] != 0))\r
+            m_message = message;\r
+          else\r
+            m_message = error_string(error);\r
+        }\r
+      }\r
+\r
+    int error(void) const\r
+      {\r
+        return m_error;\r
+      }\r
+\r
+    void clear_error (void) const\r
+      {\r
+        m_error = 0;\r
+        m_message.erase();\r
+      }\r
+\r
+    std::string message(void) const\r
+      {\r
+        return m_message;\r
+      }\r
+\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Socket - common code to manipulate a socket\r
+\r
+  // create an uninitialised socket\r
+  IP_socket::IP_socket(void) : m_impl(new IP_socket_internals)\r
+  {\r
+  }\r
+\r
+  // create an initialised socket\r
+  // - type: create either a TCP or UDP socket - if neither, creates an uninitialised socket\r
+  IP_socket::IP_socket(IP_socket_type type) : m_impl(new IP_socket_internals)\r
+  {\r
+    initialise(type);\r
+  }\r
+\r
+  // destroy the socket, closing it if open\r
+  IP_socket::~IP_socket(void)\r
+  {\r
+    if (m_impl->decrement())\r
+      delete m_impl;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // copying is implemented as aliasing\r
+\r
+  IP_socket::IP_socket(const IP_socket& right) : m_impl(0)\r
+  {\r
+    // make this an alias of right\r
+    m_impl = right.m_impl;\r
+    m_impl->increment();\r
+  }\r
+\r
+  IP_socket& IP_socket::operator=(const IP_socket& right)\r
+  {\r
+    // make self-copy safe\r
+    if (m_impl == right.m_impl) return *this;\r
+    // first dealias the existing implementation\r
+    if (m_impl->decrement())\r
+      delete m_impl;\r
+    // now make this an alias of right\r
+    m_impl = right.m_impl;\r
+    m_impl->increment();\r
+    return *this;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // initialisation, connection\r
+\r
+  // initialise the socket\r
+  // - type: create either a TCP or UDP socket\r
+  // - returns success status\r
+  bool IP_socket::initialise(IP_socket_type type)\r
+  {\r
+    return m_impl->initialise(type);\r
+  }\r
+\r
+  // test whether this is an initialised socket\r
+  // - returns whether this is initialised\r
+  bool IP_socket::initialised(void) const\r
+  {\r
+    return m_impl->initialised();\r
+  }\r
+\r
+  // close, i.e. disconnect the socket\r
+  // - returns a success flag\r
+  bool IP_socket::close(void)\r
+  {\r
+    return m_impl->close();\r
+  }\r
+\r
+  // function for performing IP lookup (i.e. gethostbyname)\r
+  // could be standalone but making it a member means that it can use the socket's error handler\r
+  // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+  // - returns the IP address as a long integer - zero if there's an error\r
+  unsigned long IP_socket::ip_lookup(const std::string& remote_address)\r
+  {\r
+    return m_impl->ip_lookup(remote_address);\r
+  }\r
+\r
+  // test whether a socket is ready to communicate\r
+  // - readable: test whether socket is ready to read\r
+  // - writeable: test whether a socket is ready to write\r
+  // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait\r
+  // returns false if not ready or error - use error() method to tell - true if ready\r
+  bool IP_socket::select(bool readable, bool writeable, unsigned timeout)\r
+  {\r
+    return m_impl->select(readable, writeable, timeout);\r
+  }\r
+\r
+  // bind the socket to a port so that it can receive from specific address - typically used by a client\r
+  // - remote_address: IP number of remote server to send/receive to/from\r
+  // - local_port: port on local machine to bind to the address\r
+  // - returns success flag\r
+  bool IP_socket::bind(unsigned long remote_address, unsigned short local_port)\r
+  {\r
+    return m_impl->bind(remote_address, local_port);\r
+  }\r
+\r
+  // bind the socket to a port so that it can receive from any address - typically used by a server\r
+  // - local_port: port on local machine to bind to the address\r
+  // - returns success flag\r
+  bool IP_socket::bind_any(unsigned short local_port)\r
+  {\r
+    return m_impl->bind_any(local_port);\r
+  }\r
+\r
+  // initialise a socket and set this socket up to be a listening port\r
+  // - queue: length of backlog queue to manage - may be zero\r
+  // - returns success status\r
+  bool IP_socket::listen(unsigned short queue)\r
+  {\r
+    return m_impl->listen(queue);\r
+  }\r
+\r
+  // test for a connection on the object's socket - only applicable if it has been set up as a listening port\r
+  // - returns true if a connection is ready to be accepted\r
+  bool IP_socket::accept_ready(unsigned timeout) const\r
+  {\r
+    return m_impl->accept_ready(timeout);\r
+  }\r
+\r
+  // accept a connection on the object's socket - only applicable if it has been set up as a listening port\r
+  // - returns the connection as a new socket\r
+  IP_socket IP_socket::accept(void)\r
+  {\r
+    return m_impl->accept();\r
+  }\r
+\r
+  // client connect to a server\r
+  // - address: IP number already lookup up with ip_lookup\r
+  // - port: port to connect to\r
+  // - returns a success flag\r
+  bool IP_socket::connect(unsigned long address, unsigned short port)\r
+  {\r
+    return m_impl->connect(address, port);\r
+  }\r
+\r
+  // test whether a socket is connected and ready to communicate, returns on successful connect or timeout\r
+  // - timeout: how long to wait in microseconds if not connected yet\r
+  // - returns success flag\r
+  bool IP_socket::connected(unsigned timeout)\r
+  {\r
+    return m_impl->connected(timeout);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // sending/receiving\r
+\r
+  // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+  // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+  // - returns status\r
+  bool IP_socket::send_ready(unsigned timeout)\r
+  {\r
+    return m_impl->send_ready(timeout);\r
+  }\r
+\r
+  // send data through the socket - if the data is long only part of it may\r
+  // be sent. The sent part is removed from the data, so the same string can\r
+  // be sent again and again until it is empty.\r
+  // - data: string containing data to be sent - any data successfully sent is removed\r
+  // - returns success flag\r
+  bool IP_socket::send (std::string& data)\r
+  {\r
+    return m_impl->send(data);\r
+  }\r
+\r
+  // send data through a connectionless (UDP) socket\r
+  // the data will be sent as a single packet\r
+  // - packet: string containing data to be sent - any data successfully sent is removed\r
+  // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote\r
+  // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote\r
+  // - returns success flag\r
+  bool IP_socket::send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port)\r
+  {\r
+    return m_impl->send_packet(packet, remote_address, remote_port);\r
+  }\r
+\r
+  // send data through a connectionless (UDP) socket\r
+  // the data will be sent as a single packet\r
+  // only works if the socket has been connected to remote\r
+  // - packet: string containing data to be sent - any data successfully sent is removed\r
+  // - returns success flag\r
+  bool IP_socket::send_packet(std::string& packet)\r
+  {\r
+    return m_impl->send_packet(packet);\r
+  }\r
+\r
+  // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+  // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+  // - returns status\r
+  bool IP_socket::receive_ready(unsigned timeout)\r
+  {\r
+    return m_impl->receive_ready(timeout);\r
+  }\r
+\r
+  // receive data through a connection-based (TCP) socket\r
+  // if the data is long only part of it may be received. The received data\r
+  // is appended to the string, building it up in stages, so the same string\r
+  // can be received again and again until all information has been\r
+  // received.\r
+  // - data: string receiving data from socket - any data successfully received is appended\r
+  // - returns success flag\r
+  bool IP_socket::receive (std::string& data)\r
+  {\r
+    return m_impl->receive(data);\r
+  }\r
+\r
+  // receive data through a connectionless (UDP) socket\r
+  // - packet: string receiving data from socket - any data successfully received is appended\r
+  // - remote_address: returns the address of the remote host received from\r
+  // - remote_port: returns the port of the remote host received from\r
+  // - returns success flag\r
+  bool IP_socket::receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)\r
+  {\r
+    return m_impl->receive_packet(packet, remote_address, remote_port);\r
+  }\r
+\r
+  // variant of above which does not give back the address and port of the sender\r
+  // receive data through a connectionless (UDP) socket\r
+  // - packet: string receiving data from socket - any data successfully received is appended\r
+  // - returns success flag\r
+  bool IP_socket::receive_packet(std::string& packet)\r
+  {\r
+    return m_impl->receive_packet(packet);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // informational\r
+\r
+  IP_socket_type IP_socket::type(void) const\r
+  {\r
+    return m_impl->type();\r
+  }\r
+\r
+  unsigned short IP_socket::local_port(void) const\r
+  {\r
+    return m_impl->local_port();\r
+  }\r
+\r
+  unsigned long IP_socket::remote_address(void) const\r
+  {\r
+    return m_impl->remote_address();\r
+  }\r
+\r
+  unsigned short IP_socket::remote_port(void) const\r
+  {\r
+    return m_impl->remote_port();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // error handling\r
+\r
+  void IP_socket::set_error (int error, const std::string& message) const\r
+  {\r
+    m_impl->set_error(error, message.c_str());\r
+  }\r
+\r
+  void IP_socket::clear_error (void) const\r
+  {\r
+    m_impl->clear_error();\r
+  }\r
+\r
+  int IP_socket::error(void) const\r
+  {\r
+    return m_impl->error();\r
+  }\r
+\r
+  std::string IP_socket::message(void) const\r
+  {\r
+    return m_impl->message();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/ip_sockets.hpp b/src/stlplus/portability/ip_sockets.hpp
new file mode 100644 (file)
index 0000000..ac86ebc
--- /dev/null
@@ -0,0 +1,233 @@
+#ifndef STLPLUS_IP_SOCKET\r
+#define STLPLUS_IP_SOCKET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author:    Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+//            (c) Andy Rushton           2004-2009\r
+// License:   BSD License, see ../docs/license.html\r
+\r
+// A platform-independent (Unix and Windows anyway) interface to Internet-Protocol sockets\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // internals\r
+  // use a PIMPL interface to hide the platform-specifics in the implementation\r
+\r
+  class IP_socket_internals;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////\r
+  // Types of socket supported\r
+\r
+  enum IP_socket_type {undefined_socket_type = -1, TCP = 0, UDP = 1};\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // Socket class\r
+\r
+  class IP_socket\r
+  {\r
+  public:\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // constructors/destructors\r
+\r
+    // create an uninitialised socket\r
+    IP_socket(void);\r
+\r
+    // create an initialised socket\r
+    // - type: create either a TCP or UDP socket\r
+    IP_socket(IP_socket_type type);\r
+\r
+    // destroy the socket, closing it if open\r
+    ~IP_socket(void);\r
+\r
+    // copying is implemented as aliasing\r
+    IP_socket(const IP_socket&);\r
+    IP_socket& operator=(const IP_socket&);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation\r
+\r
+    // initialise the socket\r
+    // - type: create either a TCP or UDP socket\r
+    // - returns success status\r
+    bool initialise(IP_socket_type type);\r
+\r
+    // test whether this is an initialised socket\r
+    // - returns whether this is initialised\r
+    bool initialised(void) const;\r
+\r
+    // close, i.e. disconnect the socket\r
+    // - returns a success flag\r
+    bool close(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // Socket configuration\r
+\r
+    // function for performing IP lookup (i.e. gethostbyname)\r
+    // could be standalone but making it a member means that it can use the socket's error handler\r
+    // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+    // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+    // - returns the IP address as a long integer - zero if there's an error\r
+    unsigned long ip_lookup(const std::string& remote_address);\r
+\r
+    // test whether a socket is ready to communicate\r
+    // - readable: test whether socket is ready to read\r
+    // - writeable: test whether a socket is ready to write\r
+    // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait\r
+    // returns false if not ready or error - use error() method to tell - true if ready\r
+    bool select(bool readable, bool writeable, unsigned timeout = 0);\r
+\r
+    // bind the socket to a port so that it can receive from specific address - typically used by a client\r
+    // - remote_address: IP number of remote server to send/receive to/from\r
+    // - local_port: port on local machine to bind to the address\r
+    // - returns success flag\r
+    bool bind(unsigned long remote_address, unsigned short local_port);\r
+\r
+    // bind the socket to a port so that it can receive from any address - typically used by a server\r
+    // - local_port: port on local machine to bind to the address\r
+    // - returns success flag\r
+    bool bind_any(unsigned short local_port);\r
+\r
+    // set this socket up to be a listening port\r
+    // socket must be bound to a port already\r
+    // - queue: length of backlog queue to manage - may be zero meaning no queue\r
+    // - returns success status\r
+    bool listen(unsigned short queue = 0);\r
+\r
+    // test for a connection on the socket\r
+    // only applicable if it has been set up as a listening port\r
+    // - timeout: how long to wait in microseconds if not connected yet\r
+    // - returns true if a connection is ready to be accepted\r
+    bool accept_ready(unsigned timeout = 0) const;\r
+\r
+    // accept a connection on the socket\r
+    // only applicable if it has been set up as a listening port\r
+    // - returns the connection as a new socket\r
+    IP_socket accept(void);\r
+\r
+    // create a connection - usually used by a client\r
+    // TCP: client connect to a remote server\r
+    // UDP: setup remote address and port for sends\r
+    // - remote_address: IP number already looked up using ip_lookup\r
+    // - remote_port: port to connect to\r
+    // - returns a success flag\r
+    bool connect(unsigned long remote_address, unsigned short remote_port);\r
+\r
+    // test whether a socket is connected and ready to communicate, returns on successful connect or timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet\r
+    // - returns true if connected and ready to communicate, false if not ready or error\r
+    bool connected(unsigned timeout = 0);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // sending/receiving\r
+\r
+    // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    bool send_ready(unsigned timeout = 0);\r
+\r
+    // send data through a connection-based (TCP) socket\r
+    // if the data is long only part of it may be sent. The sent part is\r
+    // removed from the data, so the same string can be sent again and again\r
+    // until it is empty.\r
+    // - data: string containing data to be sent - any data successfully sent is removed\r
+    // - returns success flag\r
+    bool send(std::string& data);\r
+\r
+    // send data through a connectionless (UDP) socket\r
+    // the data will be sent as a single packet\r
+    // - packet: string containing data to be sent - any data successfully sent is removed\r
+    // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote\r
+    // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote\r
+    // - returns success flag\r
+    bool send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port);\r
+\r
+    // send data through a connectionless (UDP) socket\r
+    // the data will be sent as a single packet\r
+    // only works if the socket has been connected to remote\r
+    // - packet: string containing data to be sent - any data successfully sent is removed\r
+    // - returns success flag\r
+    bool send_packet(std::string& packet);\r
+\r
+    // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    bool receive_ready(unsigned wait = 0);\r
+\r
+    // receive data through a connection-based (TCP) socket\r
+    // if the data is long only part of it may be received. The received data\r
+    // is appended to the string, building it up in stages, so the same string\r
+    // can be received again and again until all information has been\r
+    // received.\r
+    // - data: string receiving data from socket - any data successfully received is appended\r
+    // - returns success flag\r
+    bool receive(std::string& data);\r
+\r
+    // receive data through a connectionless (UDP) socket\r
+    // - packet: string receiving data from socket - any data successfully received is appended\r
+    // - remote_address: returns the address of the remote host received from\r
+    // - remote_port: returns the port of the remote host received from\r
+    // - returns success flag\r
+    bool receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port);\r
+\r
+    // variant of above which does not give back the address and port of the sender\r
+    // receive data through a connectionless (UDP) socket\r
+    // - packet: string receiving data from socket - any data successfully received is appended\r
+    // - returns success flag\r
+    bool receive_packet(std::string& packet);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // informational\r
+\r
+    // gets the type of socket\r
+    // - returns undefined_socket_type, TCP or UDP\r
+    IP_socket_type type(void) const;\r
+\r
+    // the local port number of the connection\r
+    // returns the port number, 0 if not bound to a port\r
+    unsigned short local_port(void) const;\r
+\r
+    // the remote address of the connection\r
+    // returns the address, 0 if not connected\r
+    unsigned long remote_address(void) const;\r
+\r
+    // the remote port number of the connection\r
+    // returns the port number, 0 if not connected to a port\r
+    unsigned short remote_port(void) const;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+    // errors are set internally\r
+    // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+    // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+    // indicate an error - mostly used internally, you can set your own errors - use a negative code\r
+    void set_error (int error, const std::string& message) const;\r
+\r
+    // if an error is set it stays set - so you must clear it before further operations\r
+    void clear_error (void) const;\r
+\r
+    // get the error code of the last error\r
+    int error(void) const;\r
+\r
+    // get the explanatory message of the last error\r
+    std::string message(void) const;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+\r
+  private:\r
+    friend class IP_socket_internals;\r
+    IP_socket_internals* m_impl;\r
+  };\r
+\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/portability.hpp b/src/stlplus/portability/portability.hpp
new file mode 100644 (file)
index 0000000..942c182
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef STLPLUS_PORTABILITY\r
+#define STLPLUS_PORTABILITY\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Allows all the STLplus portability packages to be included in one go\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "build.hpp"\r
+#include "debug.hpp"\r
+#include "dprintf.hpp"\r
+#include "dynaload.hpp"\r
+#include "file_system.hpp"\r
+#include "inf.hpp"\r
+#include "subprocesses.hpp"\r
+#include "tcp_sockets.hpp"\r
+#include "udp_sockets.hpp"\r
+#include "time.hpp"\r
+#include "version.hpp"\r
+#include "wildcard.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/portability/portability_exceptions.hpp b/src/stlplus/portability/portability_exceptions.hpp
new file mode 100644 (file)
index 0000000..c66b7bf
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_PORTABILITY_EXCEPTIONS\r
+#define STLPLUS_PORTABILITY_EXCEPTIONS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Adds missing arithmetic exceptions used in this library but missing from std\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // thrown by division when the divisor is zero\r
+  // This is a subclass of std::logic_error so can be caught by a generic catch clause for the superclass\r
+\r
+  class divide_by_zero : public std::logic_error {\r
+  public:\r
+    divide_by_zero (const std::string& what_arg): std::logic_error (what_arg) { }\r
+  };\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/portability_fixes.cpp b/src/stlplus/portability/portability_fixes.cpp
new file mode 100644 (file)
index 0000000..e26ee28
--- /dev/null
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+\r
+#ifdef MSWINDOWS\r
+#include "windows.h"\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// problems with missing functions\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+unsigned sleep(unsigned seconds)\r
+{\r
+  Sleep(1000*seconds);\r
+  // should return remaining time if interrupted - however Windoze Sleep cannot be interrupted\r
+  return 0;\r
+}\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Function for establishing endian-ness\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+bool stlplus::little_endian(void)\r
+{\r
+  int sample = 1;\r
+  char* sample_bytes = (char*)&sample;\r
+  return sample_bytes[0] != 0;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/portability/portability_fixes.hpp b/src/stlplus/portability/portability_fixes.hpp
new file mode 100644 (file)
index 0000000..b6a030c
--- /dev/null
@@ -0,0 +1,127 @@
+#ifndef STLPLUS_PORTABILITY_FIXES\r
+#define STLPLUS_PORTABILITY_FIXES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Contains work arounds for OS or Compiler specific problems to try to make\r
+//   them look more alike\r
+\r
+//   It is strongly recommended that this header be included as the first\r
+//   #include in every source file\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problem with MicroSoft defining two different macros to identify Windows\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#if defined(_WIN32) || defined(_WIN32_WCE)\r
+#define MSWINDOWS\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problems with unnecessary or unfixable compiler warnings\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef _MSC_VER\r
+// Microsoft Visual Studio\r
+// shut up the following irritating warnings\r
+//   4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)\r
+//   4305 - VC6, identifier type was converted to a smaller type\r
+//   4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)\r
+//   4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it\r
+//   4290 - VC6, C++ exception specification ignored\r
+//   4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)\r
+//   4675 - VC7.1, "change" in function overload resolution _might_ have altered program\r
+//   4996 - VC8, 'xxxx' was declared deprecated\r
+#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996)\r
+#endif\r
+\r
+#ifdef __BORLANDC__\r
+// Borland\r
+// Shut up the following irritating warnings\r
+//   8026 - Functions with exception specifications are not expanded inline\r
+//   8027 - Functions with xxx are not expanded inline\r
+#pragma warn -8026\r
+#pragma warn -8027\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problems with redefinition of min/max in various different versions of library headers\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// The Windows headers define macros called max/min which conflict with the templates std::max and std::min.\r
+// So, to avoid conflicts, MS removed the std::max/min rather than fixing the problem!\r
+// From Visual Studio .NET (SV7, compiler version 13.00) the STL templates have been added correctly.\r
+// For MFC compatibility, only undef min and max in non-MFC programs - some bits of MFC\r
+// use macro min/max in headers. \r
+\r
+// I've created extra template function definitions minimum/maximum that avoid all the problems above\r
+\r
+namespace stlplus\r
+{\r
+  template<typename T> const T& maximum(const T& l, const T& r) {return l > r ? l : r;}\r
+  template<typename T> const T& minimum(const T& l, const T& r) {return l < r ? l : r;}\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problems with differences between namespaces\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Note: not sure of the relevance of this - maybe deprecated?\r
+// problem in gcc pre-v3 where the sub-namespaces in std aren't present\r
+// this mean that the statement "using namespace std::rel_ops" created an error because the namespace didn't exist\r
+\r
+// I've done a fix here that creates an empty namespace for this case, but I\r
+// do *not* try to move the contents of std::rel_ops into namespace std\r
+// This fix only works if you use "using namespace std::rel_ops" to bring in the template relational operators (e.g. != defined i.t.o. ==)\r
+\r
+#ifdef __GNUC__\r
+namespace std\r
+{\r
+  namespace rel_ops\r
+  {\r
+  }\r
+}\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// problems with missing functions\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+unsigned sleep(unsigned seconds);\r
+#else\r
+#include <unistd.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Function for establishing endian-ness\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Different machine architectures store data using different byte orders.\r
+// This is referred to as Big- and Little-Endian Byte Ordering. \r
+//\r
+// The issue is: where does a pointer to an integer type actually point?\r
+//\r
+// In both conventions, the address points to the left of the word but:\r
+// Big-Endian - The most significant byte is on the left end of a word\r
+// Little-Endian - The least significant byte is on the left end of a word\r
+//\r
+// Bytes are addressed left to right, so in big-endian order byte 0 is the\r
+// msB, whereas in little-endian order byte 0 is the lsB. For example,\r
+// Intel-based machines store data in little-endian byte order so byte 0 is\r
+// the lsB.\r
+//\r
+// This function establishes byte order at run-time\r
+\r
+namespace stlplus\r
+{\r
+  bool little_endian(void);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/portability/subprocesses.cpp b/src/stlplus/portability/subprocesses.cpp
new file mode 100644 (file)
index 0000000..a8139c2
--- /dev/null
@@ -0,0 +1,2077 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Bug fix by Alistair Low: kill on Windows now kills grandchild processes as\r
+// well as the child process. This is done using jobs - which has to be\r
+// enabled by stating that the version of Windows is at least 5.0\r
+#if defined(_WIN32) || defined(_WIN32_WCE)\r
+#define _WIN32_WINNT 0x0500\r
+#endif\r
+\r
+#include "subprocesses.hpp"\r
+#include "file_system.hpp"\r
+#include "dprintf.hpp"\r
+#include <ctype.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+\r
+#ifdef MSWINDOWS\r
+#else\r
+#include <signal.h>\r
+#include <errno.h>\r
+#include <sys/wait.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // argument-vector related stuff\r
+\r
+  static void skip_white (const std::string& command, unsigned& i)\r
+  {\r
+    while(i < command.size() && isspace(command[i]))\r
+      i++;\r
+  }\r
+\r
+  // get_argument is the main function for breaking a string down into separate command arguments\r
+  // it mimics the way shells break down a command into an argv[] and unescapes the escaped characters on the way\r
+\r
+  static std::string get_argument (const std::string& command, unsigned& i)\r
+  {\r
+    std::string result;\r
+#ifdef MSWINDOWS\r
+\r
+  // as far as I know, there is only double-quoting and no escape character in DOS\r
+  // so, how do you include a double-quote in an argument???\r
+\r
+    bool dquote = false;\r
+    for ( ; i < command.size(); i++)\r
+    {\r
+      char ch = command[i];\r
+      if (!dquote && isspace(ch)) break;\r
+      if (dquote)\r
+      {\r
+        if (ch == '\"')\r
+          dquote = false;\r
+        else\r
+          result += ch;\r
+      }\r
+      else if (ch == '\"')\r
+        dquote = true;\r
+      else\r
+        result += ch;\r
+    }\r
+#else\r
+    bool squote = false;\r
+    bool dquote = false;\r
+    bool escaped = false;\r
+    for ( ; i < command.size(); i++)\r
+    {\r
+      char ch = command[i];\r
+      if (!squote && !dquote && !escaped && isspace(ch)) break;\r
+      if (escaped)\r
+      {\r
+        result += ch;\r
+        escaped = false;\r
+      }\r
+      else if (squote)\r
+      {\r
+        if (ch == '\'')\r
+          squote = false;\r
+        else\r
+          result += ch;\r
+      }\r
+      else if (ch == '\\')\r
+        escaped = true;\r
+      else if (dquote)\r
+      {\r
+        if (ch == '\"')\r
+          dquote = false;\r
+        else\r
+          result += ch;\r
+      }\r
+      else if (ch == '\'')\r
+        squote = true;\r
+      else if (ch == '\"')\r
+        dquote = true;\r
+      else\r
+        result += ch;\r
+    }\r
+#endif\r
+\r
+    return result;\r
+  }\r
+\r
+\r
+  // this function performs the reverse of the above on a single argument\r
+  // it escapes special characters and quotes the argument if necessary ready for shell interpretation\r
+\r
+  static std::string make_argument (const std::string& arg)\r
+  {\r
+    std::string result;\r
+    bool needs_quotes = false;\r
+\r
+    for (unsigned i = 0; i < arg.size(); i++)\r
+    {\r
+      switch (arg[i])\r
+      {\r
+        // set of characters requiring escapes\r
+#ifdef MSWINDOWS\r
+#else\r
+      case '\\': case '\'': case '\"': case '`': case '(': case ')': \r
+      case '&': case '|': case '<': case '>': case '*': case '?': case '!':\r
+        result += '\\';\r
+        result += arg[i];\r
+        break;\r
+#endif\r
+        // set of whitespace characters that force quoting\r
+      case ' ':\r
+        result += arg[i];\r
+        needs_quotes = true;\r
+        break;\r
+      default:\r
+        result += arg[i];\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (needs_quotes)\r
+    {\r
+      result.insert(result.begin(), '"');\r
+      result += '"';\r
+    }\r
+    return result;\r
+  }\r
+\r
+  static char* copy_string (const char* str)\r
+  {\r
+    char* result = new char[strlen(str)+1];\r
+    strcpy(result,str);\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  arg_vector::arg_vector (void)\r
+  {\r
+    m_argv = 0;\r
+  }\r
+\r
+  arg_vector::arg_vector (const arg_vector& a)\r
+  {\r
+    m_argv = 0;\r
+    *this = a;\r
+  }\r
+\r
+  arg_vector::arg_vector (char** a)\r
+  {\r
+    m_argv = 0;\r
+    *this = a;\r
+  }\r
+\r
+  arg_vector::arg_vector (const std::string& command)\r
+  {\r
+    m_argv = 0;\r
+    *this = command;\r
+  }\r
+\r
+  arg_vector::arg_vector (const char* command)\r
+  {\r
+    m_argv = 0;\r
+    *this = command;\r
+  }\r
+\r
+  arg_vector::~arg_vector (void)\r
+  {\r
+    clear();\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (const arg_vector& a)\r
+  {\r
+    return *this = a.m_argv;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (char** argv)\r
+  {\r
+    clear();\r
+    for (unsigned i = 0; argv[i]; i++)\r
+      operator += (argv[i]);\r
+    return *this;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (const std::string& command)\r
+  {\r
+    clear();\r
+    for (unsigned i = 0; i < command.size(); )\r
+    {\r
+      std::string argument = get_argument(command, i);\r
+      operator += (argument);\r
+      skip_white(command, i);\r
+    }\r
+    return *this;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (const char* command)\r
+  {\r
+    return operator = (std::string(command));\r
+  }\r
+\r
+  arg_vector& arg_vector::operator += (const std::string& str)\r
+  {\r
+    insert(size(), str);\r
+    return *this;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator -= (const std::string& str)\r
+  {\r
+    insert(0, str);\r
+    return *this;\r
+  }\r
+\r
+  void arg_vector::insert (unsigned index, const std::string& str) throw(std::out_of_range)\r
+  {\r
+    if (index > size()) throw std::out_of_range("arg_vector::insert");\r
+    // copy up to but not including index, then add the new argument, then copy the rest\r
+    char** new_argv = new char*[size()+2];\r
+    unsigned i = 0;\r
+    for ( ; i < index; i++)\r
+      new_argv[i] = copy_string(m_argv[i]);\r
+    new_argv[index] = copy_string(str.c_str());\r
+    for ( ; i < size(); i++)\r
+      new_argv[i+1] = copy_string(m_argv[i]);\r
+    new_argv[i+1] = 0;\r
+    clear();\r
+    m_argv = new_argv;\r
+  }\r
+\r
+  void arg_vector::clear (unsigned index) throw(std::out_of_range)\r
+  {\r
+    if (index >= size()) throw std::out_of_range("arg_vector::clear");\r
+    // copy up to index, skip it, then copy the rest\r
+    char** new_argv = new char*[size()];\r
+    unsigned i = 0;\r
+    for ( ; i < index; i++)\r
+      new_argv[i] = copy_string(m_argv[i]);\r
+    i++;\r
+    for ( ; i < size(); i++)\r
+      new_argv[i-1] = copy_string(m_argv[i]);\r
+    new_argv[i-1] = 0;\r
+    clear();\r
+    m_argv = new_argv;\r
+  }\r
+\r
+  void arg_vector::clear(void)\r
+  {\r
+    if (m_argv)\r
+    {\r
+      for (unsigned i = 0; m_argv[i]; i++)\r
+        delete[] m_argv[i];\r
+      delete[] m_argv;\r
+      m_argv = 0;\r
+    }\r
+  }\r
+\r
+  unsigned arg_vector::size (void) const\r
+  {\r
+    unsigned i = 0;\r
+    if (m_argv)\r
+      while (m_argv[i])\r
+        i++;\r
+    return i;\r
+  }\r
+\r
+  arg_vector::operator char** (void) const\r
+  {\r
+    return m_argv;\r
+  }\r
+\r
+  char** arg_vector::argv (void) const\r
+  {\r
+    return m_argv;\r
+  }\r
+\r
+  char* arg_vector::operator [] (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    if (index >= size()) throw std::out_of_range("arg_vector::operator[]");\r
+    return m_argv[index];\r
+  }\r
+\r
+  char* arg_vector::argv0 (void) const throw(std::out_of_range)\r
+  {\r
+    return operator [] (0);\r
+  }\r
+\r
+  std::string arg_vector::image (void) const\r
+  {\r
+    std::string result;\r
+    for (unsigned i = 0; i < size(); i++)\r
+    {\r
+      if (i) result += ' ';\r
+      result += make_argument(m_argv[i]);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // environment-vector\r
+\r
+  // Windoze environment is a single string containing null-terminated\r
+  // name=value strings and the whole terminated by a null\r
+\r
+  // Unix environment is a null-terminated vector of pointers to null-terminated strings\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // platform specifics\r
+\r
+#ifdef MSWINDOWS\r
+  // Windows utilities\r
+\r
+  // Windows environment variables are case-insensitive and I do comparisons by converting to lowercase\r
+  static std::string lowercase(const std::string& val)\r
+  {\r
+    std::string text = val;\r
+    for (unsigned i = 0; i < text.size(); i++)\r
+      text[i] = tolower(text[i]);\r
+    return text;\r
+  }\r
+\r
+  static unsigned envp_size(const char* envp)\r
+  {\r
+    unsigned size = 0;\r
+    while (envp[size] || (size > 0 && envp[size-1])) size++;\r
+    size++;\r
+    return size;\r
+  }\r
+\r
+  static void envp_extract(std::string& name, std::string& value, const char* envp, unsigned& envi)\r
+  {\r
+    name.erase();\r
+    value.erase();\r
+    if (!envp[envi]) return;\r
+    // some special variables start with '=' so ensure at least one character in the name\r
+    name += envp[envi++];\r
+    while(envp[envi] != '=')\r
+      name += envp[envi++];\r
+    envi++;\r
+    while(envp[envi])\r
+      value += envp[envi++];\r
+    envi++;\r
+  }\r
+\r
+  static void envp_append(const std::string& name, const std::string& value, char* envp, unsigned& envi)\r
+  {\r
+    for (unsigned i = 0; i < name.size(); i++)\r
+      envp[envi++] = name[i];\r
+    envp[envi++] = '=';\r
+    for (unsigned j = 0; j < value.size(); j++)\r
+      envp[envi++] = value[j];\r
+    envp[envi++] = '\0';\r
+    envp[envi] = '\0';\r
+  }\r
+\r
+  static char* envp_copy(const char* envp)\r
+  {\r
+    unsigned size = envp_size(envp);\r
+    char* result = new char[size];\r
+    result[0] = '\0';\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(envp[i])\r
+    {\r
+      std::string name;\r
+      std::string value;\r
+      envp_extract(name, value, envp, i);\r
+      envp_append(name, value, result, j);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  static void envp_clear(char*& envp)\r
+  {\r
+    if (envp)\r
+    {\r
+      delete[] envp;\r
+      envp = 0;\r
+    }\r
+  }\r
+\r
+  static bool envp_equal(const std::string& left, const std::string& right)\r
+  {\r
+    return lowercase(left) == lowercase(right);\r
+  }\r
+\r
+  static bool envp_less(const std::string& left, const std::string& right)\r
+  {\r
+    return lowercase(left) < lowercase(right);\r
+  }\r
+\r
+#else\r
+  // Unix utilities\r
+\r
+  extern char** environ;\r
+\r
+  static unsigned envp_size(char* const* envp)\r
+  {\r
+    unsigned size = 0;\r
+    while(envp[size]) size++;\r
+    size++;\r
+    return size;\r
+  }\r
+\r
+  static void envp_extract(std::string& name, std::string& value, char* const* envp, unsigned& envi)\r
+  {\r
+    name = "";\r
+    value = "";\r
+    if (!envp[envi]) return;\r
+    unsigned i = 0;\r
+    while(envp[envi][i] != '=')\r
+      name += envp[envi][i++];\r
+    i++;\r
+    while(envp[envi][i])\r
+      value += envp[envi][i++];\r
+    envi++;\r
+  }\r
+\r
+  static void envp_append(const std::string& name, const std::string& value, char** envp, unsigned& envi)\r
+  {\r
+    std::string entry = name + "=" + value;\r
+    envp[envi] = copy_string(entry.c_str());\r
+    envi++;\r
+    envp[envi] = 0;\r
+  }\r
+\r
+  static char** envp_copy(char* const* envp)\r
+  {\r
+    unsigned size = envp_size(envp);\r
+    char** result = new char*[size];\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(envp[i])\r
+    {\r
+      std::string name;\r
+      std::string value;\r
+      envp_extract(name, value, envp, i);\r
+      envp_append(name, value, result, j);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  static void envp_clear(char**& envp)\r
+  {\r
+    if (envp)\r
+    {\r
+      for (unsigned i = 0; envp[i]; i++)\r
+        delete[] envp[i];\r
+      delete[] envp;\r
+      envp = 0;\r
+    }\r
+  }\r
+\r
+  static bool envp_equal(const std::string& left, const std::string& right)\r
+  {\r
+    return left == right;\r
+  }\r
+\r
+  static bool envp_less(const std::string& left, const std::string& right)\r
+  {\r
+    return left < right;\r
+  }\r
+\r
+#endif\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  env_vector::env_vector(void)\r
+  {\r
+#ifdef MSWINDOWS\r
+    char* env = (char*)GetEnvironmentStringsA();\r
+    m_env = envp_copy(env);\r
+    FreeEnvironmentStringsA(env);\r
+#else\r
+    m_env = envp_copy(::environ);\r
+#endif\r
+  }\r
+\r
+  env_vector::env_vector (const env_vector& a)\r
+  {\r
+    m_env = 0;\r
+    *this = a;\r
+  }\r
+\r
+  env_vector::~env_vector (void)\r
+  {\r
+    clear();\r
+  }\r
+\r
+  env_vector& env_vector::operator = (const env_vector& a)\r
+  {\r
+    clear();\r
+    m_env = envp_copy(a.m_env);\r
+    return *this;\r
+  }\r
+\r
+  void env_vector::clear(void)\r
+  {\r
+    envp_clear(m_env);\r
+  }\r
+\r
+  void env_vector::add(const std::string& name, const std::string& value)\r
+  {\r
+    // the trick is to add the value in alphabetic order\r
+    // this is done by copying the existing environment string to a new\r
+    // string, inserting the new value when a name greater than it is found\r
+    unsigned size = envp_size(m_env);\r
+#ifdef MSWINDOWS\r
+    unsigned new_size = size + name.size() + value.size() + 2;\r
+    char* new_v = new char[new_size];\r
+    new_v[0] = '\0';\r
+#else\r
+    unsigned new_size = size + 1;\r
+    char** new_v = new char*[new_size];\r
+    new_v[0] = 0;\r
+#endif\r
+    // now extract each name=value pair and check the ordering\r
+    bool added = false;\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(m_env[i])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, i);\r
+      if (envp_equal(name,current_name))\r
+      {\r
+        // replace an existing value\r
+        envp_append(name, value, new_v, j);\r
+      }\r
+      else if (!added && envp_less(name,current_name))\r
+      {\r
+        // add the new value first, then the existing one\r
+        envp_append(name, value, new_v, j);\r
+        envp_append(current_name, current_value, new_v, j);\r
+        added = true;\r
+      }\r
+      else\r
+      {\r
+        // just add the existing value\r
+        envp_append(current_name, current_value, new_v, j);\r
+      }\r
+    }\r
+    if (!added)\r
+      envp_append(name, value, new_v, j);\r
+    envp_clear(m_env);\r
+    m_env = new_v;\r
+  }\r
+\r
+\r
+  bool env_vector::remove (const std::string& name)\r
+  {\r
+    bool result = false;\r
+    // this is done by copying the existing environment string to a new string, but excluding the specified name\r
+    unsigned size = envp_size(m_env);\r
+#ifdef MSWINDOWS\r
+    char* new_v = new char[size];\r
+    new_v[0] = '\0';\r
+#else\r
+    char** new_v = new char*[size];\r
+    new_v[0] = 0;\r
+#endif\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(m_env[i])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, i);\r
+      if (envp_equal(name,current_name))\r
+        result = true;\r
+      else\r
+        envp_append(current_name, current_value, new_v, j);\r
+    }\r
+    envp_clear(m_env);\r
+    m_env = new_v;\r
+    return result;\r
+  }\r
+\r
+  std::string env_vector::operator [] (const std::string& name) const\r
+  {\r
+    return get(name);\r
+  }\r
+\r
+  std::string env_vector::get (const std::string& name) const\r
+  {\r
+    unsigned i = 0;\r
+    while(m_env[i])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, i);\r
+      if (envp_equal(name,current_name))\r
+        return current_value;\r
+    }\r
+    return std::string();\r
+  }\r
+\r
+  unsigned env_vector::size (void) const\r
+  {\r
+    unsigned i = 0;\r
+#ifdef MSWINDOWS\r
+    unsigned offset = 0;\r
+    while(m_env[offset])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, offset);\r
+      i++;\r
+    }\r
+#else\r
+    while(m_env[i])\r
+      i++;\r
+#endif\r
+\r
+    return i;\r
+  }\r
+\r
+  std::pair<std::string,std::string> env_vector::operator [] (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    return get(index);\r
+  }\r
+\r
+  std::pair<std::string,std::string> env_vector::get (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    if (index >= size()) throw std::out_of_range("arg_vector::get");\r
+    unsigned j = 0;\r
+    for (unsigned i = 0; i < index; i++)\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, j);\r
+    }\r
+    std::string name;\r
+    std::string value;\r
+    envp_extract(name, value, m_env, j);\r
+    return std::make_pair(name,value);\r
+  }\r
+\r
+  ENVIRON_TYPE env_vector::envp (void) const\r
+  {\r
+    return m_env;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Synchronous subprocess\r
+  // Win32 implementation mostly cribbed from MSDN examples and then made (much) more readable\r
+  // Unix implementation mostly from man pages and bitter experience\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  subprocess::subprocess(void)\r
+  {\r
+    m_pid.hProcess = 0;\r
+    m_job = 0;\r
+    m_child_in = 0;\r
+    m_child_out = 0;\r
+    m_child_err = 0;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#else\r
+\r
+  subprocess::subprocess(void)\r
+  {\r
+    m_pid = -1;\r
+    m_child_in = -1;\r
+    m_child_out = -1;\r
+    m_child_err = -1;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  subprocess::~subprocess(void)\r
+  {\r
+    if (m_pid.hProcess != 0)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  subprocess::~subprocess(void)\r
+  {\r
+    if (m_pid != -1)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      for (;;)\r
+      {\r
+        int wait_status = 0;\r
+        int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+        if (wait_ret_val != -1 || errno != EINTR) break;\r
+      }\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  void subprocess::add_variable(const std::string& name, const std::string& value)\r
+  {\r
+    m_env.add(name, value);\r
+  }\r
+\r
+  bool subprocess::remove_variable(const std::string& name)\r
+  {\r
+    return m_env.remove(name);\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                         bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+    // If no pipes requested, then connect to the parent stdin/out/err\r
+    // for some reason you have to create a pipe handle, then duplicate it\r
+    // This is not well explained in MSDN but seems to work\r
+    PIPE_TYPE parent_stdin = 0;\r
+    if (!connect_stdin)\r
+      parent_stdin = GetStdHandle(STD_INPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stdout = 0;\r
+    if (!connect_stdout)\r
+      parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stderr = 0;\r
+    if (!connect_stderr)\r
+      parent_stderr = GetStdHandle(STD_ERROR_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    // Now create the subprocess\r
+    // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work\r
+    // Note that the child will inherit a copy of the pipe handles\r
+    STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,\r
+                            STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,\r
+                            parent_stdin,parent_stdout,parent_stderr};\r
+    bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;\r
+    // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them\r
+    if (connect_stdin) CloseHandle(parent_stdin);\r
+    if (connect_stdout) CloseHandle(parent_stdout);\r
+    if (connect_stderr) CloseHandle(parent_stderr);\r
+    if (!created)\r
+    {\r
+      m_err = GetLastError();\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      result = false;\r
+    }\r
+    else\r
+    {\r
+      m_job = CreateJobObject(NULL, NULL);\r
+      AssignProcessToJobObject(m_job, m_pid.hProcess);\r
+      ResumeThread(m_pid.hThread);\r
+\r
+      // The child process is now running so call the user's callback\r
+      // The convention is that the callback can return false, in which case this will kill the child (if its still running)\r
+      if (!callback())\r
+      {\r
+        result = false;\r
+        kill();\r
+      }\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      // wait for the child to finish\r
+      // TODO - kill the child if a timeout happens\r
+      WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+      DWORD exit_status = 0;\r
+      if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))\r
+      {\r
+        m_err = GetLastError();\r
+        result = false;\r
+      }\r
+      else if (exit_status != 0)\r
+        result = false;\r
+      m_status = (int)exit_status;\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+    }\r
+    m_pid.hProcess = 0;\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  bool subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                         bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+\r
+    int stdin_pipe [2] = {-1, -1};\r
+    if (connect_stdin)\r
+      pipe(stdin_pipe);\r
+\r
+    int stdout_pipe [2] = {-1, -1};\r
+    if (connect_stdout)\r
+      pipe(stdout_pipe);\r
+\r
+    int stderr_pipe [2] = {-1, -1};\r
+    if (connect_stderr)\r
+      pipe(stderr_pipe);\r
+\r
+    // now create the subprocess\r
+    // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec\r
+    m_pid = ::fork();\r
+    switch(m_pid)\r
+    {\r
+    case -1:   // failed to fork\r
+      m_err = errno;\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        ::close(stdin_pipe[1]);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        ::close(stdout_pipe[1]);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        ::close(stderr_pipe[1]);\r
+      }\r
+      result = false;\r
+      break;\r
+    case 0:  // in child;\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the parent\r
+      // and connect the child's end of the pipe to the appropriate standard I/O device\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[1]);\r
+        dup2(stdin_pipe[0],STDIN_FILENO);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        dup2(stdout_pipe[1],STDOUT_FILENO);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        dup2(stderr_pipe[1],STDERR_FILENO);\r
+      }\r
+      execve(path.c_str(), argv.argv(), m_env.envp());\r
+      // will only ever get here if the exec() failed completely - *must* now exit the child process\r
+      // by using errno, the parent has some chance of diagnosing the cause of the problem\r
+      exit(errno);\r
+    }\r
+    break;\r
+    default:  // in parent\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the child\r
+      // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        m_child_in = stdin_pipe[1];\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[1]);\r
+        m_child_out = stdout_pipe[0];\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[1]);\r
+        m_child_err = stderr_pipe[0];\r
+      }\r
+      // call the user's callback\r
+      if (!callback())\r
+      {\r
+        result = false;\r
+        kill();\r
+      }\r
+      // close the pipes and wait for the child to finish\r
+      // wait exits on a signal which may be the child signalling its exit or may be an interrupt\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      int wait_status = 0;\r
+      for (;;)\r
+      {\r
+        int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+        if (wait_ret_val != -1 || errno != EINTR) break;\r
+      }\r
+      // establish whether an error occurred\r
+      if (WIFSIGNALED(wait_status))\r
+      {\r
+        // set_error(errno);\r
+        m_status = WTERMSIG(wait_status);\r
+        result = false;\r
+      }\r
+      else if (WIFEXITED(wait_status))\r
+      {\r
+        m_status = WEXITSTATUS(wait_status);\r
+        if (m_status != 0)\r
+          result = false;\r
+      }\r
+      m_pid = -1;\r
+    }\r
+    break;\r
+    }\r
+    return result;\r
+  }\r
+\r
+#endif\r
+\r
+  bool subprocess::spawn(const std::string& command_line,\r
+                                  bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    arg_vector arguments = command_line;\r
+    if (arguments.size() == 0) return false;\r
+    std::string path = path_lookup(arguments.argv0());\r
+    if (path.empty()) return false;\r
+    return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);\r
+  }\r
+\r
+  bool subprocess::callback(void)\r
+  {\r
+    return true;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool subprocess::kill (void)\r
+  {\r
+    if (!m_pid.hProcess) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (!TerminateJobObject(m_job, (UINT)-1))\r
+    {\r
+      m_err = GetLastError();\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#else\r
+\r
+  bool subprocess::kill (void)\r
+  {\r
+    if (m_pid == -1) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (::kill(m_pid, SIGINT) == -1)\r
+    {\r
+      m_err = errno;\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == 0) return -1;\r
+    // do a blocking write of the whole buffer\r
+    DWORD bytes = 0;\r
+    if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))\r
+    {\r
+      m_err = GetLastError();\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == -1) return -1;\r
+    // do a blocking write of the whole buffer\r
+    int bytes = write(m_child_in, buffer.c_str(), buffer.size());\r
+    if (bytes == -1)\r
+    {\r
+      m_err = errno;\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == 0) return -1;\r
+    DWORD bytes = 0;\r
+    DWORD buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        m_err = GetLastError();\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == -1) return -1;\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_out, tmp, buffer_size);\r
+    if (bytes == -1)\r
+    {\r
+      m_err = errno;\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int subprocess::read_stderr(std::string& buffer)\r
+  {\r
+    if (m_child_err == 0) return -1;\r
+    DWORD bytes = 0;\r
+    DWORD buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        m_err = GetLastError();\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int subprocess::read_stderr (std::string& buffer)\r
+  {\r
+    if (m_child_err == -1) return -1;\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_err, tmp, buffer_size);\r
+    if (bytes == -1)\r
+    {\r
+      m_err = errno;\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in)\r
+    {\r
+      CloseHandle(m_child_in);\r
+      m_child_in = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in != -1)\r
+    {\r
+      ::close(m_child_in);\r
+      m_child_in = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out)\r
+    {\r
+      CloseHandle(m_child_out);\r
+      m_child_out = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out != -1)\r
+    {\r
+      ::close(m_child_out);\r
+      m_child_out = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err)\r
+    {\r
+      CloseHandle(m_child_err);\r
+      m_child_err = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err != -1)\r
+    {\r
+      ::close(m_child_err);\r
+      m_child_err = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  bool subprocess::error(void) const\r
+  {\r
+    return m_err != 0;\r
+  }\r
+\r
+  int subprocess::error_number(void) const\r
+  {\r
+    return m_err;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  std::string subprocess::error_text(void) const\r
+  {\r
+    if (m_err == 0) return std::string();\r
+    char* message;\r
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                  0,\r
+                  m_err,\r
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+                  (LPTSTR)&message,\r
+                  0,0);\r
+    std::string result = message;\r
+    LocalFree(message);\r
+    // the error message is for some perverse reason newline terminated - remove this\r
+    if (result[result.size()-1] == '\n')\r
+      result.erase(result.end()-1);\r
+    if (result[result.size()-1] == '\r')\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  std::string subprocess::error_text(void) const\r
+  {\r
+    if (m_err == 0) return std::string();\r
+    char* text = strerror(m_err);\r
+    if (text) return std::string(text);\r
+    return "error number " + dformat("%d",m_err);\r
+  }\r
+\r
+#endif\r
+\r
+  int subprocess::exit_status(void) const\r
+  {\r
+    return m_status;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // backtick subprocess and operations\r
+\r
+  backtick_subprocess::backtick_subprocess(void) : subprocess()\r
+  {\r
+  }\r
+\r
+  bool backtick_subprocess::callback(void)\r
+  {\r
+    for (;;)\r
+    {\r
+      std::string buffer;\r
+      int read_size = read_stdout(buffer);\r
+      if (read_size < 0) break;\r
+      m_text += buffer;\r
+    }\r
+    return !error();\r
+  }\r
+\r
+  bool backtick_subprocess::spawn(const std::string& path, const arg_vector& argv)\r
+  {\r
+    return subprocess::spawn(path, argv, false, true, false);\r
+  }\r
+\r
+  bool backtick_subprocess::spawn(const std::string& command_line)\r
+  {\r
+    return subprocess::spawn(command_line, false, true, false);\r
+  }\r
+\r
+  std::vector<std::string> backtick_subprocess::text(void) const\r
+  {\r
+    std::vector<std::string> result;\r
+    // convert the raw text into a vector of strings, each corresponding to a line\r
+    // in the process, strip out platform-specific line-endings\r
+    for (unsigned i = 0; i < m_text.size(); i++)\r
+    {\r
+      // handle any kind of line-ending - Dos, Unix or MacOS\r
+      switch(m_text[i])\r
+      {\r
+      case '\xd': // carriage-return - optionally followed by linefeed\r
+      {\r
+        // discard optional following linefeed\r
+        if ((i+1 < m_text.size()) && (m_text[i+1] == '\xa'))\r
+          i++;\r
+        // add a new line to the end of the vector\r
+        result.push_back(std::string());\r
+        break;\r
+      }\r
+      case '\xa': // linefeed\r
+      {\r
+        // add a new line to the end of the vector\r
+        result.push_back(std::string());\r
+        break;\r
+      }\r
+      default:\r
+      {\r
+        result.back() += m_text[i];\r
+        break;\r
+      }\r
+      }\r
+    }\r
+    // tidy up - if the last line ended with a newline, the vector will end with an empty string - discard this\r
+    if ((result.size()) > 0 && result.back().empty())\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+  std::vector<std::string> backtick(const std::string& path, const arg_vector& argv)\r
+  {\r
+    backtick_subprocess sub;\r
+    sub.spawn(path, argv);\r
+    return sub.text();\r
+  }\r
+\r
+  std::vector<std::string> backtick(const std::string& command_line)\r
+  {\r
+    backtick_subprocess sub;\r
+    sub.spawn(command_line);\r
+    return sub.text();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Asynchronous subprocess\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  async_subprocess::async_subprocess(void)\r
+  {\r
+    m_pid.hProcess = 0;\r
+    m_job = 0;\r
+    m_child_in = 0;\r
+    m_child_out = 0;\r
+    m_child_err = 0;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#else\r
+\r
+  async_subprocess::async_subprocess(void)\r
+  {\r
+    m_pid = -1;\r
+    m_child_in = -1;\r
+    m_child_out = -1;\r
+    m_child_err = -1;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  async_subprocess::~async_subprocess(void)\r
+  {\r
+    if (m_pid.hProcess != 0)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  async_subprocess::~async_subprocess(void)\r
+  {\r
+    if (m_pid != -1)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      for (;;)\r
+      {\r
+        int wait_status = 0;\r
+        int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+        if (wait_ret_val != -1 || errno != EINTR) break;\r
+      }\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  void async_subprocess::set_error(int e)\r
+  {\r
+    m_err = e;\r
+  }\r
+\r
+  void async_subprocess::add_variable(const std::string& name, const std::string& value)\r
+  {\r
+    m_env.add(name, value);\r
+  }\r
+\r
+  bool async_subprocess::remove_variable(const std::string& name)\r
+  {\r
+    return m_env.remove(name);\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                                        bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+    // If no pipes requested, then connect to the parent stdin/out/err\r
+    // for some reason you have to create a pipe handle, then duplicate it\r
+    // This is not well explained in MSDN but seems to work\r
+    PIPE_TYPE parent_stdin = 0;\r
+    if (!connect_stdin)\r
+      parent_stdin = GetStdHandle(STD_INPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stdout = 0;\r
+    if (!connect_stdout)\r
+      parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stderr = 0;\r
+    if (!connect_stderr)\r
+      parent_stderr = GetStdHandle(STD_ERROR_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    // Now create the subprocess\r
+    // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work\r
+    // Note that the child will inherit a copy of the pipe handles\r
+    STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,\r
+                            STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,\r
+                            parent_stdin,parent_stdout,parent_stderr};\r
+    bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;\r
+    // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them\r
+    if (connect_stdin) CloseHandle(parent_stdin);\r
+    if (connect_stdout) CloseHandle(parent_stdout);\r
+    if (connect_stderr) CloseHandle(parent_stderr);\r
+    if (!created)\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      result = false;\r
+    }\r
+    else\r
+    {\r
+      m_job = CreateJobObject(NULL, NULL);\r
+      AssignProcessToJobObject(m_job, m_pid.hProcess);\r
+      ResumeThread(m_pid.hThread);\r
+    }\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                               bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+\r
+    int stdin_pipe [2] = {-1, -1};\r
+    if (connect_stdin)\r
+      pipe(stdin_pipe);\r
+\r
+    int stdout_pipe [2] = {-1, -1};\r
+    if (connect_stdout)\r
+      pipe(stdout_pipe);\r
+\r
+    int stderr_pipe [2] = {-1, -1};\r
+    if (connect_stderr)\r
+      pipe(stderr_pipe);\r
+\r
+    // now create the subprocess\r
+    // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec\r
+    m_pid = ::fork();\r
+    switch(m_pid)\r
+    {\r
+    case -1:   // failed to fork\r
+      set_error(errno);\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        ::close(stdin_pipe[1]);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        ::close(stdout_pipe[1]);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        ::close(stderr_pipe[1]);\r
+      }\r
+      result = false;\r
+      break;\r
+    case 0:  // in child;\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the parent\r
+      // and connect the child's end of the pipe to the appropriate standard I/O device\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[1]);\r
+        dup2(stdin_pipe[0],STDIN_FILENO);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        dup2(stdout_pipe[1],STDOUT_FILENO);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        dup2(stderr_pipe[1],STDERR_FILENO);\r
+      }\r
+      execve(path.c_str(), argv.argv(), m_env.envp());\r
+      // will only ever get here if the exec() failed completely - *must* now exit the child process\r
+      // by using errno, the parent has some chance of diagnosing the cause of the problem\r
+      exit(errno);\r
+    }\r
+    break;\r
+    default:  // in parent\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the child\r
+      // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        m_child_in = stdin_pipe[1];\r
+        if (fcntl(m_child_in, F_SETFL, O_NONBLOCK) == -1)\r
+        {\r
+          set_error(errno);\r
+          result = false;\r
+        }\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[1]);\r
+        m_child_out = stdout_pipe[0];\r
+        if (fcntl(m_child_out, F_SETFL, O_NONBLOCK) == -1)\r
+        {\r
+          set_error(errno);\r
+          result = false;\r
+        }\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[1]);\r
+        m_child_err = stderr_pipe[0];\r
+        if (fcntl(m_child_err, F_SETFL, O_NONBLOCK) == -1)\r
+        {\r
+          set_error(errno);\r
+          result = false;\r
+        }\r
+      }\r
+    }\r
+    break;\r
+    }\r
+    return result;\r
+  }\r
+\r
+#endif\r
+\r
+  bool async_subprocess::spawn(const std::string& command_line,\r
+                               bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    arg_vector arguments = command_line;\r
+    if (arguments.size() == 0) return false;\r
+    std::string path = path_lookup(arguments.argv0());\r
+    if (path.empty()) return false;\r
+    return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);\r
+  }\r
+\r
+  bool async_subprocess::callback(void)\r
+  {\r
+    return true;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool async_subprocess::tick(void)\r
+  {\r
+    bool result = true;\r
+    if (!callback())\r
+      kill();\r
+    DWORD exit_status = 0;\r
+    if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))\r
+    {\r
+      set_error(GetLastError());\r
+      result = false;\r
+    }\r
+    else if (exit_status != STILL_ACTIVE)\r
+    {\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+      m_pid.hProcess = 0;\r
+      result = false;\r
+    }\r
+    m_status = (int)exit_status;\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  bool async_subprocess::tick(void)\r
+  {\r
+    bool result = true;\r
+    if (!callback())\r
+      kill();\r
+    int wait_status = 0;\r
+    int wait_ret_val = waitpid(m_pid, &wait_status, WNOHANG);\r
+    if (wait_ret_val == -1 && errno != EINTR)\r
+    {\r
+      set_error(errno);\r
+      result = false;\r
+    }\r
+    else if (wait_ret_val != 0)\r
+    {\r
+      // the only states that indicate a terminated child are WIFSIGNALLED and WIFEXITED\r
+      if (WIFSIGNALED(wait_status))\r
+      {\r
+        // set_error(errno);\r
+        m_status = WTERMSIG(wait_status);\r
+        result = false;\r
+      }\r
+      else if (WIFEXITED(wait_status))\r
+      {\r
+        // child has exited\r
+        m_status = WEXITSTATUS(wait_status);\r
+        result = false;\r
+      }\r
+    }\r
+    if (!result)\r
+      m_pid = -1;\r
+    return result;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool async_subprocess::kill(void)\r
+  {\r
+    if (!m_pid.hProcess) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (!TerminateJobObject(m_job, (UINT)-1))\r
+    {\r
+      set_error(GetLastError());\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#else\r
+\r
+  bool async_subprocess::kill(void)\r
+  {\r
+    if (m_pid == -1) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (::kill(m_pid, SIGINT) == -1)\r
+    {\r
+      set_error(errno);\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int async_subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == 0) return -1;\r
+    // there doesn't seem to be a way of doing non-blocking writes under Windoze\r
+    DWORD bytes = 0;\r
+    if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int async_subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == -1) return -1;\r
+    // relies on the pipe being non-blocking\r
+    // This does block under Windoze\r
+    int bytes = write(m_child_in, buffer.c_str(), buffer.size());\r
+    if (bytes == -1 && errno == EAGAIN)\r
+    {\r
+      // not ready\r
+      return 0;\r
+    }\r
+    if (bytes == -1)\r
+    {\r
+      // error on write - close the pipe and give up\r
+      set_error(errno);\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // successful write\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int async_subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == 0) return -1;\r
+    // peek at the buffer to see how much data there is in the first place\r
+    DWORD buffer_size = 0;\r
+    if (!PeekNamedPipe(m_child_out, 0, 0, 0, &buffer_size, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        set_error(GetLastError());\r
+      close_stdout();\r
+      return -1;\r
+    }\r
+    if (buffer_size == 0) return 0;\r
+    DWORD bytes = 0;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int async_subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == -1) return -1;\r
+    // rely on the pipe being non-blocking\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_out, tmp, buffer_size);\r
+    if (bytes == -1 && errno == EAGAIN)\r
+    {\r
+      // not ready\r
+      delete[] tmp;\r
+      return 0;\r
+    }\r
+    if (bytes == -1)\r
+    {\r
+      // error\r
+      set_error(errno);\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    // successful read\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int async_subprocess::read_stderr (std::string& buffer)\r
+  {\r
+    if (m_child_err == 0) return -1;\r
+    // peek at the buffer to see how much data there is in the first place\r
+    DWORD buffer_size = 0;\r
+    if (!PeekNamedPipe(m_child_err, 0, 0, 0, &buffer_size, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        set_error(GetLastError());\r
+      close_stderr();\r
+      return -1;\r
+    }\r
+    if (buffer_size == 0) return 0;\r
+    DWORD bytes = 0;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      set_error(GetLastError());\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int async_subprocess::read_stderr (std::string& buffer)\r
+  {\r
+    if (m_child_err == -1) return -1;\r
+    // rely on the pipe being non-blocking\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_err, tmp, buffer_size);\r
+    if (bytes == -1 && errno == EAGAIN)\r
+    {\r
+      // not ready\r
+      delete[] tmp;\r
+      return 0;\r
+    }\r
+    if (bytes == -1)\r
+    {\r
+      // error\r
+      set_error(errno);\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    // successful read\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void async_subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in)\r
+    {\r
+      CloseHandle(m_child_in);\r
+      m_child_in = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void async_subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in != -1)\r
+    {\r
+      ::close(m_child_in);\r
+      m_child_in = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void async_subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out)\r
+    {\r
+      CloseHandle(m_child_out);\r
+      m_child_out = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void async_subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out != -1)\r
+    {\r
+      ::close(m_child_out);\r
+      m_child_out = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void async_subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err)\r
+    {\r
+      CloseHandle(m_child_err);\r
+      m_child_err = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void async_subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err != -1)\r
+    {\r
+      ::close(m_child_err);\r
+      m_child_err = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  bool async_subprocess::error(void) const\r
+  {\r
+    return m_err != 0;\r
+  }\r
+\r
+  int async_subprocess::error_number(void) const\r
+  {\r
+    return m_err;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  std::string async_subprocess::error_text(void) const\r
+  {\r
+    if (m_err == 0) return std::string();\r
+    char* message;\r
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                  0,\r
+                  m_err,\r
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+                  (LPTSTR)&message,\r
+                  0,0);\r
+    std::string result = message;\r
+    LocalFree(message);\r
+    // the error message is for some perverse reason newline terminated - remove this\r
+    if (result[result.size()-1] == '\n')\r
+      result.erase(result.end()-1);\r
+    if (result[result.size()-1] == '\r')\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  std::string async_subprocess::error_text(void) const\r
+  {\r
+    if (m_err == 0) return std::string();\r
+    char* text = strerror(m_err);\r
+    if (text) return std::string(text);\r
+    return "error number " + dformat("%d",m_err);\r
+  }\r
+\r
+#endif\r
+\r
+  int async_subprocess::exit_status(void) const\r
+  {\r
+    return m_status;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/subprocesses.hpp b/src/stlplus/portability/subprocesses.hpp
new file mode 100644 (file)
index 0000000..0369daa
--- /dev/null
@@ -0,0 +1,282 @@
+#ifndef STLPLUS_SUBPROCESSES\r
+#define STLPLUS_SUBPROCESSES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+  \r
+//   Platform-independent wrapper around the very platform-specific handling of\r
+//   subprocesses. Uses the C++ convention that all resources must be contained in\r
+//   an object so that when a subprocess object goes out of scope the subprocess\r
+//   itself gets closed down.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#ifdef MSWINDOWS\r
+#include <windows.h>\r
+#endif\r
+#include <stdexcept>\r
+#include <vector>\r
+#include <string>\r
+#include <map> // for std::pair - why is this not defined separately?\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Argument vector class\r
+  // allows manipulation of argv-like vectors\r
+  // includes splitting of command lines into argvectors as per the shell\r
+  // (removing quotes) and the reverse conversion (adding quotes where necessary)\r
+\r
+  class arg_vector\r
+  {\r
+  private:\r
+    char** m_argv;\r
+\r
+  public:\r
+    // create an empty vector\r
+    arg_vector (void);\r
+\r
+    // copy constructor (yes it copies)\r
+    arg_vector (const arg_vector&);\r
+\r
+    // construct from an argv\r
+    arg_vector (char**);\r
+\r
+    // construct from a command-line string\r
+    // includes de-quoting of values\r
+    arg_vector (const std::string&);\r
+    arg_vector (const char*);\r
+\r
+    ~arg_vector (void);\r
+\r
+    // assignment operators are compatible with the constructors\r
+    arg_vector& operator = (const arg_vector&);\r
+    arg_vector& operator = (char**);\r
+    arg_vector& operator = (const std::string&);\r
+    arg_vector& operator = (const char*);\r
+\r
+    // add an argument to the vector\r
+    arg_vector& operator += (const std::string&);\r
+    arg_vector& operator -= (const std::string&);\r
+\r
+    // insert/clear an argument at a certain index\r
+    // adding is like the other array classes - it moves the current item at index\r
+    // up one (and all subsequent values) to make room\r
+    void insert (unsigned index, const std::string&) throw(std::out_of_range);\r
+    void clear (unsigned index) throw(std::out_of_range);\r
+    void clear (void);\r
+\r
+    // number of values in the vector (including argv[0], the command itself\r
+    unsigned size (void) const;\r
+\r
+    // type conversion to the argv type\r
+    operator char** (void) const;\r
+    // function-based version of the above for people who don't like type conversions\r
+    char** argv (void) const;\r
+\r
+    // access individual values in the vector\r
+    char* operator [] (unsigned index) const throw(std::out_of_range);\r
+\r
+    // special-case access of the command name (e.g. to do path lookup on the command)\r
+    char* argv0 (void) const throw(std::out_of_range);\r
+\r
+    // get the command-line string represented by this vector\r
+    // includes escaping of special characters and quoting\r
+    std::string image (void) const;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Environment class\r
+  // Allows manipulation of an environment vector\r
+  // This is typically used to create an environment to be used by a subprocess\r
+  // It does NOT modify the environment of the current process\r
+\r
+#ifdef MSWINDOWS\r
+#define ENVIRON_TYPE char*\r
+#else\r
+#define ENVIRON_TYPE char**\r
+#endif\r
+\r
+  class env_vector\r
+  {\r
+  private:\r
+    ENVIRON_TYPE m_env;\r
+\r
+  public:\r
+    // create an env_vector vector from the current process\r
+    env_vector (void);\r
+    env_vector (const env_vector&);\r
+    ~env_vector (void);\r
+\r
+    env_vector& operator = (const env_vector&);\r
+\r
+    void clear (void);\r
+\r
+    // manipulate the env_vector by adding or removing variables\r
+    // adding a name that already exists replaces its value\r
+    void add (const std::string& name, const std::string& value);\r
+    bool remove (const std::string& name);\r
+\r
+    // get the value associated with a name\r
+    // the first uses an indexed notation (e.g. env["PATH"] )\r
+    // the second is a function based form (e.g. env.get("PATH"))\r
+    std::string operator [] (const std::string& name) const;\r
+    std::string get (const std::string& name) const;\r
+\r
+    // number of name=value pairs in the env_vector\r
+    unsigned size (void) const;\r
+\r
+    // get the name=value pairs by index (in the range 0 to size()-1)\r
+    std::pair<std::string,std::string> operator [] (unsigned index) const throw(std::out_of_range);\r
+    std::pair<std::string,std::string> get (unsigned index) const throw(std::out_of_range);\r
+\r
+    // access the env_vector as an envp type - used for passing to subprocesses\r
+    ENVIRON_TYPE envp (void) const;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+#define PID_TYPE PROCESS_INFORMATION\r
+#define PIPE_TYPE HANDLE\r
+#else\r
+#define PID_TYPE int\r
+#define PIPE_TYPE int\r
+#endif\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Synchronous subprocess\r
+\r
+  class subprocess\r
+  {\r
+  private:\r
+\r
+    PID_TYPE m_pid;\r
+#ifdef MSWINDOWS\r
+    HANDLE m_job;\r
+#endif\r
+    PIPE_TYPE m_child_in;\r
+    PIPE_TYPE m_child_out;\r
+    PIPE_TYPE m_child_err;\r
+    env_vector m_env;\r
+    int m_err;\r
+    int m_status;\r
+\r
+  public:\r
+    subprocess(void);\r
+    virtual ~subprocess(void);\r
+\r
+    void add_variable(const std::string& name, const std::string& value);\r
+    bool remove_variable(const std::string& name);\r
+\r
+    bool spawn(const std::string& path, const arg_vector& argv,\r
+               bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+    bool spawn(const std::string& command_line,\r
+               bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+\r
+    virtual bool callback(void);\r
+    bool kill(void);\r
+\r
+    int write_stdin(std::string& buffer);\r
+    int read_stdout(std::string& buffer);\r
+    int read_stderr(std::string& buffer);\r
+\r
+    void close_stdin(void);\r
+    void close_stdout(void);\r
+    void close_stderr(void);\r
+\r
+    bool error(void) const;\r
+    int error_number(void) const;\r
+    std::string error_text(void) const;\r
+\r
+    int exit_status(void) const;\r
+\r
+  private:\r
+    // disallow copying\r
+    subprocess(const subprocess&);\r
+    subprocess& operator=(const subprocess&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Preconfigured subprocess which executes a command and captures its output\r
+\r
+  class backtick_subprocess : public subprocess\r
+  {\r
+  private:\r
+    std::string m_text;\r
+  public:\r
+    backtick_subprocess(void);\r
+    virtual bool callback(void);\r
+    bool spawn(const std::string& path, const arg_vector& argv);\r
+    bool spawn(const std::string& command_line);\r
+    std::vector<std::string> text(void) const;\r
+  };\r
+\r
+  std::vector<std::string> backtick(const std::string& path, const arg_vector& argv);\r
+  std::vector<std::string> backtick(const std::string& command_line);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Asynchronous subprocess\r
+\r
+  class async_subprocess\r
+  {\r
+  private:\r
+    PID_TYPE m_pid;\r
+#ifdef MSWINDOWS\r
+    HANDLE m_job;\r
+#endif\r
+    PIPE_TYPE m_child_in;\r
+    PIPE_TYPE m_child_out;\r
+    PIPE_TYPE m_child_err;\r
+    env_vector m_env;\r
+    int m_err;\r
+    int m_status;\r
+    void set_error(int);\r
+\r
+  public:\r
+    async_subprocess(void);\r
+    virtual ~async_subprocess(void);\r
+\r
+    void add_variable(const std::string& name, const std::string& value);\r
+    bool remove_variable(const std::string& name);\r
+\r
+    bool spawn(const std::string& path, const arg_vector& argv,\r
+               bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+    bool spawn(const std::string& command_line,\r
+               bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+\r
+    virtual bool callback(void);\r
+    bool tick(void);\r
+    bool kill(void);\r
+\r
+    int write_stdin(std::string& buffer);\r
+    int read_stdout(std::string& buffer);\r
+    int read_stderr(std::string& buffer);\r
+\r
+    void close_stdin(void);\r
+    void close_stdout(void);\r
+    void close_stderr(void);\r
+\r
+    bool error(void) const;\r
+    int error_number(void) const;\r
+    std::string error_text(void) const;\r
+\r
+    int exit_status(void) const;\r
+\r
+  private:\r
+    // disallow copying\r
+    async_subprocess(const async_subprocess&);\r
+    async_subprocess& operator=(const async_subprocess&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/tcp.hpp b/src/stlplus/portability/tcp.hpp
new file mode 100644 (file)
index 0000000..3af0d3f
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef STLPLUS_TCP\r
+#define STLPLUS_TCP\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A deprecated legacy header - please use tcp_socket.hpp\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include "tcp_sockets.hpp"\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/tcp_sockets.cpp b/src/stlplus/portability/tcp_sockets.cpp
new file mode 100644 (file)
index 0000000..84152f4
--- /dev/null
@@ -0,0 +1,119 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "tcp_sockets.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // TCP Connection\r
+\r
+\r
+  TCP_connection::TCP_connection(const IP_socket& socket) : IP_socket(socket)\r
+  {\r
+  }\r
+\r
+  TCP_connection::TCP_connection(void) : IP_socket(TCP)\r
+  {\r
+  }\r
+\r
+  unsigned short TCP_connection::port(void) const\r
+  {\r
+    return remote_port();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Server\r
+\r
+  TCP_server::TCP_server(void) : IP_socket(TCP)\r
+  {\r
+  }\r
+\r
+  TCP_server::TCP_server(unsigned short port, unsigned short queue) : IP_socket(TCP)\r
+  {\r
+    initialise(port,queue);\r
+  }\r
+\r
+  bool TCP_server::initialise(unsigned short port, unsigned short queue)\r
+  {\r
+    if (!IP_socket::bind_any(port)) return false;\r
+    return IP_socket::listen(queue);\r
+  }\r
+\r
+  TCP_connection TCP_server::accept(void)\r
+  {\r
+    return TCP_connection(IP_socket::accept());\r
+  }\r
+\r
+  bool TCP_server::connection_ready(unsigned timeout)\r
+  {\r
+    return accept_ready(timeout);\r
+  }\r
+\r
+  TCP_connection TCP_server::connection(void)\r
+  {\r
+    return accept();\r
+  }\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // Client\r
+\r
+  TCP_client::TCP_client(void) : IP_socket(TCP)\r
+  {\r
+  }\r
+\r
+  TCP_client::TCP_client(const std::string& address, unsigned short port, unsigned int timeout) : IP_socket(TCP)\r
+  {\r
+    initialise(address,port,timeout);\r
+  }\r
+\r
+  TCP_client::TCP_client(unsigned long address, unsigned short port, unsigned int timeout) : IP_socket(TCP)\r
+  {\r
+    initialise(address,port,timeout);\r
+  }\r
+\r
+  bool TCP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned int timeout)\r
+  {\r
+    if (!IP_socket::connect(remote_address, remote_port))\r
+    {\r
+      close();\r
+      return false;\r
+    }\r
+    if (timeout && !IP_socket::connected(timeout))\r
+    {\r
+      close();\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+  bool TCP_client::initialise(const std::string& address, unsigned short remote_port, unsigned int timeout)\r
+  {\r
+    // lookup the address and convert it into an IP number\r
+    unsigned long remote_address = IP_socket::ip_lookup(address);\r
+    if (!remote_address) return false;\r
+    return initialise(remote_address, remote_port, timeout);\r
+  }\r
+\r
+  unsigned short TCP_client::port(void) const\r
+  {\r
+    return remote_port();\r
+  }\r
+\r
+  unsigned long TCP_client::address(void) const\r
+  {\r
+    return remote_address();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/tcp_sockets.hpp b/src/stlplus/portability/tcp_sockets.hpp
new file mode 100644 (file)
index 0000000..94b98c5
--- /dev/null
@@ -0,0 +1,385 @@
+#ifndef STLPLUS_TCP_SOCKET\r
+#define STLPLUS_TCP_SOCKET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A platform-independent (Unix and Windows anyway) interface to TCP sockets\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include "ip_sockets.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // Server Classes: A server creates a listening port which waits for incoming\r
+  // connections. This is placed on the port number appropriate for the service\r
+  // - for example, a Telnet server would typically use port 23. For a new\r
+  // service you should of course use any port not allocated to a standard\r
+  // service. I believe that RFC 1700 defines the standard service port numbers.\r
+  // When an incoming connection is made, the server accepts it and in the\r
+  // process creates a new connection on a different port. This leaves the\r
+  // standard port listening for further connections. In effect, the server\r
+  // farms out the handling of the connections themselves and only takes\r
+  // responsibility for accepting the connection. This is reflected in the class\r
+  // structure. A TCP_server performs the listening and accepting roles, but\r
+  // creates a TCP_connection to handle the accepted connection.\r
+  //////////////////////////////////////////////////////////////////////////////\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // connection class created by TCP_server when a connection is accepted\r
+  // this is then used to perform any communications with the remote client\r
+\r
+  class TCP_connection : protected IP_socket\r
+  {\r
+  private:\r
+    // constructor to actually initialise the class - can only be constructed by TCP_server\r
+    friend class TCP_server;\r
+    TCP_connection(const IP_socket& socket);\r
+\r
+  public:\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // constructors/destructors\r
+\r
+    // create an uninitialised connection\r
+    TCP_connection(void);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation, connection control\r
+    // Note: TCP connections can only be initialised by a TCP server\r
+\r
+    // test whether this is an initialised socket\r
+    // - returns whether this is initialised\r
+    // bool initialised(void) const;\r
+    using IP_socket::initialised;\r
+\r
+    // close, i.e. disconnect the socket\r
+    // - returns a success flag\r
+    // bool close(void);\r
+    using IP_socket::close;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // sending/receiving\r
+\r
+    // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool send_ready(unsigned timeout = 0);\r
+    using IP_socket::send_ready;\r
+\r
+    // send data through the socket - if the data is long only part of it may\r
+    // be sent. The sent part is removed from the data, so the same string can\r
+    // be sent again and again until it is empty.\r
+    // - data: string containing data to be sent - any data successfully sent is removed\r
+    // - returns success flag\r
+    // bool send (std::string& data);\r
+    using IP_socket::send;\r
+\r
+    // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool receive_ready(unsigned timeout = 0);\r
+    using IP_socket::receive_ready;\r
+\r
+    // receive data through the socket - if the data is long only part of it\r
+    // may be received. The received data is appended to the string, building\r
+    // it up in stages, so the same string can be received again and again\r
+    // until all information has been received.\r
+    // - data: string receiving data from socket - any data successfully received is appended\r
+    // - returns success flag\r
+    // bool receive (std::string& data);\r
+    using IP_socket::receive;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // informational\r
+\r
+    // the local port number of the connection\r
+    // - returns the port number, 0 if not bound to a port\r
+    // unsigned short local_port(void) const;\r
+    using IP_socket::local_port;\r
+\r
+    // the remote address of the connection\r
+    // - returns the address, 0 if not connected\r
+    // unsigned long remote_address(void) const;\r
+    using IP_socket::remote_address;\r
+\r
+    // the remote port number of the connection\r
+    // - returns the port number, 0 if not connected to a port\r
+    // unsigned short remote_port(void) const;\r
+    using IP_socket::remote_port;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+    // errors are set internally\r
+    // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+    // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+    // if an error is set it stays set - so you must clear it before further operations\r
+    // void clear_error(void) const;\r
+    using IP_socket::clear_error;\r
+\r
+    // get the error code of the last error\r
+    // int error(void) const;\r
+    using IP_socket::error;\r
+\r
+    // get the explanatory message of the last error\r
+    // std::string message(void) const;\r
+    using IP_socket::message;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+\r
+    // deprecated version of remote_port\r
+    unsigned short port(void) const;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+  };\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // server class that does the listening on the designated port\r
+  // incoming connections can be queued up to a maximum queue length specified\r
+  // in the constructor/initialise\r
+\r
+  class TCP_server : protected IP_socket\r
+  {\r
+  public:\r
+\r
+    // create an uninitialised server\r
+    TCP_server(void);\r
+\r
+    // initialise a socket and set it up to be a listening port\r
+    // - port: port to listen on\r
+    // - queue: length of backlog queue to manage - may be zero\r
+    // - returns success status\r
+    TCP_server(unsigned short port, unsigned short queue = 0);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation\r
+\r
+    // initialise a socket and set it up to be a listening port\r
+    // - port: port to listen on\r
+    // - queue: length of backlog queue to manage - may be zero\r
+    // - returns success status\r
+    bool initialise(unsigned short port, unsigned short queue = 0);\r
+\r
+    // test whether this is an initialised socket\r
+    // - returns whether this is initialised\r
+    // bool initialised(void) const;\r
+    using IP_socket::initialised;\r
+\r
+    // close, i.e. disconnect the socket\r
+    // - returns a success flag\r
+    // bool close(void);\r
+    using IP_socket::close;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // server operation - accepting a connection\r
+\r
+    // test for a connection on the object's socket - only applicable if it has been set up as a listening port\r
+    // - timeout: how long to wait in microseconds if not connected yet\r
+    // - returns true if a connection is ready to be accepted\r
+    // bool accept_ready(unsigned timeout = 0);\r
+    using IP_socket::accept_ready;\r
+\r
+    // accept a connection on the object's socket - only applicable if it has been set up as a listening port\r
+    // - returns the connection as a new socket\r
+    TCP_connection accept(void);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+    // errors are set internally\r
+    // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+    // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+    // if an error is set it stays set - so you must clear it before further operations\r
+    // void clear_error (void) const;\r
+    using IP_socket::clear_error;\r
+\r
+    // get the error code of the last error\r
+    // int error(void) const;\r
+    using IP_socket::error;\r
+\r
+    // get the explanatory message of the last error\r
+    // std::string message(void) const;\r
+    using IP_socket::message;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+\r
+    // deprecated versions of accept_ready and accept\r
+    bool connection_ready(unsigned timeout = 0);\r
+    TCP_connection connection(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Client Class: a client is simpler in that there is no listening port - you\r
+  // just create a connection and get on with it. Thus the client class does the\r
+  // whole job - create the connection and handle communications to/from it.\r
+  //\r
+  // Blocking mode: To use the client in blocking mode, use non-zero timeout for\r
+  // the initialisation method. In this mode, the connection operation must\r
+  // complete before the call will return or an error is indicated if the\r
+  // timeout is reached without completion. This usage was designed for\r
+  // applications which either just to TCP and nothing else or which do TCP\r
+  // operations in a separate thread.\r
+  //\r
+  // Non-blocking mode: To use the client in non-blocking mode, use a zero\r
+  // timeout for the initialisation method. Instead, you can ask it if it has\r
+  // connected once you've initialised it. It is not an error for it to be\r
+  // initialised but not connected. This usage was designed so that you can poll\r
+  // the connection periodically to implement a timeout for as long as you like for\r
+  // the connection to occur without blocking the thread that uses the client.\r
+  //\r
+  // In both modes, the send_ready/receive_ready methods can be called with any\r
+  // timeout including zero.\r
+\r
+  class TCP_client : protected IP_socket\r
+  {\r
+  public:\r
+\r
+    // create an uninitialised client\r
+    TCP_client(void);\r
+\r
+    // client connect to a server\r
+    // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+    // - remote_port: port number of remote host\r
+    // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+    TCP_client(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+    // client connect to a server\r
+    // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup\r
+    // - remote_port: port number of remote host\r
+    // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+    TCP_client(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation, connection\r
+\r
+    // function for performing IP lookup (i.e. gethostbyname)\r
+    // could be standalone but making it a member means that it can use the socket's error handler\r
+    // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+    // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+    // - returns the IP address as a long integer - zero if there's an error\r
+    // unsigned long ip_lookup(const std::string& remote_address);\r
+    using IP_socket::ip_lookup;\r
+\r
+    // client connect to a server\r
+    // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+    // - remote_port: port number of remote host\r
+    // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+    // - returns a success flag\r
+    bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+    // client connect to a server\r
+    // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup\r
+    // - remote_port: port number of remote host\r
+    // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+    // - returns a success flag\r
+    bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+    // test whether this is an initialised socket\r
+    // - returns whether this is initialised\r
+    // bool initialised(void) const;\r
+    using IP_socket::initialised;\r
+\r
+    // test whether a socket is connected and ready to communicate, returns on successful connect or timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet\r
+    // - returns success flag\r
+    // bool connected(unsigned timeout = 0);\r
+    using IP_socket::connected;\r
+\r
+    // close, i.e. disconnect the socket\r
+    // - returns a success flag\r
+    // bool close(void);\r
+    using IP_socket::close;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // sending/receiving\r
+\r
+    // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool send_ready(unsigned timeout = 0);\r
+    using IP_socket::send_ready;\r
+\r
+    // send data through the socket - if the data is long only part of it may\r
+    // be sent. The sent part is removed from the data, so the same string can\r
+    // be sent again and again until it is empty.\r
+    // - data: string containing data to be sent - any data successfully sent is removed\r
+    // - returns success flag\r
+    // bool send (std::string& data);\r
+    using IP_socket::send;\r
+\r
+    // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool receive_ready(unsigned timeout = 0);\r
+    using IP_socket::receive_ready;\r
+\r
+    // receive data through the socket - if the data is long only part of it\r
+    // may be received. The received data is appended to the string, building\r
+    // it up in stages, so the same string can be received again and again\r
+    // until all information has been received.\r
+    // - data: string receiving data from socket - any data successfully received is appended\r
+    // - returns success flag\r
+    // bool receive (std::string& data);\r
+    using IP_socket::receive;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // informational\r
+\r
+    // the local port number of the connection\r
+    // - returns the port number, 0 if not bound to a port\r
+    // unsigned short local_port(void) const;\r
+    using IP_socket::local_port;\r
+\r
+    // the remote address of the connection\r
+    // - returns the address, 0 if not connected\r
+    // unsigned long remote_address(void) const;\r
+    using IP_socket::remote_address;\r
+\r
+    // the remote port number of the connection\r
+    // - returns the port number, 0 if not connected to a port\r
+    // unsigned short remote_port(void) const;\r
+    using IP_socket::remote_port;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+    // errors are set internally\r
+    // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+    // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+    // if an error is set it stays set - so you must clear it before further operations\r
+    // void clear_error (void) const;\r
+    using IP_socket::clear_error;\r
+\r
+    // get the error code of the last error\r
+    // int error(void) const;\r
+    using IP_socket::error;\r
+\r
+    // get the explanatory message of the last error\r
+    // std::string message(void) const;\r
+    using IP_socket::message;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+\r
+    // deprecated versions\r
+    unsigned long address(void) const;\r
+    unsigned short port(void) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/time.cpp b/src/stlplus/portability/time.cpp
new file mode 100644 (file)
index 0000000..250a825
--- /dev/null
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "time.hpp"\r
+#include "dprintf.hpp"\r
+#include <ctype.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  time_t time_now(void)\r
+  {\r
+    return time(0);\r
+  }\r
+\r
+  time_t localtime_create(int year, int month, int day, int hour, int minute, int second)\r
+  {\r
+    tm tm_time;\r
+    tm_time.tm_year = year-1900;  // years are represented as an offset from 1900, for reasons unknown\r
+    tm_time.tm_mon = month-1;     // internal format represents month as 0-11, but it is friendlier to take an input 1-12\r
+    tm_time.tm_mday = day;\r
+    tm_time.tm_hour = hour;\r
+    tm_time.tm_min = minute;\r
+    tm_time.tm_sec = second;\r
+    tm_time.tm_isdst = -1;        // specify that the function should work out daylight savings\r
+    time_t result = mktime(&tm_time);\r
+    return result;\r
+  }\r
+\r
+  int localtime_year(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_year + 1900;\r
+  }\r
+\r
+  int localtime_month(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_mon + 1;\r
+  }\r
+\r
+  int localtime_day(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_mday;\r
+  }\r
+\r
+  int localtime_hour(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_hour;\r
+  }\r
+\r
+  int localtime_minute(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_min;\r
+  }\r
+\r
+  int localtime_second(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_sec;\r
+  }\r
+\r
+  int localtime_weekday(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_wday;\r
+  }\r
+\r
+  int localtime_yearday(time_t t)\r
+  {\r
+    tm* tm_time = localtime(&t);\r
+    return tm_time->tm_yday;\r
+  }\r
+\r
+  std::string localtime_string(time_t t)\r
+  {\r
+    tm* local = localtime(&t);\r
+    std::string result = local ? asctime(local) : "*time not available*";\r
+    // ctime appends a newline for no apparent reason - clean up\r
+    while (!result.empty() && isspace(result[result.size()-1]))\r
+      result.erase(result.size()-1,1);\r
+    return result;\r
+  }\r
+\r
+  std::string delaytime_string(time_t seconds)\r
+  {\r
+    unsigned minutes = (unsigned)seconds / 60;\r
+    seconds %= 60;\r
+    unsigned hours = minutes / 60;\r
+    minutes %= 60;\r
+    unsigned days = hours / 24;\r
+    hours %= 24;\r
+    unsigned weeks = days / 7;\r
+    days %= 7;\r
+    std::string result;\r
+    if (weeks > 0)\r
+      result += dformat("%dw ",weeks);\r
+    if (!result.empty() || days > 0)\r
+      result += dformat("%dd ", days);\r
+    if (!result.empty() || hours > 0)\r
+      result += dformat("%d:", hours);\r
+    if (!result.empty() || minutes > 0)\r
+    {\r
+      if (!result.empty())\r
+        result += dformat("%02d:", minutes);\r
+      else\r
+        result += dformat("%d:", minutes);\r
+    }\r
+    if (!result.empty())\r
+      result += dformat("%02d:", seconds);\r
+    else\r
+      result += dformat("%ds", seconds);\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/time.hpp b/src/stlplus/portability/time.hpp
new file mode 100644 (file)
index 0000000..f8abe6c
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef STLPLUS_TIME\r
+#define STLPLUS_TIME\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Simplified access to representations of time and conversions between them.\r
+//   The motivation for this package is that the low-level system calls for\r
+//   accessing time are ugly and therefore potentially error-prone. I hope that\r
+//   this interface is much simpler and therefore easier to use and more likely\r
+//   to yield first-time right programs.\r
+\r
+//   time is represented as the built-in integer type time_t - this is the\r
+//   standard representation of system time in computerland and represents the\r
+//   number of seconds since midnight 1 Jan 1970, believe it or not.\r
+\r
+//   Functions are provided here for converting to and from more\r
+//   human-comprehendable forms.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <time.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // get the integer representing the time now\r
+  time_t time_now(void);\r
+\r
+  // get the integer representing the requested time - the local time is expressed in the local timezone\r
+  time_t localtime_create(int year, int month, int day, int hour, int minute, int second);\r
+\r
+  // extract human-centric form of the machine representation time_t\r
+  int localtime_year(time_t);    // the year e.g. 1962\r
+  int localtime_month(time_t);   // the month, numbered 1-12 e.g. August = 8\r
+  int localtime_day(time_t);     // the day of the month numbered 1-31 e.g. 29\r
+  int localtime_hour(time_t);    // the hour of day numbered 0-23\r
+  int localtime_minute(time_t);  // minute past the hour numbered 0-59\r
+  int localtime_second(time_t);  // second past the minute numbered 0-59\r
+  int localtime_weekday(time_t); // the day of the week numbered 0-6 with 0=Sunday\r
+  int localtime_yearday(time_t); // the number of days into the year\r
+\r
+  // convert the integer representation of time to a human-readable form\r
+  std::string localtime_string(time_t);\r
+\r
+  // convert a time delay in seconds to human-readable form\r
+  std::string delaytime_string(time_t);\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/udp_sockets.cpp b/src/stlplus/portability/udp_sockets.cpp
new file mode 100644 (file)
index 0000000..137be3d
--- /dev/null
@@ -0,0 +1,167 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Daniel Milton adapted by Andy Rushton\r
+//   Copyright: (c) Daniel Milton, Andy Rushton 2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "udp_sockets.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // UDP client\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // create an uninitialised socket\r
+  UDP_client::UDP_client(void) : IP_socket(UDP)\r
+  {\r
+  }\r
+\r
+  // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+  // Enables default send to remote address/port\r
+  // - remote_address: IP name or number of remote host\r
+  // - remote_port: port number of remote host\r
+  // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+  UDP_client::UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port) :\r
+    IP_socket(UDP)\r
+  {\r
+    initialise(remote_address, remote_port, local_port);\r
+  }\r
+\r
+  // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+  // Enables default send to remote address/port\r
+  // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+  // - remote_port: port number of remote host\r
+  // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+  UDP_client::UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port) :\r
+    IP_socket(UDP)\r
+  {\r
+    initialise(remote_address, remote_port, local_port);\r
+  }\r
+\r
+  // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+  // Enables default send to remote address/port\r
+  // - remote_address: IP name or number of remote host\r
+  // - remote_port: port number of remote host\r
+  // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+  // - returns a success flag\r
+  bool UDP_client::initialise(const std::string& address, unsigned short remote_port, unsigned short local_port)\r
+  {\r
+    // lookup the address and convert it into an IP number\r
+    unsigned long remote_address = IP_socket::ip_lookup(address);\r
+    if (!remote_address) return false;\r
+    return initialise(remote_address, remote_port, local_port);\r
+  }\r
+\r
+  // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+  // Enables default send to remote address/port\r
+  // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+  // - remote_port: port number of remote host\r
+  // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+  // - returns a success flag\r
+  bool UDP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port)\r
+  {\r
+    if (!IP_socket::bind(remote_address, local_port)) return false;\r
+    return IP_socket::connect(remote_address, remote_port);\r
+  }\r
+\r
+  // send to the remote address/port setup in initialise, from the local port also setup in initialise.\r
+  // send data through the socket as a single datagram\r
+  // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+  // - returns success flag\r
+  bool UDP_client::send(std::string& packet)\r
+  {\r
+    return IP_socket::send_packet(packet);\r
+  }\r
+\r
+  // datagram receive\r
+  // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+  // - returns success flag - i.e. packet successfully received\r
+  bool UDP_client::receive(std::string& packet)\r
+  {\r
+    return IP_socket::receive_packet(packet);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // UDP Server\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // create an uninitialised socket\r
+  UDP_server::UDP_server(void) : IP_socket(UDP)\r
+  {\r
+  }\r
+\r
+  // Initialise socket.\r
+  // Receive datagram packets from any address on provided local receiving port.\r
+  // No default send possible.\r
+  // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+  UDP_server::UDP_server(unsigned short local_port) : IP_socket(UDP)\r
+  {\r
+    initialise(local_port);\r
+  }\r
+\r
+  // Initialise socket.\r
+  // Receive datagram packets from any address on provided local receiving port.\r
+  // No default send possible.\r
+  // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+  // - returns a success flag\r
+  bool UDP_server::initialise(unsigned short local_port)\r
+  {\r
+    return IP_socket::bind_any(local_port);\r
+  }\r
+\r
+  // send to the address/port given here, from the local port setup in initialise.\r
+  // send data through the socket as a single datagram\r
+  // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+  // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+  // - remote_port: port number of remote host\r
+  // - returns success flag\r
+  bool UDP_server::send(std::string& packet, const std::string& remote_address, unsigned short remote_port)\r
+  {\r
+    unsigned long ip_address = ip_lookup(remote_address);\r
+    if (ip_address == 0) return false;\r
+    return send(packet, ip_address, remote_port);\r
+  }\r
+\r
+  // send to the address/port given here, from the local port setup in initialise.\r
+  // send data through the socket as a single datagram\r
+  // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+  // - remote_address: pre-looked-up IP address of remote host\r
+  // - remote_port: port number of remote host\r
+  // - returns success flag\r
+  bool UDP_server::send(std::string& packet, unsigned long remote_address, unsigned short remote_port)\r
+  {\r
+    return IP_socket::send_packet(packet, remote_address, remote_port);\r
+  }\r
+\r
+  // datagram receive\r
+  // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+  // - remote_address: the address of the client that sent the packet, can then be used to reply\r
+  // - remote_port: the port of the client that sent the packet, can then be used to reply\r
+  // - returns success flag - i.e. packet successfully received\r
+  bool UDP_server::receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)\r
+  {\r
+    return IP_socket::receive_packet(packet, remote_address, remote_port);\r
+  }\r
+\r
+  /////////////////////////////////////////////////////////////////////////////\r
+  // fire and forget UDP client packet send function\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  bool UDP_send(const std::string& packet,\r
+                const std::string& remote_address, unsigned short remote_port, unsigned short local_port)\r
+  {\r
+    UDP_client client(remote_address, remote_port, local_port);\r
+    if (!client.initialised()) return false;\r
+    std::string packet_copy = packet;\r
+    return client.send(packet_copy);\r
+  }\r
+\r
+  /////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/udp_sockets.hpp b/src/stlplus/portability/udp_sockets.hpp
new file mode 100644 (file)
index 0000000..5097d44
--- /dev/null
@@ -0,0 +1,268 @@
+#ifndef STLPLUS_UDP_SOCKET\r
+#define STLPLUS_UDP_SOCKET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author:    Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+//            (c) Andy Rushton           2004-2009\r
+// License:   BSD License, see ../docs/license.html\r
+\r
+// A platform-independent (Unix and Windows anyway) interface to UDP sockets\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include "ip_sockets.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // UDP client - creates a connectioned socket\r
+\r
+  class UDP_client : protected IP_socket\r
+  {\r
+  public:\r
+\r
+    // create an uninitialised socket\r
+    UDP_client(void);\r
+\r
+    // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+    // - remote_address: IP name or number of remote host\r
+    // - remote_port: port number of remote host\r
+    // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+    UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+    // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+    // Enables default send to remote address/port\r
+    // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+    // - remote_port: port number of remote host\r
+    // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+    UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation, connection\r
+\r
+    // function for performing IP lookup (i.e. gethostbyname)\r
+    // could be standalone but making it a member means that it can use the socket's error handler\r
+    // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+    // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+    // - returns the IP address as a long integer - zero if there's an error\r
+    // unsigned long ip_lookup(const std::string& remote_address);\r
+    using IP_socket::ip_lookup;\r
+\r
+    // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+    // Enables default send to remote address/port\r
+    // - remote_address: IP name or number of remote host\r
+    // - remote_port: port number of remote host\r
+    // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+    // - returns a success flag\r
+    bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+    // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+    // Enables default send to remote address/port\r
+    // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+    // - remote_port: port number of remote host\r
+    // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+    // - returns a success flag\r
+    bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+    // test whether this is an initialised socket\r
+    // - returns whether this is initialised\r
+    // bool initialised(void) const;\r
+    using IP_socket::initialised;\r
+\r
+    // close, i.e. disconnect the socket\r
+    // - returns a success flag\r
+    // bool close(void);\r
+    using IP_socket::close;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // sending/receiving\r
+\r
+    // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool send_ready(unsigned timeout = 0);\r
+    using IP_socket::send_ready;\r
+\r
+    // send to the remote address/port setup in initialise, from the local port also setup in initialise.\r
+    // send data through the socket as a single datagram\r
+    // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+    // - returns success flag\r
+    bool send(std::string& packet);\r
+\r
+    // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool receive_ready(unsigned timeout = 0);\r
+    using IP_socket::receive_ready;\r
+\r
+    // datagram receive\r
+    // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+    // - returns success flag - i.e. packet successfully received\r
+    bool receive(std::string& packet);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // informational\r
+\r
+    // the local port number of the connection\r
+    // returns the port number, 0 if not bound to a port\r
+    // unsigned short local_port(void) const;\r
+    using IP_socket::local_port;\r
+\r
+    // the remote address of the connection\r
+    // returns the address, 0 if ANY address\r
+    // unsigned long remote_address(void) const;\r
+    using IP_socket::remote_address;\r
+\r
+    // the remote port number of the connection\r
+    // returns the port number, 0 if not bound to a port\r
+    // unsigned short remote_port(void) const;\r
+    using IP_socket::remote_port;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+    // errors are set internally\r
+    // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+    // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+    // if an error is set it stays set - so you must clear it before further operations\r
+    // void clear_error (void);\r
+    using IP_socket::clear_error ;\r
+\r
+    // get the error code of the last error\r
+    // int error(void) const;\r
+    using IP_socket::error;\r
+\r
+    // get the explanatory message of the last error\r
+    // std::string message(void) const;\r
+    using IP_socket::message;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+\r
+  private:\r
+    IP_socket m_socket;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // UDP server - creates a connectionless (multicast) listener socket\r
+\r
+  class UDP_server : protected IP_socket\r
+  {\r
+  public:\r
+\r
+    // create an uninitialised socket\r
+    UDP_server(void);\r
+\r
+    // Initialise socket.\r
+    // Receive datagram packets from any address on provided local receiving port.\r
+    // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+    UDP_server(unsigned short local_port);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // initialisation, connection\r
+\r
+    // function for performing IP lookup (i.e. gethostbyname)\r
+    // could be standalone but making it a member means that it can use the socket's error handler\r
+    // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+    // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+    // - returns the IP address as a long integer - zero if there's an error\r
+    // unsigned long ip_lookup(const std::string& remote_address);\r
+    using IP_socket::ip_lookup;\r
+\r
+    // Initialise socket.\r
+    // Receive datagram packets from any address on provided local receiving port.\r
+    // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+    // - returns a success flag\r
+    bool initialise(unsigned short local_port);\r
+\r
+    // test whether this is an initialised socket\r
+    // - returns whether this is initialised\r
+    // bool initialised(void) const;\r
+    using IP_socket::initialised;\r
+\r
+    // close, i.e. disconnect the socket\r
+    // - returns a success flag\r
+    // bool close(void);\r
+    using IP_socket::close;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // sending/receiving\r
+\r
+    // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool send_ready(unsigned timeout = 0);\r
+    using IP_socket::send_ready;\r
+\r
+    // send to the address/port given here, from the local port setup in initialise.\r
+    // send data through the socket as a single datagram\r
+    // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+    // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+    // - remote_port: port number of remote host\r
+    // - returns success flag\r
+    bool send(std::string& packet, const std::string& remote_address, unsigned short remote_port);\r
+\r
+    // send to the address/port given here, from the local port setup in initialise.\r
+    // send data through the socket as a single datagram\r
+    // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+    // - remote_address: pre-looked-up IP address of remote host\r
+    // - remote_port: port number of remote host\r
+    // - returns success flag\r
+    bool send(std::string& packet, unsigned long remote_address, unsigned short remote_port);\r
+\r
+    // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+    // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+    // - returns status\r
+    // bool receive_ready(unsigned timeout = 0);\r
+    using IP_socket::receive_ready;\r
+\r
+    // datagram receive\r
+    // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+    // - remote_address: the address of the client that sent the packet, can then be used to reply\r
+    // - remote_port: the port of the client that sent the packet, can then be used to reply\r
+    // - returns success flag - i.e. packet successfully received\r
+    bool receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port);\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // informational\r
+\r
+    // the local port number of the connection\r
+    // returns the port number, 0 if not bound to a port\r
+    // unsigned short local_port(void) const;\r
+    using IP_socket::local_port;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+    // error handling\r
+    // errors are set internally\r
+    // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+    // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+    // if an error is set it stays set - so you must clear it before further operations\r
+    // void clear_error(void);\r
+    using IP_socket::clear_error;\r
+\r
+    // get the error code of the last error\r
+    // int error(void) const;\r
+    using IP_socket::error;\r
+\r
+    // get the explanatory message of the last error\r
+    // std::string message(void) const;\r
+    using IP_socket::message;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////\r
+  };\r
+\r
+  /////////////////////////////////////////////////////////////////////////////\r
+  // fire and forget UDP client packet send function\r
+\r
+  bool UDP_send(const std::string& packet,\r
+                const std::string& remote_address, unsigned short remote_port, unsigned short local_port = 0);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/portability/version.cpp b/src/stlplus/portability/version.cpp
new file mode 100644 (file)
index 0000000..8a34c6e
--- /dev/null
@@ -0,0 +1,20 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "version.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  std::string version(void)\r
+  {\r
+    return STLPLUS_VERSION;\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/version.hpp b/src/stlplus/portability/version.hpp
new file mode 100644 (file)
index 0000000..027e131
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef STLPLUS_VERSION\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Contains just the STLplus version number\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+#define STLPLUS_VERSION "3.5"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  std::string version(void);\r
+\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/portability/wildcard.cpp b/src/stlplus/portability/wildcard.cpp
new file mode 100644 (file)
index 0000000..c15252e
--- /dev/null
@@ -0,0 +1,164 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Simple wildcard matching function.\r
+\r
+//   WARNING: wheel re-invention follows\r
+//   Given that all shells perform wildcard matching, why don't the library writers put it in the C run-time????????\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "wildcard.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // function for testing whether a character matches a set\r
+  // I can't remember the exact rules and I have no definitive references but:\r
+  // a set contains characters, escaped characters (I think) and ranges in the form a-z\r
+  // The character '-' can only appear at the start of the set where it is not interpreted as a range\r
+  // This is a horrible mess - blame the Unix folks for making a hash of wildcards\r
+  // first expand any ranges and remove escape characters to make life more palatable\r
+\r
+  static bool match_set (const std::string& set, char match)\r
+  {\r
+    std::string simple_set;\r
+    for (std::string::const_iterator i = set.begin(); i != set.end(); ++i)\r
+    {\r
+      switch(*i)\r
+      {\r
+      case '-':\r
+      {\r
+        if (i == set.begin())\r
+        {\r
+          simple_set += *i;\r
+        }\r
+        else if (i+1 == set.end())\r
+        {\r
+          return false;\r
+        }\r
+        else\r
+        {\r
+          // found a set. The first character is already in the result, so first remove it (the set might be empty)\r
+          simple_set.erase(simple_set.end()-1);\r
+          char last = *++i;\r
+          for (char ch = *(i-2); ch <= last; ch++)\r
+          {\r
+            simple_set += ch;\r
+          }\r
+        }\r
+        break;\r
+      }\r
+      case '\\':\r
+        if (i+1 == set.end()) {return false;}\r
+        simple_set += *++i;\r
+        break;\r
+      default:\r
+        simple_set += *i;\r
+        break;\r
+      }\r
+    }\r
+    std::string::size_type result = simple_set.find(match);\r
+    return result != std::string::npos;\r
+  }\r
+\r
+  // the recursive bit - basically whenever a * is found you recursively call this for each candidate substring match\r
+  // until either it succeeds or you run out of string to match\r
+  // for each * in the wildcard another level of recursion is created\r
+\r
+  static bool match_remainder (const std::string& wild, std::string::const_iterator wildi, const std::string& match, std::string::const_iterator matchi)\r
+  {\r
+    //cerr << "match_remainder called at " << *matchi << " with wildcard " << *wildi << endl;\r
+    while (wildi != wild.end() && matchi != match.end())\r
+    {\r
+      //cerr << "trying to match " << *matchi << " with wildcard " << *wildi << endl;\r
+      switch(*wildi)\r
+      {\r
+      case '*':\r
+      {\r
+        ++wildi;\r
+        ++matchi;\r
+        for (std::string::const_iterator i = matchi; i != match.end(); ++i)\r
+        {\r
+          // deal with * at the end of the wildcard - there is no remainder then\r
+          if (wildi == wild.end())\r
+          {\r
+            if (i == match.end()-1)\r
+              return true;\r
+          }\r
+          else if (match_remainder(wild, wildi, match, i))\r
+          {\r
+            return true;\r
+          }\r
+        }\r
+        return false;\r
+      }\r
+      case '[':\r
+      {\r
+        // scan for the end of the set using a similar method for avoiding escaped characters\r
+        bool found = false;\r
+        std::string::const_iterator end = wildi + 1;\r
+        for (; !found && end != wild.end(); ++end)\r
+        {\r
+          switch(*end)\r
+          {\r
+          case ']':\r
+          {\r
+            // found the set, now match with its contents excluding the brackets\r
+            if (!match_set(wild.substr(wildi - wild.begin() + 1, end - wildi - 1), *matchi))\r
+              return false;\r
+            found = true;\r
+            break;\r
+          }\r
+          case '\\':\r
+            if (end == wild.end()-1)\r
+              return false;\r
+            ++end;\r
+            break;\r
+          default:\r
+            break;\r
+          }\r
+        }\r
+        if (!found)\r
+          return false;\r
+        ++matchi;\r
+        wildi = end;\r
+        break;\r
+      }\r
+      case '?':\r
+        ++wildi;\r
+        ++matchi;\r
+        break;\r
+      case '\\':\r
+        if (wildi == wild.end()-1)\r
+          return false;\r
+        ++wildi;\r
+        if (*wildi != *matchi)\r
+          return false;\r
+        ++wildi;\r
+        ++matchi;\r
+        break;\r
+      default:\r
+        if (*wildi != *matchi)\r
+          return false;\r
+        ++wildi;\r
+        ++matchi;\r
+        break;\r
+      }\r
+    }\r
+    bool result = wildi == wild.end() && matchi == match.end();\r
+    return result;\r
+  }\r
+\r
+  // like all recursions the exported function has a simpler interface than the\r
+  // recursive function and is just a 'seed' to the recursion itself\r
+\r
+  bool wildcard(const std::string& wild, const std::string& match)\r
+  {\r
+    return match_remainder(wild, wild.begin(), match, match.begin());\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/portability/wildcard.hpp b/src/stlplus/portability/wildcard.hpp
new file mode 100644 (file)
index 0000000..398c3dd
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef STLPLUS_WILDCARD\r
+#define STLPLUS_WILDCARD\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   This is a portable interface to wildcard matching.\r
+\r
+//   The problem:\r
+//     *  matches any number of characters - this is achieved by matching 1 and seeing if the remainder matches\r
+//        if not, try 2 characters and see if the remainder matches etc.\r
+//        this must be recursive, not iterative, so that multiple *s can appear in the same wildcard expression\r
+//     ?  matches exactly one character so doesn't need the what-if approach\r
+//     \  escapes special characters such as *, ? and [\r
+//     [] matches exactly one character in the set - the difficulty is the set can contain ranges, e.g [a-zA-Z0-9]\r
+//        a set cannot be empty and the ] character can be included by making it the first character\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // wild = the wildcard expression\r
+  // match = the string to test against that expression\r
+  // e.g. wildcard("[a-f]*", "fred") returns true\r
+  bool wildcard(const std::string& wild, const std::string& match);\r
+\r
+}\r
+\r
+#endif\r
diff --git a/src/stlplus/rules.mk b/src/stlplus/rules.mk
new file mode 100644 (file)
index 0000000..74f1c37
--- /dev/null
@@ -0,0 +1,79 @@
+
+#########################
+sp             := $(sp).x
+dirstack_$(sp) := $(d)
+d              := $(dir)
+#########################
+
+
+#
+# Define rules and targets for libstlplus.
+#
+
+OBJS_$(d) := \
+             $(d)/persistence/persistent_bool.o \
+             $(d)/persistence/persistent_contexts.o \
+             $(d)/persistence/persistent_cstring.o \
+             $(d)/persistence/persistent_exceptions.o \
+             $(d)/persistence/persistent_float.o \
+             $(d)/persistence/persistent_inf.o \
+             $(d)/persistence/persistent_int.o \
+             $(d)/persistence/persistent_string.o \
+             $(d)/persistence/persistent_vector.o \
+             $(d)/portability/build.o \
+             $(d)/portability/debug.o \
+             $(d)/portability/dprintf.o \
+             $(d)/portability/dynaload.o \
+             $(d)/portability/file_system.o \
+             $(d)/portability/inf.o \
+             $(d)/portability/ip_sockets.o \
+             $(d)/portability/portability_fixes.o \
+             $(d)/portability/subprocesses.o \
+             $(d)/portability/tcp_sockets.o \
+             $(d)/portability/time.o \
+             $(d)/portability/udp_sockets.o \
+             $(d)/portability/version.o \
+             $(d)/portability/wildcard.o \
+             $(d)/strings/print_address.o \
+             $(d)/strings/print_bool.o \
+             $(d)/strings/print_cstring.o \
+             $(d)/strings/print_float.o \
+             $(d)/strings/print_inf.o \
+             $(d)/strings/print_int.o \
+             $(d)/strings/print_string.o \
+             $(d)/strings/print_vector.o \
+             $(d)/strings/string_address.o \
+             $(d)/strings/string_bool.o \
+             $(d)/strings/string_cstring.o \
+             $(d)/strings/string_float.o \
+             $(d)/strings/string_inf.o \
+             $(d)/strings/string_int.o \
+             $(d)/strings/string_string.o \
+             $(d)/strings/string_utilities.o \
+             $(d)/strings/string_vector.o \
+             $(d)/subsystems/cli_parser.o \
+             $(d)/subsystems/ini_manager.o \
+             $(d)/subsystems/library_manager.o \
+             $(d)/subsystems/message_handler.o \
+             $(d)/subsystems/timer.o \
+                        $(_END_)
+
+TGTS_$(d) := $(d)/libstlplus.a
+DEPS_$(d) := $(OBJS_$(d):%=%.d)
+
+CLEAN     := $(CLEAN) $(OBJS_$(d)) $(DEPS_$(d)) $(TGTS_$(d))
+
+
+$(OBJS_$(d)): CF_TGT := -I$(d) -I$(d)/containers -I$(d)/portability
+$(OBJS_$(d)): $(d)/rules.mk
+
+$(TGTS_$(d)): $(OBJS_$(d))
+       $(DO_AR)
+
+
+#######################
+-include $(DEPS_$(d))
+d  := $(dirstack_$(sp))
+sp := $(basename $(sp))
+#######################
+
diff --git a/src/stlplus/strings/format_types.hpp b/src/stlplus/strings/format_types.hpp
new file mode 100644 (file)
index 0000000..5df7711
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef STLPLUS_FORMAT_TYPES\r
+#define STLPLUS_FORMAT_TYPES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A Set of enumerations controlling the string formatting of numbers.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Integer radix display representations:\r
+//   There are three ways in which the radix is represented:\r
+//     - none - the number is just printed as a number (e.g. 12345). Can be confusing for non-decimal radix\r
+//     - C style - for binary, octal and hex, the C-style prefices 0b, 0 and 0x are used\r
+//       note that this is an unsigned representation\r
+//     - Hash style - in the form radix#value - the value may be signed, e.g. 10#-9\r
+\r
+enum radix_display_t \r
+{\r
+  radix_none,               // just print the number with no radix indicated\r
+  radix_hash_style,         // none for decimal, hash style for all others\r
+  radix_hash_style_all,     // hash style for all radices including decimal\r
+  radix_c_style,            // C style for hex and octal, none for others\r
+  radix_c_style_or_hash     // C style for hex and octal, none for decimal, hash style for others\r
+};\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Floating-point display representations:\r
+// There are three formats for the printout:\r
+//   - fixed - formatted as a fixed-point number, so no mantissa is printed (equivalent to %f in printf)\r
+//   - floating - formatted as a normalised floating-point number (equivalent to %e in printf)\r
+//   - mixed - formatted as fixed-point if appropriate, otherwise the floating format (equivalent to %g in printf)\r
+\r
+enum real_display_t\r
+{\r
+  display_fixed,    // %f\r
+  display_floating, // %e\r
+  display_mixed     // %g\r
+};\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Alignment:\r
+//   There are three field alignments:\r
+//     - left aligned - the value is to the left of the field which is padded to the right with spaces\r
+//     - right aligned - the value is to the right of the field which is padded to the left with spaces\r
+//     - centred - the value is in the centre of the field and spaces added to both left and right\r
+\r
+enum alignment_t\r
+{\r
+  align_left,\r
+  align_right,\r
+  align_centre\r
+};\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/strings/print_address.cpp b/src/stlplus/strings/print_address.cpp
new file mode 100644 (file)
index 0000000..3962557
--- /dev/null
@@ -0,0 +1,28 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   use the unsigned long representation for pointers\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_address.hpp"\r
+#include "print_int.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void print_address(std::ostream& device, const void* i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    print_unsigned_long(device, (unsigned long)i, radix, display, width);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_address.hpp b/src/stlplus/strings/print_address.hpp
new file mode 100644 (file)
index 0000000..f89bdae
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_PRINT_ADDRESS\r
+#define STLPLUS_PRINT_ADDRESS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void print_address(std::ostream& device, \r
+                     const void*,\r
+                     unsigned radix = 16,\r
+                     radix_display_t display = radix_c_style_or_hash,\r
+                     unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_basic.hpp b/src/stlplus/strings/print_basic.hpp
new file mode 100644 (file)
index 0000000..9d67862
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef STLPLUS_PRINT_BASIC\r
+#define STLPLUS_PRINT_BASIC\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Utilities for converting printing basic C types\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "print_address.hpp"\r
+#include "print_bool.hpp"\r
+#include "print_cstring.hpp"\r
+#include "print_float.hpp"\r
+#include "print_int.hpp"\r
+#include "print_pointer.hpp"\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_bitset.hpp b/src/stlplus/strings/print_bitset.hpp
new file mode 100644 (file)
index 0000000..f7151dd
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef STLPLUS_PRINT_BITSET\r
+#define STLPLUS_PRINT_BITSET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a bitset\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <bitset>\r
+#include <string>\r
+#include <iostream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<size_t N>\r
+  void print_bitset(std::ostream& device, const std::bitset<N>& data);\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_bitset.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_bitset.tpp b/src/stlplus/strings/print_bitset.tpp
new file mode 100644 (file)
index 0000000..833103a
--- /dev/null
@@ -0,0 +1,20 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_bitset.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<size_t N>\r
+  void print_bitset(std::ostream& device, const std::bitset<N>& data)\r
+  {\r
+    device << bitset_to_string(data);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_bool.cpp b/src/stlplus/strings/print_bool.cpp
new file mode 100644 (file)
index 0000000..adcebc1
--- /dev/null
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   use the unsigned short representation for bool\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_bool.hpp"\r
+#include "print_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void print_bool(std::ostream& device, bool i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    print_unsigned_short(device, (unsigned short)i, radix, display, width);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_bool.hpp b/src/stlplus/strings/print_bool.hpp
new file mode 100644 (file)
index 0000000..5e808d1
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PRINT_BOOL\r
+#define STLPLUS_PRINT_BOOL\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Conversion of string to/from bool\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void print_bool(std::ostream& device, bool i,\r
+                  unsigned radix = 10,\r
+                  radix_display_t display = radix_c_style_or_hash,\r
+                  unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_cstring.cpp b/src/stlplus/strings/print_cstring.cpp
new file mode 100644 (file)
index 0000000..423c741
--- /dev/null
@@ -0,0 +1,19 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_cstring.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void print_cstring(std::ostream& device, const char* value)\r
+  {\r
+    device << value;\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_cstring.hpp b/src/stlplus/strings/print_cstring.hpp
new file mode 100644 (file)
index 0000000..57cdbcd
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef STLPLUS_PRINT_CSTRING\r
+#define STLPLUS_PRINT_CSTRING\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Functions for converting C/STL strings to string\r
+\r
+//   This is necessary for completeness\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void print_cstring(std::ostream& device, const char* value);\r
+\r
+}\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_digraph.hpp b/src/stlplus/strings/print_digraph.hpp
new file mode 100644 (file)
index 0000000..e5c2ff8
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_PRINT_DIGRAPH\r
+#define STLPLUS_PRINT_DIGRAPH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a digraph\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "digraph.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename NT, typename AT, typename NS, typename AS>\r
+  void print_digraph(std::ostream& device,\r
+                     const digraph<NT,AT>& values,\r
+                     NS node_print_fn,\r
+                     AS arc_print_fn,\r
+                     const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_digraph.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_digraph.tpp b/src/stlplus/strings/print_digraph.tpp
new file mode 100644 (file)
index 0000000..69e8018
--- /dev/null
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename NT, typename AT, typename NS, typename AS>\r
+  void print_digraph(std::ostream& device, const digraph<NT,AT>& values,\r
+                     NS node_print_fn,\r
+                     AS arc_print_fn,\r
+                     const std::string& separator)\r
+  {\r
+    device << "nodes:";\r
+    device << separator;\r
+    print_sequence(device, values.begin(), values.end(), node_print_fn, separator);\r
+    device << "arcs:";\r
+    device << separator;\r
+    print_sequence(device, values.arc_begin(), values.arc_end(), arc_print_fn, separator);\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_float.cpp b/src/stlplus/strings/print_float.cpp
new file mode 100644 (file)
index 0000000..a36c2c1
--- /dev/null
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_float.hpp"\r
+#include "string_float.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // floating-point types\r
+\r
+  void print_float(std::ostream& device, float f, real_display_t display, unsigned width, unsigned precision)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << float_to_string(f, display, width, precision);\r
+  }\r
+\r
+  void print_double(std::ostream& device, double f, real_display_t display, unsigned width, unsigned precision)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << double_to_string(f, display, width, precision);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_float.hpp b/src/stlplus/strings/print_float.hpp
new file mode 100644 (file)
index 0000000..2de8b8f
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef STLPLUS_PRINT_FLOAT\r
+#define STLPLUS_PRINT_FLOAT\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Convert a float/double to/from string\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // convert a real type to string\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // Only decimal radix is supported\r
+\r
+  // The way in which the number is displayed is defined in radix_types.hpp\r
+  // Using any other value for the display type causes std::invalid_argument to be thrown\r
+\r
+  void print_float(std::ostream& device, float f,\r
+                   real_display_t display = display_mixed,\r
+                   unsigned width = 0,\r
+                   unsigned precision = 6)\r
+    throw(std::invalid_argument);\r
+\r
+  void print_double(std::ostream& device, double f,\r
+                    real_display_t display = display_mixed,\r
+                    unsigned width = 0,\r
+                    unsigned precision = 6)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_foursome.hpp b/src/stlplus/strings/print_foursome.hpp
new file mode 100644 (file)
index 0000000..3ee3696
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef STLPLUS_PRINT_FOURSOME\r
+#define STLPLUS_PRINT_FOURSOME\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a foursome\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "foursome.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename S1, typename S2, typename S3, typename S4>\r
+  void print_foursome(std::ostream& device,\r
+                      const foursome<T1,T2,T3,T4>& values,\r
+                      S1 print_fn1,\r
+                      S2 print_fn2,\r
+                      S3 print_fn3,\r
+                      S4 print_fn4,\r
+                      const std::string& separator = ":");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_foursome.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_foursome.tpp b/src/stlplus/strings/print_foursome.tpp
new file mode 100644 (file)
index 0000000..8b9f319
--- /dev/null
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename S1, typename S2, typename S3, typename S4>\r
+  void print_foursome(std::ostream& device,\r
+                      const foursome<T1,T2,T3,T4>& values,\r
+                      S1 print_fn1,\r
+                      S2 print_fn2,\r
+                      S3 print_fn3,\r
+                      S4 print_fn4,\r
+                      const std::string& separator)\r
+  {\r
+    print_fn1(device, values.first);\r
+    device << separator;\r
+    print_fn2(device, values.second);\r
+    device << separator;\r
+    print_fn3(device, values.third);\r
+    device << separator;\r
+    print_fn4(device, values.fourth);\r
+  }\r
+\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_hash.hpp b/src/stlplus/strings/print_hash.hpp
new file mode 100644 (file)
index 0000000..43a76f4
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PRINT_HASH\r
+#define STLPLUS_PRINT_HASH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a hash\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "hash.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename H, typename E, typename KS, typename TS>\r
+  void print_hash(std::ostream& device,\r
+                  const hash<K,T,H,E>& values,\r
+                  KS key_print_fn,\r
+                  TS value_print_fn,\r
+                  const std::string& pair_separator = ":",\r
+                  const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_hash.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_hash.tpp b/src/stlplus/strings/print_hash.tpp
new file mode 100644 (file)
index 0000000..5b3bd96
--- /dev/null
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename H, typename E, typename KS, typename TS>\r
+  void print_hash(std::ostream& device,\r
+                  const hash<K,T,H,E>& values,\r
+                  KS key_print_fn,\r
+                  TS value_print_fn,\r
+                  const std::string& pair_separator,\r
+                  const std::string& separator)\r
+  {\r
+    print_pair_sequence(device, \r
+                        values.begin(), values.end(),\r
+                        key_print_fn, value_print_fn,\r
+                        pair_separator, separator);\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_inf.cpp b/src/stlplus/strings/print_inf.cpp
new file mode 100644 (file)
index 0000000..8a12f40
--- /dev/null
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   String conversion functions for the infinite precision integer type inf\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// can be excluded from the build to break the dependency on the portability library\r
+#ifndef NO_STLPLUS_INF\r
+\r
+#include "print_inf.hpp"\r
+#include "string_inf.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void print_inf(std::ostream& device,\r
+                 const stlplus::inf& data,\r
+                 unsigned radix,\r
+                 radix_display_t display,\r
+                 unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << inf_to_string(data, radix, display, width);\r
+  }\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_inf.hpp b/src/stlplus/strings/print_inf.hpp
new file mode 100644 (file)
index 0000000..51b0377
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef STLPLUS_PRINT_INF\r
+#define STLPLUS_PRINT_INF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   String conversion functions for the infinite precision integer type inf\r
+\r
+//   The conversion supports all the formatting modes defined on format_types\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "strings_fixes.hpp"\r
+#include "inf.hpp"\r
+#include "format_types.hpp"\r
+#include <stdexcept>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void print_inf(std::ostream& device,\r
+                 const inf&,\r
+                 unsigned radix = 10,\r
+                 radix_display_t display = radix_c_style_or_hash,\r
+                 unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
+\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_int.cpp b/src/stlplus/strings/print_int.cpp
new file mode 100644 (file)
index 0000000..e2b63be
--- /dev/null
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_int.hpp"\r
+#include "string_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void print_short(std::ostream& device, short i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << short_to_string(i, radix, display, width);\r
+  }\r
+\r
+  void print_unsigned_short(std::ostream& device, unsigned short i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << unsigned_short_to_string(i, radix, display, width);\r
+  }\r
+\r
+  void print_int(std::ostream& device, int i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << int_to_string(i, radix, display, width);\r
+  }\r
+\r
+  void print_unsigned(std::ostream& device, unsigned i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << unsigned_to_string(i, radix, display, width);\r
+  }\r
+\r
+  void print_long(std::ostream& device, long i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << long_to_string(i, radix, display, width);\r
+  }\r
+\r
+  void print_unsigned_long(std::ostream& device, unsigned long i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    device << unsigned_long_to_string(i, radix, display, width);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_int.hpp b/src/stlplus/strings/print_int.hpp
new file mode 100644 (file)
index 0000000..4f97293
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef STLPLUS_PRINT_INT\r
+#define STLPLUS_PRINT_INT\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Print integer types\r
+\r
+//   This extends the formatting available from iostream\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <iostream>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions of Integer types to string\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // The radix (i.e. base) for these conversions can be any value from base 2 to base 36\r
+  // specifying any other radix causes std::invalid_argument to be thrown\r
+\r
+  // The way in which the radix is displayed is defined in radix_types.hpp\r
+  // If any other value is used, std::invalid_argument is thrown\r
+\r
+  // The width argument specifies the number of numerical digits to use in the result\r
+  // This is a minimum - if the value requires more digits then it will be wider than the width argument\r
+  // However, if it is smaller, then it will be extended to the specified width\r
+  // Then, the radix display prefix is added to this width\r
+\r
+  // For example, using the hash representation of 0 in hex with width=4 gives:\r
+  // 16#0000 - so there's 4 digits in the number part\r
+\r
+  void print_short(std::ostream& device, short i,\r
+                   unsigned radix = 10,\r
+                   radix_display_t display = radix_c_style_or_hash,\r
+                   unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  void print_unsigned_short(std::ostream& device, unsigned short i,\r
+                            unsigned radix = 10,\r
+                            radix_display_t display = radix_c_style_or_hash,\r
+                            unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  void print_int(std::ostream& device, int i,\r
+                 unsigned radix = 10,\r
+                 radix_display_t display = radix_c_style_or_hash,\r
+                 unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  void print_unsigned(std::ostream& device, unsigned i,\r
+                      unsigned radix = 10,\r
+                      radix_display_t display = radix_c_style_or_hash,\r
+                      unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  void print_long(std::ostream& device, long i,\r
+                  unsigned radix = 10,\r
+                  radix_display_t display = radix_c_style_or_hash,\r
+                  unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  void print_unsigned_long(std::ostream& device, unsigned long i,\r
+                           unsigned radix = 10,\r
+                           radix_display_t display = radix_c_style_or_hash,\r
+                           unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_list.hpp b/src/stlplus/strings/print_list.hpp
new file mode 100644 (file)
index 0000000..d1767b2
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef STLPLUS_PRINT_LIST\r
+#define STLPLUS_PRINT_LIST\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a list\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <list>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_list(std::ostream& device,\r
+                  const std::list<T>& values,\r
+                  S print_fn,\r
+                  const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_list.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_list.tpp b/src/stlplus/strings/print_list.tpp
new file mode 100644 (file)
index 0000000..e60c39a
--- /dev/null
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   template implementations\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_list(std::ostream& device,\r
+                  const std::list<T>& values,\r
+                  S print_fn,\r
+                  const std::string& separator)\r
+  {\r
+    print_sequence(device, values.begin(), values.end(), print_fn, separator);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_map.hpp b/src/stlplus/strings/print_map.hpp
new file mode 100644 (file)
index 0000000..6935037
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef STLPLUS_PRINT_MAP\r
+#define STLPLUS_PRINT_MAP\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a map/multimap\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <map>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  void print_map(std::ostream& device, const std::map<K,T,C>& values,\r
+                 SK key_print_fn,\r
+                 ST value_print_fn,\r
+                 const std::string& pair_separator = ":",\r
+                 const std::string& separator = ",");\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  void print_multimap(std::ostream& device, const std::multimap<K,T,C>& values,\r
+                      SK key_print_fn,\r
+                      ST value_print_fn,\r
+                      const std::string& pair_separator = ":",\r
+                      const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_map.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_map.tpp b/src/stlplus/strings/print_map.tpp
new file mode 100644 (file)
index 0000000..b3a727a
--- /dev/null
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // map\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  void print_map(std::ostream& device, const std::map<K,T,C>& values,\r
+                 SK key_print_fn,\r
+                 ST value_print_fn,\r
+                 const std::string& pair_separator,\r
+                 const std::string& separator)\r
+  {\r
+    print_pair_sequence(values.begin(), values.end(),\r
+                        key_print_fn, value_print_fn,\r
+                        pair_separator, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // multimap\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  void print_multimap(std::ostream& device, const std::multimap<K,T,C>& values,\r
+                      SK key_print_fn,\r
+                      ST value_print_fn,\r
+                      const std::string& pair_separator,\r
+                      const std::string& separator)\r
+  {\r
+    print_pair_sequence(device,\r
+                        values.begin(), values.end(),\r
+                        key_print_fn, value_print_fn,\r
+                        pair_separator, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_matrix.hpp b/src/stlplus/strings/print_matrix.hpp
new file mode 100644 (file)
index 0000000..f3fb8d5
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_PRINT_MATRIX\r
+#define STLPLUS_PRINT_MATRIX\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a matrix\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "matrix.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_matrix(std::ostream& device,\r
+                    const matrix<T>& values,\r
+                    S print_fn,\r
+                    const std::string& column_separator = "|",\r
+                    const std::string& row_separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_matrix.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_matrix.tpp b/src/stlplus/strings/print_matrix.tpp
new file mode 100644 (file)
index 0000000..406c69f
--- /dev/null
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename S>\r
+  void print_matrix(std::ostream& device, const matrix<T>& values,\r
+                    S print_fn,\r
+                    const std::string& column_separator,\r
+                    const std::string& row_separator)\r
+  {\r
+    for (unsigned r = 0; r < values.rows(); r++)\r
+    {\r
+      if (r != 0) device << row_separator;\r
+      for (unsigned c = 0; c < values.columns(); c++)\r
+      {\r
+        if (c != 0) device << column_separator;\r
+        print_fn(device, values(r,c));\r
+      }\r
+    }\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_ntree.hpp b/src/stlplus/strings/print_ntree.hpp
new file mode 100644 (file)
index 0000000..47c1a46
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_PRINT_NTREE\r
+#define STLPLUS_PRINT_NTREE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of an ntree\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "ntree.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_ntree(std::ostream& device,\r
+                   const ntree<T>& values,\r
+                   S print_fn,\r
+                   const std::string& separator = "|",\r
+                   const std::string& indent_string = "  ");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_ntree.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_ntree.tpp b/src/stlplus/strings/print_ntree.tpp
new file mode 100644 (file)
index 0000000..ab85611
--- /dev/null
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_ntree(std::ostream& device,\r
+                   const ntree<T>& values,\r
+                   S print_fn,\r
+                   const std::string& separator,\r
+                   const std::string& indent_string)\r
+  {\r
+    for (TYPENAME ntree<T>::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++)\r
+    {\r
+      if (i != values.prefix_begin()) device << separator;\r
+      for (unsigned indent = values.depth(i.simplify()); --indent; )\r
+        device << indent_string;\r
+      print_fn(device, *i);\r
+    }\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_pair.hpp b/src/stlplus/strings/print_pair.hpp
new file mode 100644 (file)
index 0000000..144461f
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef STLPLUS_PRINT_PAIR\r
+#define STLPLUS_PRINT_PAIR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a pair\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <map>\r
+#include <string>\r
+#include <iostream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename V1, typename V2, typename S1, typename S2>\r
+  void print_pair(std::ostream& device,\r
+                  const std::pair<V1,V2>& values,\r
+                  S1 print_fn1,\r
+                  S2 print_fn2,\r
+                  const std::string& separator = ":");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_pair.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_pair.tpp b/src/stlplus/strings/print_pair.tpp
new file mode 100644 (file)
index 0000000..a93c0c0
--- /dev/null
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename V1, typename V2, typename S1, typename S2>\r
+  void print_pair(std::ostream& device,\r
+                  const std::pair<V1,V2>& values,\r
+                  S1 print_fn1,\r
+                  S2 print_fn2,\r
+                  const std::string& separator)\r
+  {\r
+    print_fn1(device, values.first);\r
+    device << separator;\r
+    print__fn2(device, values.second);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_pointer.hpp b/src/stlplus/strings/print_pointer.hpp
new file mode 100644 (file)
index 0000000..cb738cc
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef STLPLUS_PRINT_POINTER\r
+#define STLPLUS_PRINT_POINTER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of an object pointed to\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template <typename T, typename S>\r
+  void print_pointer(std::ostream& device,\r
+                     const T* value,\r
+                     S print_fn,\r
+                     const std::string& null_string = "<null>",\r
+                     const std::string& prefix = "(",\r
+                     const std::string& suffix = ")");\r
+\r
+\r
+}\r
+\r
+#include "print_pointer.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_pointer.tpp b/src/stlplus/strings/print_pointer.tpp
new file mode 100644 (file)
index 0000000..be96d91
--- /dev/null
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template <typename T, typename S>\r
+  void print_pointer(std::ostream& device,\r
+                     const T* value,\r
+                     S print_fn,\r
+                     const std::string& null_string,\r
+                     const std::string& prefix,\r
+                     const std::string& suffix)\r
+  {\r
+    if (value)\r
+    {\r
+      device << prefix;\r
+      print_fn(device, *value);\r
+      device << suffix;\r
+    }\r
+    else\r
+    {\r
+      device << null_string;\r
+    }\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_sequence.hpp b/src/stlplus/strings/print_sequence.hpp
new file mode 100644 (file)
index 0000000..411ddd5
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef STLPLUS_PRINT_SEQUENCE\r
+#define STLPLUS_PRINT_SEQUENCE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate string representations of sequences represented by forward iterators\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any forward iterator sequence\r
+\r
+  template <typename I, typename S>\r
+  void print_sequence(std::ostream& device,\r
+                      I begin, I end, \r
+                      S print_fn,\r
+                      const std::string& separator);\r
+\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any forward iterator sequence of pairs\r
+\r
+  template <typename I, typename S1, typename S2>\r
+  void print_pair_sequence(std::ostream& device,\r
+                           I begin, I end,\r
+                           S1 print_fn1,\r
+                           S2 print_fn2,\r
+                           const std::string& pair_separator,\r
+                           const std::string& separator);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_sequence.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_sequence.tpp b/src/stlplus/strings/print_sequence.tpp
new file mode 100644 (file)
index 0000000..cea8056
--- /dev/null
@@ -0,0 +1,50 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_pair.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any forward iterator sequence\r
+\r
+  template <typename I, typename S>\r
+  void print_sequence(std::ostream& device,\r
+                      I begin, I end, \r
+                      S print_fn,\r
+                      const std::string& separator)\r
+  {\r
+    for (I i = begin; i != end; i++)\r
+    {\r
+      if (i != begin) device << separator;\r
+      print_fn(device, *i);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any sequence where the value is a pair\r
+\r
+  template <typename I, typename S1, typename S2>\r
+  void print_pair_sequence(std::ostream& device,\r
+                           I begin, I end,\r
+                           S1 print_fn1,\r
+                           S2 print_fn2,\r
+                           const std::string& pair_separator,\r
+                           const std::string& separator)\r
+  {\r
+    for (I i = begin; i != end; i++)\r
+    {\r
+      if (i != begin) device << separator;\r
+      print_pair(device, *i, print_fn1, print_fn2, pair_separator);\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_set.hpp b/src/stlplus/strings/print_set.hpp
new file mode 100644 (file)
index 0000000..33c3835
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef STLPLUS_PRINT_SET\r
+#define STLPLUS_PRINT_SET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a set/multiset\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <set>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename C, typename S>\r
+  void print_set(std::ostream& device,\r
+                 const std::set<K,C>& values,\r
+                 S print_fn,\r
+                 const std::string& separator = ",");\r
+\r
+  template<typename K, typename C, typename S>\r
+  void print_multiset(std::ostream& device,\r
+                      const std::multiset<K,C>& values,\r
+                      S print_fn,\r
+                      const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_set.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_set.tpp b/src/stlplus/strings/print_set.tpp
new file mode 100644 (file)
index 0000000..25c88ef
--- /dev/null
@@ -0,0 +1,41 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   template implementations\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // set\r
+\r
+  template<typename K, typename C, typename S>\r
+  void print_set(std::ostream& device,\r
+                 const std::set<K,C>& values,\r
+                 S print_fn,\r
+                 const std::string& separator)\r
+  {\r
+    print_sequence(device, values.begin(), values.end(), print_fn, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // multiset\r
+\r
+  template<typename K, typename C, typename S>\r
+  void print_multiset(std::ostream& device,\r
+                      const std::multiset<K,C>& values,\r
+                      S print_fn,\r
+                      const std::string& separator)\r
+  {\r
+    print_sequence(device, values.begin(), values.end(), print_fn, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_simple_ptr.hpp b/src/stlplus/strings/print_simple_ptr.hpp
new file mode 100644 (file)
index 0000000..e34a8ec
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef STLPLUS_PRINT_SIMPLE_PTR\r
+#define STLPLUS_PRINT_SIMPLE_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a smart pointer\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "simple_ptr.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_simple_ptr(std::ostream& device,\r
+                       const simple_ptr<T>& value,\r
+                       S print_fn,\r
+                       const std::string& null_string = "<null>",\r
+                       const std::string& prefix = "(",\r
+                       const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  void print_simple_ptr_clone(std::ostream& device,\r
+                             const simple_ptr_clone<T>& value,\r
+                             S print_fn,\r
+                             const std::string& null_string = "<null>",\r
+                             const std::string& prefix = "(",\r
+                             const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  void print_simple_ptr_nocopy(std::ostream& device,\r
+                              const simple_ptr_nocopy<T>& value,\r
+                              S print_fn,\r
+                              const std::string& null_string = "<null>",\r
+                              const std::string& prefix = "(",\r
+                              const std::string& suffix = ")");\r
+\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_simple_ptr.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_simple_ptr.tpp b/src/stlplus/strings/print_simple_ptr.tpp
new file mode 100644 (file)
index 0000000..30af07e
--- /dev/null
@@ -0,0 +1,74 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_simple_ptr(std::ostream& device,\r
+                       const simple_ptr<T>& value,\r
+                       S print_fn,\r
+                       const std::string& null_string,\r
+                       const std::string& prefix,\r
+                       const std::string& suffix)\r
+  {\r
+    if (value)\r
+    {\r
+      device << prefix;\r
+      print_fn(device, *value);\r
+      device << suffix;\r
+    }\r
+    else\r
+    {\r
+      device << null_string;\r
+    }\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  void print_simple_ptr_clone(std::ostream& device,\r
+                             const simple_ptr_clone<T>& value,\r
+                             S print_fn,\r
+                             const std::string& null_string,\r
+                             const std::string& prefix,\r
+                             const std::string& suffix)\r
+  {\r
+    if (value)\r
+    {\r
+      device << prefix;\r
+      print_fn(device, *value);\r
+      device << suffix;\r
+    }\r
+    else\r
+    {\r
+      device << null_string;\r
+    }\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  void print_simple_ptr_nocopy(std::ostream& device,\r
+                              const simple_ptr_nocopy<T>& value,\r
+                              S print_fn,\r
+                              const std::string& null_string,\r
+                              const std::string& prefix,\r
+                              const std::string& suffix)\r
+  {\r
+    if (value)\r
+    {\r
+      device << prefix;\r
+      print_fn(device, *value);\r
+      device << suffix;\r
+    }\r
+    else\r
+    {\r
+      device << null_string;\r
+    }\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_smart_ptr.hpp b/src/stlplus/strings/print_smart_ptr.hpp
new file mode 100644 (file)
index 0000000..031c814
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef STLPLUS_PRINT_SMART_PTR\r
+#define STLPLUS_PRINT_SMART_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a smart pointer\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "smart_ptr.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_smart_ptr(std::ostream& device,\r
+                       const smart_ptr<T>& value,\r
+                       S print_fn,\r
+                       const std::string& null_string = "<null>",\r
+                       const std::string& prefix = "(",\r
+                       const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  void print_smart_ptr_clone(std::ostream& device,\r
+                             const smart_ptr_clone<T>& value,\r
+                             S print_fn,\r
+                             const std::string& null_string = "<null>",\r
+                             const std::string& prefix = "(",\r
+                             const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  void print_smart_ptr_nocopy(std::ostream& device,\r
+                              const smart_ptr_nocopy<T>& value,\r
+                              S print_fn,\r
+                              const std::string& null_string = "<null>",\r
+                              const std::string& prefix = "(",\r
+                              const std::string& suffix = ")");\r
+\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_smart_ptr.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_smart_ptr.tpp b/src/stlplus/strings/print_smart_ptr.tpp
new file mode 100644 (file)
index 0000000..51877c7
--- /dev/null
@@ -0,0 +1,74 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_smart_ptr(std::ostream& device,\r
+                       const smart_ptr<T>& value,\r
+                       S print_fn,\r
+                       const std::string& null_string,\r
+                       const std::string& prefix,\r
+                       const std::string& suffix)\r
+  {\r
+    if (value)\r
+    {\r
+      device << prefix;\r
+      print_fn(device, *value);\r
+      device << suffix;\r
+    }\r
+    else\r
+    {\r
+      device << null_string;\r
+    }\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  void print_smart_ptr_clone(std::ostream& device,\r
+                             const smart_ptr_clone<T>& value,\r
+                             S print_fn,\r
+                             const std::string& null_string,\r
+                             const std::string& prefix,\r
+                             const std::string& suffix)\r
+  {\r
+    if (value)\r
+    {\r
+      device << prefix;\r
+      print_fn(device, *value);\r
+      device << suffix;\r
+    }\r
+    else\r
+    {\r
+      device << null_string;\r
+    }\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  void print_smart_ptr_nocopy(std::ostream& device,\r
+                              const smart_ptr_nocopy<T>& value,\r
+                              S print_fn,\r
+                              const std::string& null_string,\r
+                              const std::string& prefix,\r
+                              const std::string& suffix)\r
+  {\r
+    if (value)\r
+    {\r
+      device << prefix;\r
+      print_fn(device, *value);\r
+      device << suffix;\r
+    }\r
+    else\r
+    {\r
+      device << null_string;\r
+    }\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_stl.hpp b/src/stlplus/strings/print_stl.hpp
new file mode 100644 (file)
index 0000000..fb2b553
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef STLPLUS_PRINT_STL\r
+#define STLPLUS_PRINT_STL\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Template string conversions for pointers and STL containers\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "print_bitset.hpp"\r
+#include "print_list.hpp"\r
+#include "print_map.hpp"\r
+#include "print_pair.hpp"\r
+#include "print_sequence.hpp"\r
+#include "print_set.hpp"\r
+#include "print_string.hpp"\r
+#include "print_vector.hpp"\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_stlplus.hpp b/src/stlplus/strings/print_stlplus.hpp
new file mode 100644 (file)
index 0000000..3f2cd46
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef STLPLUS_PRINT_STLPLUS\r
+#define STLPLUS_PRINT_STLPLUS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Template string conversions for the STLplus containers\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifndef NO_STLPLUS_CONTAINERS\r
+#include "print_digraph.hpp"\r
+#include "print_foursome.hpp"\r
+#include "print_hash.hpp"\r
+#include "print_matrix.hpp"\r
+#include "print_ntree.hpp"\r
+#include "print_smart_ptr.hpp"\r
+#include "print_triple.hpp"\r
+#endif\r
+\r
+#include "print_inf.hpp"\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_string.cpp b/src/stlplus/strings/print_string.cpp
new file mode 100644 (file)
index 0000000..8cf3b5b
--- /dev/null
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_string.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // strings\r
+\r
+  void print_string(std::ostream& device, const std::string& value)\r
+  {\r
+    device << value;\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_string.hpp b/src/stlplus/strings/print_string.hpp
new file mode 100644 (file)
index 0000000..dd23f56
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef STLPLUS_PRINT_STRING\r
+#define STLPLUS_PRINT_STRING\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Functions for converting C/STL strings to string\r
+\r
+//   This is necessary for completeness, e.g. for use in print_vector for vector<string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  void print_string(std::ostream& device, const std::string& value);\r
+}\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/print_triple.hpp b/src/stlplus/strings/print_triple.hpp
new file mode 100644 (file)
index 0000000..37b7af9
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef STLPLUS_PRINT_TRIPLE\r
+#define STLPLUS_PRINT_TRIPLE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a triple\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "triple.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename S1, typename S2, typename S3>\r
+  void print_triple(std::ostream& device,\r
+                    const triple<T1,T2,T3>& values,\r
+                    S1 print_fn1,\r
+                    S2 print_fn2,\r
+                    S3 print_fn3,\r
+                    const std::string& separator = ":");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_triple.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_triple.tpp b/src/stlplus/strings/print_triple.tpp
new file mode 100644 (file)
index 0000000..079c592
--- /dev/null
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename S1, typename S2, typename S3>\r
+  void print_triple(std::ostream& device,\r
+                    const triple<T1,T2,T3>& values,\r
+                    S1 print_fn1,\r
+                    S2 print_fn2,\r
+                    S3 print_fn3,\r
+                    const std::string& separator)\r
+  {\r
+    \r
+    print_fn1(device, values.first);\r
+    device << separator;\r
+    print_fn2(device, values.second);\r
+    device << separator;\r
+    print_fn3(device, values.third);\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/print_vector.cpp b/src/stlplus/strings/print_vector.cpp
new file mode 100644 (file)
index 0000000..41ea42e
--- /dev/null
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_vector.hpp"\r
+#include "string_vector.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // special case of vector<bool>\r
+\r
+  void print_bool_vector(std::ostream& device, const std::vector<bool>& values)\r
+  {\r
+    device << bool_vector_to_string(values);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/print_vector.hpp b/src/stlplus/strings/print_vector.hpp
new file mode 100644 (file)
index 0000000..5916532
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef STLPLUS_PRINT_VECTOR\r
+#define STLPLUS_PRINT_VECTOR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a vector\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <iostream>\r
+#include <vector>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // vector\r
+\r
+  template<typename T, typename S>\r
+  void print_vector(std::ostream& device,\r
+                    const std::vector<T>& values,\r
+                    S print_fn,\r
+                    const std::string& separator);\r
+\r
+  // specialisation for vector<bool> which has a different implementation\r
+  void print_bool_vector(std::ostream& device, const std::vector<bool>& values);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "print_vector.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/print_vector.tpp b/src/stlplus/strings/print_vector.tpp
new file mode 100644 (file)
index 0000000..102cfd2
--- /dev/null
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "print_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  void print_vector(std::ostream& device, const std::vector<T>& values,\r
+                               S print_fn,\r
+                               const std::string& separator)\r
+  {\r
+    print_sequence(device, values.begin(), values.end(), print_fn, separator);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_address.cpp b/src/stlplus/strings/string_address.cpp
new file mode 100644 (file)
index 0000000..74315e0
--- /dev/null
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   use the unsigned long representation for pointers\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_address.hpp"\r
+#include "string_int.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string address_to_string(const void* i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return unsigned_long_to_string((unsigned long)i, radix, display, width);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void* string_to_address(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return (void*)string_to_unsigned_long(str, radix);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_address.hpp b/src/stlplus/strings/string_address.hpp
new file mode 100644 (file)
index 0000000..f73d581
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef STLPLUS_STRING_ADDRESS\r
+#define STLPLUS_STRING_ADDRESS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Functions for converting addresses to/from strings\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string address_to_string(const void*,\r
+                                unsigned radix = 16,\r
+                                radix_display_t display = radix_c_style_or_hash,\r
+                                unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void* string_to_address(const std::string& value,\r
+                          unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_basic.hpp b/src/stlplus/strings/string_basic.hpp
new file mode 100644 (file)
index 0000000..f88dbcd
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef STLPLUS_STRING_BASIC\r
+#define STLPLUS_STRING_BASIC\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Utilities for converting basic C types to/from std::strings\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "string_address.hpp"\r
+#include "string_bool.hpp"\r
+#include "string_cstring.hpp"\r
+#include "string_float.hpp"\r
+#include "string_int.hpp"\r
+#include "string_pointer.hpp"\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_bitset.hpp b/src/stlplus/strings/string_bitset.hpp
new file mode 100644 (file)
index 0000000..8c96ef3
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef STLPLUS_STRING_BITSET\r
+#define STLPLUS_STRING_BITSET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a bitset\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <bitset>\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<size_t N>\r
+  std::string bitset_to_string(const std::bitset<N>& data);\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_bitset.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_bitset.tpp b/src/stlplus/strings/string_bitset.tpp
new file mode 100644 (file)
index 0000000..13b78a3
--- /dev/null
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<size_t N>\r
+  std::string bitset_to_string(const std::bitset<N>& data)\r
+  {\r
+    std::string result;\r
+    for (unsigned i = data.size(); i--; )\r
+      result += data.test(i) ? '1' : '0';\r
+    return result;\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_bool.cpp b/src/stlplus/strings/string_bool.cpp
new file mode 100644 (file)
index 0000000..ee25231
--- /dev/null
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   use the unsigned short representation for bool\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_bool.hpp"\r
+#include "string_int.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string bool_to_string(bool i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return unsigned_short_to_string((unsigned short)i, radix, display, width);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  bool string_to_bool(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return string_to_unsigned_short(str, radix) != 0;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_bool.hpp b/src/stlplus/strings/string_bool.hpp
new file mode 100644 (file)
index 0000000..adadcc6
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef STLPLUS_STRING_BOOL\r
+#define STLPLUS_STRING_BOOL\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Conversion of string to/from bool\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string bool_to_string(bool i,\r
+                             unsigned radix = 10,\r
+                             radix_display_t display = radix_c_style_or_hash,\r
+                             unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  bool string_to_bool(const std::string& value,\r
+                      unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_cstring.cpp b/src/stlplus/strings/string_cstring.cpp
new file mode 100644 (file)
index 0000000..0745049
--- /dev/null
@@ -0,0 +1,19 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_cstring.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  std::string cstring_to_string(const char* value)\r
+  {\r
+    return std::string(value);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_cstring.hpp b/src/stlplus/strings/string_cstring.hpp
new file mode 100644 (file)
index 0000000..554fbe3
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef STLPLUS_STRING_CSTRING\r
+#define STLPLUS_STRING_CSTRING\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Functions for converting C strings to string\r
+\r
+//   This is necessary for completeness\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  std::string cstring_to_string(const char* value);\r
+\r
+}\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_digraph.hpp b/src/stlplus/strings/string_digraph.hpp
new file mode 100644 (file)
index 0000000..ea70cc7
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef STLPLUS_STRING_DIGRAPH\r
+#define STLPLUS_STRING_DIGRAPH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a digraph\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "digraph.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename NT, typename AT, typename NS, typename AS>\r
+  std::string digraph_to_string(const digraph<NT,AT>& values,\r
+                                NS node_to_string_fn,\r
+                                AS arc_to_string_fn,\r
+                                const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_digraph.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_digraph.tpp b/src/stlplus/strings/string_digraph.tpp
new file mode 100644 (file)
index 0000000..9fd63d5
--- /dev/null
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename NT, typename AT, typename NS, typename AS>\r
+  std::string digraph_to_string(const digraph<NT,AT>& values,\r
+                                NS node_to_string_fn,\r
+                                AS arc_to_string_fn,\r
+                                const std::string& separator)\r
+  {\r
+    std::string result;\r
+    result += "nodes:";\r
+    result += separator;\r
+    result += sequence_to_string(values.begin(), values.end(), node_to_string_fn, separator);\r
+    result += "arcs:";\r
+    result += separator;\r
+    result += sequence_to_string(values.arc_begin(), values.arc_end(), arc_to_string_fn, separator);\r
+    return result;\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_float.cpp b/src/stlplus/strings/string_float.cpp
new file mode 100644 (file)
index 0000000..0de056f
--- /dev/null
@@ -0,0 +1,96 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_float.hpp"\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // added as a local copy to break the dependency on the portability library\r
+  static std::string local_dformat(const char* format, ...) throw(std::invalid_argument)\r
+  {\r
+    std::string formatted;\r
+    va_list args;\r
+    va_start(args, format);\r
+#ifdef MSWINDOWS\r
+    int length = 0;\r
+    char* buffer = 0;\r
+    for(int buffer_length = 256; ; buffer_length*=2)\r
+    {\r
+      buffer = (char*)malloc(buffer_length);\r
+      if (!buffer) throw std::invalid_argument("string_float");\r
+      length = _vsnprintf(buffer, buffer_length-1, format, args);\r
+      if (length >= 0)\r
+      {\r
+        buffer[length] = 0;\r
+        formatted += std::string(buffer);\r
+        free(buffer);\r
+        break;\r
+      }\r
+      free(buffer);\r
+    }\r
+#else\r
+    char* buffer = 0;\r
+    int length = vasprintf(&buffer, format, args);\r
+    if (!buffer) throw std::invalid_argument("string_float");\r
+    if (length >= 0)\r
+      formatted += std::string(buffer);\r
+    free(buffer);\r
+#endif\r
+    va_end(args);\r
+    if (length < 0) throw std::invalid_argument("string_float");\r
+    return formatted;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // floating-point types\r
+\r
+  std::string float_to_string(float f, real_display_t display, unsigned width, unsigned precision)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return double_to_string((double)f, display, width, precision);\r
+  }\r
+\r
+  std::string double_to_string(double f, real_display_t display, unsigned width, unsigned precision)\r
+    throw(std::invalid_argument)\r
+  {\r
+    switch(display)\r
+    {\r
+    case display_fixed:\r
+      return local_dformat("%*.*f", width, precision, f);\r
+    case display_floating:\r
+      return local_dformat("%*.*e", width, precision, f);\r
+    case display_mixed:\r
+      return local_dformat("%*.*g", width, precision, f);\r
+    default:\r
+      throw std::invalid_argument("invalid radix display value");\r
+    }\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  float string_to_float(const std::string& value)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return (float)string_to_double(value);\r
+  }\r
+\r
+  double string_to_double(const std::string& value)\r
+    throw(std::invalid_argument)\r
+  {\r
+    // TODO - error checking\r
+    return strtod(value.c_str(), 0);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_float.hpp b/src/stlplus/strings/string_float.hpp
new file mode 100644 (file)
index 0000000..797d4a4
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef STLPLUS_STRING_FLOAT\r
+#define STLPLUS_STRING_FLOAT\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Convert a float/double to/from string\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // convert a real type to string\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // Only decimal radix is supported\r
+\r
+  // The way in which the number is displayed is defined in radix_types.hpp\r
+  // Using any other value for the display type causes std::invalid_argument to be thrown\r
+\r
+  std::string float_to_string(float f,\r
+                              real_display_t display = display_mixed,\r
+                              unsigned width = 0,\r
+                              unsigned precision = 6)\r
+    throw(std::invalid_argument);\r
+\r
+  std::string double_to_string(double f,\r
+                               real_display_t display = display_mixed,\r
+                               unsigned width = 0,\r
+                               unsigned precision = 6)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Convert a string to a floating-point type\r
+\r
+  float string_to_float(const std::string& value)\r
+    throw(std::invalid_argument);\r
+\r
+  double string_to_double(const std::string& value)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_foursome.hpp b/src/stlplus/strings/string_foursome.hpp
new file mode 100644 (file)
index 0000000..1cf3db7
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_STRING_FOURSOME\r
+#define STLPLUS_STRING_FOURSOME\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a foursome\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "foursome.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename S1, typename S2, typename S3, typename S4>\r
+  std::string foursome_to_string(const foursome<T1,T2,T3,T4>& values,\r
+                                 S1 to_string_fn1,\r
+                                 S2 to_string_fn2,\r
+                                 S3 to_string_fn3,\r
+                                 S4 to_string_fn4,\r
+                                 const std::string& separator = ":");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_foursome.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_foursome.tpp b/src/stlplus/strings/string_foursome.tpp
new file mode 100644 (file)
index 0000000..a3e1536
--- /dev/null
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename T4, typename S1, typename S2, typename S3, typename S4>\r
+  std::string foursome_to_string(const foursome<T1,T2,T3,T4>& values,\r
+                                 S1 to_string_fn1,\r
+                                 S2 to_string_fn2,\r
+                                 S3 to_string_fn3,\r
+                                 S4 to_string_fn4,\r
+                                 const std::string& separator)\r
+  {\r
+    return \r
+      to_string_fn1(values.first) + \r
+      separator + \r
+      to_string_fn2(values.second) + \r
+      separator + \r
+      to_string_fn3(values.third) +\r
+      separator + \r
+      to_string_fn4(values.fourth);\r
+  }\r
+\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_hash.hpp b/src/stlplus/strings/string_hash.hpp
new file mode 100644 (file)
index 0000000..a01f87b
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef STLPLUS_STRING_HASH\r
+#define STLPLUS_STRING_HASH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a hash\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "hash.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename H, typename E, typename KS, typename TS>\r
+  std::string hash_to_string(const hash<K,T,H,E>& values,\r
+                             KS key_to_string_fn,\r
+                             TS value_to_string_fn,\r
+                             const std::string& pair_separator = ":",\r
+                             const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_hash.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_hash.tpp b/src/stlplus/strings/string_hash.tpp
new file mode 100644 (file)
index 0000000..b3b2a41
--- /dev/null
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename H, typename E, typename KS, typename TS>\r
+  std::string hash_to_string(const hash<K,T,H,E>& values,\r
+                             KS key_to_string_fn,\r
+                             TS value_to_string_fn,\r
+                             const std::string& pair_separator,\r
+                             const std::string& separator)\r
+  {\r
+    return pair_sequence_to_string(values.begin(), values.end(),\r
+                                   key_to_string_fn, value_to_string_fn,\r
+                                   pair_separator, separator);\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_inf.cpp b/src/stlplus/strings/string_inf.cpp
new file mode 100644 (file)
index 0000000..1734335
--- /dev/null
@@ -0,0 +1,536 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   String conversion functions for the infinite precision integer type inf\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// can be excluded from the build to break the dependency on the portability library\r
+#ifndef NO_STLPLUS_INF\r
+\r
+#include "string_inf.hpp"\r
+#include "string_basic.hpp"\r
+#include <ctype.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz";\r
+  static int from_char [] = \r
+  {\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string inf_to_string(const stlplus::inf& data, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    std::string result;\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value");\r
+    inf local_i = data;\r
+    // untangle all the options\r
+    bool hashed = false;\r
+    bool binary = false;\r
+    bool octal = false;\r
+    bool hex = false;\r
+    switch(display)\r
+    {\r
+    case radix_none:\r
+      break;\r
+    case radix_hash_style:\r
+      hashed = radix != 10;\r
+      break;\r
+    case radix_hash_style_all:\r
+      hashed = true;\r
+      break;\r
+    case radix_c_style:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      break;\r
+    case radix_c_style_or_hash:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      else if (radix != 10)\r
+        hashed = true;\r
+      break;\r
+    default:\r
+      throw std::invalid_argument("invalid radix display value");\r
+    }\r
+    // create constants of the same type as the template parameter to avoid type mismatches\r
+    const inf t_zero(0);\r
+    const inf t_radix(radix);\r
+    // the C representations for binary, octal and hex use 2's-complement representation\r
+    // all other represenations use sign-magnitude\r
+    if (hex || octal || binary)\r
+    {\r
+      // bit-pattern representation\r
+      // this is the binary representation optionally shown in octal or hex\r
+      // first generate the binary by masking the bits\r
+      for (unsigned j = local_i.bits(); j--; )\r
+        result += (local_i.bit(j) ? '1' : '0');\r
+      // the result is now the full width of the type - e.g. int will give a 32-bit result\r
+      // now interpret this as either binary, octal or hex and add the prefix\r
+      if (binary)\r
+      {\r
+        // the result is already binary - but the width may be wrong\r
+        // if this is still smaller than the width field, sign extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        while (result.size() < width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > width)\r
+        {\r
+          // do not trim to less than 1 bit (sign only)\r
+          if (result.size() <= 1) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0b");\r
+      }\r
+      else if (octal)\r
+      {\r
+        // the result is currently binary - but before converting get the width right\r
+        // the width is expressed in octal digits so make the binary 3 times this\r
+        // if this is still smaller than the width field, sign extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier\r
+        while (result.size() < 3*width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > 3*width)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 3 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to octal\r
+        std::string octal_result;\r
+        for (unsigned i = 0; i < result.size()/3; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*3] == '0')\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '0';\r
+              else\r
+                octal_result += '1';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '2';\r
+              else\r
+                octal_result += '3';\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '4';\r
+              else\r
+                octal_result += '5';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '6';\r
+              else\r
+                octal_result += '7';\r
+            }\r
+          }\r
+        }\r
+        result = octal_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0");\r
+      }\r
+      else\r
+      {\r
+        // similar to octal\r
+        while (result.size() < 4*width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > 4*width)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 4 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to hex\r
+        std::string hex_result;\r
+        for (unsigned i = 0; i < result.size()/4; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*4] == '0')\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '0';\r
+                else\r
+                  hex_result += '1';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '2';\r
+                else\r
+                  hex_result += '3';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '4';\r
+                else\r
+                  hex_result += '5';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '6';\r
+                else\r
+                  hex_result += '7';\r
+              }\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '8';\r
+                else\r
+                  hex_result += '9';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'a';\r
+                else\r
+                  hex_result += 'b';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'c';\r
+                else\r
+                  hex_result += 'd';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'e';\r
+                else\r
+                  hex_result += 'f';\r
+              }\r
+            }\r
+          }\r
+        }\r
+        result = hex_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0x");\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // convert to sign-magnitude\r
+      // the representation is:\r
+      // [radix#][sign]magnitude\r
+      bool negative = local_i.negative();\r
+      local_i.abs();\r
+      // create a representation of the magnitude by successive division\r
+      do\r
+      {\r
+        std::pair<inf,inf> divided = local_i.divide(t_radix);\r
+        unsigned remainder = divided.second.to_unsigned();\r
+        char digit = to_char[remainder];\r
+        result.insert((std::string::size_type)0, 1, digit);\r
+        local_i = divided.first;\r
+      }\r
+      while(!local_i.zero() || result.size() < width);\r
+      // add the prefixes\r
+      // add a sign only for negative values\r
+      if (negative)\r
+        result.insert((std::string::size_type)0, 1, '-');\r
+      // then prefix everything with the radix if the hashed representation was requested\r
+      if (hashed)\r
+        result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#");\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions FROM string\r
+\r
+  inf string_to_inf(const std::string& str, unsigned radix) throw(std::invalid_argument)\r
+  {\r
+    inf result;\r
+    if (radix != 0 && (radix < 2 || radix > 36))\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    unsigned i = 0;\r
+    // the radix passed as a parameter is just the default - it can be\r
+    // overridden by either the C prefix or the hash prefix\r
+    // Note: a leading zero is the C-style prefix for octal - I only make this\r
+    // override the default when the default radix is not specified\r
+    // first check for a C-style prefix\r
+    bool c_style = false;\r
+    if (i < str.size() && str[i] == '0')\r
+    {\r
+      // binary or hex\r
+      if (i+1 < str.size() && tolower(str[i+1]) == 'x')\r
+      {\r
+        c_style = true;\r
+        radix = 16;\r
+        i += 2;\r
+      }\r
+      else if (i+1 < str.size() && tolower(str[i+1]) == 'b')\r
+      {\r
+        c_style = true;\r
+        radix = 2;\r
+        i += 2;\r
+      }\r
+      else if (radix == 0)\r
+      {\r
+        c_style = true;\r
+        radix = 8;\r
+        i += 1;\r
+      }\r
+    }\r
+    // now check for a hash-style prefix if a C-style prefix was not found\r
+    if (i == 0)\r
+    {\r
+      // scan for the sequence {digits}#\r
+      bool hash_found = false;\r
+      unsigned j = i;\r
+      for (; j < str.size(); j++)\r
+      {\r
+        if (!isdigit(str[j]))\r
+        {\r
+          if (str[j] == '#')\r
+            hash_found = true;\r
+          break;\r
+        }\r
+      }\r
+      if (hash_found)\r
+      {\r
+        // use the hash prefix to define the radix\r
+        // i points to the start of the radix and j points to the # character\r
+        std::string slice = str.substr(i, j-i);\r
+        radix = string_to_unsigned(slice);\r
+        i = j+1;\r
+      }\r
+    }\r
+    if (radix == 0)\r
+      radix = 10;\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value");\r
+    if (c_style)\r
+    {\r
+      // the C style formats are bit patterns not integer values - these need\r
+      // to be sign-extended to get the right value\r
+      std::string binary;\r
+      if (radix == 2)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += '0';\r
+            break;\r
+          case '1':\r
+            binary += '1';\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid binary character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      else if (radix == 8)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += "000";\r
+            break;\r
+          case '1':\r
+            binary += "001";\r
+            break;\r
+          case '2':\r
+            binary += "010";\r
+            break;\r
+          case '3':\r
+            binary += "011";\r
+            break;\r
+          case '4':\r
+            binary += "100";\r
+            break;\r
+          case '5':\r
+            binary += "101";\r
+            break;\r
+          case '6':\r
+            binary += "110";\r
+            break;\r
+          case '7':\r
+            binary += "111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid octal character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      else\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(tolower(str[j]))\r
+          {\r
+          case '0':\r
+            binary += "0000";\r
+            break;\r
+          case '1':\r
+            binary += "0001";\r
+            break;\r
+          case '2':\r
+            binary += "0010";\r
+            break;\r
+          case '3':\r
+            binary += "0011";\r
+            break;\r
+          case '4':\r
+            binary += "0100";\r
+            break;\r
+          case '5':\r
+            binary += "0101";\r
+            break;\r
+          case '6':\r
+            binary += "0110";\r
+            break;\r
+          case '7':\r
+            binary += "0111";\r
+            break;\r
+          case '8':\r
+            binary += "1000";\r
+            break;\r
+          case '9':\r
+            binary += "1001";\r
+            break;\r
+          case 'a':\r
+            binary += "1010";\r
+            break;\r
+          case 'b':\r
+            binary += "1011";\r
+            break;\r
+          case 'c':\r
+            binary += "1100";\r
+            break;\r
+          case 'd':\r
+            binary += "1101";\r
+            break;\r
+          case 'e':\r
+            binary += "1110";\r
+            break;\r
+          case 'f':\r
+            binary += "1111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid hex character in string " + str);\r
+          }\r
+        }\r
+      }\r
+      // now convert the value\r
+      result.resize(binary.size());\r
+      for (unsigned j = 0; j < binary.size(); j++)\r
+        result.preset(binary.size() - j - 1, binary[j] == '1');\r
+    }\r
+    else\r
+    {\r
+      // now scan for a sign and find whether this is a negative number\r
+      bool negative = false;\r
+      if (i < str.size())\r
+      {\r
+        switch (str[i])\r
+        {\r
+        case '-':\r
+          negative = true;\r
+          i++;\r
+          break;\r
+        case '+':\r
+          i++;\r
+          break;\r
+        }\r
+      }\r
+      for (; i < str.size(); i++)\r
+      {\r
+        result *= inf(radix);\r
+        int ch = from_char[(unsigned char)str[i]] ;\r
+        if (ch == -1)\r
+          throw std::invalid_argument("invalid character in string " + str + " for radix " + unsigned_to_string(radix));\r
+        result += inf(ch);\r
+      }\r
+      if (negative)\r
+        result.negate();\r
+    }\r
+    return result;\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_inf.hpp b/src/stlplus/strings/string_inf.hpp
new file mode 100644 (file)
index 0000000..1543263
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef STLPLUS_STRING_INF\r
+#define STLPLUS_STRING_INF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   String conversion functions for the infinite precision integer type inf\r
+\r
+//   The conversion supports all the formatting modes defined on format_types\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "inf.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // conversion TO string\r
+  std::string inf_to_string(const inf&,\r
+                            unsigned radix = 10,\r
+                            radix_display_t display = radix_c_style_or_hash,\r
+                            unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  // conversion FROM string\r
+  inf string_to_inf(const std::string&,\r
+                    unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
+#endif\r
diff --git a/src/stlplus/strings/string_int.cpp b/src/stlplus/strings/string_int.cpp
new file mode 100644 (file)
index 0000000..178cf4c
--- /dev/null
@@ -0,0 +1,1111 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_int.hpp"\r
+#include <ctype.h>\r
+#include <stdlib.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // character mappings\r
+\r
+  static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz";\r
+  static int from_char [] = \r
+  {\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions to string\r
+  // Local generic routines\r
+\r
+  // signed version of the generic image generation function for all integer types\r
+  template<typename T>\r
+  static std::string simage (T i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    // untangle all the options\r
+    bool hashed = false;\r
+    bool binary = false;\r
+    bool octal = false;\r
+    bool hex = false;\r
+    switch(display)\r
+    {\r
+    case radix_none:\r
+      break;\r
+    case radix_hash_style:\r
+      hashed = radix != 10;\r
+      break;\r
+    case radix_hash_style_all:\r
+      hashed = true;\r
+      break;\r
+    case radix_c_style:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      break;\r
+    case radix_c_style_or_hash:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      else if (radix != 10)\r
+        hashed = true;\r
+      break;\r
+    default:\r
+      throw std::invalid_argument("invalid radix display value");\r
+    }\r
+    // create constants of the same type as the template parameter to avoid type mismatches\r
+    const T t_zero(0);\r
+    const T t_radix(radix);\r
+    // the C representations for binary, octal and hex use 2's-complement representation\r
+    // all other represenations use sign-magnitude\r
+    std::string result;\r
+    if (hex || octal || binary)\r
+    {\r
+      // bit-pattern representation\r
+      // this is the binary representation optionally shown in octal or hex\r
+      // first generate the binary by masking the bits\r
+      // ensure that it has at least one bit!\r
+      for (T mask(1); ; mask <<= 1)\r
+      {\r
+        result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0');\r
+        if (mask == t_zero) break;\r
+      }\r
+      // the result is now the full width of the type - e.g. int will give a 32-bit result\r
+      // now interpret this as either binary, octal or hex and add the prefix\r
+      if (binary)\r
+      {\r
+        // the result is already binary - but the width may be wrong\r
+        // if this is still smaller than the width field, sign extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        while (result.size() < width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > width)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0b");\r
+      }\r
+      else if (octal)\r
+      {\r
+        // the result is currently binary - but before converting get the width right\r
+        // the width is expressed in octal digits so make the binary 3 times this\r
+        // if this is still smaller than the width field, sign extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier\r
+        while (result.size() < 3*width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > 3*width)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 3 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to octal\r
+        std::string octal_result;\r
+        for (unsigned i = 0; i < result.size()/3; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*3] == '0')\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '0';\r
+              else\r
+                octal_result += '1';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '2';\r
+              else\r
+                octal_result += '3';\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '4';\r
+              else\r
+                octal_result += '5';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '6';\r
+              else\r
+                octal_result += '7';\r
+            }\r
+          }\r
+        }\r
+        result = octal_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0");\r
+      }\r
+      else\r
+      {\r
+        // hex - similar to octal\r
+        while (result.size() < 4*width)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        while (result.size() > 4*width)\r
+        {\r
+          // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+          if (result.size() <= 2) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != result[1]) break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 4 != 0)\r
+          result.insert((std::string::size_type)0, 1, result[0]);\r
+        // now convert to hex\r
+        std::string hex_result;\r
+        for (unsigned i = 0; i < result.size()/4; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*4] == '0')\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '0';\r
+                else\r
+                  hex_result += '1';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '2';\r
+                else\r
+                  hex_result += '3';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '4';\r
+                else\r
+                  hex_result += '5';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '6';\r
+                else\r
+                  hex_result += '7';\r
+              }\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '8';\r
+                else\r
+                  hex_result += '9';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'a';\r
+                else\r
+                  hex_result += 'b';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'c';\r
+                else\r
+                  hex_result += 'd';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'e';\r
+                else\r
+                  hex_result += 'f';\r
+              }\r
+            }\r
+          }\r
+        }\r
+        result = hex_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0x");\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // convert to sign-magnitude\r
+      // the representation is:\r
+      // [radix#][sign]magnitude\r
+      bool negative = i < t_zero;\r
+      // create a representation of the magnitude by successive division\r
+      do\r
+      {\r
+        T ch = abs(i % t_radix);\r
+        i /= t_radix;\r
+        result.insert((std::string::size_type)0, 1, to_char[ch]);\r
+      }\r
+      while(i != t_zero || result.size() < width);\r
+      // add the prefixes\r
+      // add a sign only for negative values\r
+      if (negative)\r
+        result.insert((std::string::size_type)0, 1, '-');\r
+      // then prefix everything with the radix if the hashed representation was requested\r
+      if (hashed)\r
+        result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#");\r
+    }\r
+    return result;\r
+  }\r
+\r
+  // unsigned version\r
+  template<typename T>\r
+  static std::string uimage (T i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    // untangle all the options\r
+    bool hashed = false;\r
+    bool binary = false;\r
+    bool octal = false;\r
+    bool hex = false;\r
+    switch(display)\r
+    {\r
+    case radix_none:\r
+      break;\r
+    case radix_hash_style:\r
+      hashed = radix != 10;\r
+      break;\r
+    case radix_hash_style_all:\r
+      hashed = true;\r
+      break;\r
+    case radix_c_style:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      break;\r
+    case radix_c_style_or_hash:\r
+      if (radix == 16)\r
+        hex = true;\r
+      else if (radix == 8)\r
+        octal = true;\r
+      else if (radix == 2)\r
+        binary = true;\r
+      else if (radix != 10)\r
+        hashed = true;\r
+      break;\r
+    default:\r
+      throw std::invalid_argument("invalid radix display value");\r
+    }\r
+    // create constants of the same type as the template parameter to avoid type mismatches\r
+    const T t_zero(0);\r
+    const T t_radix(radix);\r
+    // the C representations for binary, octal and hex use 2's-complement representation\r
+    // all other represenations use sign-magnitude\r
+    std::string result;\r
+    if (hex || octal || binary)\r
+    {\r
+      // bit-pattern representation\r
+      // this is the binary representation optionally shown in octal or hex\r
+      // first generate the binary by masking the bits\r
+      // ensure at least one bit\r
+      for (T mask(1); ; mask <<= 1)\r
+      {\r
+        result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0');\r
+        if (mask == t_zero) break;\r
+      }\r
+      // the result is now the full width of the type - e.g. int will give a 32-bit result\r
+      // now interpret this as either binary, octal or hex and add the prefix\r
+      if (binary)\r
+      {\r
+        // the result is already binary - but the width may be wrong\r
+        // if this is still smaller than the width field, zero extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        while (result.size() < width)\r
+          result.insert((std::string::size_type)0, 1, '0');\r
+        while (result.size() > width)\r
+        {\r
+          // do not trim to less than 1 bit (1-bit magnitude)\r
+          if (result.size() <= 1) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != '0') break;\r
+          result.erase(0,1);\r
+        }\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0b");\r
+      }\r
+      else if (octal)\r
+      {\r
+        // the result is currently binary - but before converting get the width right\r
+        // the width is expressed in octal digits so make the binary 3 times this\r
+        // if this is still smaller than the width field, sign extend\r
+        // otherwise trim down to either the width or the smallest string that preserves the value\r
+        // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier\r
+        while (result.size() < 3*width)\r
+          result.insert((std::string::size_type)0, 1, '0');\r
+        while (result.size() > 3*width)\r
+        {\r
+          // do not trim to less than 1 bit (1-bit magnitude)\r
+          if (result.size() <= 1) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != '0') break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 3 != 0)\r
+          result.insert((std::string::size_type)0, 1, '0');\r
+        // now convert to octal\r
+        std::string octal_result;\r
+        for (unsigned i = 0; i < result.size()/3; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*3] == '0')\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '0';\r
+              else\r
+                octal_result += '1';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '2';\r
+              else\r
+                octal_result += '3';\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*3+1] == '0')\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '4';\r
+              else\r
+                octal_result += '5';\r
+            }\r
+            else\r
+            {\r
+              if (result[i*3+2] == '0')\r
+                octal_result += '6';\r
+              else\r
+                octal_result += '7';\r
+            }\r
+          }\r
+        }\r
+        result = octal_result;\r
+        // add the prefix if the leading digit is not already 0\r
+        if (result.empty() || result[0] != '0') result.insert((std::string::size_type)0, "0");\r
+      }\r
+      else\r
+      {\r
+        // similar to octal\r
+        while (result.size() < 4*width)\r
+          result.insert((std::string::size_type)0, 1, '0');\r
+        while (result.size() > 4*width)\r
+        {\r
+          // do not trim to less than 1 bit (1-bit magnitude)\r
+          if (result.size() <= 1) break;\r
+          // only trim if it doesn't change the sign and therefore the value\r
+          if (result[0] != '0') break;\r
+          result.erase(0,1);\r
+        }\r
+        while (result.size() % 4 != 0)\r
+          result.insert((std::string::size_type)0, 1, '0');\r
+        // now convert to hex\r
+        std::string hex_result;\r
+        for (unsigned i = 0; i < result.size()/4; i++)\r
+        {\r
+          // yuck - ugly or what?\r
+          if (result[i*4] == '0')\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '0';\r
+                else\r
+                  hex_result += '1';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '2';\r
+                else\r
+                  hex_result += '3';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '4';\r
+                else\r
+                  hex_result += '5';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '6';\r
+                else\r
+                  hex_result += '7';\r
+              }\r
+            }\r
+          }\r
+          else\r
+          {\r
+            if (result[i*4+1] == '0')\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += '8';\r
+                else\r
+                  hex_result += '9';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'a';\r
+                else\r
+                  hex_result += 'b';\r
+              }\r
+            }\r
+            else\r
+            {\r
+              if (result[i*4+2] == '0')\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'c';\r
+                else\r
+                  hex_result += 'd';\r
+              }\r
+              else\r
+              {\r
+                if (result[i*4+3] == '0')\r
+                  hex_result += 'e';\r
+                else\r
+                  hex_result += 'f';\r
+              }\r
+            }\r
+          }\r
+        }\r
+        result = hex_result;\r
+        // add the prefix\r
+        result.insert((std::string::size_type)0, "0x");\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // convert to sign-magnitude\r
+      // the representation is:\r
+      // [radix#]magnitude\r
+      // create a representation of the magnitude by successive division\r
+      do\r
+      {\r
+        T ch = i % t_radix;\r
+        i /= t_radix;\r
+        result.insert((std::string::size_type)0, 1, to_char[(int)ch]);\r
+      }\r
+      while(i != t_zero || result.size() < width);\r
+      // prefix everything with the radix if the hashed representation was requested\r
+      if (hashed)\r
+        result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#");\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // exported conversions to string\r
+\r
+  std::string short_to_string(short i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return simage(i, radix, display, width);\r
+  }\r
+\r
+  std::string unsigned_short_to_string(unsigned short i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return uimage(i, radix, display, width);\r
+  }\r
+\r
+  std::string int_to_string(int i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return simage(i, radix, display, width);\r
+  }\r
+\r
+  std::string unsigned_to_string(unsigned i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return uimage(i, radix, display, width);\r
+  }\r
+\r
+  std::string long_to_string(long i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return simage(i, radix, display, width);\r
+  }\r
+\r
+  std::string unsigned_long_to_string(unsigned long i, unsigned radix, radix_display_t display, unsigned width)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return uimage(i, radix, display, width);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions FROM string\r
+  // local template function\r
+  // Note: this has been copied and modified for the inf class - so any changes here must be made there too\r
+\r
+  // signed version\r
+  template<typename T>\r
+  static T svalue(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    if (radix != 0 && (radix < 2 || radix > 36))\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    std::string::size_type i = 0;\r
+    // the radix passed as a parameter is just the default - it can be\r
+    // overridden by either the C prefix or the hash prefix. Note: a leading zero\r
+    // is the C-style prefix for octal - I only make this override the default\r
+    // when the default prefix is not specified\r
+    // First check for a C-style prefix\r
+    bool c_style = false;\r
+    if (i < str.size() && str[i] == '0')\r
+    {\r
+      // octal, binary or hex\r
+      if (i+1 < str.size() && tolower(str[i+1]) == 'x')\r
+      {\r
+        radix = 16;\r
+        i += 2;\r
+        c_style = true;\r
+      }\r
+      else if (i+1 < str.size() && tolower(str[i+1]) == 'b')\r
+      {\r
+        radix = 2;\r
+        i += 2;\r
+        c_style = true;\r
+      }\r
+      else if (radix == 0)\r
+      {\r
+        radix = 8;\r
+        i += 1;\r
+        c_style = true;\r
+      }\r
+    }\r
+    // now check for a hash-style prefix if a C-style prefix was not found\r
+    if (i == 0)\r
+    {\r
+      // scan for the sequence {digits}#\r
+      bool hash_found = false;\r
+      std::string::size_type j = i;\r
+      for (; j < str.size(); j++)\r
+      {\r
+        if (!isdigit(str[j]))\r
+        {\r
+          if (str[j] == '#')\r
+            hash_found = true;\r
+          break;\r
+        }\r
+      }\r
+      if (hash_found)\r
+      {\r
+        // use the hash prefix to define the radix\r
+        // i points to the start of the radix and j points to the # character\r
+        std::string slice = str.substr(i, j-i);\r
+        radix = string_to_unsigned(slice);\r
+        i = j+1;\r
+      }\r
+    }\r
+    if (radix == 0)\r
+      radix = 10;\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    T val(0);\r
+    if (c_style)\r
+    {\r
+      // the C style formats are bit patterns not integer values - these need\r
+      // to be sign-extended to get the right value\r
+      std::string binary;\r
+      if (radix == 2)\r
+      {\r
+        for (std::string::size_type j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += '0';\r
+            break;\r
+          case '1':\r
+            binary += '1';\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid binary character in string " + str);\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      else if (radix == 8)\r
+      {\r
+        for (std::string::size_type j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += "000";\r
+            break;\r
+          case '1':\r
+            binary += "001";\r
+            break;\r
+          case '2':\r
+            binary += "010";\r
+            break;\r
+          case '3':\r
+            binary += "011";\r
+            break;\r
+          case '4':\r
+            binary += "100";\r
+            break;\r
+          case '5':\r
+            binary += "101";\r
+            break;\r
+          case '6':\r
+            binary += "110";\r
+            break;\r
+          case '7':\r
+            binary += "111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid octal character in string " + str);\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      else\r
+      {\r
+        for (std::string::size_type j = i; j < str.size(); j++)\r
+        {\r
+          switch(tolower(str[j]))\r
+          {\r
+          case '0':\r
+            binary += "0000";\r
+            break;\r
+          case '1':\r
+            binary += "0001";\r
+            break;\r
+          case '2':\r
+            binary += "0010";\r
+            break;\r
+          case '3':\r
+            binary += "0011";\r
+            break;\r
+          case '4':\r
+            binary += "0100";\r
+            break;\r
+          case '5':\r
+            binary += "0101";\r
+            break;\r
+          case '6':\r
+            binary += "0110";\r
+            break;\r
+          case '7':\r
+            binary += "0111";\r
+            break;\r
+          case '8':\r
+            binary += "1000";\r
+            break;\r
+          case '9':\r
+            binary += "1001";\r
+            break;\r
+          case 'a':\r
+            binary += "1010";\r
+            break;\r
+          case 'b':\r
+            binary += "1011";\r
+            break;\r
+          case 'c':\r
+            binary += "1100";\r
+            break;\r
+          case 'd':\r
+            binary += "1101";\r
+            break;\r
+          case 'e':\r
+            binary += "1110";\r
+            break;\r
+          case 'f':\r
+            binary += "1111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid hex character in string " + str);\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      // now sign-extend to the right number of bits for the type\r
+      while (binary.size() < sizeof(T)*8)\r
+        binary.insert((std::string::size_type)0, 1, binary.empty() ? '0' : binary[0]);\r
+      // now convert the value\r
+      for (std::string::size_type j = 0; j < binary.size(); j++)\r
+      {\r
+        val *= 2;\r
+        int ch = from_char[(unsigned char)binary[j]] ;\r
+        val += T(ch);\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // now scan for a sign and find whether this is a negative number\r
+      bool negative = false;\r
+      if (i < str.size())\r
+      {\r
+        switch (str[i])\r
+        {\r
+        case '-':\r
+          negative = true;\r
+          i++;\r
+          break;\r
+        case '+':\r
+          i++;\r
+          break;\r
+        }\r
+      }\r
+      for (; i < str.size(); i++)\r
+      {\r
+        val *= T(radix);\r
+        int ch = from_char[(unsigned char)str[i]] ;\r
+        if (ch == -1 || (unsigned)ch >= radix)\r
+          throw std::invalid_argument("invalid character in string " + str);\r
+        val += T(ch);\r
+      }\r
+      if (negative)\r
+        val = -val;\r
+    }\r
+    return val;\r
+  }\r
+\r
+  // unsigned version\r
+  template<typename T>\r
+  static T uvalue(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    if (radix != 0 && (radix < 2 || radix > 36))\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    unsigned i = 0;\r
+    // the radix passed as a parameter is just the default - it can be\r
+    // overridden by either the C prefix or the hash prefix. Note: a leading\r
+    // zero is the C-style prefix for octal - I only make this override the\r
+    // default when the default prefix is not specified\r
+    // First check for a C-style prefix\r
+    bool c_style = false;\r
+    if (i < str.size() && str[i] == '0')\r
+    {\r
+      // binary or hex\r
+      if (i+1 < str.size() && tolower(str[i+1]) == 'x')\r
+      {\r
+        radix = 16;\r
+        i += 2;\r
+        c_style = true;\r
+      }\r
+      else if (i+1 < str.size() && tolower(str[i+1]) == 'b')\r
+      {\r
+        radix = 2;\r
+        i += 2;\r
+        c_style = true;\r
+      }\r
+      else if (radix == 0)\r
+      {\r
+        radix = 8;\r
+        i += 1;\r
+        c_style = true;\r
+      }\r
+    }\r
+    // now check for a hash-style prefix if a C-style prefix was not found\r
+    if (i == 0)\r
+    {\r
+      // scan for the sequence {digits}#\r
+      bool hash_found = false;\r
+      unsigned j = i;\r
+      for (; j < str.size(); j++)\r
+      {\r
+        if (!isdigit(str[j]))\r
+        {\r
+          if (str[j] == '#')\r
+            hash_found = true;\r
+          break;\r
+        }\r
+      }\r
+      if (hash_found)\r
+      {\r
+        // use the hash prefix to define the radix\r
+        // i points to the start of the radix and j points to the # character\r
+        std::string slice = str.substr(i, j-i);\r
+        radix = string_to_unsigned(slice);\r
+        i = j+1;\r
+      }\r
+    }\r
+    if (radix == 0)\r
+      radix = 10;\r
+    if (radix < 2 || radix > 36)\r
+      throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));\r
+    T val(0);\r
+    if (c_style)\r
+    {\r
+      // the C style formats are bit patterns not integer values - these need\r
+      // to be sign-extended to get the right value\r
+      std::string binary;\r
+      if (radix == 2)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += '0';\r
+            break;\r
+          case '1':\r
+            binary += '1';\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid hex character in string " + str);\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      else if (radix == 8)\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(str[j])\r
+          {\r
+          case '0':\r
+            binary += "000";\r
+            break;\r
+          case '1':\r
+            binary += "001";\r
+            break;\r
+          case '2':\r
+            binary += "010";\r
+            break;\r
+          case '3':\r
+            binary += "011";\r
+            break;\r
+          case '4':\r
+            binary += "100";\r
+            break;\r
+          case '5':\r
+            binary += "101";\r
+            break;\r
+          case '6':\r
+            binary += "110";\r
+            break;\r
+          case '7':\r
+            binary += "111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid octal character in string " + str);\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      else\r
+      {\r
+        for (unsigned j = i; j < str.size(); j++)\r
+        {\r
+          switch(tolower(str[j]))\r
+          {\r
+          case '0':\r
+            binary += "0000";\r
+            break;\r
+          case '1':\r
+            binary += "0001";\r
+            break;\r
+          case '2':\r
+            binary += "0010";\r
+            break;\r
+          case '3':\r
+            binary += "0011";\r
+            break;\r
+          case '4':\r
+            binary += "0100";\r
+            break;\r
+          case '5':\r
+            binary += "0101";\r
+            break;\r
+          case '6':\r
+            binary += "0110";\r
+            break;\r
+          case '7':\r
+            binary += "0111";\r
+            break;\r
+          case '8':\r
+            binary += "1000";\r
+            break;\r
+          case '9':\r
+            binary += "1001";\r
+            break;\r
+          case 'a':\r
+            binary += "1010";\r
+            break;\r
+          case 'b':\r
+            binary += "1011";\r
+            break;\r
+          case 'c':\r
+            binary += "1100";\r
+            break;\r
+          case 'd':\r
+            binary += "1101";\r
+            break;\r
+          case 'e':\r
+            binary += "1110";\r
+            break;\r
+          case 'f':\r
+            binary += "1111";\r
+            break;\r
+          default:\r
+            throw std::invalid_argument("invalid hex character in string " + str);\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      // now zero-extend to the right number of bits for the type\r
+      while (binary.size() < sizeof(T)*8)\r
+        binary.insert((std::string::size_type)0, 1, '0');\r
+      // now convert the value\r
+      for (unsigned j = 0; j < binary.size(); j++)\r
+      {\r
+        val *= 2;\r
+        int ch = from_char[(unsigned char)binary[j]] ;\r
+        val += T(ch);\r
+      }\r
+    }\r
+    else\r
+    {\r
+      // now scan for a sign and find whether this is a negative number\r
+      if (i < str.size())\r
+      {\r
+        switch (str[i])\r
+        {\r
+        case '-':\r
+          throw std::invalid_argument("invalid sign character in string " + str + " for unsigned value");\r
+          i++;\r
+          break;\r
+        case '+':\r
+          i++;\r
+          break;\r
+        }\r
+      }\r
+      for (; i < str.size(); i++)\r
+      {\r
+        val *= T(radix);\r
+        int ch = from_char[(unsigned char)str[i]] ;\r
+        if (ch == -1 || (unsigned)ch >= radix)\r
+        {\r
+          throw std::invalid_argument("invalid character in string " + str);\r
+        }\r
+        val += T(ch);\r
+      }\r
+    }\r
+    return val;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // exported functions\r
+\r
+  short string_to_short(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return svalue<short>(str, radix);\r
+  }\r
+\r
+  unsigned short string_to_unsigned_short(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return uvalue<unsigned short>(str, radix);\r
+  }\r
+\r
+  int string_to_int(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return svalue<int>(str, radix);\r
+  }\r
+\r
+  unsigned string_to_unsigned(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return uvalue<unsigned>(str, radix);\r
+  }\r
+\r
+  long string_to_long(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return svalue<long>(str, radix);\r
+  }\r
+\r
+  unsigned long string_to_unsigned_long(const std::string& str, unsigned radix)\r
+    throw(std::invalid_argument)\r
+  {\r
+    return uvalue<unsigned long>(str, radix);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_int.hpp b/src/stlplus/strings/string_int.hpp
new file mode 100644 (file)
index 0000000..a45b982
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef STLPLUS_STRING_INT\r
+#define STLPLUS_STRING_INT\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Convert integer types to/from string\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Conversions of Integer types to string\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // The radix (i.e. base) for these conversions can be any value from base 2 to base 36\r
+  // specifying any other radix causes std::invalid_argument to be thrown\r
+\r
+  // The way in which the radix is displayed is defined in radix_types.hpp\r
+  // If any other value is used, std::invalid_argument is thrown\r
+\r
+  // The width argument specifies the number of numerical digits to use in the result\r
+  // This is a minimum - if the value requires more digits then it will be wider than the width argument\r
+  // However, if it is smaller, then it will be extended to the specified width\r
+  // Then, the radix display prefix is added to this width\r
+\r
+  // For example, using the hash representation of 0 in hex with width=4 gives:\r
+  // 16#0000 - so there's 4 digits in the number part\r
+\r
+  std::string short_to_string(short i,\r
+                              unsigned radix = 10,\r
+                              radix_display_t display = radix_c_style_or_hash,\r
+                              unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  std::string unsigned_short_to_string(unsigned short i,\r
+                                       unsigned radix = 10,\r
+                                       radix_display_t display = radix_c_style_or_hash,\r
+                                       unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  std::string int_to_string(int i,\r
+                            unsigned radix = 10,\r
+                            radix_display_t display = radix_c_style_or_hash,\r
+                            unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  std::string unsigned_to_string(unsigned i,\r
+                                 unsigned radix = 10,\r
+                                 radix_display_t display = radix_c_style_or_hash,\r
+                                 unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  std::string long_to_string(long i,\r
+                             unsigned radix = 10,\r
+                             radix_display_t display = radix_c_style_or_hash,\r
+                             unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  std::string unsigned_long_to_string(unsigned long i,\r
+                                      unsigned radix = 10,\r
+                                      radix_display_t display = radix_c_style_or_hash,\r
+                                      unsigned width = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Convert a string to an integer type\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // supports all the formats described above for the reverse conversion\r
+  // If the radix is set to zero, the conversions deduce the radix from the string representation\r
+  // So,\r
+  //   0b prefix is binary,\r
+  //   0 prefix is octal,\r
+  //   0x is hex\r
+  //   <base># prefix is my hash format\r
+  // The radix must be either zero as explained above, or in the range 2 to 16\r
+  // A non-zero radix should be used when the string value has no radix information and is non-decimal\r
+  // e.g. the hex value FEDCBA has no indication that it is hex, so specify radix 16\r
+  // Any other value of radix will cause std::invalid_argument to be thrown\r
+\r
+  short string_to_short(const std::string& value,\r
+                        unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  unsigned short string_to_unsigned_short(const std::string& value,\r
+                                          unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  int string_to_int(const std::string& value,\r
+                    unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  unsigned string_to_unsigned(const std::string& value,\r
+                              unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  long string_to_long(const std::string& value,\r
+                      unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  unsigned long string_to_unsigned_long(const std::string& value,\r
+                                        unsigned radix = 0)\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_list.hpp b/src/stlplus/strings/string_list.hpp
new file mode 100644 (file)
index 0000000..6728690
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef STLPLUS_STRING_LIST\r
+#define STLPLUS_STRING_LIST\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a list\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <list>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string list_to_string(const std::list<T>& values,\r
+                             S to_string_fn,\r
+                             const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_list.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_list.tpp b/src/stlplus/strings/string_list.tpp
new file mode 100644 (file)
index 0000000..061f427
--- /dev/null
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   template implementations\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string list_to_string(const std::list<T>& values,\r
+                             S to_string_fn,\r
+                             const std::string& separator)\r
+  {\r
+    return sequence_to_string(values.begin(), values.end(), to_string_fn, separator);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_map.hpp b/src/stlplus/strings/string_map.hpp
new file mode 100644 (file)
index 0000000..8bf7e8b
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef STLPLUS_STRING_MAP\r
+#define STLPLUS_STRING_MAP\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a map/multimap\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <map>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  std::string map_to_string(const std::map<K,T,C>& values,\r
+                            SK key_to_string_fn,\r
+                            ST value_to_string_fn,\r
+                            const std::string& pair_separator = ":",\r
+                            const std::string& separator = ",");\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  std::string multimap_to_string(const std::multimap<K,T,C>& values,\r
+                                 SK key_to_string_fn,\r
+                                 ST value_to_string_fn,\r
+                                 const std::string& pair_separator = ":",\r
+                                 const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_map.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_map.tpp b/src/stlplus/strings/string_map.tpp
new file mode 100644 (file)
index 0000000..49cb51f
--- /dev/null
@@ -0,0 +1,45 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // map\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  std::string map_to_string(const std::map<K,T,C>& values,\r
+                            SK key_to_string_fn,\r
+                            ST value_to_string_fn,\r
+                            const std::string& pair_separator,\r
+                            const std::string& separator)\r
+  {\r
+    return pair_sequence_to_string(values.begin(), values.end(),\r
+                                   key_to_string_fn, value_to_string_fn,\r
+                                   pair_separator, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // multimap\r
+\r
+  template<typename K, typename T, typename C, typename SK, typename ST>\r
+  std::string multimap_to_string(const std::multimap<K,T,C>& values,\r
+                                 SK key_to_string_fn,\r
+                                 ST value_to_string_fn,\r
+                                 const std::string& pair_separator,\r
+                                 const std::string& separator)\r
+  {\r
+    return pair_sequence_to_string(values.begin(), values.end(),\r
+                                   key_to_string_fn, value_to_string_fn,\r
+                                   pair_separator, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_matrix.hpp b/src/stlplus/strings/string_matrix.hpp
new file mode 100644 (file)
index 0000000..dd1fa2b
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef STLPLUS_STRING_MATRIX\r
+#define STLPLUS_STRING_MATRIX\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a matrix\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "matrix.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string matrix_to_string(const matrix<T>& values,\r
+                               S to_string_fn,\r
+                               const std::string& column_separator = "|",\r
+                               const std::string& row_separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_matrix.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_matrix.tpp b/src/stlplus/strings/string_matrix.tpp
new file mode 100644 (file)
index 0000000..345d15c
--- /dev/null
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  template<typename T, typename S>\r
+  std::string matrix_to_string(const matrix<T>& values,\r
+                               S to_string_fn,\r
+                               const std::string& column_separator,\r
+                               const std::string& row_separator)\r
+  {\r
+    std::string result;\r
+    for (unsigned r = 0; r < values.rows(); r++)\r
+    {\r
+      if (r != 0) result += row_separator;\r
+      for (unsigned c = 0; c < values.columns(); c++)\r
+      {\r
+        if (c != 0) result += column_separator;\r
+        result += to_string_fn(values(r,c));\r
+      }\r
+    }\r
+    return result;\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_ntree.hpp b/src/stlplus/strings/string_ntree.hpp
new file mode 100644 (file)
index 0000000..177b53d
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef STLPLUS_STRING_NTREE\r
+#define STLPLUS_STRING_NTREE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of an ntree\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "ntree.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string ntree_to_string(const ntree<T>& values,\r
+                              S to_string_fn,\r
+                              const std::string& separator = "|",\r
+                              const std::string& indent_string = "  ");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_ntree.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_ntree.tpp b/src/stlplus/strings/string_ntree.tpp
new file mode 100644 (file)
index 0000000..040dad3
--- /dev/null
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string ntree_to_string(const ntree<T>& values,\r
+                              S to_string_fn,\r
+                              const std::string& separator,\r
+                              const std::string& indent_string)\r
+  {\r
+    std::string result;\r
+    for (TYPENAME ntree<T>::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++)\r
+    {\r
+      if (i != values.prefix_begin()) result += separator;\r
+      for (unsigned indent = values.depth(i.simplify()); --indent; )\r
+        result += indent_string;\r
+      result += to_string_fn(*i);\r
+    }\r
+    return result;\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_pair.hpp b/src/stlplus/strings/string_pair.hpp
new file mode 100644 (file)
index 0000000..483a0b1
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef STLPLUS_STRING_PAIR\r
+#define STLPLUS_STRING_PAIR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a pair\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <map>\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename V1, typename V2, typename S1, typename S2>\r
+  std::string pair_to_string(const std::pair<V1,V2>& values,\r
+                             S1 to_string_fn1,\r
+                             S2 to_string_fn2,\r
+                             const std::string& separator = ":");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_pair.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_pair.tpp b/src/stlplus/strings/string_pair.tpp
new file mode 100644 (file)
index 0000000..f458836
--- /dev/null
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename V1, typename V2, typename S1, typename S2>\r
+  std::string pair_to_string(const std::pair<V1,V2>& values,\r
+                             S1 to_string_fn1,\r
+                             S2 to_string_fn2,\r
+                             const std::string& separator)\r
+  {\r
+    return to_string_fn1(values.first) + separator + to_string_fn2(values.second);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_pointer.hpp b/src/stlplus/strings/string_pointer.hpp
new file mode 100644 (file)
index 0000000..2170000
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef STLPLUS_STRING_POINTER\r
+#define STLPLUS_STRING_POINTER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of an object pointed to\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template <typename T, typename S>\r
+  std::string pointer_to_string(const T* value,\r
+                                S to_string_fn,\r
+                                const std::string& null_string = "<null>",\r
+                                const std::string& prefix = "(",\r
+                                const std::string& suffix = ")");\r
+\r
+\r
+}\r
+\r
+#include "string_pointer.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_pointer.tpp b/src/stlplus/strings/string_pointer.tpp
new file mode 100644 (file)
index 0000000..d77a902
--- /dev/null
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template <typename T, typename S>\r
+  std::string pointer_to_string(const T* value,\r
+                                S to_string_fn,\r
+                                const std::string& null_string,\r
+                                const std::string& prefix,\r
+                                const std::string& suffix)\r
+  {\r
+    return value ? (prefix + to_string_fn(*value) + suffix) : null_string;\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_sequence.hpp b/src/stlplus/strings/string_sequence.hpp
new file mode 100644 (file)
index 0000000..f029031
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef STLPLUS_STRING_SEQUENCE\r
+#define STLPLUS_STRING_SEQUENCE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate string representations of sequences represented by forward iterators\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any forward iterator sequence\r
+\r
+  template <typename I, typename S>\r
+  std::string sequence_to_string(I begin,\r
+                                 I end, \r
+                                 S to_string,\r
+                                 const std::string& separator);\r
+\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any forward iterator sequence of pairs\r
+\r
+  template <typename I, typename S1, typename S2>\r
+  std::string pair_sequence_to_string(I begin,\r
+                                      I end,\r
+                                      S1 to_string_fn1,\r
+                                      S2 to_string_fn2,\r
+                                      const std::string& pair_separator,\r
+                                      const std::string& separator);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_sequence.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_sequence.tpp b/src/stlplus/strings/string_sequence.tpp
new file mode 100644 (file)
index 0000000..0bc522e
--- /dev/null
@@ -0,0 +1,54 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_pair.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any forward iterator sequence\r
+\r
+  template <typename I, typename S>\r
+  std::string sequence_to_string(I begin,\r
+                                 I end, \r
+                                 S to_string,\r
+                                 const std::string& separator)\r
+  {\r
+    std::string result;\r
+    for (I i = begin; i != end; i++)\r
+    {\r
+      if (i != begin) result += separator;\r
+      result += to_string(*i);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // any sequence where the value is a pair\r
+\r
+  template <typename I, typename S1, typename S2>\r
+  std::string pair_sequence_to_string(I begin,\r
+                                      I end,\r
+                                      S1 to_string_fn1,\r
+                                      S2 to_string_fn2,\r
+                                      const std::string& pair_separator,\r
+                                      const std::string& separator)\r
+  {\r
+    std::string result;\r
+    for (I i = begin; i != end; i++)\r
+    {\r
+      if (i != begin) result += separator;\r
+      result += pair_to_string(*i, to_string_fn1, to_string_fn2, pair_separator);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_set.hpp b/src/stlplus/strings/string_set.hpp
new file mode 100644 (file)
index 0000000..ce06768
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef STLPLUS_STRING_SET\r
+#define STLPLUS_STRING_SET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a set/multiset\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <set>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename K, typename C, typename S>\r
+  std::string set_to_string(const std::set<K,C>& values,\r
+                            S to_string_fn,\r
+                            const std::string& separator = ",");\r
+\r
+  template<typename K, typename C, typename S>\r
+  std::string multiset_to_string(const std::multiset<K,C>& values,\r
+                                 S to_string_fn,\r
+                                 const std::string& separator = ",");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_set.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_set.tpp b/src/stlplus/strings/string_set.tpp
new file mode 100644 (file)
index 0000000..d7c7e80
--- /dev/null
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   template implementations\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // set\r
+\r
+  template<typename K, typename C, typename S>\r
+  std::string set_to_string(const std::set<K,C>& values,\r
+                            S to_string_fn,\r
+                            const std::string& separator)\r
+  {\r
+    return sequence_to_string(values.begin(), values.end(), to_string_fn, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // multiset\r
+\r
+  template<typename K, typename C, typename S>\r
+  std::string multiset_to_string(const std::multiset<K,C>& values,\r
+                                 S to_string_fn,\r
+                                 const std::string& separator)\r
+  {\r
+    return sequence_to_string(values.begin(), values.end(), to_string_fn, separator);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_simple_ptr.hpp b/src/stlplus/strings/string_simple_ptr.hpp
new file mode 100644 (file)
index 0000000..813432c
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef STLPLUS_STRING_SIMPLE_PTR\r
+#define STLPLUS_STRING_SIMPLE_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a smart pointer\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "simple_ptr.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string simple_ptr_to_string(const simple_ptr<T>& value,\r
+                                  S to_string_fn,\r
+                                  const std::string& null_string = "<null>",\r
+                                  const std::string& prefix = "(",\r
+                                  const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  std::string simple_ptr_clone_to_string(const simple_ptr_clone<T>& value,\r
+                                        S to_string_fn,\r
+                                        const std::string& null_string = "<null>",\r
+                                        const std::string& prefix = "(",\r
+                                        const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  std::string simple_ptr__nocopy_to_string(const simple_ptr_nocopy<T>& value,\r
+                                          S to_string_fn,\r
+                                          const std::string& null_string = "<null>",\r
+                                          const std::string& prefix = "(",\r
+                                          const std::string& suffix = ")");\r
+\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_simple_ptr.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_simple_ptr.tpp b/src/stlplus/strings/string_simple_ptr.tpp
new file mode 100644 (file)
index 0000000..7da84af
--- /dev/null
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string simple_ptr_to_string(const simple_ptr<T>& value,\r
+                                  S to_string_fn,\r
+                                  const std::string& null_string,\r
+                                  const std::string& prefix,\r
+                                  const std::string& suffix)\r
+  {\r
+    return value ? (prefix + to_string_fn(*value) + suffix) : null_string;\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  std::string simple_ptr_clone_to_string(const simple_ptr_clone<T>& value,\r
+                                        S to_string_fn,\r
+                                        const std::string& null_string,\r
+                                        const std::string& prefix,\r
+                                        const std::string& suffix)\r
+  {\r
+    return value ? (prefix + to_string_fn(*value) + suffix) : null_string;\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  std::string simple_ptr_nocopy_to_string(const simple_ptr_nocopy<T>& value,\r
+                                         S to_string_fn,\r
+                                         const std::string& null_string,\r
+                                         const std::string& prefix,\r
+                                         const std::string& suffix)\r
+  {\r
+    return value ? (prefix + to_string_fn(*value) + suffix) : null_string;\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_smart_ptr.hpp b/src/stlplus/strings/string_smart_ptr.hpp
new file mode 100644 (file)
index 0000000..3a5e253
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef STLPLUS_STRING_SMART_PTR\r
+#define STLPLUS_STRING_SMART_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a smart pointer\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "smart_ptr.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string smart_ptr_to_string(const smart_ptr<T>& value,\r
+                                  S to_string_fn,\r
+                                  const std::string& null_string = "<null>",\r
+                                  const std::string& prefix = "(",\r
+                                  const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  std::string smart_ptr_clone_to_string(const smart_ptr_clone<T>& value,\r
+                                        S to_string_fn,\r
+                                        const std::string& null_string = "<null>",\r
+                                        const std::string& prefix = "(",\r
+                                        const std::string& suffix = ")");\r
+\r
+  template<typename T, typename S>\r
+  std::string smart_ptr__nocopy_to_string(const smart_ptr_nocopy<T>& value,\r
+                                          S to_string_fn,\r
+                                          const std::string& null_string = "<null>",\r
+                                          const std::string& prefix = "(",\r
+                                          const std::string& suffix = ")");\r
+\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_smart_ptr.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_smart_ptr.tpp b/src/stlplus/strings/string_smart_ptr.tpp
new file mode 100644 (file)
index 0000000..7953fbf
--- /dev/null
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string smart_ptr_to_string(const smart_ptr<T>& value,\r
+                                  S to_string_fn,\r
+                                  const std::string& null_string,\r
+                                  const std::string& prefix,\r
+                                  const std::string& suffix)\r
+  {\r
+    return value ? (prefix + to_string_fn(*value) + suffix) : null_string;\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  std::string smart_ptr_clone_to_string(const smart_ptr_clone<T>& value,\r
+                                        S to_string_fn,\r
+                                        const std::string& null_string,\r
+                                        const std::string& prefix,\r
+                                        const std::string& suffix)\r
+  {\r
+    return value ? (prefix + to_string_fn(*value) + suffix) : null_string;\r
+  }\r
+\r
+  template<typename T, typename S>\r
+  std::string smart_ptr_nocopy_to_string(const smart_ptr_nocopy<T>& value,\r
+                                         S to_string_fn,\r
+                                         const std::string& null_string,\r
+                                         const std::string& prefix,\r
+                                         const std::string& suffix)\r
+  {\r
+    return value ? (prefix + to_string_fn(*value) + suffix) : null_string;\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_stl.hpp b/src/stlplus/strings/string_stl.hpp
new file mode 100644 (file)
index 0000000..11e05fb
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef STLPLUS_STRING_STL\r
+#define STLPLUS_STRING_STL\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Template string conversions for pointers and STL containers\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "string_bitset.hpp"\r
+#include "string_list.hpp"\r
+#include "string_map.hpp"\r
+#include "string_pair.hpp"\r
+#include "string_sequence.hpp"\r
+#include "string_set.hpp"\r
+#include "string_string.hpp"\r
+#include "string_vector.hpp"\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_stlplus.hpp b/src/stlplus/strings/string_stlplus.hpp
new file mode 100644 (file)
index 0000000..bdcad7b
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef STLPLUS_STRING_STLPLUS\r
+#define STLPLUS_STRING_STLPLUS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Template string conversions for the STLplus containers\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// can be excluded to break the dependency on the containers library\r
+#ifndef NO_STLPLUS_CONTAINERS\r
+#include "string_digraph.hpp"\r
+#include "string_foursome.hpp"\r
+#include "string_hash.hpp"\r
+#include "string_matrix.hpp"\r
+#include "string_ntree.hpp"\r
+#include "string_smart_ptr.hpp"\r
+#include "string_triple.hpp"\r
+#endif\r
+\r
+// can be excluded to break the dependency on the portability library\r
+#ifndef NO_STLPLUS_INF\r
+#include "string_inf.hpp"\r
+#endif\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_string.cpp b/src/stlplus/strings/string_string.cpp
new file mode 100644 (file)
index 0000000..53e8189
--- /dev/null
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_string.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // strings\r
+\r
+  std::string string_to_string(const std::string& value)\r
+  {\r
+    return value;\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_string.hpp b/src/stlplus/strings/string_string.hpp
new file mode 100644 (file)
index 0000000..5f5b044
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef STLPLUS_STRING_STRING\r
+#define STLPLUS_STRING_STRING\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Functions for converting C/STL strings to string\r
+\r
+//   This is necessary for completeness, e.g. for use in vector_to_string for vector<string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  std::string string_to_string(const std::string& value);\r
+\r
+}\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_triple.hpp b/src/stlplus/strings/string_triple.hpp
new file mode 100644 (file)
index 0000000..c812789
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef STLPLUS_STRING_TRIPLE\r
+#define STLPLUS_STRING_TRIPLE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a triple\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "triple.hpp"\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename S1, typename S2, typename S3>\r
+  std::string triple_to_string(const triple<T1,T2,T3>& values,\r
+                               S1 to_string_fn1,\r
+                               S2 to_string_fn2,\r
+                               S3 to_string_fn3,\r
+                               const std::string& separator = ":");\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_triple.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_triple.tpp b/src/stlplus/strings/string_triple.tpp
new file mode 100644 (file)
index 0000000..ddd574a
--- /dev/null
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T1, typename T2, typename T3, typename S1, typename S2, typename S3>\r
+  std::string triple_to_string(const triple<T1,T2,T3>& values,\r
+                               S1 to_string_fn1,\r
+                               S2 to_string_fn2,\r
+                               S3 to_string_fn3,\r
+                               const std::string& separator)\r
+  {\r
+    return \r
+      to_string_fn1(values.first) + \r
+      separator + \r
+      to_string_fn2(values.second) + \r
+      separator + \r
+      to_string_fn3(values.third);\r
+  }\r
+\r
+} // end namespace stlplus\r
+\r
diff --git a/src/stlplus/strings/string_utilities.cpp b/src/stlplus/strings/string_utilities.cpp
new file mode 100644 (file)
index 0000000..9ff5345
--- /dev/null
@@ -0,0 +1,442 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_utilities.hpp"\r
+#include "string_basic.hpp"\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+#include <stdarg.h>\r
+#include <stdio.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  // added as a local copy to break the dependency on the portability library\r
+  static std::string local_dformat(const char* format, ...) throw(std::invalid_argument)\r
+  {\r
+    std::string formatted;\r
+    va_list args;\r
+    va_start(args, format);\r
+#ifdef MSWINDOWS\r
+    int length = 0;\r
+    char* buffer = 0;\r
+    for(int buffer_length = 256; ; buffer_length*=2)\r
+    {\r
+      buffer = (char*)malloc(buffer_length);\r
+      if (!buffer) throw std::invalid_argument("string_utilities");\r
+      length = _vsnprintf(buffer, buffer_length-1, format, args);\r
+      if (length >= 0)\r
+      {\r
+        buffer[length] = 0;\r
+        formatted += std::string(buffer);\r
+        free(buffer);\r
+        break;\r
+      }\r
+      free(buffer);\r
+    }\r
+#else\r
+    char* buffer = 0;\r
+    int length = vasprintf(&buffer, format, args);\r
+    if (!buffer) throw std::invalid_argument("string_utilities");\r
+    if (length >= 0)\r
+      formatted += std::string(buffer);\r
+    free(buffer);\r
+#endif\r
+    va_end(args);\r
+    if (length < 0) throw std::invalid_argument("string_utilities");\r
+    return formatted;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string pad(const std::string& str, alignment_t alignment, unsigned width, char padch)\r
+    throw(std::invalid_argument)\r
+  {\r
+    std::string result = str;\r
+    switch(alignment)\r
+    {\r
+    case align_left:\r
+    {\r
+      unsigned padding = width>str.size() ? width - str.size() : 0;\r
+      unsigned i = 0;\r
+      while (i++ < padding)\r
+        result.insert(result.end(), padch);\r
+      break;\r
+    }\r
+    case align_right:\r
+    {\r
+      unsigned padding = width>str.size() ? width - str.size() : 0;\r
+      unsigned i = 0;\r
+      while (i++ < padding)\r
+        result.insert(result.begin(), padch);\r
+      break;\r
+    }\r
+    case align_centre:\r
+    {\r
+      unsigned padding = width>str.size() ? width - str.size() : 0;\r
+      unsigned i = 0;\r
+      while (i++ < padding/2)\r
+        result.insert(result.end(), padch);\r
+      i--;\r
+      while (i++ < padding)\r
+        result.insert(result.begin(), padch);\r
+      break;\r
+    }\r
+    default:\r
+      throw std::invalid_argument("invalid alignment value");\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string trim_left(const std::string& val)\r
+  {\r
+    std::string result = val;\r
+    while (!result.empty() && isspace(result[0]))\r
+      result.erase(result.begin());\r
+    return result;\r
+  }\r
+\r
+  std::string trim_right(const std::string& val)\r
+  {\r
+    std::string result = val;\r
+    while (!result.empty() && isspace(result[result.size()-1]))\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+  std::string trim(const std::string& val)\r
+  {\r
+    std::string result = val;\r
+    while (!result.empty() && isspace(result[0]))\r
+      result.erase(result.begin());\r
+    while (!result.empty() && isspace(result[result.size()-1]))\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string lowercase(const std::string& val)\r
+  {\r
+    std::string text = val;\r
+    for (unsigned i = 0; i < text.size(); i++)\r
+      text[i] = tolower(text[i]);\r
+    return text;\r
+  }\r
+\r
+  std::string uppercase(const std::string& val)\r
+  {\r
+    std::string text = val;\r
+    for (unsigned i = 0; i < text.size(); i++)\r
+      text[i] = toupper(text[i]);\r
+    return text;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string translate(const std::string& input, const std::string& from_set, const std::string& to_set)\r
+  {\r
+    std::string result;\r
+    for (unsigned i = 0; i < input.size(); i++)\r
+    {\r
+      char ch = input[i];\r
+      // check to see if the character is in the from set\r
+      std::string::size_type found = from_set.find(ch);\r
+      if (found == std::string::npos)\r
+      {\r
+        // not found so just copy across\r
+        result += ch;\r
+      }\r
+      else if (found < to_set.size())\r
+      {\r
+        // found and in range so translate\r
+        result += to_set[found];\r
+      }\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // WARNING: wheel re-invention follows\r
+  // Given that all shells perform wildcard matching, why don't the library writers put it in the C run-time????????\r
+  // The problem:\r
+  //   *  matches any number of characters - this is achieved by matching 1 and seeing if the remainder matches\r
+  //      if not, try 2 characters and see if the remainder matches etc.\r
+  //      this must be recursive, not iterative, so that multiple *s can appear in the same wildcard expression\r
+  //   ?  matches exactly one character so doesn't need the what-if approach\r
+  //   \  escapes special characters such as *, ? and [\r
+  //   [] matches exactly one character in the set - the difficulty is the set can contain ranges, e.g [a-zA-Z0-9]\r
+  //      a set cannot be empty and the ] character can be included by making it the first character\r
+\r
+  // function for testing whether a character matches a set\r
+  // I can't remember the exact rules and I have no definitive references but:\r
+  // a set contains characters, escaped characters (I think) and ranges in the form a-z\r
+  // The character '-' can only appear at the start of the set where it is not interpreted as a range\r
+  // This is a horrible mess - blame the Unix folks for making a hash of wildcards\r
+\r
+  static bool match_set (const std::string& set, char match)\r
+  {\r
+    // first expand any ranges and remove escape characters to make life more palatable\r
+    std::string simple_set;\r
+    for (std::string::const_iterator i = set.begin(); i != set.end(); ++i)\r
+    {\r
+      switch(*i)\r
+      {\r
+      case '-':\r
+      {\r
+        if (i == set.begin())\r
+        {\r
+          simple_set += *i;\r
+        }\r
+        else if (i+1 == set.end())\r
+        {\r
+          return false;\r
+        }\r
+        else\r
+        {\r
+          // found a set. The first character is already in the result, so first remove it (the set might be empty)\r
+          simple_set.erase(simple_set.end()-1);\r
+          char last = *++i;\r
+          for (char ch = *(i-2); ch <= last; ch++)\r
+          {\r
+            simple_set += ch;\r
+          }\r
+        }\r
+        break;\r
+      }\r
+      case '\\':\r
+        if (i+1 == set.end()) {return false;}\r
+        simple_set += *++i;\r
+        break;\r
+      default:\r
+        simple_set += *i;\r
+        break;\r
+      }\r
+    }\r
+    std::string::size_type result = simple_set.find(match);\r
+    return result != std::string::npos;\r
+  }\r
+\r
+  // the recursive bit - basically whenever a * is found you recursively call this for each candidate substring match\r
+  // until either it succeeds or you run out of string to match\r
+  // for each * in the wildcard another level of recursion is created\r
+\r
+  static bool match_remainder (const std::string& wild, std::string::const_iterator wildi,\r
+                               const std::string& match, std::string::const_iterator matchi)\r
+  {\r
+    //cerr << "match_remainder called at " << *matchi << " with wildcard " << *wildi << endl;\r
+    while (wildi != wild.end() && matchi != match.end())\r
+    {\r
+      //cerr << "trying to match " << *matchi << " with wildcard " << *wildi << endl;\r
+      switch(*wildi)\r
+      {\r
+      case '*':\r
+      {\r
+        ++wildi;\r
+        ++matchi;\r
+        for (std::string::const_iterator i = matchi; i != match.end(); ++i)\r
+        {\r
+          // deal with * at the end of the wildcard - there is no remainder then\r
+          if (wildi == wild.end())\r
+          {\r
+            if (i == match.end()-1)\r
+              return true;\r
+          }\r
+          else if (match_remainder(wild, wildi, match, i))\r
+          {\r
+            return true;\r
+          }\r
+        }\r
+        return false;\r
+      }\r
+      case '[':\r
+      {\r
+        // scan for the end of the set using a similar method for avoiding escaped characters\r
+        bool found = false;\r
+        std::string::const_iterator end = wildi + 1;\r
+        for (; !found && end != wild.end(); ++end)\r
+        {\r
+          switch(*end)\r
+          {\r
+          case ']':\r
+          {\r
+            // found the set, now match with its contents excluding the brackets\r
+            if (!match_set(wild.substr(wildi - wild.begin() + 1, end - wildi - 1), *matchi))\r
+              return false;\r
+            found = true;\r
+            break;\r
+          }\r
+          case '\\':\r
+            if (end == wild.end()-1)\r
+              return false;\r
+            ++end;\r
+            break;\r
+          default:\r
+            break;\r
+          }\r
+        }\r
+        if (!found)\r
+          return false;\r
+        ++matchi;\r
+        wildi = end;\r
+        break;\r
+      }\r
+      case '?':\r
+        ++wildi;\r
+        ++matchi;\r
+        break;\r
+      case '\\':\r
+        if (wildi == wild.end()-1)\r
+          return false;\r
+        ++wildi;\r
+        if (*wildi != *matchi)\r
+          return false;\r
+        ++wildi;\r
+        ++matchi;\r
+        break;\r
+      default:\r
+        if (*wildi != *matchi)\r
+          return false;\r
+        ++wildi;\r
+        ++matchi;\r
+        break;\r
+      }\r
+    }\r
+    bool result = wildi == wild.end() && matchi == match.end();\r
+    return result;\r
+  }\r
+\r
+  // like all recursions the exported function has a simpler interface than the\r
+  // recursive function and is just a 'seed' to the recursion itself\r
+\r
+  bool match_wildcard(const std::string& wild, const std::string& match)\r
+  {\r
+    return match_remainder(wild, wild.begin(), match, match.begin());\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::vector<std::string> split(const std::string& str, const std::string& splitter)\r
+  {\r
+    std::vector<std::string> result;\r
+    if (!str.empty())\r
+    {\r
+      for(std::string::size_type offset = 0;;)\r
+      {\r
+        std::string::size_type found = str.find(splitter, offset);\r
+        if (found != std::string::npos)\r
+        {\r
+          result.push_back(str.substr(offset, found-offset));\r
+          offset = found + splitter.size();\r
+        }\r
+        else\r
+        {\r
+          result.push_back(str.substr(offset, str.size()-offset));\r
+          break;\r
+        }\r
+      }\r
+    }\r
+    return result;\r
+  }\r
+\r
+  std::string join (const std::vector<std::string>& str,\r
+                    const std::string& joiner,\r
+                    const std::string& prefix,\r
+                    const std::string& suffix)\r
+  {\r
+    std::string result = prefix;\r
+    for (unsigned i = 0; i < str.size(); i++)\r
+    {\r
+      if (i) result += joiner;\r
+      result += str[i];\r
+    }\r
+    result += suffix;\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string display_bytes(long bytes)\r
+  {\r
+    std::string result;\r
+    if (bytes < 0)\r
+    {\r
+      result += '-';\r
+      bytes = -bytes;\r
+    }\r
+    static const long kB = 1024l;\r
+    static const long MB = kB * kB;\r
+    static const long GB = MB * kB;\r
+    if (bytes < kB)\r
+      result += local_dformat("%i", bytes);\r
+    else if (bytes < (10l * kB))\r
+      result += local_dformat("%.2fk", ((float)bytes / (float)kB));\r
+    else if (bytes < (100l * kB))\r
+      result += local_dformat("%.1fk", ((float)bytes / (float)kB));\r
+    else if (bytes < MB)\r
+      result += local_dformat("%.0fk", ((float)bytes / (float)kB));\r
+    else if (bytes < (10l * MB))\r
+      result += local_dformat("%.2fM", ((float)bytes / (float)MB));\r
+    else if (bytes < (100l * MB))\r
+      result += local_dformat("%.1fM", ((float)bytes / (float)MB));\r
+    else if (bytes < GB)\r
+      result += local_dformat("%.0fM", ((float)bytes / (float)MB));\r
+    else\r
+      result += local_dformat("%.2fG", ((float)bytes / (float)GB));\r
+    return result;\r
+  }\r
+\r
+  std::string display_time(time_t seconds)\r
+  {\r
+    unsigned minutes = (unsigned)seconds / 60;\r
+    seconds %= 60;\r
+    unsigned hours = minutes / 60;\r
+    minutes %= 60;\r
+    unsigned days = hours / 24;\r
+    hours %= 24;\r
+    unsigned weeks = days / 7;\r
+    days %= 7;\r
+    std::string result;\r
+    if (weeks > 0)\r
+    {\r
+      result += unsigned_to_string(weeks, 10, radix_none, 1);\r
+      result += "w ";\r
+    }\r
+    if (!result.empty() || days > 0)\r
+    {\r
+      result += unsigned_to_string(days, 10, radix_none, 1);\r
+      result += "d ";\r
+    }\r
+    if (!result.empty() || hours > 0)\r
+    {\r
+      result += unsigned_to_string(hours, 10, radix_none, 1);\r
+      result += ":";\r
+    }\r
+    if (!result.empty() || minutes > 0)\r
+    {\r
+      if (!result.empty())\r
+        result += unsigned_to_string(minutes, 10, radix_none, 2);\r
+      else\r
+        result += unsigned_to_string(minutes, 10, radix_none, 1);\r
+      result += ":";\r
+    }\r
+    if (!result.empty())\r
+      result += unsigned_to_string((unsigned)seconds, 10, radix_none, 2);\r
+    else\r
+    {\r
+      result += unsigned_to_string((unsigned)seconds, 10, radix_none, 1);\r
+      result += "s";\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_utilities.hpp b/src/stlplus/strings/string_utilities.hpp
new file mode 100644 (file)
index 0000000..d8d39c4
--- /dev/null
@@ -0,0 +1,120 @@
+#ifndef STLPLUS_STRING_UTILITIES\r
+#define STLPLUS_STRING_UTILITIES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Utilities for manipulating std::strings\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include "format_types.hpp"\r
+#include <vector>\r
+#include <string>\r
+#include <stdexcept>\r
+#include <time.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Padding function allows a string to be printed in a fixed-width field\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // The definitions for the alignment are declared in format_types.hpp\r
+  // Any other value will cause std::invalid_argument to be thrown\r
+\r
+  std::string pad(const std::string& str,\r
+                  alignment_t alignment,\r
+                  unsigned width,\r
+                  char padch = ' ')\r
+    throw(std::invalid_argument);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // whitespace trimming\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string trim_left(const std::string& val);\r
+  std::string trim_right(const std::string& val);\r
+  std::string trim(const std::string& val);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // case conversion for std::strings\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::string lowercase(const std::string& val);\r
+  std::string uppercase(const std::string& val);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // character translation - inspired by Unix 'tr' command\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // convert characters represented in from_set to the characters in the same position in to_set\r
+  // for example:\r
+  //   filename = translate(filename,"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ");\r
+  // converts the filename to uppercase and returns the result (Note that the\r
+  // uppercase function does this more easily). If the from_set is longer than\r
+  // the to_set, then the overlap represents characters to delete (i.e. they map\r
+  // to nothing)\r
+\r
+  std::string translate(const std::string& input,\r
+                        const std::string& from_set,\r
+                        const std::string& to_set = std::string());\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // wildcard matching\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // this function does wildcard matching of the wildcard expression against the candidate std::string\r
+  // wildcards are NOT regular expressions\r
+  // the wildcard characters are * and ? where * matches 1 or more characters and ? matches only one\r
+  // there are also character sets [a-z] [qwertyuiop] etc. which match 1 character\r
+  // TODO: character sets like [:alpha:]\r
+  // TODO eventually: regular expression matching and substitution (3rd party library?)\r
+\r
+  bool match_wildcard(const std::string& wild,\r
+                      const std::string& match);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Perl-inspired split/join functions\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // splits the string at every occurance of splitter and adds it as a separate string to the return value\r
+  // the splitter is removed\r
+  // a string with no splitter in it will give a single-value vector\r
+  // an empty string gives an empty vector\r
+\r
+  std::vector<std::string> split (const std::string& str,\r
+                                  const std::string& splitter = "\n");\r
+\r
+  // the reverse of the above\r
+  // joins the string vector to create a single string with the joiner inserted between the joins\r
+  // Note: the joiner will not be added at the beginning or the end\r
+  // However, there are optional fields to add such prefix and suffix strings\r
+\r
+  std::string join (const std::vector<std::string>&,\r
+                    const std::string& joiner = "\n",\r
+                    const std::string& prefix = "",\r
+                    const std::string& suffix = "");\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // special displays\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // display the parameter as a number in bytes, kbytes, Mbytes, Gbytes depending on range\r
+\r
+  std::string display_bytes(long bytes);\r
+\r
+  // display the parameter in seconds as a string representation in weeks, days, hours, minutes, seconds\r
+  // e.g. "1d 1:01:01" means 1 day, 1 hour, 1 minute and 1 second\r
+\r
+  std::string display_time(time_t seconds);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/strings/string_vector.cpp b/src/stlplus/strings/string_vector.cpp
new file mode 100644 (file)
index 0000000..bee9b88
--- /dev/null
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_vector.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // special case of vector<bool>\r
+\r
+  std::string bool_vector_to_string(const std::vector<bool>& values)\r
+  {\r
+    std::string result;\r
+    for (size_t i = 0; i < values.size(); i++)\r
+      result.append(1, values[i] ? '1' : '0');\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/string_vector.hpp b/src/stlplus/strings/string_vector.hpp
new file mode 100644 (file)
index 0000000..6842a98
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef STLPLUS_STRING_VECTOR\r
+#define STLPLUS_STRING_VECTOR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generate a string representation of a vector\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "strings_fixes.hpp"\r
+#include <string>\r
+#include <vector>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // vector\r
+\r
+  template<typename T, typename S>\r
+  std::string vector_to_string(const std::vector<T>& values,\r
+                               S to_string_fn,\r
+                               const std::string& separator = ",");\r
+\r
+  // specialisation for vector<bool> which has a different implementation\r
+  std::string bool_vector_to_string(const std::vector<bool>& values);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "string_vector.tpp"\r
+#endif\r
diff --git a/src/stlplus/strings/string_vector.tpp b/src/stlplus/strings/string_vector.tpp
new file mode 100644 (file)
index 0000000..de535ee
--- /dev/null
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "string_sequence.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+  template<typename T, typename S>\r
+  std::string vector_to_string(const std::vector<T>& values,\r
+                               S to_string_fn,\r
+                               const std::string& separator)\r
+  {\r
+    return sequence_to_string(values.begin(), values.end(), to_string_fn, separator);\r
+  }\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/strings/strings.hpp b/src/stlplus/strings/strings.hpp
new file mode 100644 (file)
index 0000000..06a6e92
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef STLPLUS_STRINGS\r
+#define STLPLUS_STRINGS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Header for including the whole strings library in one go\r
+\r
+//   The strings library prints C and C++ types into a std::string\r
+\r
+//   - it extends the numeric presentation of integers to include any radix from 2 - 36\r
+//   - it allows the printing of data structures - useful for diagnostic dumps\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "string_utilities.hpp"\r
+\r
+#include "string_basic.hpp"\r
+#include "string_stl.hpp"\r
+#include "string_stlplus.hpp"\r
+\r
+#include "print_basic.hpp"\r
+#include "print_stl.hpp"\r
+#include "print_stlplus.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/strings/strings_fixes.hpp b/src/stlplus/strings/strings_fixes.hpp
new file mode 100644 (file)
index 0000000..2ed1dc5
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef STLPLUS_STRINGS_FIXES\r
+#define STLPLUS_STRINGS_FIXES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Contains work arounds for OS or Compiler specific problems\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problem with MicroSoft defining two different macros to identify Windows\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#if defined(_WIN32) || defined(_WIN32_WCE)\r
+#define MSWINDOWS\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Unnecessary compiler warnings\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef _MSC_VER\r
+// Microsoft Visual Studio\r
+// shut up the following irritating warnings\r
+//   4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)\r
+//   4305 - VC6, identifier type was converted to a smaller type\r
+//   4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)\r
+//   4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it\r
+//   4290 - VC6, C++ exception specification ignored\r
+//   4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)\r
+//   4675 - VC7.1, "change" in function overload resolution _might_ have altered program\r
+//   4996 - VC8, 'xxxx' was declared deprecated\r
+#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996)\r
+#endif\r
+\r
+#ifdef __BORLANDC__\r
+// Borland\r
+// Shut up the following irritating warnings\r
+//   8026 - Functions with exception specifications are not expanded inline\r
+//   8027 - Functions with xxx are not expanded inline\r
+//   8066 - Unreachable code.\r
+//          A break, continue, goto, or return statement was not followed by a\r
+//          label or the end of a loop or function. The compiler checks while,\r
+//          do, and for loops with a constant test condition, and attempts to\r
+//          recognize loops that can't fall through.\r
+#pragma warn -8026\r
+#pragma warn -8027\r
+#pragma warn -8066\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/subsystems/cli_parser.cpp b/src/stlplus/subsystems/cli_parser.cpp
new file mode 100644 (file)
index 0000000..1088f38
--- /dev/null
@@ -0,0 +1,637 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "cli_parser.hpp"\r
+#include "file_system.hpp"\r
+#include "dprintf.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus \r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // cli_definition internals\r
+\r
+  const std::string& stlplus::cli_definition::name(void) const\r
+  {\r
+    return m_name;\r
+  }\r
+\r
+  stlplus::cli_kind_t stlplus::cli_definition::kind(void) const\r
+  { \r
+    return m_kind;\r
+  }\r
+\r
+  stlplus::cli_mode_t stlplus::cli_definition::mode(void) const\r
+  {\r
+    return m_mode;\r
+  }\r
+\r
+  const std::string& stlplus::cli_definition::message(void) const\r
+  {\r
+    return m_message;\r
+  }\r
+\r
+  const std::string& stlplus::cli_definition::default_value(void) const\r
+  {\r
+    return m_default;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // internal data structures\r
+\r
+  class cli_value\r
+  {\r
+  public:\r
+    unsigned m_definition;\r
+    std::string m_value;\r
+    unsigned m_level;\r
+    std::string m_source;\r
+\r
+    cli_value(unsigned definition, const std::string& value, unsigned level, const std::string& source) :\r
+      m_definition(definition), m_value(value), m_level(level), m_source(source) \r
+      {\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class cli_parser_data\r
+  {\r
+  public:\r
+    message_handler& m_messages;\r
+    std::string m_executable;\r
+    cli_parser::definitions m_definitions;\r
+    std::vector<cli_value> m_values;\r
+    unsigned m_level;\r
+    bool m_valid;\r
+    std::vector<std::string> m_ini_files;\r
+\r
+  public:\r
+\r
+    cli_parser_data(message_handler& messages) : \r
+      m_messages(messages), m_level(1), m_valid(true)\r
+      {\r
+        // ensure that the CLI's messages are in the message handler - these\r
+        // can be overridden from a file later - see message_handler\r
+        if (!m_messages.message_present("CLI_VALUE_MISSING"))\r
+          m_messages.add_message("CLI_VALUE_MISSING", "option @0 requires a value - end of line was reached instead");\r
+        if (!m_messages.message_present("CLI_UNRECOGNISED_OPTION"))\r
+          m_messages.add_message("CLI_UNRECOGNISED_OPTION", "@0 is not a recognised option for this command");\r
+        if (!m_messages.message_present("CLI_NO_VALUES"))\r
+          m_messages.add_message("CLI_NO_VALUES", "argument @0 is invalid - this program doesn't take command-line arguments");\r
+        if (!m_messages.message_present("CLI_USAGE_PROGRAM"))\r
+          m_messages.add_message("CLI_USAGE_PROGRAM", "usage:\n\t@0 { arguments }");\r
+        if (!m_messages.message_present("CLI_USAGE_DEFINITIONS"))\r
+          m_messages.add_message("CLI_USAGE_DEFINITIONS", "arguments:");\r
+        if (!m_messages.message_present("CLI_USAGE_VALUES"))\r
+          m_messages.add_message("CLI_USAGE_VALUES", "values:");\r
+        if (!m_messages.message_present("CLI_USAGE_VALUE_RESULT"))\r
+          m_messages.add_message("CLI_USAGE_VALUE_RESULT", "\t@0 : from @1");\r
+        if (!m_messages.message_present("CLI_USAGE_SWITCH_RESULT"))\r
+          m_messages.add_message("CLI_USAGE_SWITCH_RESULT", "\t-@0 : from @1");\r
+        if (!m_messages.message_present("CLI_USAGE_OPTION_RESULT"))\r
+          m_messages.add_message("CLI_USAGE_OPTION_RESULT", "\t-@0 @1 : from @2");\r
+        if (!m_messages.message_present("CLI_INI_HEADER"))\r
+          m_messages.add_message("CLI_INI_HEADER", "configuration files:");\r
+        if (!m_messages.message_present("CLI_INI_FILE_PRESENT"))\r
+          m_messages.add_message("CLI_INI_FILE_PRESENT", "\t@0");\r
+        if (!m_messages.message_present("CLI_INI_FILE_ABSENT"))\r
+          m_messages.add_message("CLI_INI_FILE_ABSENT", "\t@0 (not found)");\r
+        if (!m_messages.message_present("CLI_INI_VARIABLE"))\r
+          m_messages.add_message("CLI_INI_VARIABLE", "unknown variable \"@0\" found in Ini file");\r
+      }\r
+\r
+    unsigned add_definition(const cli_parser::definition& definition)\r
+      {\r
+        m_definitions.push_back(definition);\r
+        return m_definitions.size()-1;\r
+      }\r
+\r
+    std::string name(unsigned i) const throw(cli_index_error)\r
+      {\r
+        if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+        return m_definitions[m_values[i].m_definition].name();\r
+      }\r
+\r
+    unsigned id(unsigned i) const throw(cli_index_error)\r
+      {\r
+        if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+        return m_values[i].m_definition;\r
+      }\r
+\r
+    cli_parser::kind_t kind(unsigned i) const throw(cli_index_error)\r
+      {\r
+        if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+        return m_definitions[m_values[i].m_definition].kind();\r
+      }\r
+\r
+    cli_parser::mode_t mode(unsigned i) const throw(cli_index_error)\r
+      {\r
+        if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+        return m_definitions[m_values[i].m_definition].mode();\r
+      }\r
+\r
+//     unsigned add_definition(const std::string& name,\r
+//                             cli_parser::kind_t kind,\r
+//                             cli_parser::mode_t mode,\r
+//                             const std::string& message)\r
+//       {\r
+//         return add_definition(cli_parser::definition(name, kind, mode, message));\r
+//       }\r
+\r
+    unsigned find_definition(const std::string& name)\r
+      {\r
+        // this does substring matching on the definitions and returns the first\r
+        // match - however, it requires at least one character in the substring so\r
+        // that the "" convention for command line arguments doesn't match with\r
+        // anything. It returns size() if it fails\r
+        for (unsigned i = 0; i < m_definitions.size(); i++)\r
+        {\r
+          std::string candidate = m_definitions[i].name();\r
+          if ((candidate.empty() && name.empty()) ||\r
+              (!name.empty() && candidate.size() >= name.size() && candidate.substr(0,name.size()) == name))\r
+            return i;\r
+        }\r
+        return m_definitions.size();\r
+      }\r
+\r
+    void clear_definitions(void)\r
+      {\r
+        m_definitions.clear();\r
+        m_values.clear();\r
+        reset_level();\r
+        set_valid();\r
+      }\r
+\r
+    void reset_level(void)\r
+      {\r
+        // the starting level is 1 so that later functions can call clear_level with\r
+        // a value of m_level-1 without causing underflow\r
+        m_level = 1;\r
+      }\r
+\r
+    void increase_level(void)\r
+      {\r
+        m_level++;\r
+      }\r
+\r
+    void clear_level(unsigned definition, unsigned level)\r
+      {\r
+        // clears all values with this definition at the specified level or below\r
+        for (std::vector<cli_value>::iterator i = m_values.begin(); i != m_values.end(); )\r
+        {\r
+          if (i->m_definition == definition && i->m_level <= level)\r
+            i = m_values.erase(i);\r
+          else\r
+            i++;\r
+        }\r
+      }\r
+\r
+    void set_valid(void)\r
+      {\r
+        m_valid = true;\r
+      }\r
+\r
+    void set_invalid(void)\r
+      {\r
+        m_valid = false;\r
+      }\r
+\r
+    bool valid(void) const\r
+      {\r
+        return m_valid;\r
+      }\r
+\r
+    unsigned add_value(unsigned definition, const std::string& value, const std::string& source)\r
+      {\r
+        // behaviour depends on mode:\r
+        //  - single: erase all previous values\r
+        //  - multiple: erase values at a lower level than current\r
+        //  - cumulative: erase no previous values\r
+        switch (m_definitions[definition].mode())\r
+        {\r
+        case cli_single_mode:\r
+          clear_level(definition, m_level);\r
+          break;\r
+        case cli_multiple_mode:\r
+          clear_level(definition, m_level-1);\r
+          break;\r
+        case cli_cumulative_mode:\r
+          break;\r
+        }\r
+        m_values.push_back(cli_value(definition,value,m_level,source));\r
+        return m_values.size()-1;\r
+      }\r
+\r
+    unsigned add_switch(unsigned definition, bool value, const std::string& source)\r
+      {\r
+        return add_value(definition, value ? "on" : "off", source);\r
+      }\r
+\r
+    void erase_value(unsigned definition)\r
+      {\r
+        // this simply erases all previous values\r
+        clear_level(definition, m_level);\r
+      }\r
+\r
+    void add_ini_file(const std::string& file)\r
+      {\r
+        m_ini_files.push_back(file);\r
+      }\r
+\r
+    unsigned ini_file_size(void) const\r
+      {\r
+        return m_ini_files.size();\r
+      }\r
+\r
+    const std::string& ini_file(unsigned i) const\r
+      {\r
+        return m_ini_files[i];\r
+      }\r
+\r
+    unsigned add_checked_definition(const cli_parser::definition& definition) throw(cli_mode_error)\r
+      {\r
+        // check for stupid combinations\r
+        // at this stage the only really stupid one is to declare command line arguments to be switch mode\r
+        if (definition.name().empty() && definition.kind() == cli_switch_kind) \r
+        {\r
+          set_invalid();\r
+          throw cli_mode_error("CLI arguments cannot be switch kind");\r
+        }\r
+        // add the definition to the set of all definitions\r
+        unsigned i = add_definition(definition);\r
+        // also add it to the list of values, but only if it has a default value\r
+        if (!definition.default_value().empty())\r
+          add_value(i, definition.default_value(), "builtin default");\r
+        return i;\r
+      }\r
+\r
+    bool switch_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+      {\r
+        if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+        if (kind(i) != cli_switch_kind) throw cli_mode_error(name(i) + " is not a switch kind");\r
+        std::string value = m_values[i].m_value;\r
+        return value == "on" || value == "true" || value == "1";\r
+      }\r
+\r
+    std::string string_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+      {\r
+        if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+        if (kind(i) != cli_value_kind) throw cli_mode_error(name(i) + " is not a value kind");\r
+        return m_values[i].m_value;\r
+      }\r
+\r
+    void set_defaults(const ini_manager& defaults, const std::string& ini_section) throw()\r
+      {\r
+        // import default values from the Ini Manager\r
+        increase_level();\r
+        // get the set of all names from the Ini manager so that illegal names generate meaningful error messages\r
+        std::vector<std::string> names = defaults.variable_names(ini_section);\r
+        for (unsigned i = 0; i < names.size(); i++)\r
+        {\r
+          std::string name = names[i];\r
+          unsigned definition = find_definition(name);\r
+          if (definition == (unsigned)-1)\r
+          {\r
+            // not found - give an error report\r
+            message_position position(defaults.variable_filename(ini_section,name),\r
+                                      defaults.variable_linenumber(ini_section,name),\r
+                                      0);\r
+            m_messages.error(position,"CLI_INI_VARIABLE", name);\r
+          }\r
+          else\r
+          {\r
+            // found - so add the value\r
+            // multi-valued variables are entered as a comma-separated list and this is then turned into a vector\r
+            // the vector is empty if the value was empty\r
+            std::vector<std::string> values = defaults.variable_values(ini_section, name);\r
+            // an empty string is used to negate the value\r
+            if (values.empty())\r
+              erase_value(definition);\r
+            else\r
+            {\r
+              std::string source = filespec_to_relative_path(defaults.variable_filename(ini_section, name));\r
+              for (unsigned j = 0; j < values.size(); j++)\r
+                add_value(definition, values[j], source);\r
+            }\r
+          }\r
+        }\r
+        // add the set of ini files to the list for usage reports\r
+        for (unsigned j = 0; j < defaults.size(); j++)\r
+          add_ini_file(defaults.filename(j));\r
+      }\r
+\r
+    bool parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error)\r
+      {\r
+        bool result = true;\r
+        if (!argv) throw cli_argument_error("Argument vector cannot be null");\r
+        increase_level();\r
+        if (argv[0])\r
+          m_executable = argv[0];\r
+        for (unsigned i = 1; argv[i]; i++)\r
+        {\r
+          std::string name = argv[i];\r
+          if (!name.empty() && name[0] == '-')\r
+          {\r
+            // we have a command line option\r
+            unsigned found = find_definition(name.substr(1, name.size()-1));\r
+            if (found < m_definitions.size())\r
+            {\r
+              // found it in its positive form\r
+              switch (m_definitions[found].kind())\r
+              {\r
+              case cli_switch_kind:\r
+                add_switch(found, true, "command line");\r
+                break;\r
+              case cli_value_kind:\r
+                // get the next argument in argv as the value of this option\r
+                // first check that there is a next value\r
+                if (!argv[i+1])\r
+                  result &= m_messages.error("CLI_VALUE_MISSING", name);\r
+                else\r
+                  add_value(found, argv[++i], "command line");\r
+                break;\r
+              }\r
+            }\r
+            else if (name.size() > 3 && name.substr(1,2) == "no")\r
+            {\r
+              found = find_definition(name.substr(3, name.size()-3));\r
+              if (found < m_definitions.size())\r
+              {\r
+                // found it in its negated form\r
+                switch (m_definitions[found].kind())\r
+                {\r
+                case cli_switch_kind:\r
+                  add_switch(found, false, "command line");\r
+                  break;\r
+                case cli_value_kind:\r
+                  erase_value(found);\r
+                  break;\r
+                }\r
+              }\r
+              else\r
+              {\r
+                // could not find this option in either its true or negated form\r
+                result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name);\r
+              }\r
+            }\r
+            else\r
+            {\r
+              // could not find this option and it could not be negated\r
+              result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name);\r
+            }\r
+          }\r
+          else\r
+          {\r
+            // we have a command-line value which is represented internally as an option with an empty string as its name\r
+            // some very obscure commands do not have values - only options, so allow for that case too\r
+            unsigned found = find_definition("");\r
+            if (found < m_definitions.size())\r
+              add_value(found, name, "command line");\r
+            else\r
+              result &= m_messages.error("CLI_NO_VALUES", name);\r
+          }\r
+        }\r
+        if (!result) set_invalid();\r
+        return result;\r
+      }\r
+\r
+    void usage(void) const throw(std::runtime_error)\r
+      {\r
+        m_messages.information("CLI_USAGE_PROGRAM", m_executable);\r
+        m_messages.information("CLI_USAGE_DEFINITIONS");\r
+        for (unsigned d = 0; d < m_definitions.size(); d++)\r
+          m_messages.information(m_definitions[d].message());\r
+        if (m_values.size() != 0)\r
+        {\r
+          m_messages.information("CLI_USAGE_VALUES");\r
+          for (unsigned v = 0; v < m_values.size(); v++)\r
+          {\r
+            std::string source = m_values[v].m_source;\r
+            std::string key = name(v);\r
+            if (key.empty())\r
+            {\r
+              // command-line values\r
+              m_messages.information("CLI_USAGE_VALUE_RESULT", string_value(v), source);\r
+            }\r
+            else if (kind(v) == cli_switch_kind)\r
+            {\r
+              // a switch\r
+              m_messages.information("CLI_USAGE_SWITCH_RESULT", (switch_value(v) ? name(v) : "no" + name(v)), source);\r
+            }\r
+            else\r
+            {\r
+              // other values\r
+              std::vector<std::string> args;\r
+              args.push_back(name(v));\r
+              args.push_back(string_value(v));\r
+              args.push_back(source);\r
+              m_messages.information("CLI_USAGE_OPTION_RESULT", args);\r
+            }\r
+          }\r
+        }\r
+        if (ini_file_size() > 0)\r
+        {\r
+          m_messages.information("CLI_INI_HEADER");\r
+          for (unsigned i = 0; i < ini_file_size(); i++)\r
+          {\r
+            if (file_exists(ini_file(i)))\r
+              m_messages.information("CLI_INI_FILE_PRESENT", filespec_to_relative_path(ini_file(i)));\r
+            else\r
+              m_messages.information("CLI_INI_FILE_ABSENT", filespec_to_relative_path(ini_file(i)));\r
+          }\r
+        }\r
+      }\r
+\r
+  private:\r
+    // make this class uncopyable\r
+    cli_parser_data(const cli_parser_data&);\r
+    cli_parser_data& operator = (const cli_parser_data&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  cli_parser::cli_parser(message_handler& messages) throw() : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+  }\r
+\r
+  cli_parser::cli_parser(cli_parser::definitions_t definitions, message_handler& messages) throw(cli_mode_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+  }\r
+\r
+  cli_parser::cli_parser(cli_parser::definitions_t definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages)  throw(cli_mode_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+    set_defaults(defaults, ini_section);\r
+  }\r
+\r
+  cli_parser::cli_parser(char* argv[], cli_parser::definitions_t definitions, message_handler& messages)  throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+    parse(argv);\r
+  }\r
+\r
+  cli_parser::cli_parser(char* argv[], cli_parser::definitions_t definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages)  throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+    set_defaults(defaults, ini_section);\r
+    parse(argv);\r
+  }\r
+\r
+  cli_parser::cli_parser(cli_parser::definitions definitions, message_handler& messages) throw(cli_mode_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+  }\r
+\r
+  cli_parser::cli_parser(cli_parser::definitions definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages)  throw(cli_mode_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+    set_defaults(defaults, ini_section);\r
+  }\r
+\r
+  cli_parser::cli_parser(char* argv[], cli_parser::definitions definitions, message_handler& messages)  throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+    parse(argv);\r
+  }\r
+\r
+  cli_parser::cli_parser(char* argv[], cli_parser::definitions definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages)  throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+    m_data(new cli_parser_data(messages))\r
+  {\r
+    add_definitions(definitions);\r
+    set_defaults(defaults, ini_section);\r
+    parse(argv);\r
+  }\r
+\r
+  cli_parser::~cli_parser(void) throw()\r
+  {\r
+  }\r
+\r
+  void cli_parser::add_definitions(cli_parser::definitions_t definitions) throw(cli_mode_error)\r
+  {\r
+    m_data->clear_definitions();\r
+    // the definitions array is terminated by a definition with a null name pointer\r
+    for (unsigned i = 0; definitions[i].m_name; i++)\r
+      add_definition(definitions[i]);\r
+  }\r
+\r
+  unsigned cli_parser::add_definition(const cli_parser::definition_t& definition) throw(cli_mode_error,cli_argument_error)\r
+  {\r
+    std::string name = definition.m_name ? definition.m_name : "";\r
+    std::string message = definition.m_message ? definition.m_message : "";\r
+    std::string value = definition.m_default ? definition.m_default : "";\r
+    return add_definition(cli_parser::definition(name, definition.m_kind, definition.m_mode, message, value));\r
+  }\r
+\r
+  void cli_parser::add_definitions(cli_parser::definitions definitions) throw(cli_mode_error)\r
+  {\r
+    m_data->clear_definitions();\r
+    for (unsigned i = 0; i < definitions.size(); i++)\r
+      add_definition(definitions[i]);\r
+  }\r
+\r
+  unsigned cli_parser::add_definition(const cli_parser::definition& definition) throw(cli_mode_error)\r
+  {\r
+    return m_data->add_checked_definition(definition);\r
+  }\r
+\r
+  void cli_parser::set_defaults(const ini_manager& defaults, const std::string& ini_section) throw()\r
+  {\r
+    m_data->set_defaults(defaults, ini_section);\r
+  }\r
+\r
+  bool cli_parser::parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return m_data->parse(argv);\r
+  }\r
+\r
+  bool cli_parser::valid(void) throw()\r
+  {\r
+    return m_data->valid();\r
+  }\r
+\r
+  unsigned cli_parser::size(void) const throw()\r
+  {\r
+    return m_data->m_values.size();\r
+  }\r
+\r
+  std::string cli_parser::name(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return m_data->name(i);\r
+  }\r
+\r
+  unsigned cli_parser::id(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return m_data->id(i);\r
+  }\r
+\r
+  cli_parser::kind_t cli_parser::kind(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return m_data->kind(i);\r
+  }\r
+\r
+  bool cli_parser::switch_kind(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return kind(i) == cli_switch_kind;\r
+  }\r
+\r
+  bool cli_parser::value_kind(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return kind(i) == cli_value_kind;\r
+  }\r
+\r
+  cli_parser::mode_t cli_parser::mode(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return m_data->mode(i);\r
+  }\r
+\r
+  bool cli_parser::single_mode(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return mode(i) == cli_single_mode;\r
+  }\r
+\r
+  bool cli_parser::multiple_mode(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return mode(i) == cli_multiple_mode;\r
+  }\r
+\r
+  bool cli_parser::cumulative_mode(unsigned i) const throw(cli_index_error)\r
+  {\r
+    return mode(i) == cli_cumulative_mode;\r
+  }\r
+\r
+  bool cli_parser::switch_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+  {\r
+    return m_data->switch_value(i);\r
+  }\r
+\r
+  std::string cli_parser::string_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+  {\r
+    return m_data->string_value(i);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void cli_parser::usage(void) const throw(std::runtime_error)\r
+  {\r
+    m_data->usage();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/subsystems/cli_parser.hpp b/src/stlplus/subsystems/cli_parser.hpp
new file mode 100644 (file)
index 0000000..dc9740d
--- /dev/null
@@ -0,0 +1,309 @@
+#ifndef STLPLUS_CLI_PARSER\r
+#define STLPLUS_CLI_PARSER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A subsystem for managing command-line parsing, including using INI files to\r
+//   control the default options.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "subsystems_fixes.hpp"\r
+#include "message_handler.hpp"\r
+#include "ini_manager.hpp"\r
+#include "smart_ptr.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Internals\r
+\r
+  class cli_parser_data;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // declarations\r
+\r
+  // enum to define the basic behaviour of an argument\r
+  //   - a switch is an option with no value but which can be switched on or off e.g. -help and -nohelp\r
+  //   - a value is an option followed by a value e.g. -output results.txt\r
+  //       (a default value can be removed by using the option as a negated switch e.g. -nooutput)\r
+  //   - command-line values (i.e. any strings not preceded by '-') are treated\r
+  //     internally as an option with no name and must be values\r
+  enum cli_kind_t {cli_switch_kind, cli_value_kind};\r
+\r
+  // the mode controls the behaviour if an option appears more than once in either the command-line or the ini files\r
+  //   - a single mode option overrides all previous values so will only be found once in the parsed result\r
+  //   - a multiple mode option can be repeated to define multiple values, but overrides values from ini files\r
+  //   - a cumulative mode option is a multiple mode option which keeps ini file values as well\r
+  enum cli_mode_t {cli_single_mode, cli_multiple_mode, cli_cumulative_mode};\r
+\r
+  // There are two structures used for defining command-line parameters\r
+  //  (1) a C struct which is used in a C array - this is used for declaring\r
+  //      command-line parameters in a static declaration\r
+  //  (2) a C++ class which is used in an STL vector - this is used for building\r
+  //      command-line parameters within code\r
+\r
+  // The C struct for definitions\r
+  struct cli_definition_t\r
+  {\r
+    // the name of the option, e.g. "help"\r
+    const char* m_name;\r
+\r
+    // the kind of the option, e.g. cli_switch_kind\r
+    cli_kind_t m_kind;\r
+\r
+    // the mode e.g. cli_single_mode\r
+    cli_mode_t m_mode;\r
+\r
+    // the mnemonic for the message giving usage information for this option\r
+    const char* m_message;\r
+\r
+    // built-in default value - null if not present\r
+    const char* m_default;\r
+  };\r
+\r
+  // The C array of the C struct. The array must be terminated by END_CLI_DEFINITIONS.\r
+  typedef cli_definition_t cli_definitions_t [];\r
+#define END_CLI_DEFINITIONS {0,stlplus::cli_switch_kind,stlplus::cli_single_mode,"",0}\r
+\r
+  // The C++ class for definitions\r
+  class cli_definition\r
+  {\r
+  public:\r
+    // constructor that allows a definition to be created in one line\r
+    cli_definition(const std::string& name, cli_kind_t kind, cli_mode_t mode, \r
+                   const std::string& message, const std::string& default_value = std::string()) : \r
+      m_name(name), m_kind(kind), m_mode(mode), m_message(message), m_default(default_value) {}\r
+\r
+    // the name of the option, e.g. "help"\r
+    const std::string& name(void) const;\r
+\r
+    // the kind of the option, e.g. switch_kind\r
+    cli_kind_t kind(void) const;\r
+\r
+    // the mode e.g. single_mode\r
+    cli_mode_t mode(void) const;\r
+\r
+    // the mnemonic for the message giving usage\r
+    const std::string& message(void) const;\r
+\r
+    // built-in default value - empty string if not present\r
+    const std::string& default_value(void) const;\r
+\r
+  private:\r
+    std::string m_name;\r
+    cli_kind_t m_kind;\r
+    cli_mode_t m_mode;\r
+    std::string m_message;\r
+    std::string m_default;\r
+  };\r
+\r
+  // The C++ vector of the C++ class\r
+  typedef std::vector<cli_definition> cli_definitions;\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // exceptions that can be thrown by the CLI parser\r
+  // they are all derivatives of std::logic_error because all errors are predictable by code inspection\r
+  // a correct program will never throw an exception\r
+\r
+  // thrown if a command-line argument is accessed with the wrong mode - i.e. attempt to get the value of a switch\r
+  class cli_mode_error : public std::invalid_argument\r
+  {\r
+  public:\r
+    cli_mode_error(const std::string& arg) : std::invalid_argument(arg) {}\r
+    ~cli_mode_error(void) throw() {}\r
+  };\r
+\r
+  // similar to std::out_of_range thrown for using an index out of range\r
+  class cli_index_error : public std::out_of_range\r
+  {\r
+  public:\r
+    cli_index_error(const std::string& arg) : std::out_of_range(arg) {}\r
+    ~cli_index_error(void) throw() {}\r
+  };\r
+\r
+  // similar to std::invalid_argument - thrown for passing an illegal argument to a method\r
+  class cli_argument_error : public std::invalid_argument\r
+  {\r
+  public:\r
+    cli_argument_error(const std::string& arg) : std::invalid_argument(arg) {}\r
+    ~cli_argument_error(void) throw() {}\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class cli_parser\r
+  {\r
+  public:\r
+    // Type definitions map the global type names onto convenient scoped names\r
+\r
+    typedef cli_kind_t kind_t;\r
+    typedef cli_mode_t mode_t;\r
+    typedef cli_definition_t definition_t;\r
+    typedef cli_definitions_t definitions_t;\r
+    typedef cli_definition definition;\r
+    typedef cli_definitions definitions;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////////\r
+    // Methods\r
+\r
+    // various constructors\r
+\r
+    // you have a choice of either creating an uninitialised CLI parser and then\r
+    // calling separate functions to set it up or of calling one of the\r
+    // composite constructors. However, you must set up the error handler in the\r
+    // constructor.\r
+\r
+    // set up the parser with its error handler\r
+    // defer everything else\r
+    cli_parser(message_handler& errors)\r
+      throw();\r
+\r
+    // constructors using the C definitions_t structure\r
+\r
+    // set up the parser with the error handler and define all the command-line options\r
+    // defer default values and parameter parsing\r
+    cli_parser(cli_definitions_t, message_handler& errors)\r
+      throw(cli_mode_error);\r
+    // set up the parser with the error handler and define all the command-line\r
+    // options and their default from the ini files\r
+    // defer parameter parsing\r
+    cli_parser(cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)\r
+      throw(cli_mode_error);\r
+    // set up the parser with the error handler and define all the command-line\r
+    // options no ini files used for default values, so only built-in defaults\r
+    // supported then parse the command line\r
+    cli_parser(char* argv[], cli_definitions_t, message_handler& errors)\r
+      throw(cli_mode_error,message_handler_id_error,message_handler_format_error);\r
+    // set up the parser with the error handler and define all the command-line\r
+    // options and their default from the ini files then parse the command line\r
+    cli_parser(char* argv[], cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)\r
+      throw(cli_mode_error,message_handler_id_error,message_handler_format_error);\r
+\r
+    // constructors using the C++ definitions structure\r
+\r
+    // set up the parser with the error handler and define all the command-line\r
+    // options from a C array of structs\r
+    // defer default values and parameter parsing\r
+    cli_parser(cli_definitions, message_handler& errors)\r
+      throw(cli_mode_error);\r
+    // set up the parser with the error handler and define all the command-line\r
+    // options and their default from the ini files\r
+    // defer parameter parsing\r
+    cli_parser(cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)\r
+      throw(cli_mode_error);\r
+    // set up the parser with the error handler and define all the command-line\r
+    // options no ini files used for default values, so only built-in defaults\r
+    // supported then parse the command line\r
+    cli_parser(char* argv[], cli_definitions, message_handler& errors)\r
+      throw(cli_mode_error,message_handler_id_error,message_handler_format_error);\r
+    // set up the parser with the error handler and define all the command-line\r
+    // options and their default from the ini files then parse the command line\r
+    cli_parser(char* argv[], cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors)\r
+      throw(cli_mode_error,message_handler_id_error,message_handler_format_error);\r
+\r
+    ~cli_parser(void)\r
+      throw();\r
+\r
+    // the separate functions for initialising the parser in steps. These are\r
+    // declared in the order of use. Firts, add definitions of command-line\r
+    // arguments. Then optionally load default values from ini files, then\r
+    // finally parse the command line.\r
+\r
+    // add a set of C definitions. The definitions will be given ID codes from 0\r
+    // to the number of elements - 1 in the array\r
+    void add_definitions(cli_definitions_t)\r
+      throw(cli_mode_error);\r
+    // add a single C definition, returning the ID code for it\r
+    unsigned add_definition(const definition_t&)\r
+      throw(cli_mode_error,cli_argument_error);\r
+    // add a set of C++ definitions. The definitions will be given ID codes from\r
+    // 0 to the number of elements - 1 in the array\r
+    void add_definitions(cli_definitions)\r
+      throw(cli_mode_error);\r
+    // add a single C++ definition, returning the ID code for it\r
+    unsigned add_definition(const definition&)\r
+      throw(cli_mode_error);\r
+\r
+    // All definitions have an optional built-in default value which is stored\r
+    // in the definition types above. However, these can optionally be\r
+    // overridden by a value from an ini file. If you want this functionality,\r
+    // call this function. If you don't want ini file handling, simply don't\r
+    // call it. The values will be searched for only in the named section of the\r
+    // ini file (sections are labelled by e.g. [vassemble]), so in this case you\r
+    // would specify the section name as "vassemble" (exclude the brackets).\r
+    void set_defaults(const ini_manager& defaults, const std::string& ini_section)\r
+      throw();\r
+\r
+    // the final stage of initialisation is to read the command-line and extract\r
+    // the values from it. If parse errors are found, this will report the\r
+    // errors using the error handler and return false.\r
+    bool parse(char* argv[])\r
+      throw(cli_argument_error,message_handler_id_error,message_handler_format_error);\r
+\r
+    // test for whether the CLI parser is still valid (no errors have happened)\r
+    // after the initialisation phase\r
+    bool valid(void)\r
+      throw();\r
+\r
+    // iteration functions avoiding the use of iterators. Just loop through the\r
+    // arguments from 0 to size()-1 and use the index of the loop to interrogate\r
+    // the command-line for the value at that position.\r
+\r
+    // the number of values to read, indexed 0 to size()-1\r
+    unsigned size(void) const\r
+      throw();\r
+\r
+    // the argument name\r
+    std::string name(unsigned i) const\r
+      throw(cli_index_error);\r
+    // the argument ID, that is, the offset into the original definitions\r
+    unsigned id(unsigned i) const\r
+      throw(cli_index_error);\r
+\r
+    // the kind (switch or value) and short-cut tests for the different kinds\r
+    cli_kind_t kind(unsigned i) const\r
+      throw(cli_index_error);\r
+    bool switch_kind(unsigned i) const\r
+      throw(cli_index_error);\r
+    bool value_kind(unsigned i) const\r
+      throw(cli_index_error);\r
+\r
+    // the mode (single, multiple, cumulative) and short-cut tests for the\r
+    // different modes - you rarely need to know this since it mainly controls\r
+    // the parsing\r
+    cli_mode_t mode(unsigned i) const \r
+      throw(cli_index_error);\r
+    bool single_mode(unsigned i) const\r
+      throw(cli_index_error);\r
+    bool multiple_mode(unsigned i) const\r
+      throw(cli_index_error);\r
+    bool cumulative_mode(unsigned i) const\r
+      throw(cli_index_error);\r
+\r
+    // get the switch's value, but only if the value is of switch kind\r
+    bool switch_value(unsigned i) const\r
+      throw(cli_mode_error,cli_index_error);\r
+\r
+    // get the option's value, but only if it is of value kind\r
+    std::string string_value(unsigned i) const\r
+      throw(cli_mode_error,cli_index_error);\r
+\r
+    // print the usage report - typically in response to the -help switch being on\r
+    void usage(void) const\r
+      throw(std::runtime_error);\r
+\r
+  private:\r
+    friend class cli_parser_data;\r
+    smart_ptr_nocopy<cli_parser_data> m_data;\r
+  };\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/subsystems/ini_manager.cpp b/src/stlplus/subsystems/ini_manager.cpp
new file mode 100644 (file)
index 0000000..41f5b69
--- /dev/null
@@ -0,0 +1,1243 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "ini_manager.hpp"\r
+#include "file_system.hpp"\r
+#include <fstream>\r
+#include <list>\r
+#include <algorithm>\r
+#include <sys/stat.h>\r
+#include <ctype.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // local utilities\r
+\r
+  static std::string trim(const std::string& val)\r
+  {\r
+    std::string result = val;\r
+    while (!result.empty() && isspace(result[0]))\r
+      result.erase(result.begin());\r
+    while (!result.empty() && isspace(result[result.size()-1]))\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // internal data structure for storing a single entry (i.e. line) from an ini file\r
+  // lines are categorised as blanks, comments or variables\r
+  // TODO - do I need an error category?\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class ini_entry\r
+  {\r
+  public:\r
+    enum kind_t {BLANK, COMMENT, VARIABLE};\r
+    friend std::string to_string(kind_t kind)\r
+      {\r
+        switch(kind)\r
+        {\r
+        case BLANK: return "BLANK";\r
+        case COMMENT: return "COMMENT";\r
+        case VARIABLE: return "VARIABLE";\r
+        }\r
+        return "<*unknown kind*>";\r
+      }\r
+\r
+  private:\r
+    unsigned m_line;\r
+    kind_t m_kind;\r
+    std::string m_text;\r
+    std::string m_name;\r
+    std::string m_value;\r
+\r
+  public:\r
+    ini_entry(unsigned line) : m_line(line), m_kind(BLANK) {}\r
+    ini_entry(unsigned line, const std::string& comment) : m_line(line), m_kind(COMMENT), m_text("; " + comment) {}\r
+    ini_entry(unsigned line, const std::string& name, const std::string& value) : m_line(line), m_kind(VARIABLE), m_text(name + " = " + value), m_name(name), m_value(value) {}\r
+    ~ini_entry(void) {}\r
+\r
+    unsigned line(void) const {return m_line;}\r
+    kind_t kind(void) const {return m_kind;}\r
+    bool blank(void) const {return m_kind == BLANK;}\r
+    bool comment(void) const {return m_kind == COMMENT;}\r
+    bool variable(void) const {return m_kind == VARIABLE;}\r
+\r
+    const std::string& text(void) const {return m_text;}\r
+    const std::string& variable_name(void) const {return m_name;}\r
+    const std::string& variable_value(void) const {return m_value;}\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "    " << m_line << ":" << to_string(m_kind) << ": " << m_text << std::endl;\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // internal data structure representing an ini file section containing all the\r
+  // entries for that section from a single ini file\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class ini_section\r
+  {\r
+  private:\r
+    friend class ini_file;\r
+    std::string m_title;\r
+    std::list<ini_entry> m_entries;\r
+\r
+  public:\r
+    ini_section(const std::string& title) : \r
+      m_title(title)\r
+      {\r
+      }\r
+\r
+    ~ini_section(void)\r
+      {\r
+      }\r
+\r
+    const std::string& title(void) const\r
+      {\r
+        return m_title;\r
+      }\r
+\r
+    bool empty(void) const\r
+      {\r
+        // a section is empty if it contains no variables\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable())\r
+            return false;\r
+        }\r
+        return true;\r
+      }\r
+\r
+    void clear(void)\r
+      {\r
+        m_entries.clear();\r
+      }\r
+\r
+    bool variable_exists(const std::string variable) const\r
+      {\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+            return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    unsigned variables_size(void) const\r
+      {\r
+        unsigned result = 0;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+          if (i->variable())\r
+            result++;\r
+        return result;\r
+      }\r
+\r
+    std::string variable_name(unsigned offset) const\r
+      {\r
+        unsigned j = 0;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable())\r
+          {\r
+            if (j == offset)\r
+              return i->variable_name();\r
+            j++;\r
+          }\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    std::vector<std::string> variable_names(void) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+          if (i->variable())\r
+            result.push_back(i->variable_name());\r
+        return result;\r
+      }\r
+\r
+    std::string variable_value(unsigned offset) const\r
+      {\r
+        unsigned j = 0;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable())\r
+          {\r
+            if (j == offset)\r
+              return i->variable_value();\r
+            j++;\r
+          }\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    std::string variable_value(const std::string variable) const\r
+      {\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+            return i->variable_value();\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    unsigned variable_line(const std::string variable) const\r
+      {\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+            return i->line();\r
+        }\r
+        return 0;\r
+      }\r
+\r
+    bool add_variable(unsigned line, const std::string& variable, const std::string& value)\r
+      {\r
+        erase_variable(variable);\r
+        m_entries.push_back(ini_entry(line ? line : m_entries.size(), variable, value));\r
+        return true;\r
+      }\r
+\r
+    bool add_comment(unsigned line, const std::string& comment)\r
+      {\r
+        m_entries.push_back(ini_entry(line ? line : m_entries.size(), comment));\r
+        return true;\r
+      }\r
+\r
+    bool add_blank(unsigned line)\r
+      {\r
+        m_entries.push_back(ini_entry(line ? line : m_entries.size()));\r
+        return true;\r
+      }\r
+\r
+    bool erase_variable(const std::string& variable)\r
+      {\r
+        for (std::list<ini_entry>::iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+          {\r
+            m_entries.erase(i);\r
+            return true;\r
+          }\r
+        }\r
+        return false;\r
+      }\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "  [" << m_title << "]" << std::endl;\r
+        for (std::list<ini_entry>::const_iterator entry = m_entries.begin(); entry != m_entries.end(); entry++)\r
+          entry->print(str);\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // internal data structure representing a single ini file\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class ini_file\r
+  {\r
+  private:\r
+    friend class ini_section;\r
+    std::string m_filename;\r
+    bool m_changed;\r
+    bool m_writable;\r
+    std::list<ini_section> m_sections;\r
+\r
+    std::list<ini_section>::const_iterator find_section(const std::string& section) const\r
+      {\r
+        for (std::list<ini_section>::const_iterator i = m_sections.begin(); i != m_sections.end(); i++)\r
+        {\r
+          if (i->title() == section)\r
+            return i;\r
+        }\r
+        return m_sections.end();\r
+      }\r
+\r
+    std::list<ini_section>::iterator find_section(const std::string& section)\r
+      {\r
+        for (std::list<ini_section>::iterator i = m_sections.begin(); i != m_sections.end(); i++)\r
+        {\r
+          if (i->title() == section)\r
+            return i;\r
+        }\r
+        return m_sections.end();\r
+      }\r
+\r
+  public:\r
+\r
+    ini_file(void) : m_changed(false), m_writable(false)\r
+      {\r
+      }\r
+\r
+    ini_file(const std::string& filename) : m_changed(false), m_writable(false)\r
+      {\r
+        read_file(filename);\r
+      }\r
+\r
+    ~ini_file(void)\r
+      {\r
+        if (writable())\r
+          save_file();\r
+      }\r
+\r
+    bool initialised(void) const\r
+      {\r
+        return !m_filename.empty();\r
+      }\r
+\r
+    bool writable(void) const\r
+      {\r
+        return m_writable;\r
+      }\r
+\r
+    bool read_file(const std::string& filename)\r
+      {\r
+        m_filename = filename;\r
+        m_changed = false;\r
+        // file may not yet exist - possible to create an Ini file using this class\r
+        // so it is writable if the file exists and is writable or if the folder is writable\r
+        m_writable = file_writable(m_filename);\r
+        if (!file_exists(m_filename))\r
+          return true;\r
+        // create a dummy top section to hold file comments\r
+        std::list<ini_section>::iterator section = m_sections.insert(m_sections.end(), ini_section(""));\r
+        std::ifstream source(m_filename.c_str());\r
+        unsigned line_number = 0;\r
+        std::string line;\r
+        while(std::getline(source,line))\r
+        {\r
+          line_number++;\r
+          unsigned i = 0;\r
+          while(i < line.size() && isspace(line[i]))\r
+            i++;\r
+          if (i < line.size() && line[i] == '[')\r
+          {\r
+            // found a section title\r
+            // skip the [\r
+            i++;\r
+            // get the text up to the end ] or the end of the line\r
+            std::string title;\r
+            while (i < line.size() && line[i] != ']')\r
+              title += line[i++];\r
+            // create a new section and make it the current section\r
+            section = m_sections.insert(m_sections.end(), ini_section(title));\r
+          }\r
+          else if (i < line.size() && line[i] == ';')\r
+          {\r
+            // found a comment\r
+            // skip the ; because that is not part of the comment\r
+            i++;\r
+            // add the rest of the line as a comment to the current section\r
+            section->add_comment(line_number, line.substr(i, line.size()-1));\r
+          }\r
+          else if (i == line.size())\r
+          {\r
+            // found a blank line\r
+            section->add_blank(line_number);\r
+          }\r
+          else\r
+          {\r
+            // found a *possible* variable - now scan forwards for the = operator\r
+            std::string name;\r
+            while (i < line.size() && line[i] != '=')\r
+              name += line[i++];\r
+            // skip the = sign\r
+            // TODO - detect a missing = sign here and convert to an error\r
+            if (i < line.size())\r
+              i++;\r
+            // trim trailing whitespace off the name\r
+            name = trim(name);\r
+            // now get the value, minus any leading whitespace\r
+            std::string value;\r
+            while(i < line.size() && isspace(line[i]))\r
+              i++;\r
+            while (i < line.size())\r
+              value += line[i++];\r
+            // trim trailing whitespace off the value\r
+            value = trim(value);\r
+            // and finally add the name/value pair to the data structure\r
+            section->add_variable(line_number, name, value);\r
+          }\r
+        }\r
+        return true;\r
+      }\r
+\r
+    bool save_file(void)\r
+      {\r
+        if (!initialised()) return false;\r
+        if (!m_changed) return true;\r
+        if (!m_writable) return false;\r
+        std::ofstream output(m_filename.c_str());\r
+        for (std::list<ini_section>::iterator section = m_sections.begin(); section != m_sections.end(); section++)\r
+        {\r
+          if (!(section->title().empty()))\r
+            output << "[" << section->title() << "]" << std::endl;\r
+          for (std::list<ini_entry>::iterator entry = section->m_entries.begin(); entry != section->m_entries.end(); entry++)\r
+            output << entry->text() << std::endl;\r
+        }\r
+        m_changed = false;\r
+        return true;\r
+      }\r
+\r
+    std::string filename(void) const\r
+      {\r
+        return m_filename;\r
+      }\r
+\r
+    bool empty(void) const\r
+      {\r
+        // file is empty if it has either no sections or an empty header section\r
+        if (m_sections.empty()) return true;\r
+        if ((m_sections.begin() == --m_sections.end()) && m_sections.begin()->empty()) return true;\r
+        return false;\r
+      }\r
+\r
+    bool section_exists(const std::string& title) const\r
+      {\r
+        return find_section(title) != m_sections.end();\r
+      }\r
+\r
+    bool add_section(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        m_sections.push_back(ini_section(section));\r
+        m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool empty_section(const std::string& section)\r
+      {\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        return found->empty();\r
+      }\r
+\r
+    bool erase_section(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        m_sections.erase(found);\r
+        m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool clear_section(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        found->clear();\r
+        m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    unsigned sections_size(void) const\r
+      {\r
+        return m_sections.size();\r
+      }\r
+\r
+    const std::string& section_name(unsigned i) const\r
+      {\r
+        std::list<ini_section>::const_iterator result = m_sections.begin();\r
+        for (unsigned j = 1; j <= i; j++)\r
+          result++;\r
+        return result->title();\r
+      }\r
+\r
+    std::vector<std::string> section_names(void) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (unsigned j = 0; j < sections_size(); j++)\r
+          result.push_back(section_name(j));\r
+        return result;\r
+      }\r
+\r
+    bool variable_exists(const std::string& section, const std::string variable) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        return found->variable_exists(variable);\r
+      }\r
+\r
+    std::vector<std::string> variable_names(const std::string& section) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::vector<std::string>();\r
+        return found->variable_names();\r
+      }\r
+\r
+    unsigned variables_size(const std::string& section) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return 0;\r
+        return found->variables_size();\r
+      }\r
+\r
+    unsigned variable_line(const std::string& section, const std::string variable) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return 0;\r
+        return found->variable_line(variable);\r
+      }\r
+\r
+    std::string variable_name(const std::string& section, unsigned i) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::string();\r
+        return found->variable_name(i);\r
+      }\r
+\r
+    std::string variable_value(const std::string& section, unsigned i) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::string();\r
+        return found->variable_value(i);\r
+      }\r
+\r
+    std::string variable_value(const std::string& section, const std::string variable) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::string();\r
+        return found->variable_value(variable);\r
+      }\r
+\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::string& value)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section));\r
+        if (found->add_variable(0,variable,value))\r
+          m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool add_comment(const std::string& section, const std::string& comment)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section));\r
+        if (found->add_comment(0,comment))\r
+          m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool add_blank(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section));\r
+        if (found->add_blank(0))\r
+          m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool erase_variable(const std::string& section, const std::string& variable)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        if (found->erase_variable(variable))\r
+        {\r
+          m_changed = true;\r
+          return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "file: " << m_filename << std::endl;\r
+        for (std::list<ini_section>::const_iterator section = m_sections.begin(); section != m_sections.end(); section++)\r
+          section->print(str);\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // body data structure contains all the data and is pointed-to by exported data structure\r
+\r
+  class ini_manager_body\r
+  {\r
+  private:\r
+    std::vector<ini_file> m_files;\r
+    unsigned m_count;\r
+\r
+    ini_manager_body(const ini_manager_body&);\r
+    ini_manager_body& operator= (const ini_manager_body&);\r
+\r
+  public:\r
+\r
+    ini_manager_body(void) : m_count(1)\r
+      {\r
+      }\r
+\r
+    ~ini_manager_body(void)\r
+      {\r
+        save();\r
+      }\r
+\r
+    void increment(void)\r
+      {\r
+        ++m_count;\r
+      }\r
+\r
+    bool decrement(void)\r
+      {\r
+        --m_count;\r
+        return m_count == 0;\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // file management\r
+\r
+    // add files starting with the most local file (e.g. the current project) which has depth 0\r
+    // and working back to the most global (e.g. the installation settings) which has a depth of size()-1\r
+    // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice.\r
+    // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did\r
+    // not exist, only read errors cause a failure)\r
+    bool add_file(const std::string& filename)\r
+      {\r
+        // if this file has been loaded, don't load it again\r
+        // this is not an error\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (filespec_to_path(filename) == filespec_to_path(m_files[i].filename()))\r
+            return true;\r
+        }\r
+        // add a new file to the back of the list and then load it\r
+        // I do it in two steps rather than passing the filename to the constructor in order to return the load status\r
+        m_files.push_back(ini_file());\r
+        return m_files.back().read_file(filename);\r
+      }\r
+\r
+    // as above, returns false if *none* of the files were added\r
+    // filenames[0] is the local file, and so on\r
+    bool add_files(const std::vector<std::string>& filenames)\r
+      {\r
+        bool result = true;\r
+        for (unsigned i = 0; i < filenames.size(); i++)\r
+          result &= add_file(filenames[i]);\r
+        return result;\r
+      }\r
+\r
+    // saves modified ini files - returns true if all modified files were written successfully\r
+    bool save(void)\r
+      {\r
+        bool result = true;\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+          result &= m_files[i].save_file();\r
+        return result;\r
+      }\r
+\r
+    // get the number of files being managed\r
+    unsigned size(void) const\r
+      {\r
+        return m_files.size();\r
+      }\r
+\r
+    // get the ini filename associated with a depth\r
+    std::string filename(unsigned depth) const\r
+      {\r
+        return m_files[depth].filename();\r
+      }\r
+\r
+    // test whether a file in the ini manager is writable\r
+    bool writable(unsigned depth) const\r
+      {\r
+        return m_files[depth].writable();\r
+      }\r
+\r
+    // test whether a file is empty\r
+    // An ini file is considered empty if it has no named sections and the header is empty or missing\r
+    bool empty(unsigned depth) const\r
+      {\r
+        return m_files[depth].empty();\r
+      }\r
+\r
+    // erase the ini file from the ini manager and from the disk\r
+    bool erase(unsigned depth)\r
+      {\r
+        std::string file = filename(depth);\r
+        remove(depth);\r
+        return file_delete(file);\r
+      }\r
+\r
+    // remove the file from the ini manager but do not erase it from the disk\r
+    bool remove(unsigned depth)\r
+      {\r
+        if (m_files[depth].writable())\r
+          m_files[depth].save_file();\r
+        m_files.erase(m_files.begin() + depth);\r
+        return true;\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // section management\r
+\r
+    // returns the union of all section names in all of the ini files\r
+    std::vector<std::string> section_names(void) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          std::vector<std::string> file_result = section_names(i);\r
+          for (unsigned j = 0; j < file_result.size(); j++)\r
+          {\r
+            if (std::find(result.begin(), result.end(), file_result[j]) == result.end())\r
+              result.push_back(file_result[j]);\r
+          }\r
+        }\r
+        return result;\r
+      }\r
+\r
+    // returns the section names in one of the ini files\r
+    std::vector<std::string> section_names(unsigned depth) const\r
+      {\r
+        return m_files[depth].section_names();\r
+      }\r
+\r
+    // tests whether a section is found in any of the ini files\r
+    bool section_exists(const std::string& title) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (m_files[i].section_exists(title))\r
+            return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    // tests whether the section is found in the specific ini file\r
+    bool section_exists(const std::string& title, unsigned depth) const\r
+      {\r
+        return m_files[depth].section_exists(title);\r
+      }\r
+\r
+    // adds a section to the specified ini file - does nothing if it is already present\r
+    bool add_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].add_section(section);\r
+      }\r
+\r
+    // test whether a section is empty\r
+    bool empty_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].empty_section(section);\r
+      }\r
+\r
+    // removes a section from the specified ini file if it exists there but cannot remove it from any other file\r
+    bool erase_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].erase_section(section);\r
+      }\r
+\r
+    // removes all the contents of a section from the specified ini file but keeps the empty section\r
+    bool clear_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].clear_section(section);\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // variable management\r
+\r
+    // test whether a variable exists in any of the ini files\r
+    bool variable_exists(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    // test whether a variable exists in specified ini file\r
+    bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const\r
+      {\r
+        return m_files[depth].variable_exists(section, variable);\r
+      }\r
+\r
+    // get the union of all variables declared in all ini files\r
+    std::vector<std::string> variable_names(const std::string& section) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          std::vector<std::string> file_result = variable_names(section, i);\r
+          for (unsigned j = 0; j < file_result.size(); j++)\r
+          {\r
+            if (std::find(result.begin(), result.end(), file_result[j]) == result.end())\r
+              result.push_back(file_result[j]);\r
+          }\r
+        }\r
+        return result;\r
+      }\r
+\r
+    // get the set of all varaibale names from one file\r
+    std::vector<std::string> variable_names(const std::string& section, unsigned depth) const\r
+      {\r
+        return m_files[depth].variable_names(section);\r
+      }\r
+\r
+    // get the depth of the first ini file to define a variable\r
+    // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist\r
+    unsigned variable_depth(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return i;\r
+        }\r
+        return (unsigned)-1;\r
+      }\r
+\r
+    // get the filename that first defines the variable\r
+    std::string variable_filename(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return filename(i);\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    unsigned variable_linenumber(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return m_files[i].variable_line(section,variable);\r
+        }\r
+        return 0;\r
+      }\r
+\r
+    // get the value of a variable as a single unprocessed string\r
+    // if the variable does not exist the string will be empty, but beware that\r
+    // you also get an empty string if a variable exists but has no value\r
+    // you can differentiate between the two cases by using variable_exists_all above\r
+    std::string variable_value(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return variable_value(section, variable, i);\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    // get the value from the specified file\r
+    std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const\r
+      {\r
+        return m_files[depth].variable_value(section, variable);\r
+      }\r
+\r
+    // get the value of a variable as a processed string\r
+    // processing splits the value at commas and furthermore supports quoted\r
+    // strings (so that values can contain commas for example)\r
+    // quoted strings are dequoted before they are added to the result\r
+    // the result is a vector of dequoted strings, one per value in the comma-separated list\r
+    std::vector<std::string> variable_values(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return variable_values(section, variable, i);\r
+        }\r
+        return std::vector<std::string>();\r
+      }\r
+\r
+    // get the processed variable from the specified file\r
+    std::vector<std::string> variable_values(const std::string& section, const std::string variable, unsigned depth) const\r
+      {\r
+        // get the unprocessed value and then do the processing into processed separate values\r
+        std::string value = variable_value(section, variable, depth);\r
+        std::vector<std::string> result;\r
+        if (!value.empty())\r
+        {\r
+          result.push_back(std::string());\r
+          unsigned i = 0;\r
+          // loop which is repeated for each element in the comma-separated list\r
+          while(i < value.size())\r
+          {\r
+            // skip leading whitespace\r
+            while (i < value.size() && isspace(value[i])) i++;\r
+            // get the value - this means all text up to the next comma or end of line\r
+            // also allow escaped characters\r
+            while (i < value.size())\r
+            {\r
+              if (value[i] == ',') break;\r
+              if (value[i] == '\\')\r
+              {\r
+                // found an escaped character - de-escape it by only getting the next character\r
+                // beware of an escape character at the end of the line which just gets ignored\r
+                i++;\r
+                if (i < value.size()) result.back() += value[i++];\r
+              }\r
+              else if (value[i] == '"')\r
+              {\r
+                // get a quoted substring\r
+                // first skip the opening quote\r
+                i++;\r
+                // keep getting characters until a close-quote, but allow the quote character to be escaped by itself\r
+                while (i < value.size())\r
+                {\r
+                  if (value[i] == '"')\r
+                  {\r
+                    // found a quote skip it\r
+                    i++;\r
+                    // now establish whether its an escaped quote\r
+                    // if it is, keep it, but de-escape it by only getting the next character\r
+                    // it it isn't, break out and continue processing the value as a non-quoted string\r
+                    if (i < value.size() && value[i] != '"') break;\r
+                    if (i < value.size()) result.back() += value[i++];\r
+                  }\r
+                  else\r
+                  {\r
+                    // non-quote and non-escape so just get the character\r
+                    result.back() += value[i++];\r
+                  }\r
+                }\r
+              }\r
+              else\r
+              {\r
+                // non-escape so just get the character\r
+                result.back() += value[i++];\r
+              }\r
+            }\r
+            // trim trailing whitespace off the value\r
+            while (!result.back().empty() && isspace(result.back()[result.back().size()-1])) result.back().erase(result.back().size()-1,1);\r
+            // now check for whether there is a comma or end of line\r
+            if (i <= value.size() && value[i] == ',')\r
+            {\r
+              // skip the comma and add a new string to the result ready for the next iteration\r
+              i++;\r
+              result.push_back(std::string());\r
+            }\r
+            else\r
+            {\r
+              // end of line found so break out\r
+              break;\r
+            }\r
+          }\r
+        }\r
+        return result;\r
+      }\r
+\r
+    // add a variable to the specified file\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth)\r
+      {\r
+        return m_files[depth].add_variable(section, variable, value);\r
+      }\r
+\r
+    // add a variable as a processed string\r
+    // processing means that the values in the string vector are converted into a comma-separated list\r
+    // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::vector<std::string>& values, unsigned depth)\r
+      {\r
+        // convert the values vector into a comma-separated string with each value escaped so that special characters do not confuse the reader\r
+        // the characters escaped are: '\', ',', '"'\r
+        std::string value;\r
+        for (unsigned v = 0; v < values.size(); v++)\r
+        {\r
+          const std::string& element = values[v];\r
+          // add the comma between values === add a comma before all but the first value\r
+          if (v > 0) value += ',';\r
+          for (unsigned i = 0; i < element.size(); i++)\r
+          {\r
+            // add a character at a time, escaping special characters\r
+            if (element[i] == '"' || element[i] == ',' || element[i] == '\\')\r
+            {\r
+              // escape the character\r
+              value += '\\';\r
+            }\r
+            value += element[i];\r
+          }\r
+        }\r
+        return add_variable(section, variable, value, depth);\r
+      }\r
+\r
+    // erase a variable from the specified file\r
+    // this does not remove the variable from other ini files, so the variable may still exist\r
+    // to mask a global variable, set the variable to an empty string instead\r
+    bool erase_variable(const std::string& section, const std::string& variable, unsigned depth)\r
+      {\r
+        return m_files[depth].erase_variable(section, variable);\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // sundry line-entry management\r
+\r
+    // add a comment to the specified ini file\r
+    bool add_comment(const std::string& section, const std::string& comment, unsigned depth)\r
+      {\r
+        return m_files[depth].add_comment(section, comment);\r
+      }\r
+\r
+    // add a blank line to the specified ini file\r
+    bool add_blank(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].add_blank(section);\r
+      }\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "----------------------------------------" << std::endl;\r
+        for (unsigned depth = 0; depth < m_files.size(); depth++)\r
+        {\r
+          m_files[depth].print(str);\r
+          str << "----------------------------------------" << std::endl;\r
+        }\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // exported data structure representing the set of all ini files and providing\r
+  // the access functions exported by the class\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // constructors/destructors\r
+\r
+  ini_manager::ini_manager(void) : m_body(new ini_manager_body)\r
+  {\r
+  }\r
+\r
+  ini_manager::ini_manager(const std::vector<std::string>& filenames) : m_body(new ini_manager_body)\r
+  {\r
+    add_files(filenames);\r
+  }\r
+\r
+  ini_manager::ini_manager(const ini_manager& manager) : m_body(0)\r
+  {\r
+    m_body = manager.m_body;\r
+    m_body->increment();\r
+  }\r
+\r
+  ini_manager& ini_manager::operator= (const ini_manager& manager)\r
+  {\r
+    if (m_body == manager.m_body) return *this;\r
+    if (m_body->decrement())\r
+      delete m_body;\r
+    m_body = manager.m_body;\r
+    m_body->increment();\r
+    return *this;\r
+  }\r
+\r
+  ini_manager::~ini_manager(void)\r
+  {\r
+    if (m_body->decrement())\r
+      delete m_body;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // file management\r
+\r
+  bool ini_manager::add_file(const std::string& filename)\r
+  {\r
+    return m_body->add_file(filename);\r
+  }\r
+\r
+  bool ini_manager::add_files(const std::vector<std::string>& filenames)\r
+  {\r
+    return m_body->add_files(filenames);\r
+  }\r
+\r
+  bool ini_manager::save(void)\r
+  {\r
+    return m_body->save();\r
+  }\r
+\r
+  unsigned ini_manager::size(void) const\r
+  {\r
+    return m_body->size();\r
+  }\r
+\r
+  std::string ini_manager::filename(unsigned depth) const\r
+  {\r
+    return m_body->filename(depth);\r
+  }\r
+\r
+  bool ini_manager::writable(unsigned depth) const\r
+  {\r
+    return m_body->writable(depth);\r
+  }\r
+\r
+  bool ini_manager::empty(unsigned depth) const\r
+  {\r
+    return m_body->empty(depth);\r
+  }\r
+\r
+  bool ini_manager::erase(unsigned depth)\r
+  {\r
+    return m_body->erase(depth);\r
+  }\r
+\r
+  bool ini_manager::remove(unsigned depth)\r
+  {\r
+    return m_body->remove(depth);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // section management\r
+\r
+  std::vector<std::string> ini_manager::section_names(void) const\r
+  {\r
+    return m_body->section_names();\r
+  }\r
+\r
+  std::vector<std::string> ini_manager::section_names(unsigned depth) const\r
+  {\r
+    return m_body->section_names(depth);\r
+  }\r
+\r
+  bool ini_manager::section_exists(const std::string& section) const\r
+  {\r
+    return m_body->section_exists(section);\r
+  }\r
+\r
+  bool ini_manager::section_exists(const std::string& section, unsigned depth) const\r
+  {\r
+    return m_body->section_exists(section, depth);\r
+  }\r
+\r
+  bool ini_manager::add_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->add_section(section, depth);  \r
+  }\r
+\r
+  bool ini_manager::empty_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->empty_section(section, depth);  \r
+  }\r
+\r
+  bool ini_manager::erase_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->erase_section(section, depth);  \r
+  }\r
+\r
+  bool ini_manager::clear_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->clear_section(section, depth);  \r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // variable management\r
+\r
+  bool ini_manager::variable_exists(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_exists(section, variable);  \r
+  }\r
+\r
+  bool ini_manager::variable_exists(const std::string& section, const std::string variable, unsigned depth) const\r
+  {\r
+    return m_body->variable_exists(section, variable, depth);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_names(const std::string& section) const\r
+  {\r
+    return m_body->variable_names(section);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_names(const std::string& section, unsigned depth) const\r
+  {\r
+    return m_body->variable_names(section, depth);  \r
+  }\r
+\r
+  unsigned ini_manager::variable_depth(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_depth(section, variable);  \r
+  }\r
+\r
+  std::string ini_manager::variable_filename(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_filename(section, variable);  \r
+  }\r
+\r
+  unsigned ini_manager::variable_linenumber(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_linenumber(section, variable);  \r
+  }\r
+\r
+  std::string ini_manager::variable_value(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_value(section, variable);  \r
+  }\r
+\r
+  std::string ini_manager::variable_value(const std::string& section, const std::string variable, unsigned depth) const\r
+  {\r
+    return m_body->variable_value(section, variable, depth);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_values(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_values(section, variable);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_values(const std::string& section, const std::string variable, unsigned depth) const\r
+  {\r
+    return m_body->variable_values(section, variable, depth);  \r
+  }\r
+\r
+  bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth)\r
+  {\r
+    return m_body->add_variable(section, variable, value, depth);\r
+  }\r
+\r
+  bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::vector<std::string>& values, unsigned depth)\r
+  {\r
+    return m_body->add_variable(section, variable, values, depth);\r
+  }\r
+\r
+  bool ini_manager::erase_variable(const std::string& section, const std::string& variable, unsigned depth)\r
+  {\r
+    return m_body->erase_variable(section, variable, depth);\r
+  }\r
+\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // sundry entries\r
+\r
+  bool ini_manager::add_comment(const std::string& section, const std::string& comment, unsigned depth)\r
+  {\r
+    return m_body->add_comment(section, comment, depth);\r
+  }\r
+\r
+  bool ini_manager::add_blank(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->add_blank(section, depth);\r
+  }\r
+\r
+  bool ini_manager::print(std::ostream& str) const\r
+  {\r
+    return m_body->print(str);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // diagnostic print\r
+\r
+  std::ostream& operator << (std::ostream& str, const ini_manager& manager)\r
+  {\r
+    manager.print(str);\r
+    return str;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/subsystems/ini_manager.hpp b/src/stlplus/subsystems/ini_manager.hpp
new file mode 100644 (file)
index 0000000..f1d7a66
--- /dev/null
@@ -0,0 +1,201 @@
+#ifndef STLPLUS_INI_MANAGER\r
+#define STLPLUS_INI_MANAGER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A subsystem for managing INI (i.e. .ini) files\r
+//   An INI file has the following format\r
+\r
+//     file           ::= header { section }*\r
+//     header         ::= { comment | blank }*\r
+//     section        ::= section_header { declaration | comment | blank }*\r
+//     section_header ::= '[' title ']' '\n'\r
+//     declaration    ::= variable '=' value '\n'\r
+//     comment        ::= ';' text '\n'\r
+//     blank          ::= '\n'\r
+//     title          ::= [~']']*\r
+//     variable       ::= [~'=']*\r
+//     value          ::= .*\r
+//     text           ::= .*\r
+\r
+//   Whitespace is trimmed from the leading and trailing ends of title, variable and value\r
+//   Note: a header is represented internally as a Clint section (i.e. a section with no name)\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "subsystems_fixes.hpp"\r
+#include <vector>\r
+#include <string>\r
+#include <iostream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Internals\r
+\r
+  class ini_manager_body;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Ini-file manager class\r
+\r
+  class ini_manager\r
+  {\r
+  public:\r
+\r
+    ini_manager(void);\r
+\r
+    explicit ini_manager(const std::vector<std::string>& filenames);\r
+\r
+    ini_manager(const ini_manager&);\r
+    ini_manager& operator= (const ini_manager&);\r
+\r
+    ~ini_manager(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // file management\r
+\r
+    // add files starting with the most local file (e.g. the current project) which has depth 0\r
+    // and working back to the most global (e.g. the installation settings) which has a depth of size()-1\r
+    // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice.\r
+    // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did\r
+    // not exist, only read errors cause a failure)\r
+    bool add_file(const std::string& filename);\r
+\r
+    // as above, returns false if *none* of the files were added\r
+    // filenames[0] is the local file, and so on\r
+    bool add_files(const std::vector<std::string>& filenames);\r
+\r
+    // saves modified ini files - returns true if all modified files were written successfully\r
+    bool save(void);\r
+\r
+    // get the number of files being managed\r
+    unsigned size(void) const;\r
+\r
+    // get the ini filename associated with a depth\r
+    std::string filename(unsigned depth = 0) const;\r
+\r
+    // test whether a file in the ini manager is writable\r
+    bool writable(unsigned depth = 0) const;\r
+\r
+    // test whether a file is empty\r
+    // An ini file is considered empty if it has no named sections and the header is empty or missing\r
+    bool empty(unsigned depth = 0) const;\r
+\r
+    // erase the ini file from the ini manager and from the disk\r
+    bool erase(unsigned depth = 0);\r
+\r
+    // remove the file from the ini manager but do not erase it from the disk\r
+    bool remove(unsigned depth = 0);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // section management\r
+\r
+    // returns the union of all section names in all of the ini files\r
+    std::vector<std::string> section_names(void) const;\r
+\r
+    // returns the section names in one of the ini files\r
+    std::vector<std::string> section_names(unsigned depth) const;\r
+\r
+    // tests whether a section is found in any of the ini files\r
+    bool section_exists(const std::string& title) const;\r
+\r
+    // tests whether the section is found in the specific ini file\r
+    bool section_exists(const std::string& title, unsigned depth) const;\r
+\r
+    // adds a section to the specified ini file - does nothing if it is already present\r
+    bool add_section(const std::string& section, unsigned depth = 0);\r
+\r
+    // test whether a section is empty\r
+    bool empty_section(const std::string& section, unsigned depth = 0);\r
+\r
+    // removes a section from the specified ini file if it exists there but cannot remove it from any other file\r
+    bool erase_section(const std::string& section, unsigned depth = 0);\r
+\r
+    // removes all the contents of a section from the specified ini file but keeps the empty section\r
+    bool clear_section(const std::string& section, unsigned depth = 0);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // variable management\r
+\r
+    // test whether a variable exists in any of the ini files\r
+    bool variable_exists(const std::string& section, const std::string variable) const;\r
+\r
+    // test whether a variable exists in specified ini file\r
+    bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const;\r
+\r
+    // get the union of all variables declared in all ini files\r
+    std::vector<std::string> variable_names(const std::string& section) const;\r
+\r
+    // get the set of all varaibale names from one file\r
+    std::vector<std::string> variable_names(const std::string& section, unsigned depth) const;\r
+\r
+    // get the depth of the first ini file to define a variable\r
+    // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist\r
+    unsigned variable_depth(const std::string& section, const std::string variable) const;\r
+\r
+    // get the filename that first defines the variable\r
+    std::string variable_filename(const std::string& section, const std::string variable) const;\r
+    // ditto for its linenumber within that file\r
+    unsigned variable_linenumber(const std::string& section, const std::string variable) const;\r
+\r
+    // get the value of a variable as a single unprocessed string\r
+    // if the variable does not exist the string will be empty, but beware that\r
+    // you also get an empty string if a variable exists but has no value\r
+    // you can differentiate between the two cases by using variable_exists_all above\r
+    std::string variable_value(const std::string& section, const std::string variable) const;\r
+\r
+    // get the value from the specified file\r
+    std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const;\r
+\r
+    // get the value of a variable as a processed string\r
+    // processing splits the value at commas and furthermore supports quoted strings (so that values can contain commas for example)\r
+    // quoted strings are dequoted before they are added to the result\r
+    // the result is a vector of dequoted strings, one per value in the comma-separated list\r
+    std::vector<std::string> variable_values(const std::string& section, const std::string variable) const;\r
+\r
+    // get the processed variable from the specified file\r
+    std::vector<std::string> variable_values(const std::string& section, const std::string variable, unsigned depth) const;\r
+\r
+    // add a variable to the specified file\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth = 0);\r
+\r
+    // add a variable as a processed string\r
+    // processing means that the values in the string vector are converted into a comma-separated list\r
+    // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::vector<std::string>& values, unsigned depth = 0);\r
+\r
+    // erase a variable from the specified file\r
+    // this does not remove the variable from other ini files, so the variable may still exist\r
+    // to mask a global variable, set the variable to an empty string instead\r
+    bool erase_variable(const std::string& section, const std::string& variable, unsigned depth = 0);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // sundry line-entry management\r
+\r
+    // add a comment to the specified ini file\r
+    bool add_comment(const std::string& section, const std::string& comment, unsigned depth = 0);\r
+\r
+    // add a blank line to the specified ini file\r
+    bool add_blank(const std::string& section, unsigned depth = 0);\r
+\r
+    bool print(std::ostream&) const;\r
+\r
+  private:\r
+    friend class ini_manager_body;\r
+    ini_manager_body* m_body;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // diagnostic print routine\r
+\r
+  std::ostream& operator << (std::ostream&, const ini_manager&);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/subsystems/library_manager.cpp b/src/stlplus/subsystems/library_manager.cpp
new file mode 100644 (file)
index 0000000..0daf1be
--- /dev/null
@@ -0,0 +1,2332 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "library_manager.hpp"\r
+#include "file_system.hpp"\r
+#include <algorithm>\r
+#include <fstream>\r
+#include <ctype.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+static const char* LibraryNameExtension = "lmn";\r
+static const char* HeaderExtension = "lmh";\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// local operations\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+typedef std::map<std::string,stlplus::lm_callback_entry> lm_callback_map;\r
+\r
+static std::string lowercase(const std::string& val)\r
+{\r
+  std::string text = val;\r
+  for (unsigned i = 0; i < text.size(); i++)\r
+    text[i] = tolower(text[i]);\r
+  return text;\r
+}\r
+\r
+// Context file and library operations\r
+// These must be readable/writeable without a library data structure present\r
+// so that the name of the library is known before creation\r
+\r
+static bool read_context(const std::string& path, const std::string& owner, std::string& name, bool& writable)\r
+{\r
+  std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension);\r
+  name = "";\r
+  writable = false;\r
+  if (!stlplus::file_exists(spec)) return false;\r
+  std::ifstream input(spec.c_str());\r
+  input >> name >> writable;\r
+  return !input.fail();\r
+}\r
+\r
+static bool write_context(const std::string& path, const std::string& owner, const std::string& name, bool writable)\r
+{\r
+  std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension);\r
+  std::ofstream output(spec.c_str());\r
+  output << name << " " << writable << std::endl;\r
+  return !output.fail();\r
+}\r
+\r
+static bool create_library(const std::string& path, const std::string& owner, const std::string& name, bool writable)\r
+{\r
+  std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension);\r
+  if (stlplus::is_present(path) && !stlplus::is_folder(path)) return false;\r
+  if (!stlplus::folder_exists(path) && !stlplus::folder_create(path)) return false;\r
+  return write_context(path, owner, name, writable);\r
+}\r
+\r
+static bool erase_library(const std::string& path, const std::string& owner)\r
+{\r
+  // check that it is a library before deleting it!\r
+  std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension);\r
+  if (!stlplus::file_exists(spec)) return false;\r
+  return stlplus::folder_delete(path, true);\r
+}\r
+\r
+// dependency checking\r
+\r
+typedef std::map<std::pair<std::string,stlplus::lm_unit_name>,stlplus::lm_dependencies> visited_map;\r
+\r
+static stlplus::lm_dependencies& out_of_date_check (visited_map& visited,\r
+                                                    const stlplus::library_manager* manager,\r
+                                                    const std::string& library,\r
+                                                    const stlplus::lm_unit_name& name)\r
+{\r
+  // the visited field contains an entry if a unit has been visited - it also contains the reason for out-of-date-ness\r
+  // this is initially set empty (up to date) since the unit has to be added\r
+  // before it is checked just in case there is a recursive loop\r
+  // the dependencies field is filled in if the unit is subsequently found to be out of date\r
+  std::pair<std::string,stlplus::lm_unit_name> full_name = std::make_pair(library,name);\r
+  // first check whether this unit has already been visited - this should in\r
+  // principle never happen because the same test is performed before trying\r
+  // to recurse, so consider this to be paranoid-mode programming\r
+  visited_map::iterator found = visited.find(full_name);\r
+  if (found != visited.end())\r
+    return found->second;\r
+  // now add this unit to prevent recursion loops later. This entry is added\r
+  // to when out-of-date dependencies are found and is also the return value\r
+  // of the function\r
+  visited[full_name] = stlplus::lm_dependencies();\r
+  // now find the unit\r
+  const stlplus::lm_unit_ptr unit = manager->find(library,name);\r
+  if (!unit)\r
+  {\r
+    // if a unit is missing it fails with a dependency on itself - this is a\r
+    // bit of a work-around, but this again is paranoid-mode programming since\r
+    // the calling function (this function calling itself recursively) should\r
+    // check the unit's existence before recursing\r
+    visited[full_name].unit_add(stlplus::lm_unit_dependency(library,name));\r
+  }\r
+  else\r
+  {\r
+    // we're onto the real checks now - first get the datestamp of this unit:\r
+    // all dependencies must be older than this\r
+    time_t unit_modified = unit->modified();\r
+    // check dependency on source file if there is one\r
+    if (unit->source_file_present())\r
+    {\r
+      std::string source_file = unit->source_file().path_full(unit->library_path());\r
+      time_t source_modified = stlplus::file_modified(source_file);\r
+      if (source_modified == 0 || source_modified > unit_modified)\r
+      {\r
+        visited[full_name].set_source_file(unit->source_file());\r
+      }\r
+    }\r
+    // now check the other file dependencies\r
+    for (unsigned i = 0; i < unit->file_size(); i++)\r
+    {\r
+      // a file dependency is a dependency on a file outside of the library system\r
+      const stlplus::lm_file_dependency& dependency = unit->file_dependency(i);\r
+      std::string file_full = dependency.path_full(unit->library_path());\r
+      time_t source_modified = stlplus::file_modified(file_full);\r
+      if (source_modified == 0 || source_modified > unit_modified)\r
+      {\r
+        visited[full_name].file_add(dependency);\r
+      }\r
+    }\r
+    // now check and recurse on unit dependencies\r
+    for (unsigned j = 0; j < unit->unit_size(); j++)\r
+    {\r
+      const stlplus::lm_unit_dependency& dependency = unit->unit_dependency(j);\r
+      std::pair<std::string,stlplus::lm_unit_name> other_name = std::make_pair(dependency.library(), dependency.unit_name());\r
+      // perform the datestamp checking at this level and only recurse if this does not detect an error\r
+      const stlplus::lm_unit_ptr other_unit = manager->find(other_name.first, other_name.second);\r
+      if (!other_unit)\r
+      {\r
+        visited[full_name].unit_add(dependency);\r
+      }\r
+      else\r
+      {\r
+        time_t other_modified = other_unit->modified();\r
+        if (other_modified == 0 || other_modified > unit_modified)\r
+        {\r
+          visited[full_name].unit_add(dependency);\r
+        }\r
+        else\r
+        {\r
+          // prevent recursion before doing it\r
+          visited_map::iterator other_found = visited.find(other_name);\r
+          if (other_found != visited.end())\r
+          {\r
+            // if the unit was found to be out of date on the previous visit, add it to the failed dependencies\r
+            if (!other_found->second.empty())\r
+            {\r
+              visited[full_name].unit_add(dependency);\r
+            }\r
+          }\r
+          else\r
+          {\r
+            // the unit hasn't been visited before, so recurse on it now\r
+            stlplus::lm_dependencies other_dependencies = \r
+              out_of_date_check(visited, manager, other_name.first, other_name.second);\r
+            if (!other_dependencies.empty())\r
+            {\r
+              visited[full_name].unit_add(dependency);\r
+            }\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+  return visited[full_name];\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// class lm_unit_name\r
+\r
+stlplus::lm_unit_name::lm_unit_name(const std::string& name, const std::string& type) :\r
+  m_name(name), m_type(type)\r
+{\r
+}\r
+\r
+stlplus::lm_unit_name::~lm_unit_name(void)\r
+{\r
+}\r
+\r
+const std::string& stlplus::lm_unit_name::name(void) const\r
+{\r
+  return m_name;\r
+}\r
+\r
+void stlplus::lm_unit_name::set_name(const std::string& name)\r
+{\r
+  m_name = name;\r
+}\r
+\r
+void stlplus::lm_unit_name::lowercase(void)\r
+{\r
+  m_name = ::lowercase(m_name);\r
+}\r
+\r
+const std::string& stlplus::lm_unit_name::type(void) const\r
+{\r
+  return m_type;\r
+}\r
+\r
+void stlplus::lm_unit_name::set_type(const std::string& type)\r
+{\r
+  m_type = type;\r
+}\r
+\r
+bool stlplus::lm_unit_name::write(std::ostream& context) const\r
+{\r
+  context << m_name << " " << m_type;\r
+  return !context.fail();\r
+}\r
+\r
+bool stlplus::lm_unit_name::read(std::istream& context)\r
+{\r
+  context >> m_name >> m_type;\r
+  return !context.fail();\r
+}\r
+\r
+std::string stlplus::lm_unit_name::to_string(void) const\r
+{\r
+  return m_name + ":" + m_type;\r
+}\r
+\r
+bool stlplus::lm_unit_name::print(std::ostream& str) const\r
+{\r
+  str << to_string();\r
+  return !str.fail();\r
+}\r
+\r
+std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_name& name)\r
+{\r
+  name.print(str);\r
+  return str;\r
+}\r
+\r
+bool stlplus::operator == (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r)\r
+{\r
+  return l.name() == r.name() && l.type() == r.type();\r
+}\r
+\r
+bool stlplus::operator < (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r)\r
+{\r
+  // sort by name then type\r
+  return (l.name() != r.name()) ? l.name() < r.name() : l.type() < r.type();\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// dependencies\r
+\r
+// file dependencies\r
+\r
+stlplus::lm_file_dependency::lm_file_dependency(void) : m_line(0), m_column(0)\r
+{\r
+}\r
+\r
+stlplus::lm_file_dependency::lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line, unsigned column)\r
+{\r
+  set_path(library_path, path);\r
+  set_line(line);\r
+  set_column(column);\r
+}\r
+\r
+stlplus::lm_file_dependency::~lm_file_dependency(void)\r
+{\r
+}\r
+\r
+const std::string& stlplus::lm_file_dependency::path(void) const\r
+{\r
+  return m_path;\r
+}\r
+\r
+std::string stlplus::lm_file_dependency::path_full(const std::string& library_path) const\r
+{\r
+  return filespec_to_path(library_path, m_path);\r
+}\r
+\r
+void stlplus::lm_file_dependency::set_path(const std::string& library_path, const std::string& path)\r
+{\r
+  m_path = filespec_to_relative_path(library_path, path);\r
+}\r
+\r
+unsigned stlplus::lm_file_dependency::line(void) const\r
+{\r
+  return m_line;\r
+}\r
+\r
+void stlplus::lm_file_dependency::set_line(unsigned line)\r
+{\r
+  m_line = line;\r
+}\r
+\r
+unsigned stlplus::lm_file_dependency::column(void) const\r
+{\r
+  return m_column;\r
+}\r
+\r
+void stlplus::lm_file_dependency::set_column(unsigned column)\r
+{\r
+  m_column = column;\r
+}\r
+\r
+bool stlplus::lm_file_dependency::write(std::ostream& context) const\r
+{\r
+  context << m_path << " " << m_line << " " << m_column;\r
+  return !context.fail();\r
+}\r
+\r
+bool stlplus::lm_file_dependency::read(std::istream& context)\r
+{\r
+  context >> m_path >> m_line >> m_column;\r
+  return !context.fail();\r
+}\r
+\r
+bool stlplus::lm_file_dependency::print(std::ostream& str) const\r
+{\r
+  str << "file: " << m_path;\r
+  if (m_line != 0)\r
+    str << ":" << m_line << ":" << m_column;\r
+  return !str.fail();\r
+}\r
+\r
+std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_file_dependency& dependency)\r
+{\r
+  dependency.print(str);\r
+  return str;\r
+}\r
+\r
+// unit dependency\r
+\r
+stlplus::lm_unit_dependency::lm_unit_dependency(void)\r
+{\r
+}\r
+\r
+stlplus::lm_unit_dependency::lm_unit_dependency(const std::string& library, const lm_unit_name& name) : \r
+  m_library(library), m_name(name)\r
+{\r
+}\r
+\r
+stlplus::lm_unit_dependency::~lm_unit_dependency(void)\r
+{\r
+}\r
+\r
+const std::string& stlplus::lm_unit_dependency::library(void) const\r
+{\r
+  return m_library;\r
+}\r
+\r
+void stlplus::lm_unit_dependency::set_library(const std::string& library)\r
+{\r
+  m_library = library;\r
+}\r
+\r
+const stlplus::lm_unit_name& stlplus::lm_unit_dependency::unit_name(void) const\r
+{\r
+  return m_name;\r
+}\r
+\r
+void stlplus::lm_unit_dependency::set_unit_name(const lm_unit_name& unit_name)\r
+{\r
+  m_name = unit_name;\r
+}\r
+\r
+const std::string& stlplus::lm_unit_dependency::name(void) const\r
+{\r
+  return m_name.name();\r
+}\r
+\r
+void stlplus::lm_unit_dependency::set_name(const std::string& name)\r
+{\r
+  m_name.set_name(name);\r
+}\r
+\r
+const std::string& stlplus::lm_unit_dependency::type(void) const\r
+{\r
+  return m_name.type();\r
+}\r
+\r
+void stlplus::lm_unit_dependency::set_type(const std::string& type)\r
+{\r
+  m_name.set_type(type);\r
+}\r
+\r
+bool stlplus::lm_unit_dependency::write(std::ostream& context) const\r
+{\r
+  context << m_library;\r
+  m_name.write(context);\r
+  return !context.fail();\r
+}\r
+\r
+bool stlplus::lm_unit_dependency::read(std::istream& context)\r
+{\r
+  context >> m_library;\r
+  m_name.read(context);;\r
+  return !context.fail();\r
+}\r
+\r
+bool stlplus::lm_unit_dependency::print(std::ostream& str) const\r
+{\r
+  str << "unit: " << m_library << "." << m_name;\r
+  return !str.fail();\r
+}\r
+\r
+std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_dependency& dependency)\r
+{\r
+  dependency.print(str);\r
+  return str;\r
+}\r
+\r
+// dependencies\r
+\r
+stlplus::lm_dependencies::lm_dependencies(void) : m_source(0)\r
+{\r
+}\r
+\r
+stlplus::lm_dependencies::lm_dependencies(const lm_dependencies& r) : m_source(0)\r
+{\r
+  *this = r;\r
+}\r
+\r
+stlplus::lm_dependencies& stlplus::lm_dependencies::operator=(const stlplus::lm_dependencies& r)\r
+{\r
+  if (m_source)\r
+    delete m_source;\r
+  m_source = 0;\r
+  if (r.m_source)\r
+    m_source = new lm_file_dependency(*r.m_source);\r
+  m_files = r.m_files;\r
+  m_units = r.m_units;\r
+  return *this;\r
+}\r
+\r
+stlplus::lm_dependencies::~lm_dependencies(void)\r
+{\r
+  if (m_source)\r
+    delete m_source;\r
+}\r
+\r
+void stlplus::lm_dependencies::set_source_file(const lm_file_dependency& source)\r
+{\r
+  if (m_source)\r
+    delete m_source;\r
+  m_source = new lm_file_dependency(source);\r
+}\r
+\r
+bool stlplus::lm_dependencies::source_file_present(void) const\r
+{\r
+  return (m_source != 0);\r
+}\r
+\r
+const stlplus::lm_file_dependency& stlplus::lm_dependencies::source_file(void) const\r
+{\r
+  return *m_source;\r
+}\r
+\r
+unsigned stlplus::lm_dependencies::file_add(const lm_file_dependency& dependency)\r
+{\r
+  m_files.push_back(dependency);\r
+  return m_files.size()-1;\r
+}\r
+\r
+unsigned stlplus::lm_dependencies::file_size(void) const\r
+{\r
+  return m_files.size();\r
+}\r
+\r
+const stlplus::lm_file_dependency& stlplus::lm_dependencies::file_dependency(unsigned i) const\r
+{\r
+  return m_files[i];\r
+}\r
+\r
+void stlplus::lm_dependencies::file_erase(unsigned i)\r
+{\r
+  m_files.erase(m_files.begin()+i);\r
+}\r
+\r
+unsigned stlplus::lm_dependencies::unit_add(const lm_unit_dependency& dependency)\r
+{\r
+  m_units.push_back(dependency);\r
+  return m_units.size()-1;\r
+}\r
+\r
+unsigned stlplus::lm_dependencies::unit_size(void) const\r
+{\r
+  return m_units.size();\r
+}\r
+\r
+const stlplus::lm_unit_dependency& stlplus::lm_dependencies::unit_dependency(unsigned i) const\r
+{\r
+  return m_units[i];\r
+}\r
+\r
+void stlplus::lm_dependencies::unit_erase(unsigned i)\r
+{\r
+  m_units.erase(m_units.begin()+i);\r
+}\r
+\r
+void stlplus::lm_dependencies::clear(void)\r
+{\r
+  if (m_source)\r
+    delete m_source;\r
+  m_source = 0;\r
+  m_files.clear();\r
+  m_units.clear();\r
+}\r
+\r
+bool stlplus::lm_dependencies::empty(void) const\r
+{\r
+  return (m_source == 0) && m_files.empty() && m_units.empty();\r
+}\r
+\r
+bool stlplus::lm_dependencies::write(std::ostream& context) const\r
+{\r
+  context << (m_source ? true : false) << " ";\r
+  if (m_source) m_source->write(context);\r
+  context << std::endl;\r
+  context << m_files.size() << std::endl;\r
+  for (unsigned i = 0; i < m_files.size(); i++)\r
+  {\r
+    m_files[i].write(context);\r
+    context << std::endl;\r
+  }\r
+  context << m_units.size() << std::endl;\r
+  for (unsigned j = 0; j < m_units.size(); j++)\r
+  {\r
+    m_units[j].write(context);\r
+    context << std::endl;\r
+  }\r
+  return !context.fail();\r
+}\r
+\r
+bool stlplus::lm_dependencies::read(std::istream& context)\r
+{\r
+  clear();\r
+  bool source_present = false;\r
+  context >> source_present;\r
+  if (source_present)\r
+  {\r
+    m_source = new lm_file_dependency();\r
+    m_source->read(context);\r
+  }\r
+  unsigned files_size = 0;\r
+  context >> files_size;\r
+  for (unsigned i = 0; i < files_size; i++)\r
+  {\r
+    m_files.push_back(lm_file_dependency());\r
+    m_files.back().read(context);\r
+  }\r
+  unsigned units_size = 0;\r
+  context >> units_size;\r
+  for (unsigned j = 0; j < units_size; j++)\r
+  {\r
+    m_units.push_back(lm_unit_dependency());\r
+    m_units.back().read(context);\r
+  }\r
+  return !context.fail();\r
+}\r
+\r
+bool stlplus::lm_dependencies::print(std::ostream& str) const\r
+{\r
+  if (m_source)\r
+    str << "  source file: " << *m_source << std::endl;\r
+  for (unsigned i = 0; i < m_files.size(); i++)\r
+    str << "  " << m_files[i] << std::endl;\r
+  for (unsigned j = 0; j < m_units.size(); j++)\r
+    str << "  " << m_units[j] << std::endl;\r
+  return !str.fail();\r
+}\r
+\r
+std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_dependencies& dependencies)\r
+{\r
+  dependencies.print(str);\r
+  return str;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// lm_unit\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+stlplus::lm_unit::lm_unit(const lm_unit_name& name, lm_library* library) : \r
+  m_name(name), m_header_modified(false), m_loaded(false), m_marked(false), m_library(library), m_error(false)\r
+{\r
+  read_header();\r
+}\r
+\r
+stlplus::lm_unit::~lm_unit(void)\r
+{\r
+  write_header();\r
+}\r
+\r
+////////////////////////////////////////\r
+// Header data\r
+\r
+const stlplus::lm_unit_name& stlplus::lm_unit::unit_name(void) const\r
+{\r
+  return m_name;\r
+}\r
+\r
+const std::string& stlplus::lm_unit::name(void) const\r
+{\r
+  return m_name.name();\r
+}\r
+\r
+const std::string& stlplus::lm_unit::type(void) const\r
+{\r
+  return m_name.type();\r
+}\r
+\r
+// dependencies\r
+\r
+// source file dependency\r
+\r
+void stlplus::lm_unit::set_source_file(const lm_file_dependency& dependency)\r
+{\r
+  m_header_modified = true;\r
+  m_dependencies.set_source_file(dependency);\r
+}\r
+\r
+bool stlplus::lm_unit::source_file_present(void) const\r
+{\r
+  return m_dependencies.source_file_present();\r
+}\r
+\r
+const stlplus::lm_file_dependency& stlplus::lm_unit::source_file(void) const\r
+{\r
+  return m_dependencies.source_file();\r
+}\r
+\r
+// other file dependencies\r
+\r
+unsigned stlplus::lm_unit::file_add(const lm_file_dependency& dependency)\r
+{\r
+  m_header_modified = true;\r
+  return m_dependencies.file_add(dependency);\r
+}\r
+\r
+unsigned stlplus::lm_unit::file_size(void) const\r
+{\r
+  return m_dependencies.file_size();\r
+}\r
+\r
+const stlplus::lm_file_dependency& stlplus::lm_unit::file_dependency(unsigned i) const\r
+{\r
+  return m_dependencies.file_dependency(i);\r
+}\r
+\r
+void stlplus::lm_unit::file_erase(unsigned i)\r
+{\r
+  m_header_modified = true;\r
+  m_dependencies.file_erase(i);\r
+}\r
+\r
+// unit dependencies\r
+\r
+unsigned stlplus::lm_unit::unit_add(const lm_unit_dependency& dependency)\r
+{\r
+  m_header_modified = true;\r
+  return m_dependencies.unit_add(dependency);\r
+}\r
+\r
+unsigned stlplus::lm_unit::unit_size(void) const\r
+{\r
+  return m_dependencies.unit_size();\r
+}\r
+\r
+const stlplus::lm_unit_dependency& stlplus::lm_unit::unit_dependency(unsigned i) const\r
+{\r
+  return m_dependencies.unit_dependency(i);\r
+}\r
+\r
+void stlplus::lm_unit::unit_erase(unsigned i)\r
+{\r
+  m_header_modified = true;\r
+  m_dependencies.unit_erase(i);\r
+}\r
+\r
+const stlplus::lm_dependencies& stlplus::lm_unit::dependencies(void) const\r
+{\r
+  return m_dependencies;\r
+}\r
+\r
+void stlplus::lm_unit::set_dependencies(const lm_dependencies& dependencies)\r
+{\r
+  m_header_modified = true;\r
+  m_dependencies = dependencies;\r
+}\r
+\r
+void stlplus::lm_unit::clear_dependencies(void)\r
+{\r
+  m_header_modified = true;\r
+  m_dependencies.clear();\r
+}\r
+\r
+bool stlplus::lm_unit::empty_dependencies(void) const\r
+{\r
+  return m_dependencies.empty();\r
+}\r
+\r
+// dependency checking\r
+\r
+bool stlplus::lm_unit::out_of_date(void) const\r
+{\r
+  return m_library->out_of_date(m_name);\r
+}\r
+\r
+bool stlplus::lm_unit::up_to_date(void) const\r
+{\r
+  return m_library->up_to_date(m_name);\r
+}\r
+\r
+stlplus::lm_dependencies stlplus::lm_unit::out_of_date_reason(void) const\r
+{\r
+  return m_library->out_of_date_reason(m_name);\r
+}\r
+\r
+// supplementary data\r
+\r
+const std::string& stlplus::lm_unit::supplementary_data(void) const\r
+{\r
+  return m_supplement;\r
+}\r
+\r
+void stlplus::lm_unit::set_supplementary_data(const std::string& data)\r
+{\r
+  m_supplement = data;\r
+  m_header_modified = true;\r
+}\r
+\r
+////////////////////////////////////////\r
+// unit data management\r
+\r
+bool stlplus::lm_unit::load(void)\r
+{\r
+  if (out_of_date()) return false;\r
+  if (m_loaded) return true;\r
+  // get the user data for this type\r
+  lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type());\r
+  if (callback == m_library->m_manager->m_callbacks.end()) return false;\r
+  void* data = callback->second.m_type_data;\r
+  bool result = read(filename(), data);\r
+  m_loaded = true;\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_unit::save(void)\r
+{\r
+  if (!m_marked) return true;\r
+  if (!m_loaded) return false;\r
+  // get the user data for this type\r
+  lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type());\r
+  if (callback == m_library->m_manager->m_callbacks.end()) return false;\r
+  void* data = callback->second.m_type_data;\r
+  bool result = write(filename(), data);\r
+  if (result) m_marked = false;\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_unit::loaded(void) const\r
+{\r
+  return m_loaded;\r
+}\r
+\r
+void stlplus::lm_unit::mark(void)\r
+{\r
+  m_marked = true;\r
+}\r
+\r
+time_t stlplus::lm_unit::modified(void) const\r
+{\r
+  return file_modified(filename());\r
+}\r
+\r
+////////////////////////////////////////\r
+// containing library manager details\r
+\r
+const stlplus::lm_library* stlplus::lm_unit::library(void) const\r
+{\r
+  return m_library;\r
+}\r
+\r
+stlplus::lm_library* stlplus::lm_unit::library(void)\r
+{\r
+  return m_library;\r
+}\r
+\r
+const std::string& stlplus::lm_unit::library_name(void) const\r
+{\r
+  return m_library->name();\r
+}\r
+\r
+const std::string& stlplus::lm_unit::library_path(void) const\r
+{\r
+  return m_library->path();\r
+}\r
+\r
+// error handling - these apply to the last read/write operation\r
+\r
+bool stlplus::lm_unit::error(void) const\r
+{\r
+  return m_error;\r
+}\r
+\r
+// functions that customise subclasses of this superclass\r
+\r
+bool stlplus::lm_unit::read(const std::string& filespec, void* type_data)\r
+{\r
+  std::ifstream input(filespec.c_str());\r
+  bool result = read(input, type_data);\r
+  if (input.fail())\r
+  {\r
+    result = false;\r
+    m_error = true;\r
+  }\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_unit::read(std::istream&, void*)\r
+{\r
+  return false;\r
+}\r
+\r
+bool stlplus::lm_unit::write(const std::string& filespec, void* type_data)\r
+{\r
+  std::ofstream output(filespec.c_str());\r
+  bool result = write(output, type_data);\r
+  if (output.fail())\r
+  {\r
+    result = false;\r
+    m_error = true;\r
+  }\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_unit::write(std::ostream&, void*)\r
+{\r
+  return false;\r
+}\r
+\r
+bool stlplus::lm_unit::purge(void)\r
+{\r
+  return true;\r
+}\r
+\r
+stlplus::lm_unit* stlplus::lm_unit::clone(void) const\r
+{\r
+  return new lm_unit(*this);\r
+}\r
+\r
+bool stlplus::lm_unit::print(std::ostream& str) const\r
+{\r
+  str << m_name << " " << (m_loaded ? "loaded" : "") << " " << (m_marked ? "needs saving" : "");\r
+  return !str.fail();\r
+}\r
+\r
+bool stlplus::lm_unit::print_long(std::ostream& str) const\r
+{\r
+  str << "header:" << std::endl;\r
+  str << "  name: " << m_name.name() << std::endl;\r
+  str << "  type: " << library()->manager()->description(m_name.type()) << std::endl;\r
+  str << "  dependencies:" << std::endl;\r
+  // Note: I've inlined this rather than call the above-defined print for dependencies\r
+  // This is so that I can use the library manager to look up the descriptions of unit types\r
+  // I can also convert paths so that they are relative to the current directory rather than the library\r
+  // print the source file dependency if present\r
+  if (m_dependencies.source_file_present())\r
+  {\r
+    str << "  source file: ";\r
+    str << filespec_to_relative_path(m_dependencies.source_file().path_full(library()->path()));\r
+    if (m_dependencies.source_file().line() != 0)\r
+      str << ":" << m_dependencies.source_file().line() << ":" << m_dependencies.source_file().column();\r
+    str << std::endl;\r
+  }\r
+  // now print other file dependencies\r
+  // convert these to relative paths too\r
+  for (unsigned f = 0; f < m_dependencies.file_size(); f++)\r
+  {\r
+    str << "  file: ";\r
+    str << filespec_to_relative_path(m_dependencies.file_dependency(f).path_full(library()->path()));\r
+    if (m_dependencies.file_dependency(f).line() != 0)\r
+      str << ":" << m_dependencies.file_dependency(f).line() << ":" << m_dependencies.file_dependency(f).column();\r
+    str << std::endl;\r
+  }\r
+  // now print unit dependencies\r
+  // convert unit types to their descriptive strings\r
+  for (unsigned u = 0; u < m_dependencies.unit_size(); u++)\r
+  {\r
+    str << "  " << library()->manager()->description(m_dependencies.unit_dependency(u).type()) << ": ";\r
+    str << m_dependencies.unit_dependency(u).library() << "." << m_dependencies.unit_dependency(u).name();\r
+    str << std::endl;\r
+  }\r
+  if (!m_supplement.empty())\r
+  {\r
+    str << "  supplementary data: " << m_supplement << std::endl;\r
+  }\r
+  return !str.fail();\r
+}\r
+\r
+// header file management\r
+\r
+std::string stlplus::lm_unit::filename(void) const\r
+{\r
+  return stlplus::create_filespec(library_path(), m_name.name(), m_name.type());\r
+}\r
+\r
+std::string stlplus::lm_unit::header_filename(void) const\r
+{\r
+  return stlplus::create_filespec(library_path(), m_name.name(), m_name.type() + std::string(HeaderExtension));\r
+}\r
+\r
+bool stlplus::lm_unit::read_header(void)\r
+{\r
+  if (file_exists(header_filename()))\r
+  {\r
+    std::ifstream input(header_filename().c_str());\r
+    m_dependencies.read(input);\r
+    input.get();\r
+    std::getline(input, m_supplement);\r
+  }\r
+  m_header_modified = false;\r
+  return true;\r
+}\r
+\r
+bool stlplus::lm_unit::write_header(void)\r
+{\r
+  if (!m_header_modified) return true;\r
+  std::ofstream output(header_filename().c_str());\r
+  m_dependencies.write(output);\r
+  output << m_supplement << std::endl;\r
+  m_header_modified = false;\r
+  return true;\r
+}\r
+\r
+// print diagnostics\r
+\r
+std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit& u)\r
+{\r
+  u.print(str);\r
+  return str;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// lm_library\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// constructors/destructors\r
+// copy operations only legal on unopened libraries\r
+\r
+stlplus::lm_library::lm_library(library_manager* manager) : m_writable(false), m_manager(manager)\r
+{\r
+}\r
+\r
+stlplus::lm_library::lm_library(const lm_library& library) : m_writable(library.m_writable), m_manager(library.m_manager)\r
+{\r
+}\r
+\r
+stlplus::lm_library& stlplus::lm_library::operator = (const stlplus::lm_library& library)\r
+{\r
+  m_writable = library.m_writable;\r
+  m_manager = library.m_manager;\r
+  return *this;\r
+}\r
+\r
+stlplus::lm_library::~lm_library(void)\r
+{\r
+  close();\r
+}\r
+\r
+const stlplus::library_manager* stlplus::lm_library::manager(void) const\r
+{\r
+  return m_manager;\r
+}\r
+\r
+stlplus::library_manager* stlplus::lm_library::manager(void)\r
+{\r
+  return m_manager;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// initialisers\r
+\r
+bool stlplus::lm_library::create(const std::string& name, const std::string& path, bool writable)\r
+{\r
+  close();\r
+  if (!create_library(path, m_manager->m_owner, name, writable)) return false;\r
+  return open(path);\r
+}\r
+\r
+bool stlplus::lm_library::open(const std::string& path)\r
+{\r
+  close();\r
+  if (!read_context(path, m_manager->m_owner, m_name, m_writable)) return false;\r
+  // convert path to full path on load\r
+  m_path = folder_to_path(path);\r
+  return load_types();\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// management of types\r
+\r
+bool stlplus::lm_library::load_type(const std::string& type)\r
+{\r
+  lm_callback_map::iterator callback = m_manager->m_callbacks.find(type);\r
+  if (callback == m_manager->m_callbacks.end()) return false;\r
+  // a null callback means create a dummy unit from the baseclass only\r
+  lm_create_callback fn = callback->second.m_callback;\r
+  void* data = callback->second.m_type_data;\r
+  // for each file in the library folder that matches the type of the create callback, create an unloaded unit\r
+  std::vector<std::string> files = folder_wildcard(m_path, stlplus::create_filename("*", type), false, true);\r
+  for (unsigned i = 0; i < files.size(); i++)\r
+  {\r
+    // key by unit name - lowercase name if case-insensitive\r
+    lm_unit_name uname(basename_part(files[i]),type);\r
+    if (!m_manager->m_unit_case) uname.lowercase();\r
+    lm_unit_ptr unit;\r
+    // if there's a callback, use it to create the subclass, else create the\r
+    // superclass which only contains header information\r
+    if (fn)\r
+      unit.set(fn(uname,this,data));\r
+    else\r
+      unit.set(new lm_unit(uname,this));\r
+    m_units[uname] = unit;\r
+  }\r
+  return true;\r
+}\r
+\r
+bool stlplus::lm_library::load_types(void)\r
+{\r
+  bool result = true;\r
+  for (lm_callback_map::const_iterator i = m_manager->m_callbacks.begin(); i != m_manager->m_callbacks.end(); i++)\r
+    result &= load_type(i->first);\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_library::remove_type(const std::string& type)\r
+{\r
+  bool result = true;\r
+  std::vector<std::string> units = names(type);\r
+  for (std::vector<std::string>::iterator i = units.begin(); i != units.end(); i++)\r
+  {\r
+    lm_unit_name name(*i, type);\r
+    std::map<lm_unit_name,lm_unit_ptr>::iterator ni = local_find(name);\r
+    if (ni != m_units.end())\r
+      m_units.erase(ni);\r
+  }\r
+  return result;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// whole library operations\r
+\r
+bool stlplus::lm_library::load(void)\r
+{\r
+  bool result = true;\r
+  for (std::map<lm_unit_name,lm_unit_ptr>::iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    result &= i->second->load();\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_library::save(void)\r
+{\r
+  bool result = true;\r
+  for (std::map<lm_unit_name,lm_unit_ptr>::iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    result &= i->second->save();\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_library::purge(void)\r
+{\r
+  bool result = true;\r
+  for (std::map<lm_unit_name,lm_unit_ptr>::iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    result &= i->second->purge();\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_library::close(void)\r
+{\r
+  bool result = save();\r
+  m_units.clear();\r
+  m_path = "";\r
+  m_writable = false;\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_library::erase(void)\r
+{\r
+  // preserve the path because close destroys it\r
+  std::string path = m_path;\r
+  return close() && erase_library(path, m_manager->m_owner);\r
+}\r
+\r
+const std::string& stlplus::lm_library::name(void) const\r
+{\r
+  return m_name;\r
+}\r
+\r
+const std::string& stlplus::lm_library::path(void) const\r
+{\r
+  return m_path;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// managing read/write status\r
+\r
+bool stlplus::lm_library::set_read_write(bool writable)\r
+{\r
+  if (m_writable == writable) return true;\r
+  if (os_read_only()) return false;\r
+  m_writable = writable;\r
+  if (!write_context(m_path, m_manager->m_owner, m_name, m_writable))\r
+    read_context(m_path, m_manager->m_owner, m_name, m_writable);\r
+  return m_writable == writable;\r
+}\r
+\r
+bool stlplus::lm_library::set_writable(void)\r
+{\r
+  return set_read_write(true);\r
+}\r
+\r
+bool stlplus::lm_library::set_read_only(void)\r
+{\r
+  return set_read_write(false);\r
+}\r
+\r
+bool stlplus::lm_library::writable(void) const\r
+{\r
+  return os_writable() && lm_writable();\r
+}\r
+\r
+bool stlplus::lm_library::read_only(void) const\r
+{\r
+  return os_read_only() || lm_read_only();\r
+}\r
+\r
+bool stlplus::lm_library::os_writable(void) const\r
+{\r
+  return folder_writable(path());\r
+}\r
+\r
+bool stlplus::lm_library::os_read_only(void) const\r
+{\r
+  return !folder_writable(path());\r
+}\r
+\r
+bool stlplus::lm_library::lm_writable(void) const\r
+{\r
+  return m_writable;\r
+}\r
+\r
+bool stlplus::lm_library::lm_read_only(void) const\r
+{\r
+  return !m_writable;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// unit management\r
+\r
+std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::iterator stlplus::lm_library::local_find(const lm_unit_name& name)\r
+{\r
+  // implement the case-sensitivity\r
+  lm_unit_name local = name;\r
+  if (!m_manager->m_unit_case) local.lowercase();\r
+  return m_units.find(local);\r
+}\r
+\r
+std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::const_iterator stlplus::lm_library::local_find(const lm_unit_name& name) const\r
+{\r
+  // implement the case-sensitivity\r
+  lm_unit_name local = name;\r
+  if (!m_manager->m_unit_case) local.set_name(lowercase(local.name()));\r
+  return m_units.find(local);\r
+}\r
+\r
+bool stlplus::lm_library::exists(const lm_unit_name& name) const\r
+{\r
+  return find(name).present();\r
+}\r
+\r
+stlplus::lm_unit_ptr stlplus::lm_library::create(const stlplus::lm_unit_name& name)\r
+{\r
+  if (read_only()) return lm_unit_ptr();\r
+  // preserve the unit's name, but use a lowercase key in case-insensitive mode\r
+  lm_unit_name uname = name;\r
+  if (!m_manager->m_unit_case) uname.lowercase();\r
+  // remove any existing unit with the same name\r
+  erase(uname);\r
+  // use the callbacks to create a new unit\r
+  lm_callback_map::iterator callback = m_manager->m_callbacks.find(name.type());\r
+  if (callback == m_manager->m_callbacks.end()) return lm_unit_ptr();\r
+  lm_unit_ptr new_unit;\r
+  new_unit.set(callback->second.m_callback(name,this,callback->second.m_type_data));\r
+  new_unit->m_loaded = true;\r
+  // add it to the library manager\r
+  m_units[uname] = new_unit;\r
+  return m_units[uname];\r
+}\r
+\r
+bool stlplus::lm_library::loaded(const lm_unit_name& name) const\r
+{\r
+  lm_unit_ptr unit = find(name);\r
+  return unit && unit->loaded();\r
+}\r
+\r
+bool stlplus::lm_library::load(const lm_unit_name& name)\r
+{\r
+  lm_unit_ptr unit = find(name);\r
+  if (!unit) return false;\r
+  return unit->load();\r
+}\r
+\r
+bool stlplus::lm_library::purge(const lm_unit_name& name)\r
+{\r
+  lm_unit_ptr unit = find(name);\r
+  if (!unit) return false;\r
+  bool result = save(name);\r
+  result &= unit->purge();\r
+  unit->m_loaded = false;\r
+  return result;\r
+}\r
+\r
+bool stlplus::lm_library::save(const lm_unit_name& name)\r
+{\r
+  if (read_only()) return false;\r
+  lm_unit_ptr unit = find(name);\r
+  if (!unit) return false;\r
+  return unit->save();\r
+}\r
+\r
+bool stlplus::lm_library::erase(const lm_unit_name& name)\r
+{\r
+  if (read_only()) return false;\r
+  std::map<lm_unit_name,lm_unit_ptr>::iterator i = local_find(name);\r
+  if (i == m_units.end()) return false;\r
+  std::string spec = i->second->filename();\r
+  std::string header_spec = i->second->header_filename();\r
+  m_units.erase(i);\r
+  file_delete(spec);\r
+  file_delete(header_spec);\r
+  return true;\r
+}\r
+\r
+bool stlplus::lm_library::mark(const lm_unit_name& name)\r
+{\r
+  if (read_only()) return false;\r
+  lm_unit_ptr unit = find(name);\r
+  if (!unit) return false;\r
+  unit->mark();\r
+  return true;\r
+}\r
+\r
+time_t stlplus::lm_library::modified(const lm_unit_name& name) const\r
+{\r
+  lm_unit_ptr unit = find(name);\r
+  return unit ? unit->modified() : 0;\r
+}\r
+\r
+bool stlplus::lm_library::erase_by_source(const std::string& source_file)\r
+{\r
+  if (read_only()) return false;\r
+  if (source_file.empty()) return false;\r
+  bool result = false;\r
+  std::string source_file_full = filespec_to_path(source_file);\r
+  // erase by unit name so that I don't have to deal with an iterator to a changing map\r
+  std::vector<lm_unit_name> units = names();\r
+  for (std::vector<lm_unit_name>::iterator i = units.begin(); i != units.end(); i++)\r
+  {\r
+    lm_unit_ptr unit = find(*i);\r
+    if (unit && unit->source_file_present())\r
+    {\r
+      std::string file_full = unit->source_file().path_full(unit->library_path());\r
+      if (file_full == source_file_full)\r
+      {\r
+        erase(*i);\r
+        result = true;\r
+      }\r
+    }\r
+  }\r
+  return result;\r
+}\r
+\r
+const stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name) const\r
+{\r
+  std::map<lm_unit_name,lm_unit_ptr>::const_iterator i = local_find(name);\r
+  if (i == m_units.end()) return lm_unit_ptr();\r
+  return i->second;\r
+}\r
+\r
+stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name)\r
+{\r
+  std::map<lm_unit_name,lm_unit_ptr>::iterator i = local_find(name);\r
+  if (i == m_units.end()) return lm_unit_ptr();\r
+  return i->second;\r
+}\r
+\r
+std::vector<stlplus::lm_unit_name> stlplus::lm_library::names(void) const\r
+{\r
+  std::vector<stlplus::lm_unit_name> result;\r
+  for (std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::const_iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    result.push_back(i->second->unit_name());\r
+  return result;\r
+}\r
+\r
+std::vector<std::string> stlplus::lm_library::names(const std::string& type) const\r
+{\r
+  std::vector<std::string> result;\r
+  for (std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::const_iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    if (i->first.type() == type)\r
+      result.push_back(i->second->name());\r
+  return result;\r
+}\r
+\r
+std::vector<stlplus::lm_unit_ptr> stlplus::lm_library::handles(void) const\r
+{\r
+  std::vector<stlplus::lm_unit_ptr> result;\r
+  for (std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::const_iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    result.push_back(i->second);\r
+  return result;\r
+}\r
+\r
+std::vector<stlplus::lm_unit_ptr> stlplus::lm_library::handles(const std::string& type) const\r
+{\r
+  std::vector<stlplus::lm_unit_ptr> result;\r
+  for (std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::const_iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    if (i->first.type() == type)\r
+      result.push_back(i->second);\r
+  return result;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// dependency checking\r
+\r
+bool stlplus::lm_library::out_of_date(const stlplus::lm_unit_name& name) const\r
+{\r
+  return m_manager->out_of_date(m_name, name);\r
+}\r
+\r
+bool stlplus::lm_library::up_to_date(const stlplus::lm_unit_name& name) const\r
+{\r
+  return m_manager->up_to_date(m_name, name);\r
+}\r
+\r
+stlplus::lm_dependencies stlplus::lm_library::out_of_date_reason(const stlplus::lm_unit_name& unit) const\r
+{\r
+  return m_manager->out_of_date_reason(m_name, unit);\r
+}\r
+\r
+std::pair<bool,unsigned> stlplus::lm_library::tidy(void)\r
+{\r
+  std::pair<bool,unsigned> result = std::make_pair(true,0);\r
+  // erase every unit that is out of date\r
+  // this will potentially make other units out of date, so keep erasing until\r
+  // everything is up to date or an error occurs\r
+  for (;;)\r
+  {\r
+    std::vector<stlplus::lm_unit_name> units = names();\r
+    unsigned initial_count = result.second;\r
+    for (std::vector<stlplus::lm_unit_name>::iterator i = units.begin(); i != units.end(); i++)\r
+    {\r
+      if (out_of_date(*i))\r
+      {\r
+        if (!erase(*i))\r
+          result.first = false;\r
+        else\r
+          result.second++;\r
+      }\r
+    }\r
+    if (!result.first) break;\r
+    if (result.second == initial_count) break;\r
+  }\r
+  return result;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// do-everything print routine!\r
+\r
+bool stlplus::lm_library::pretty_print(std::ostream& str,\r
+                                       bool print_units,\r
+                                       const std::string& type) const\r
+{\r
+  // print the library information\r
+  if (this == m_manager->work())\r
+    str << "->> ";\r
+  else\r
+    str << "    ";\r
+  str << name() << " in directory " << folder_to_relative_path(path());\r
+  if (read_only()) str << " (locked)";\r
+  str << std::endl;\r
+  // select the units\r
+  if (print_units)\r
+  {\r
+    // separate into a block per unit kind\r
+    for (lm_callback_map::const_iterator j = m_manager->m_callbacks.begin(); j != m_manager->m_callbacks.end(); j++)\r
+    {\r
+      // select the requested unit kind\r
+      if (type.empty() || type == j->first)\r
+      {\r
+        // get all the units of this kind\r
+        std::vector<std::string> unit_names = names(j->first);\r
+        if (!unit_names.empty())\r
+        {\r
+          str << "    " << j->second.m_description << std::endl;\r
+          for (unsigned k = 0; k < unit_names.size(); k++)\r
+          {\r
+            lm_dependencies reason = out_of_date_reason(stlplus::lm_unit_name(unit_names[k],j->first));\r
+            str << "    - " << unit_names[k];\r
+            if (!reason.empty()) str << " (out of date)";\r
+            str << std::endl;\r
+            if (!reason.empty())\r
+            {\r
+              if (reason.source_file_present())\r
+              {\r
+                const lm_file_dependency& file = reason.source_file();\r
+                str << "    * source file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl;\r
+              }\r
+              for (unsigned i1 = 0; i1 < reason.file_size(); i1++)\r
+              {\r
+                const lm_file_dependency& file = reason.file_dependency(i1);\r
+                str << "    * file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl;\r
+              }\r
+              for (unsigned i2 = 0; i2 < reason.unit_size(); i2++)\r
+              {\r
+                const lm_unit_dependency& file = reason.unit_dependency(i2);\r
+                lm_callback_map::const_iterator entry = m_manager->m_callbacks.find(file.type());\r
+                str << "    * " << entry->second.m_description << " " << file.library() << "." << file.name() << " has changed" << std::endl;\r
+              }\r
+            }\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+  return true;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// diagnostic print routines\r
+\r
+bool stlplus::lm_library::print(std::ostream& str) const\r
+{\r
+  str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl;\r
+  for (std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::const_iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    i->second->print(str);\r
+  return !str.fail();\r
+}\r
+\r
+bool stlplus::lm_library::print_long(std::ostream& str) const\r
+{\r
+  str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl;\r
+  for (std::map<stlplus::lm_unit_name,stlplus::lm_unit_ptr>::const_iterator i = m_units.begin(); i != m_units.end(); i++)\r
+    i->second->print_long(str);\r
+  return !str.fail();\r
+}\r
+\r
+std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_library& lib)\r
+{\r
+  lib.print(str);\r
+  return str;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// library manager\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// static functions allow you to test whether a directory is a library before opening it\r
+// you can also find the library's name without opening it\r
+\r
+bool stlplus::library_manager::is_library(const std::string& path, const std::string& owner)\r
+{\r
+  std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension);\r
+  return file_exists(spec);\r
+}\r
+\r
+std::string stlplus::library_manager::library_name(const std::string& path, const std::string& owner)\r
+{\r
+  std::string name;\r
+  bool writable = false;\r
+  if (!read_context(path, owner, name, writable))\r
+    return std::string();\r
+  return name;\r
+}\r
+\r
+bool stlplus::library_manager::is_library(const std::string& path)\r
+{\r
+  return is_library(path, m_owner);\r
+}\r
+\r
+std::string stlplus::library_manager::library_name(const std::string& path)\r
+{\r
+  return library_name(path, m_owner);\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// tructors\r
+\r
+stlplus::library_manager::library_manager(const std::string& owner, bool library_case, bool unit_case) :\r
+  m_owner(owner), m_ini_files(0), m_library_case(library_case), m_unit_case(unit_case)\r
+{\r
+}\r
+\r
+stlplus::library_manager::~library_manager(void)\r
+{\r
+  close();\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// case sensitivity\r
+\r
+bool stlplus::library_manager::library_case(void) const\r
+{\r
+  return m_library_case;\r
+}\r
+\r
+void stlplus::library_manager::set_library_case(bool library_case)\r
+{\r
+  m_library_case = library_case;\r
+}\r
+\r
+bool stlplus::library_manager::unit_case(void) const\r
+{\r
+  return m_unit_case;\r
+}\r
+\r
+void stlplus::library_manager::set_unit_case(bool unit_case)\r
+{\r
+  m_unit_case = unit_case;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// type handling\r
+\r
+bool stlplus::library_manager::add_type(const std::string& type,\r
+                                        const std::string& description,\r
+                                        lm_create_callback fn,\r
+                                        void* type_data)\r
+{\r
+  bool result = true;\r
+  m_callbacks[type] = lm_callback_entry(fn, description, type_data);\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result &= i->load_type(type);\r
+  return result;\r
+}\r
+\r
+bool stlplus::library_manager::remove_type(const std::string& type)\r
+{\r
+  bool result = true;\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result &= i->remove_type(type);\r
+  m_callbacks.erase(type);\r
+  return result;\r
+}\r
+\r
+std::vector<std::string> stlplus::library_manager::types(void) const\r
+{\r
+  std::vector<std::string> result;\r
+  for (lm_callback_map::const_iterator i = m_callbacks.begin(); i != m_callbacks.end(); i++)\r
+    result.push_back(i->first);\r
+  return result;\r
+}\r
+\r
+std::string stlplus::library_manager::description(const std::string& type) const\r
+{\r
+  lm_callback_map::const_iterator found = m_callbacks.find(type);\r
+  if (found == m_callbacks.end()) return std::string();\r
+  return found->second.m_description;\r
+}\r
+\r
+stlplus::lm_create_callback stlplus::library_manager::callback(const std::string& type) const\r
+{\r
+  lm_callback_map::const_iterator found = m_callbacks.find(type);\r
+  if (found == m_callbacks.end()) return 0;\r
+  return found->second.m_callback;\r
+}\r
+\r
+void* stlplus::library_manager::type_data(const std::string& type) const\r
+{\r
+  lm_callback_map::const_iterator found = m_callbacks.find(type);\r
+  if (found == m_callbacks.end()) return 0;\r
+  return found->second.m_type_data;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// mapping file handling\r
+\r
+void stlplus::library_manager::set_mapping_file(const std::string& mapping_file)\r
+{\r
+  m_mapping_file = mapping_file;\r
+}\r
+\r
+bool stlplus::library_manager::load_mappings(const std::string& mapping_file)\r
+{\r
+  m_mapping_file = mapping_file;\r
+  if (!file_exists(mapping_file))\r
+  {\r
+    return false;\r
+  }\r
+  std::ifstream input(mapping_file.c_str());\r
+  if (input.fail())\r
+    return false;\r
+  // each line of the map file is a path to a library\r
+  // the first line is the work library - may be empty\r
+  // mappings are saved as paths relative to the mapping file and converted to full paths on load\r
+  bool result = true;\r
+  unsigned line = 1;\r
+  for (std::string path = ""; std::getline(input,path); line++)\r
+  {\r
+    if (path.empty()) continue;\r
+    std::string full_path = folder_to_path(folder_part(m_mapping_file), path);\r
+    if (!is_library(full_path))\r
+    {\r
+      result = false;\r
+    }\r
+    else\r
+    {\r
+      lm_library* lib = open(full_path);\r
+      if (!lib)\r
+        result = false;\r
+      else if (line == 1)\r
+        setwork(lib->name());\r
+    }\r
+  }\r
+  return result;\r
+}\r
+\r
+std::string stlplus::library_manager::mapping_file()\r
+{\r
+  return m_mapping_file;\r
+}\r
+\r
+bool stlplus::library_manager::set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_name)\r
+{\r
+  if (!ini_files) return false;\r
+  bool result = true;\r
+  m_ini_files = ini_files;\r
+  m_ini_section = library_section;\r
+  m_ini_work = work_name;\r
+  // now load the existing library mappings if present - in any case create the sections\r
+  // Note: a library mapping is saved as a path relative to the ini file - on load convert it to a path relative to the current directory\r
+  if (m_ini_files->section_exists(m_ini_section))\r
+  {\r
+    std::vector<std::string> library_names = m_ini_files->variable_names(m_ini_section);\r
+    std::string work_name;\r
+    for (unsigned i = 0; i < library_names.size(); i++)\r
+    {\r
+      std::string library_name = library_names[i];\r
+      // if the variable name is the work name, then this is a mapping to an existing library name\r
+      // if it is null, then it is masking a global library definition so ignore it\r
+      // otherwise it is a mapping to a directory containing the library\r
+      if (library_name.empty())\r
+      {\r
+      }\r
+      else if (library_name == m_ini_work)\r
+      {\r
+        work_name = m_ini_files->variable_value(m_ini_section, library_name);\r
+      }\r
+      else\r
+      {\r
+        std::string value = m_ini_files->variable_value(m_ini_section, library_name);\r
+        std::string filename = m_ini_files->variable_filename(m_ini_section, library_name);\r
+        // get the path to the ini file defining this library, strip off the ini filename to get the folder\r
+        // then combine this with the library path from that ini file to the library to get a full path to the library\r
+        // whew!\r
+        std::string full_path = folder_to_path(folder_part(filename),value);\r
+        if (!is_library(full_path))\r
+          result = false;\r
+        else\r
+        {\r
+          lm_library* lib = open(full_path);\r
+          if (!lib)\r
+            result = false;\r
+        }\r
+      }\r
+    }\r
+    // work must be set after all the libraries have been opened because it is\r
+    // illegal to set work to a library that doesn't already exist in the\r
+    // library manager\r
+    if (work_name.empty())\r
+      unsetwork();\r
+    else\r
+      result &= setwork(work_name);\r
+  }\r
+  return result;\r
+}\r
+\r
+stlplus::ini_manager* stlplus::library_manager::get_ini_manager(void) const\r
+{\r
+  return m_ini_files;\r
+}\r
+\r
+bool stlplus::library_manager::save_mappings (void)\r
+{\r
+  bool result = true;\r
+  // save to mapping file or ini manager or both\r
+  if (!m_mapping_file.empty())\r
+  {\r
+    if (m_libraries.size() == 0)\r
+    {\r
+      // if the file would be empty, delete it\r
+      if (!file_delete(m_mapping_file))\r
+        result = false;\r
+    }\r
+    else\r
+    {\r
+      std::ofstream output(m_mapping_file.c_str());\r
+      if (output.fail())\r
+      {\r
+        result = false;\r
+      }\r
+      else\r
+      {\r
+        // each line of the map file is a path to a library\r
+        // the first line is the work library\r
+        // mappings are saved as relative paths to the mapping file and converted to full paths on load\r
+        if (!work_name().empty())\r
+          output << folder_to_relative_path(folder_part(m_mapping_file), path(work_name()));\r
+        output << std::endl;\r
+        std::vector<std::string> libraries = names();\r
+        for (unsigned i = 0; i < libraries.size(); ++i)\r
+        {\r
+          if (libraries[i] != work_name())\r
+            output << folder_to_relative_path(folder_part(m_mapping_file), path(libraries[i])) << std::endl;\r
+        }\r
+        if (output.fail())\r
+          result = false;\r
+      }\r
+    }\r
+  }\r
+  if (m_ini_files)\r
+  {\r
+    // this is somewhat tricky!\r
+    // first remove all local mappings\r
+    // then need to compare the surviving library mappings with the contents of the library manager\r
+    // if there's a library in the ini files not in the library manager, mask it with an empty local declaration\r
+    // if there's a library in the ini files with the same mapping as the library manager, do nothing\r
+    // if there's a library in the ini files with a different mapping write that library mapping\r
+    // if there's a mapping missing from the ini files, write it\r
+    // finally write the work mapping if there is one\r
+    // clear all local mappings\r
+    // TODO - rework this so that the ini files only change if the mappings change\r
+    m_ini_files->clear_section(m_ini_section);\r
+    m_ini_files->add_comment(m_ini_section, "generated automatically by the library manager");\r
+    // look for globally defined library mappings that need to be overridden in the local ini file\r
+    std::vector<std::string> ini_names = m_ini_files->variable_names(m_ini_section);\r
+    for (unsigned i = 0; i < ini_names.size(); i++)\r
+    {\r
+      std::string ini_name = ini_names[i];\r
+      // check for a global library that needs to be locally masked\r
+      if (!exists(ini_name))\r
+        m_ini_files->add_variable(m_ini_section, ini_name, "");\r
+      else\r
+      {\r
+        // check for a library that is locally remapped\r
+        std::string value = m_ini_files->variable_value(m_ini_section, ini_name);\r
+        std::string filename = m_ini_files->variable_filename(m_ini_section, ini_name);\r
+        std::string full_ini_path = folder_to_path(folder_part(filename), value);\r
+        std::string full_lib_path = folder_to_path(path(ini_name));\r
+        if (full_ini_path != full_lib_path)\r
+        {\r
+          // write the path relative to the ini file\r
+          std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path);\r
+          m_ini_files->add_variable(m_ini_section, ini_name, relative_path);\r
+        }\r
+      }\r
+    }\r
+    // now scan the library for mappings that aren't yet in the ini file\r
+    std::vector<std::string> lib_names = names();\r
+    for (unsigned j = 0; j < lib_names.size(); j++)\r
+    {\r
+      std::string lib_name = lib_names[j];\r
+      if (std::find(ini_names.begin(), ini_names.end(), lib_name) == ini_names.end())\r
+      {\r
+        // write the path relative to the ini file\r
+        std::string full_lib_path = folder_to_path(path(lib_name));\r
+        std::string filename = m_ini_files->variable_filename(m_ini_section, lib_name);\r
+        std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path);\r
+        m_ini_files->add_variable(m_ini_section, lib_name, relative_path);\r
+      }\r
+    }\r
+    // write the work library - also write a blank value if work is not set but is defined in another library\r
+    if (!work_name().empty())\r
+      m_ini_files->add_variable(m_ini_section, m_ini_work, work_name());\r
+    else if (m_ini_files->variable_exists(m_ini_section, m_ini_work))\r
+      m_ini_files->add_variable(m_ini_section, m_ini_work, "");\r
+    m_ini_files->add_blank(m_ini_section);\r
+    // remove the section from the ini file manager if there's nothing in it\r
+    if (m_ini_files->empty_section(m_ini_section))\r
+      m_ini_files->erase_section(m_ini_section);\r
+    if (!m_ini_files->save())\r
+      result = false;\r
+  }\r
+  return result;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// library management\r
+\r
+bool stlplus::library_manager::exists(const std::string& name) const\r
+{\r
+  return find(name) != 0;\r
+}\r
+\r
+stlplus::lm_library* stlplus::library_manager::create(const std::string& name, const std::string& path, bool writable)\r
+{\r
+  if (!create_library(path, m_owner, name, writable)) return 0;\r
+  return open(path);\r
+}\r
+\r
+stlplus::lm_library* stlplus::library_manager::open(const std::string& path)\r
+{\r
+  std::string name;\r
+  bool writable = false;\r
+  if (!read_context(path, m_owner, name, writable)) return 0;\r
+  // remove any pre-existing library with the same name\r
+  close(name);\r
+  // add the library to the manager and open it\r
+  m_libraries.push_back(lm_library(this));\r
+  if (!m_libraries.back().open(folder_to_path(path)))\r
+  {\r
+    // remove the library in the event of an error\r
+    m_libraries.erase(--m_libraries.end());\r
+    return 0;\r
+  }\r
+  return &m_libraries.back();\r
+}\r
+\r
+bool stlplus::library_manager::load(const std::string& name)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->load();\r
+}\r
+\r
+bool stlplus::library_manager::save(const std::string& name)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->save();\r
+}\r
+\r
+bool stlplus::library_manager::purge(const std::string& name)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->purge();\r
+}\r
+\r
+bool stlplus::library_manager::close(const std::string& name)\r
+{\r
+  bool result= true;\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  result &= found->close();\r
+  m_libraries.erase(found);\r
+  if (name == m_work) m_work = "";\r
+  return result;\r
+}\r
+\r
+bool stlplus::library_manager::erase(const std::string& name)\r
+{\r
+  bool result= true;\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  result &= found->erase();\r
+  m_libraries.erase(found);\r
+  if (name == m_work) m_work = "";\r
+  return result;\r
+}\r
+\r
+// operations on all libraries - as above but applied to all the libraries in the manager\r
+\r
+bool stlplus::library_manager::load(void)\r
+{\r
+  bool result = true;\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result &= i->load();\r
+  return result;\r
+}\r
+\r
+bool stlplus::library_manager::save(void)\r
+{\r
+  bool result = true;\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result &= i->save();\r
+  return result;\r
+}\r
+\r
+bool stlplus::library_manager::purge(void)\r
+{\r
+  bool result = true;\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result &= i->purge();\r
+  return result;\r
+}\r
+\r
+bool stlplus::library_manager::close(void)\r
+{\r
+  bool result = true;\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); )\r
+  {\r
+    std::list<lm_library>::iterator next = i;\r
+    next++;\r
+    result &= i->close();\r
+    m_libraries.erase(i);\r
+    i = next;\r
+  }\r
+  return result;\r
+}\r
+\r
+bool stlplus::library_manager::erase(void)\r
+{\r
+  bool result = true;\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); )\r
+  {\r
+    std::list<lm_library>::iterator next = i;\r
+    next++;\r
+    result &= i->erase();\r
+    m_libraries.erase(i);\r
+    i = next;\r
+  }\r
+  return result;\r
+}\r
+\r
+// get name and path of a library - name can differ in case if the library manager is case-insensitive\r
+\r
+std::string stlplus::library_manager::name(const std::string& name) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return std::string();\r
+  return found->name();\r
+}\r
+\r
+std::string stlplus::library_manager::path(const std::string& name) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return std::string();\r
+  return found->path();\r
+}\r
+\r
+// control and test read/write status\r
+\r
+bool stlplus::library_manager::set_writable(const std::string& name)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->set_writable();\r
+}\r
+\r
+bool stlplus::library_manager::set_read_only(const std::string& name)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->set_read_only();\r
+}\r
+\r
+bool stlplus::library_manager::writable(const std::string& name) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->writable();\r
+}\r
+\r
+bool stlplus::library_manager::read_only(const std::string& name) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->read_only();\r
+}\r
+\r
+bool stlplus::library_manager::os_writable(const std::string& library) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(library);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->os_writable();\r
+}\r
+\r
+bool stlplus::library_manager::os_read_only(const std::string& library) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(library);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->os_read_only();\r
+}\r
+\r
+bool stlplus::library_manager::lm_writable(const std::string& library) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(library);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->lm_writable();\r
+}\r
+\r
+bool stlplus::library_manager::lm_read_only(const std::string& library) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(library);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->lm_read_only();\r
+}\r
+\r
+// find a library in the manager - returns null if not found\r
+\r
+stlplus::lm_library* stlplus::library_manager::find(const std::string& name)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return 0;\r
+  return &(*found);\r
+}\r
+\r
+const stlplus::lm_library* stlplus::library_manager::find(const std::string& name) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return 0;\r
+  return &(*found);\r
+}\r
+\r
+// get the set of all library names\r
+\r
+std::vector<std::string> stlplus::library_manager::names(void) const\r
+{\r
+  std::vector<std::string> result;\r
+  for (std::list<lm_library>::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result.push_back(i->name());\r
+  return result;\r
+}\r
+\r
+// get the set of all libraries\r
+\r
+std::vector<const stlplus::lm_library*> stlplus::library_manager::handles(void) const\r
+{\r
+  std::vector<const lm_library*> result;\r
+  for (std::list<lm_library>::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result.push_back(&(*i));\r
+  return result;\r
+}\r
+\r
+std::vector<stlplus::lm_library*> stlplus::library_manager::handles(void)\r
+{\r
+  std::vector<stlplus::lm_library*> result;\r
+  for (std::list<stlplus::lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    result.push_back(&(*i));\r
+  return result;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// current library management\r
+\r
+bool stlplus::library_manager::setwork(const std::string& name)\r
+{\r
+  unsetwork();\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  m_work = found->name();\r
+  return true;\r
+}\r
+\r
+bool stlplus::library_manager::unsetwork(void)\r
+{\r
+  m_work = "";\r
+  return true;\r
+}\r
+\r
+const stlplus::lm_library* stlplus::library_manager::work(void) const\r
+{\r
+  if (m_work.empty()) return 0;\r
+  return find(m_work);\r
+}\r
+\r
+stlplus::lm_library* stlplus::library_manager::work(void)\r
+{\r
+  if (m_work.empty()) return 0;\r
+  return find(m_work);\r
+}\r
+\r
+std::string stlplus::library_manager::work_name(void) const\r
+{\r
+  return m_work;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// unit management within a library\r
+\r
+bool stlplus::library_manager::exists(const std::string& name, const stlplus::lm_unit_name& unit) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->exists(unit);\r
+}\r
+\r
+stlplus::lm_unit_ptr stlplus::library_manager::create(const std::string& name, const stlplus::lm_unit_name& unit)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return stlplus::lm_unit_ptr();\r
+  return found->create(unit);\r
+}\r
+\r
+bool stlplus::library_manager::loaded(const std::string& name, const stlplus::lm_unit_name& unit) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return stlplus::lm_unit_ptr();\r
+  return found->loaded(unit);\r
+}\r
+\r
+bool stlplus::library_manager::load(const std::string& name, const stlplus::lm_unit_name& unit)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return stlplus::lm_unit_ptr();\r
+  return found->load(unit);\r
+}\r
+\r
+bool stlplus::library_manager::purge(const std::string& name, const stlplus::lm_unit_name& unit)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->purge(unit);\r
+}\r
+\r
+bool stlplus::library_manager::save(const std::string& name, const stlplus::lm_unit_name& unit)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->save(unit);\r
+}\r
+\r
+bool stlplus::library_manager::erase(const std::string& name, const stlplus::lm_unit_name& unit)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->erase(unit);\r
+}\r
+\r
+bool stlplus::library_manager::mark(const std::string& name, const stlplus::lm_unit_name& unit)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return false;\r
+  return found->mark(unit);\r
+}\r
+\r
+time_t stlplus::library_manager::modified(const std::string& name, const stlplus::lm_unit_name& unit) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return 0;\r
+  return found->modified(unit);\r
+}\r
+\r
+bool stlplus::library_manager::erase_by_source(const std::string& source_file)\r
+{\r
+  bool result = true;\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+    if (i->writable())\r
+      result &= i->erase_by_source(source_file);\r
+  return result;\r
+}\r
+\r
+const stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end())\r
+    return stlplus::lm_unit_ptr();\r
+  return found->find(unit);\r
+}\r
+\r
+stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return stlplus::lm_unit_ptr();\r
+  return found->find(unit);\r
+}\r
+\r
+std::vector<stlplus::lm_unit_name> stlplus::library_manager::names(const std::string& name) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return std::vector<stlplus::lm_unit_name>();\r
+  return found->names();\r
+}\r
+\r
+std::vector<std::string> stlplus::library_manager::names(const std::string& name, const std::string& type) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return std::vector<std::string>();\r
+  return found->names(type);\r
+}\r
+\r
+std::vector<stlplus::lm_unit_ptr> stlplus::library_manager::handles(const std::string& name) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return std::vector<stlplus::lm_unit_ptr>();\r
+  return found->handles();\r
+}\r
+\r
+std::vector<stlplus::lm_unit_ptr> stlplus::library_manager::handles(const std::string& name, const std::string& type) const\r
+{\r
+  std::list<lm_library>::const_iterator found = local_find(name);\r
+  if (found == m_libraries.end()) return std::vector<stlplus::lm_unit_ptr>();\r
+  return found->handles(type);\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// dependency checking\r
+// done at the top level because a global view of the libraries is required\r
+\r
+bool stlplus::library_manager::out_of_date(const std::string& library, const stlplus::lm_unit_name& name) const\r
+{\r
+  return !up_to_date(library, name);\r
+}\r
+\r
+bool stlplus::library_manager::up_to_date(const std::string& library, const stlplus::lm_unit_name& name) const\r
+{\r
+  lm_dependencies reason = out_of_date_reason(library, name);\r
+  return reason.empty();\r
+}\r
+\r
+stlplus::lm_dependencies stlplus::library_manager::out_of_date_reason(const std::string& library, const stlplus::lm_unit_name& name) const\r
+{\r
+  std::map<std::pair<std::string,stlplus::lm_unit_name>,lm_dependencies> visited;\r
+  return out_of_date_check(visited, this, library, name);\r
+}\r
+\r
+std::pair<bool,unsigned> stlplus::library_manager::tidy(const std::string& library)\r
+{\r
+  std::list<lm_library>::iterator found = local_find(library);\r
+  if (found == m_libraries.end()) return std::make_pair(false,0);\r
+  return found->tidy();\r
+}\r
+\r
+std::pair<bool,unsigned> stlplus::library_manager::tidy(void)\r
+{\r
+  std::pair<bool,unsigned> result = std::make_pair(true,0);\r
+  for (std::list<lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+  {\r
+    if (i->writable())\r
+    {\r
+      std::pair<bool,unsigned> library_result = i->tidy();\r
+      result.second += library_result.second;\r
+      result.first &= library_result.first;\r
+    }\r
+  }\r
+  return result;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////////////\r
+// do-everything print routine!\r
+\r
+bool stlplus::library_manager::pretty_print(std::ostream& str,\r
+                                            bool print_units,\r
+                                            const std::string& lib,\r
+                                            const std::string& type) const\r
+{\r
+  bool library_found = false;\r
+  for (std::list<lm_library>::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++)\r
+  {\r
+    // select the library\r
+    if (lib.empty() || lib == l->name())\r
+    {\r
+      l->pretty_print(str, print_units, type);\r
+      library_found = true;\r
+    }\r
+  }\r
+  if (!library_found)\r
+  {\r
+    if (lib.empty())\r
+      str << "there are no libraries in the library list" << std::endl;\r
+    else\r
+      str << "there is no library called " << lib << " in the library list" << std::endl;\r
+    return false;\r
+  }\r
+  return true;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// diagnostic print routines\r
+\r
+bool stlplus::library_manager::print(std::ostream& str) const\r
+{\r
+  for (std::list<lm_library>::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++)\r
+    l->print(str);\r
+  return str;\r
+}\r
+\r
+bool stlplus::library_manager::print_long(std::ostream& str) const\r
+{\r
+  for (std::list<lm_library>::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++)\r
+    l->print_long(str);\r
+  return str;\r
+}\r
+\r
+// find a library by name\r
+\r
+std::list<stlplus::lm_library>::iterator stlplus::library_manager::local_find(const std::string& name)\r
+{\r
+  for (std::list<stlplus::lm_library>::iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+  {\r
+    if (m_library_case)\r
+    {\r
+      if (i->name() == name) return i;\r
+    }\r
+    else\r
+    {\r
+      if (lowercase(i->name()) == lowercase(name)) return i;\r
+    }\r
+  }\r
+  return m_libraries.end();\r
+}\r
+\r
+std::list<stlplus::lm_library>::const_iterator stlplus::library_manager::local_find(const std::string& name) const\r
+{\r
+  for (std::list<stlplus::lm_library>::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++)\r
+  {\r
+    if (m_library_case)\r
+    {\r
+      if (i->name() == name) return i;\r
+    }\r
+    else\r
+    {\r
+      if (lowercase(i->name()) == lowercase(name)) return i;\r
+    }\r
+  }\r
+  return m_libraries.end();\r
+}\r
+\r
+std::ostream& stlplus::operator << (std::ostream& str, const stlplus::library_manager& manager)\r
+{\r
+  manager.print(str);\r
+  return str;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
diff --git a/src/stlplus/subsystems/library_manager.hpp b/src/stlplus/subsystems/library_manager.hpp
new file mode 100644 (file)
index 0000000..96e1afd
--- /dev/null
@@ -0,0 +1,708 @@
+#ifndef STLPLUS_LIBRARY_MANAGER\r
+#define STLPLUS_LIBRARY_MANAGER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Generalised library manager.\r
+\r
+//   Manages library units in a set of library directories. A unit is both a file\r
+//   on-disk and a data-structure in memory. To use the library manager, you need\r
+//   to:\r
+\r
+//     - design a type based on lm_unit with serialising functions read/write \r
+//     - decide on a file extension for the type\r
+//     - decide on a description of the type\r
+//     - write a create callback for this type\r
+//     - register the file extension, description and callback with the library manager\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "subsystems_fixes.hpp"\r
+#include "ini_manager.hpp"\r
+#include "smart_ptr.hpp"\r
+#include <iostream>\r
+#include <string>\r
+#include <vector>\r
+#include <list>\r
+#include <map>\r
+#include <time.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Internals\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class lm_library;\r
+  class library_manager;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // unit names\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class lm_unit_name\r
+  {\r
+  public:\r
+    lm_unit_name(const std::string& name = std::string(), const std::string& type = std::string());\r
+    ~lm_unit_name(void);\r
+\r
+    const std::string& name(void) const;\r
+    void set_name(const std::string& name);\r
+    void lowercase(void);\r
+\r
+    const std::string& type(void) const;\r
+    void set_type(const std::string& type);\r
+\r
+    bool write(std::ostream& context) const;\r
+    bool read(std::istream& context);\r
+\r
+    std::string to_string(void) const;\r
+    bool print(std::ostream&) const;\r
+\r
+  private:\r
+    std::string m_name;\r
+    std::string m_type;\r
+  };\r
+\r
+  std::ostream& operator << (std::ostream&, const lm_unit_name&);\r
+  bool operator == (const lm_unit_name& l, const lm_unit_name& r);\r
+  bool operator < (const lm_unit_name& l, const lm_unit_name& r);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // dependencies\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // dependencies on external files\r
+\r
+  class lm_file_dependency\r
+  {\r
+  public:\r
+    lm_file_dependency(void);\r
+    lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line = 0, unsigned column = 0);\r
+    ~lm_file_dependency(void);\r
+\r
+    // a path can be retrieved as either a relative path to the library or as a\r
+    // full path by providing the library path as an argument\r
+    const std::string& path(void) const;\r
+    std::string path_full(const std::string& library_path) const;\r
+    void set_path(const std::string& library_path, const std::string& path);\r
+\r
+    unsigned line(void) const;\r
+    void set_line(unsigned line = 0);\r
+\r
+    unsigned column(void) const;\r
+    void set_column(unsigned column = 0);\r
+\r
+    bool write(std::ostream& context) const;\r
+    bool read(std::istream& context);\r
+\r
+    bool print(std::ostream&) const;\r
+\r
+  private:\r
+    std::string m_path; // file dependencies are stored as paths relative to the containing library\r
+    unsigned m_line;    // line - starts at 1, 0 means no line/column information\r
+    unsigned m_column;  // column - starts at 0\r
+  };\r
+\r
+  std::ostream& operator <<(std::ostream&, const lm_file_dependency&);\r
+\r
+  // dependencies on other units\r
+\r
+  class lm_unit_dependency\r
+  {\r
+  public:\r
+    lm_unit_dependency(void);\r
+    lm_unit_dependency(const std::string& library, const lm_unit_name& name);\r
+    ~lm_unit_dependency(void);\r
+\r
+    const std::string& library(void) const;\r
+    void set_library(const std::string& library);\r
+\r
+    const lm_unit_name& unit_name(void) const;\r
+    void set_unit_name(const lm_unit_name& unit_name);\r
+\r
+    const std::string& name(void) const;\r
+    void set_name(const std::string& name);\r
+\r
+    const std::string& type(void) const;\r
+    void set_type(const std::string& type);\r
+\r
+    bool write(std::ostream& context) const;\r
+    bool read(std::istream& context);\r
+\r
+    bool print(std::ostream&) const;\r
+\r
+  private:\r
+    std::string m_library;\r
+    lm_unit_name m_name;\r
+  };\r
+\r
+  std::ostream& operator<<(std::ostream&, const lm_unit_dependency&);\r
+\r
+  // the set of all dependencies\r
+\r
+  class lm_dependencies\r
+  {\r
+  public:\r
+    lm_dependencies(void);\r
+    lm_dependencies(const lm_dependencies&);\r
+    lm_dependencies& operator=(const lm_dependencies&);\r
+    ~lm_dependencies(void);\r
+\r
+    // source file dependency\r
+    void set_source_file(const lm_file_dependency&);\r
+    bool source_file_present(void) const;\r
+    const lm_file_dependency& source_file(void) const;\r
+\r
+    // other file dependencies\r
+    unsigned file_add(const lm_file_dependency& dependency);\r
+    unsigned file_size(void) const;\r
+    const lm_file_dependency& file_dependency(unsigned) const;\r
+    void file_erase(unsigned);\r
+\r
+    // unit dependencies\r
+    unsigned unit_add(const lm_unit_dependency& dependency);\r
+    unsigned unit_size(void) const;\r
+    const lm_unit_dependency& unit_dependency(unsigned) const;\r
+    void unit_erase(unsigned);\r
+\r
+    void clear(void);\r
+    bool empty(void) const;\r
+\r
+    bool write(std::ostream& context) const;\r
+    bool read(std::istream& context);\r
+\r
+    bool print(std::ostream&) const;\r
+\r
+  private:\r
+    lm_file_dependency* m_source;            // source file dependency (optional)\r
+    std::vector<lm_file_dependency> m_files; // other file dependencies\r
+    std::vector<lm_unit_dependency> m_units; // unit dependencies\r
+  };\r
+\r
+  std::ostream& operator << (std::ostream&, const lm_dependencies&);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // library unit superclass\r
+  // user's units must be derivatives of lm_unit and overload all the virtuals\r
+\r
+  class lm_unit\r
+  {\r
+    friend class lm_library;\r
+  public:\r
+    ////////////////////////////////////////\r
+    // constructor/destructor\r
+\r
+    lm_unit(const lm_unit_name& name, lm_library* library);\r
+    virtual ~lm_unit(void);\r
+\r
+    ////////////////////////////////////////\r
+    // Header data\r
+\r
+    // unit name\r
+    const lm_unit_name& unit_name(void) const;\r
+    const std::string& name(void) const;\r
+    const std::string& type(void) const;\r
+\r
+    // dependencies\r
+    // all file dependencies are converted for internal use to a path relative to the library\r
+    // they can be retrieved either in this form or as a full path\r
+\r
+    // source file dependency\r
+    void set_source_file(const lm_file_dependency&);\r
+    bool source_file_present(void) const;\r
+    const lm_file_dependency& source_file(void) const;\r
+\r
+    // other file dependencies\r
+    unsigned file_add(const lm_file_dependency& dependency);\r
+    unsigned file_size(void) const;\r
+    const lm_file_dependency& file_dependency(unsigned) const;\r
+    void file_erase(unsigned);\r
+\r
+    // unit dependencies\r
+    unsigned unit_add(const lm_unit_dependency& dependency);\r
+    unsigned unit_size(void) const;\r
+    const lm_unit_dependency& unit_dependency(unsigned) const;\r
+    void unit_erase(unsigned);\r
+\r
+    const lm_dependencies& dependencies(void) const;\r
+    void set_dependencies(const lm_dependencies&);\r
+    void clear_dependencies(void);\r
+    bool empty_dependencies(void) const;\r
+\r
+    // dependency checking\r
+\r
+    bool out_of_date(void) const;\r
+    bool up_to_date(void) const;\r
+    lm_dependencies out_of_date_reason(void) const;\r
+\r
+    // supplementary data\r
+\r
+    const std::string& supplementary_data(void) const;\r
+    void set_supplementary_data(const std::string& data);\r
+\r
+    ////////////////////////////////////////\r
+    // unit data management\r
+\r
+    bool load(void);\r
+    bool save(void);\r
+    bool loaded(void) const;\r
+    void mark(void);\r
+\r
+    // file modified time - only changes after a save\r
+\r
+    time_t modified(void) const;\r
+\r
+    ////////////////////////////////////////\r
+    // containing library manager details\r
+\r
+    // get the owning library\r
+    const lm_library* library(void) const;\r
+    lm_library* library(void);\r
+\r
+    // owning library name and path\r
+    const std::string& library_name(void) const;\r
+    const std::string& library_path(void) const;\r
+\r
+    ////////////////////////////////////////\r
+    // error handling - these apply to the last read/write operation\r
+\r
+    bool error(void) const;\r
+\r
+    ////////////////////////////////////////\r
+    // functions that customise subclasses of this superclass\r
+    // You MUST provide at least:\r
+    //   - read - either read operation can be overloaded\r
+    //   - write - either write operation can be overloaded\r
+    //   - clone\r
+\r
+    // read(filename) is the one actually called to read your data\r
+    // the default read(filename) simply calls read(istream) to actually read the file\r
+    // the default read(istream) does nothing but fail by returning false so you must overload one or other\r
+\r
+    // you can just overload read(istream) if you want to use IOstream, or you\r
+    // can overload read(filename) to use any I/O system\r
+\r
+    virtual bool read(const std::string& filename, void* type_data);\r
+    virtual bool read(std::istream& file, void* type_data);\r
+\r
+    // as above, but for writing the data type\r
+\r
+    // write(filename) is the one actually called to write your data\r
+    // the default write(filename) simply calls write(ostream) to actually write the file\r
+    // the default write(ostream) does nothing but fail by returning false so you must overload one or other\r
+\r
+    // you can just overload write(ostream) if you want to use IOstream, or you\r
+    // can overload write(filename) to use any I/O system\r
+\r
+    virtual bool write(const std::string& filename, void* type_data);\r
+    virtual bool write(std::ostream& file, void* type_data);\r
+\r
+    // purge clears any memory associated with the unit - makes the unit unloaded\r
+    // the default does nothing\r
+    virtual bool purge(void);\r
+\r
+    // the clone function creates a new-ed copy of the subclass\r
+    virtual lm_unit* clone(void) const;\r
+\r
+    // the default print routines print header-only information\r
+    // you can overload these to provide a debug printout of the data structure\r
+    virtual bool print(std::ostream&) const;\r
+    virtual bool print_long(std::ostream&) const;\r
+\r
+  protected:\r
+    // header file management\r
+    std::string filename(void) const;\r
+    std::string header_filename(void) const;\r
+    bool write_header(void);\r
+    bool read_header(void);\r
+\r
+  private:\r
+    // header fields\r
+    lm_unit_name m_name;            // name\r
+    lm_dependencies m_dependencies; // file and unit dependencies\r
+    std::string m_supplement;       // supplementary data\r
+    bool m_header_modified;         // header modified\r
+\r
+    // internal fields\r
+    bool m_loaded;                  // loaded flag\r
+    bool m_marked;                  // mark - determines whether the unit needs saving\r
+\r
+    // library manager fields\r
+    lm_library* m_library;          // parent library\r
+\r
+    // error handling fields\r
+    bool m_error;                    // error flag if load or save fails - from IOstream\r
+  };\r
+\r
+  // Iostream print calls the short print method\r
+  std::ostream& operator << (std::ostream& str, const lm_unit& u);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // other types used in the library manager\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // user types\r
+\r
+  typedef smart_ptr_nocopy<lm_unit> lm_unit_ptr;\r
+  typedef lm_unit* (*lm_create_callback)(const lm_unit_name& unit_name, lm_library* parent_library, void* type_data);\r
+\r
+  // internal types used in the library manager but made global because they are shared\r
+\r
+  struct lm_callback_entry\r
+  {\r
+    lm_create_callback m_callback;\r
+    std::string m_description;\r
+    void* m_type_data;\r
+\r
+    lm_callback_entry(lm_create_callback callback = 0, const std::string& description = std::string(), void* type_data = 0) :\r
+      m_callback(callback), m_description(description), m_type_data(type_data) {}\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Library\r
+  // Must be contained in a library_manager\r
+  // Manages objects of class lm_unit and its subclasses\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class lm_library\r
+  {\r
+  public:\r
+    friend class library_manager;\r
+    friend class lm_unit;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // constructors/destructor - lm_library should only ever be constructed by library_manager\r
+\r
+    lm_library(library_manager* manager);\r
+    lm_library(const lm_library&);\r
+    lm_library& operator = (const lm_library&);\r
+    ~lm_library(void);\r
+\r
+  public:\r
+\r
+    const library_manager* manager(void) const;\r
+    library_manager* manager(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // initialisers\r
+\r
+    bool create(const std::string& name, const std::string& path, bool writable);\r
+    bool open(const std::string& path);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // management of types\r
+\r
+    bool load_type(const std::string& type);\r
+    bool load_types(void);\r
+    bool remove_type(const std::string& type);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // whole library operations\r
+\r
+    bool load(void);\r
+    bool save(void);\r
+    bool purge(void);\r
+    bool close(void);\r
+    bool erase(void);\r
+\r
+    const std::string& name(void) const;\r
+    const std::string& path(void) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // managing read/write status\r
+\r
+    bool set_read_write(bool writable);\r
+    bool set_writable(void);\r
+    bool set_read_only(void);\r
+    bool writable(void) const;\r
+    bool read_only(void) const;\r
+    bool os_writable(void) const;\r
+    bool os_read_only(void) const;\r
+    bool lm_writable(void) const;\r
+    bool lm_read_only(void) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // unit management\r
+\r
+    bool exists(const lm_unit_name& name) const;\r
+    lm_unit_ptr create(const lm_unit_name&);\r
+    bool loaded(const lm_unit_name& name) const;\r
+    bool load(const lm_unit_name& unit);\r
+    bool purge(const lm_unit_name& unit);\r
+    bool save(const lm_unit_name& unit);\r
+    bool erase(const lm_unit_name& name);\r
+    bool mark(const lm_unit_name& name);\r
+    time_t modified(const lm_unit_name& name) const;\r
+\r
+    bool erase_by_source(const std::string& source_file);\r
+\r
+    const lm_unit_ptr find(const lm_unit_name& name) const;\r
+    lm_unit_ptr find(const lm_unit_name& name);\r
+\r
+    std::vector<lm_unit_name> names(void) const;\r
+    std::vector<std::string> names(const std::string& type) const;\r
+    std::vector<lm_unit_ptr> handles(void) const;\r
+    std::vector<lm_unit_ptr> handles(const std::string& type) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // dependency checking\r
+\r
+    bool out_of_date(const lm_unit_name& name) const;\r
+    bool up_to_date(const lm_unit_name& name) const;\r
+    lm_dependencies out_of_date_reason(const lm_unit_name& name) const;\r
+\r
+    std::pair<bool,unsigned> tidy(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // do-everything print function\r
+\r
+    bool pretty_print(std::ostream& str,\r
+                      bool print_units = false,                       // print the unit names not just the library names\r
+                      const std::string& type = std::string()) const; // print just this type ("" means all)\r
+\r
+    ////////////////////////////////////////////////////////////////////////////////\r
+    // diagnostic print routines\r
+\r
+    bool print(std::ostream& str) const;\r
+    bool print_long(std::ostream& str) const;\r
+\r
+  private:\r
+\r
+    std::map<lm_unit_name,lm_unit_ptr>::iterator local_find(const lm_unit_name& name);\r
+    std::map<lm_unit_name,lm_unit_ptr>::const_iterator local_find(const lm_unit_name& name) const;\r
+\r
+    std::string m_name;                         // name\r
+    std::string m_path;                         // path\r
+    bool m_writable;                            // writable\r
+    std::map<lm_unit_name,lm_unit_ptr> m_units; // units\r
+    library_manager* m_manager;                 // parent library manager\r
+  };\r
+\r
+  std::ostream& operator << (std::ostream& str, const lm_library& lib);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Library Manager\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class library_manager\r
+  {\r
+  public:\r
+    friend class lm_library;\r
+    friend class lm_unit;\r
+\r
+    ////////////////////////////////////////////////////////////////////////////////\r
+    // static functions allow you to test whether a directory is a library before opening it\r
+    // you can also find the library's name without opening it\r
+\r
+    static bool is_library(const std::string& path, const std::string& owner);\r
+    static std::string library_name(const std::string& path, const std::string& owner);\r
+\r
+    // non-static forms test for libraries with the same owner as the library manager\r
+\r
+    bool is_library(const std::string& path);\r
+    std::string library_name(const std::string& path);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // tructors\r
+\r
+    explicit library_manager(const std::string& owner, bool library_case = false, bool unit_case = false);\r
+    ~library_manager(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // case sensitivity\r
+\r
+    bool library_case(void) const;\r
+    void set_library_case(bool library_case);\r
+\r
+    bool unit_case(void) const;\r
+    void set_unit_case(bool library_case);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // type handling\r
+    // only units of types added in this way will be recognised\r
+\r
+    bool add_type(const std::string& type,\r
+                  const std::string& description,\r
+                  lm_create_callback fn = 0,\r
+                  void* type_data = 0);\r
+    bool remove_type(const std::string& type);\r
+    std::vector<std::string> types(void) const;\r
+\r
+    std::string description(const std::string& type) const;\r
+    lm_create_callback callback(const std::string& type) const;\r
+    void* type_data(const std::string& type) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // Library mappings\r
+    // The library manager implements two different styles of library mappings\r
+    //   - mapping file\r
+    //   - ini file\r
+    // mapping file handling uses a simple text file to store the mappings in an internally-defined format\r
+    // ini file handling stores library mappings using the ini_manager component\r
+    // These modes are switched on by simply specifying a mapping file or an ini file to hold the mappings\r
+\r
+    // mapping file methods\r
+\r
+    // set but do not load - use this when you want to create a new mapping file\r
+    void set_mapping_file(const std::string& mapping_file);\r
+    // set and load - use this with an existing mapping file\r
+    bool load_mappings (const std::string& mapping_file);\r
+    // return the mapping file string\r
+    std::string mapping_file();\r
+\r
+    // ini file methods - the ini manager must be pre-loaded with the list of ini files to manage\r
+\r
+    // set and load - this will create the relevant sections in the local ini file if not present already\r
+    bool set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_section);\r
+    ini_manager* get_ini_manager(void) const;\r
+\r
+    // save to the library mapping handler, whichever kind it is\r
+    bool save_mappings (void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // library management\r
+\r
+    // operations on a single library\r
+    // test whether a named library exists\r
+    bool exists(const std::string& name) const;\r
+    // create a new libarry in the specified directory\r
+    lm_library* create(const std::string& name, const std::string& path, bool writable = true);\r
+    // open an existing library\r
+    lm_library* open(const std::string& path);\r
+    // load all units in the library\r
+    bool load(const std::string& name);\r
+    // save all marked units in the library\r
+    bool save(const std::string& name);\r
+    // purge all loaded units in the library\r
+    bool purge(const std::string& name);\r
+    // close the library - remove it from the manager but leave on disk\r
+    bool close(const std::string& name);\r
+    // erase the library - delete the directory and remove the library from the manager\r
+    bool erase(const std::string& name);\r
+\r
+    // operations on all libraries - as above but applied to all the libraries in the manager\r
+    bool load(void);\r
+    bool save(void);\r
+    bool purge(void);\r
+    bool close(void);\r
+    bool erase(void);\r
+\r
+    // get name and path of a library - name can differ in case if the library manager is case-insensitive\r
+    std::string name(const std::string& library) const;\r
+    std::string path(const std::string& library) const;\r
+\r
+    // control and test read/write status\r
+    bool set_writable(const std::string& library);\r
+    bool set_read_only(const std::string& library);\r
+    bool writable(const std::string& library) const;\r
+    bool read_only(const std::string& library) const;\r
+    bool os_writable(const std::string& library) const;\r
+    bool os_read_only(const std::string& library) const;\r
+    bool lm_writable(const std::string& library) const;\r
+    bool lm_read_only(const std::string& library) const;\r
+\r
+    // find a library in the manager - returns null if not found\r
+    lm_library* find(const std::string& name);\r
+    const lm_library* find(const std::string& name) const;\r
+\r
+    // get the set of all library names\r
+    std::vector<std::string> names(void) const;\r
+    // get the set of all libraries\r
+    std::vector<const lm_library*> handles(void) const;\r
+    std::vector<lm_library*> handles(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // current library management\r
+\r
+    bool setwork(const std::string& library);\r
+    bool unsetwork(void);\r
+    const lm_library* work(void) const;\r
+    lm_library* work(void);\r
+    std::string work_name(void) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // unit management within a library\r
+    // Note: you can also manipulate the library class through a handle returned by find() or handles()\r
+\r
+    bool exists(const std::string& library, const lm_unit_name& name) const;\r
+    lm_unit_ptr create(const std::string& library, const lm_unit_name& name);\r
+    bool loaded(const std::string& library, const lm_unit_name& name) const;\r
+    bool load(const std::string& library, const lm_unit_name& name);\r
+    bool purge(const std::string& library, const lm_unit_name& name);\r
+    bool save(const std::string& library, const lm_unit_name& name);\r
+    bool erase(const std::string& library, const lm_unit_name& name);\r
+    bool mark(const std::string& library, const lm_unit_name& name);\r
+    time_t modified(const std::string& library, const lm_unit_name& name) const;\r
+\r
+    bool erase_by_source(const std::string& source_file);\r
+\r
+    const lm_unit_ptr find(const std::string& library, const lm_unit_name& name) const;\r
+    lm_unit_ptr find(const std::string& library, const lm_unit_name& name);\r
+\r
+    std::vector<lm_unit_name> names(const std::string& library) const;\r
+    std::vector<std::string> names(const std::string& library, const std::string& type) const;\r
+    std::vector<lm_unit_ptr> handles(const std::string& library) const;\r
+    std::vector<lm_unit_ptr> handles(const std::string& library, const std::string& type) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // dependency checking\r
+\r
+    bool out_of_date(const std::string& library, const lm_unit_name& name) const;\r
+    bool up_to_date(const std::string& library, const lm_unit_name& name) const;\r
+    lm_dependencies out_of_date_reason(const std::string& library, const lm_unit_name& name) const;\r
+\r
+    // delete out of date units from a library or all libraries\r
+    // return the number of units tidied and a flag to say whether all units were successfully tidied\r
+    std::pair<bool,unsigned> tidy(const std::string& library);\r
+    std::pair<bool,unsigned> tidy(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // do-everything print routine!\r
+\r
+    bool pretty_print(std::ostream& str,\r
+                      bool print_units = false,                       // print the unit names not just the library names\r
+                      const std::string& library = std::string(),     // print just the specified library ("" means all)\r
+                      const std::string& type = std::string()) const; // print just this type ("" means all)\r
+\r
+    ////////////////////////////////////////////////////////////////////////////////\r
+    // diagnostic print routines\r
+\r
+    bool print(std::ostream& str) const;\r
+    bool print_long(std::ostream& str) const;\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // internals\r
+\r
+  private:\r
+    // NOT a copyable object\r
+    library_manager(const library_manager&);\r
+    library_manager& operator = (const library_manager&);\r
+\r
+  protected:\r
+    std::list<lm_library>::iterator local_find(const std::string& name);\r
+    std::list<lm_library>::const_iterator local_find(const std::string& name) const;\r
+\r
+    std::string m_owner;                               // owner application name\r
+    std::string m_mapping_file;                        // mapping file method of library management\r
+    ini_manager* m_ini_files;                          // ini manager method of library management\r
+    std::string m_ini_section;                         // ini manager method of library management\r
+    std::string m_ini_work;                            // ini manager method of library management\r
+    std::list<lm_library> m_libraries;                 // libraries\r
+    std::string m_work;                                // work library\r
+    std::map<std::string,lm_callback_entry> m_callbacks; // callbacks\r
+    bool m_library_case;                               // case sensitivity for library names\r
+    bool m_unit_case;                                  // case sensitivity for unit names\r
+  };\r
+\r
+  std::ostream& operator << (std::ostream& str, const library_manager& libraries);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/subsystems/message_handler.cpp b/src/stlplus/subsystems/message_handler.cpp
new file mode 100644 (file)
index 0000000..fd1d2e8
--- /dev/null
@@ -0,0 +1,2014 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "message_handler.hpp"\r
+#include "dprintf.hpp"\r
+#include <fstream>\r
+#include <map>\r
+#include <list>\r
+#include <ctype.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Utilities\r
+\r
+  static std::ostream& operator<< (std::ostream& device, const std::vector<std::string>& data)\r
+  {\r
+    for (unsigned i = 0; i < data.size(); i++)\r
+      device << data[i] << std::endl;\r
+    device.flush();\r
+    return device;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  static const char* information_id = "INFORMATION";\r
+  static const char* context_id = "CONTEXT";\r
+  static const char* supplement_id = "SUPPLEMENT";\r
+  static const char* warning_id = "WARNING";\r
+  static const char* error_id = "ERROR";\r
+  static const char* fatal_id = "FATAL";\r
+  static const char* position_id = "POSITION";\r
+\r
+  static const char* default_information_format = "@0";\r
+  static const char* default_context_format = "context: @0";\r
+  static const char* default_supplement_format = "supplement: @0";\r
+  static const char* default_warning_format = "warning: @0";\r
+  static const char* default_error_format = "error: @0";\r
+  static const char* default_fatal_format = "FATAL: @0";\r
+  static const char* default_position_format = "\"@1\" (@2,@3) : @0";\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // position class\r
+\r
+  message_position::message_position(void) :\r
+    m_filename(std::string()), m_line(0), m_column(0) \r
+  {\r
+  }\r
+\r
+  message_position::message_position(const std::string& filename, unsigned line, unsigned column) :\r
+    m_filename(filename), m_line(line), m_column(column)\r
+  {\r
+  }\r
+\r
+  message_position::~message_position(void)\r
+  {\r
+  }\r
+\r
+  const std::string& message_position::filename(void) const\r
+  {\r
+    return m_filename;\r
+  }\r
+\r
+  unsigned message_position::line(void) const\r
+  {\r
+    return m_line;\r
+  }\r
+\r
+  unsigned message_position::column(void) const\r
+  {\r
+    return m_column;\r
+  }\r
+\r
+  message_position message_position::operator + (unsigned offset) const\r
+  {\r
+    message_position result(*this);\r
+    result += offset;\r
+    return result;\r
+  }\r
+\r
+  message_position& message_position::operator += (unsigned offset)\r
+  {\r
+    m_column += offset;\r
+    return *this;\r
+  }\r
+\r
+  bool message_position::empty(void) const\r
+  {\r
+    return m_filename.empty();\r
+  }\r
+\r
+  bool message_position::valid(void) const\r
+  {\r
+    return !empty();\r
+  }\r
+\r
+  std::vector<std::string> message_position::show(void) const\r
+  {\r
+    std::vector<std::string> result;\r
+    if (valid())\r
+    {\r
+      // skip row-1 lines of the file and print out the resultant line\r
+      std::ifstream source(filename().c_str());\r
+      std::string current_line;\r
+      for (unsigned i = 0; i < line(); i++)\r
+      {\r
+        if (!source.good())\r
+          return result;\r
+        std::getline(source,current_line);\r
+      }\r
+      result.push_back(current_line);\r
+      // now put an up-arrow at the appropriate column\r
+      // preserve any tabs in the original line\r
+      result.push_back(std::string());\r
+      for (unsigned j = 0; j < column(); j++)\r
+      {\r
+        if (j < current_line.size() && current_line[j] == '\t')\r
+          result.back() += '\t';\r
+        else\r
+          result.back() += ' ';\r
+      }\r
+      result.back() += '^';\r
+    }\r
+    return result;\r
+  }\r
+\r
+  std::string to_string(const message_position& where)\r
+  {\r
+    return dformat("{%s:%u:%u}", where.filename().c_str(), where.line(), where.column());\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // context subclass\r
+\r
+  class message_context_body\r
+  {\r
+  private:\r
+    unsigned m_depth;\r
+    message_handler_base* m_base;\r
+\r
+  public:\r
+    message_context_body(message_handler_base& handler) :\r
+      m_depth(0), m_base(0)\r
+      {\r
+        set(handler);\r
+      }\r
+\r
+    ~message_context_body(void)\r
+      {\r
+        pop();\r
+      }\r
+\r
+    void set(message_handler_base& handler)\r
+      {\r
+        m_base = &handler;\r
+        m_depth = m_base->context_depth();\r
+      }\r
+\r
+    void pop(void)\r
+      {\r
+        if (m_base)\r
+          m_base->pop_context(m_depth);\r
+      }\r
+  private:\r
+    message_context_body(const message_context_body&);\r
+    message_context_body& operator=(const message_context_body&);\r
+  };\r
+\r
+  // exported context class\r
+\r
+  message_context::message_context(message_handler_base& handler) : m_body(new message_context_body(handler))\r
+  {\r
+  }\r
+\r
+  void message_context::set(message_handler_base& handler)\r
+  {\r
+    m_body->set(handler);\r
+  }\r
+\r
+  void message_context::pop(void)\r
+  {\r
+    m_body->pop();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // exceptions\r
+\r
+  // read_error\r
+\r
+  message_handler_read_error::message_handler_read_error(const message_position& position, const std::string& reason) :\r
+    std::runtime_error(to_string(position) + std::string(": Message handler read error - ") + reason), \r
+    m_position(position)\r
+  {\r
+  }\r
+\r
+  message_handler_read_error::~message_handler_read_error(void) throw()\r
+  {\r
+  }\r
+\r
+  const message_position& message_handler_read_error::where(void) const\r
+  {\r
+    return m_position;\r
+  }\r
+\r
+  // format error\r
+\r
+  message_handler_format_error::message_handler_format_error(const std::string& format, unsigned offset) :\r
+    std::runtime_error(std::string("Message handler formatting error in \"") + format + std::string("\"")),\r
+    m_position("",0,0), m_format(format), m_offset(offset)\r
+  {\r
+  }\r
+\r
+  message_handler_format_error::message_handler_format_error(const message_position& pos, const std::string& format, unsigned offset) : \r
+    std::runtime_error(to_string(pos) + std::string(": Message handler formatting error in \"") + format + std::string("\"")), \r
+    m_position(pos), m_format(format), m_offset(offset)\r
+  {\r
+  }\r
+\r
+  message_handler_format_error::~message_handler_format_error(void) throw()\r
+  {\r
+  }\r
+\r
+  const message_position& message_handler_format_error::where(void) const\r
+  {\r
+    return m_position;\r
+  }\r
+\r
+  const std::string& message_handler_format_error::format(void) const\r
+  {\r
+    return m_format;\r
+  }\r
+\r
+  unsigned message_handler_format_error::offset(void) const\r
+  {\r
+    return m_offset;\r
+  }\r
+\r
+  // id_error\r
+\r
+  message_handler_id_error::message_handler_id_error(const std::string& id) :\r
+    std::runtime_error(std::string("Message handler message ID not found: ") + id), m_id(id)\r
+  {\r
+  }\r
+\r
+  message_handler_id_error::~message_handler_id_error(void) throw()\r
+  {\r
+  }\r
+\r
+  const std::string& message_handler_id_error::id(void) const\r
+  {\r
+    return m_id;\r
+  }\r
+\r
+  // limit_error\r
+\r
+  message_handler_limit_error::message_handler_limit_error(unsigned limit) : \r
+    std::runtime_error(std::string("Message handler limit error: ") + dformat("%u",limit) + std::string(" reached")),\r
+    m_limit(limit)\r
+  {\r
+  }\r
+\r
+  message_handler_limit_error::~message_handler_limit_error(void) throw()\r
+  {\r
+  }\r
+\r
+  unsigned message_handler_limit_error::limit(void) const\r
+  {\r
+    return m_limit;\r
+  }\r
+\r
+  // fatal_error\r
+\r
+  message_handler_fatal_error::message_handler_fatal_error(const std::string& id) : \r
+    std::runtime_error(std::string("Message Handler Fatal error: ") + id),\r
+    m_id(id)\r
+  {\r
+  }\r
+\r
+  message_handler_fatal_error::~message_handler_fatal_error(void) throw()\r
+  {\r
+  }\r
+\r
+  const std::string& message_handler_fatal_error::id(void) const\r
+  {\r
+    return m_id;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class message\r
+  {\r
+  private:\r
+    unsigned m_index;    // index into message files\r
+    unsigned m_line;     // line\r
+    unsigned m_column;   // column\r
+    std::string m_text;  // text\r
+\r
+  public:\r
+    message(unsigned index = (unsigned)-1,\r
+            unsigned line = 0,\r
+            unsigned column = 0,\r
+            const std::string& text = "") :\r
+      m_index(index),m_line(line),m_column(column),m_text(text)\r
+      {\r
+      }\r
+    message(const std::string& text) :\r
+      m_index((unsigned)-1),m_line(0),m_column(0),m_text(text)\r
+      {\r
+      }\r
+\r
+    ~message(void)\r
+      {\r
+      }\r
+\r
+    unsigned index(void) const\r
+      {\r
+        return m_index;\r
+      }\r
+\r
+    unsigned line(void) const\r
+      {\r
+        return m_line;\r
+      }\r
+\r
+    unsigned column(void) const\r
+      {\r
+        return m_column;\r
+      }\r
+\r
+    const std::string& text(void) const\r
+      {\r
+        return m_text;\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class pending_message\r
+  {\r
+  private:\r
+    message_position m_position;\r
+    std::string m_id;\r
+    std::vector<std::string> m_args;\r
+  public:\r
+    pending_message(const message_position& position, const std::string& id, const std::vector<std::string>& args) :\r
+      m_position(position), m_id(id), m_args(args) {}\r
+    pending_message(const std::string& id, const std::vector<std::string>& args) :\r
+      m_position(message_position()), m_id(id), m_args(args) {}\r
+    ~pending_message(void) {}\r
+\r
+    const message_position& position(void) const {return m_position;}\r
+    const std::string& id(void) const {return m_id;}\r
+    const std::vector<std::string>& args(void) const {return m_args;}\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class message_handler_base_body\r
+  {\r
+  public:\r
+    std::vector<std::string> m_files;          // message files in the order they were added\r
+    std::map<std::string,message> m_messages;  // messages stored as id:message pairs\r
+    bool m_show;                               // show source\r
+    std::list<pending_message> m_context;      // context message stack\r
+    std::list<pending_message> m_supplement;   // supplementary message stack\r
+\r
+  public:\r
+    message_handler_base_body(void) :\r
+      m_show(false)\r
+      {\r
+        // preload with default formats\r
+        add_message(information_id,default_information_format);\r
+        add_message(warning_id,default_warning_format);\r
+        add_message(error_id,default_error_format);\r
+        add_message(fatal_id,default_fatal_format);\r
+        add_message(position_id,default_position_format);\r
+        add_message(context_id,default_context_format);\r
+        add_message(supplement_id,default_supplement_format);\r
+      }\r
+\r
+    ~message_handler_base_body(void)\r
+      {\r
+      }\r
+\r
+    void set_show(bool show)\r
+      {\r
+        m_show = show;\r
+      }\r
+\r
+    void add_message_file(const std::string& file)\r
+      throw(message_handler_read_error)\r
+      {\r
+        m_files.push_back(file);\r
+        std::ifstream input(file.c_str());\r
+        if (!input.good()) \r
+          throw message_handler_read_error(message_position(file,0,0), std::string("file not found: ") + file);\r
+        std::string line;\r
+        unsigned l = 0;\r
+        while(input.good())\r
+        {\r
+          std::getline(input,line);\r
+          l++;\r
+          if (line.size() > 0 && isalpha(line[0]))\r
+          {\r
+            // Get the id field which starts with an alphabetic and contains alphanumerics and underscore\r
+            std::string id;\r
+            unsigned i = 0;\r
+            for (; i < line.size(); i++)\r
+            {\r
+              if (isalnum(line[i]) || line[i] == '_')\r
+                id += line[i];\r
+              else\r
+                break;\r
+            }\r
+            // check this ID is unique within the files - however it is legal to override a hard-coded message\r
+            std::map<std::string,message>::iterator found = m_messages.find(id);\r
+            if (found != m_messages.end() && found->second.index() != (unsigned)-1)\r
+              throw message_handler_read_error(message_position(file,l,i), std::string("repeated ID ") + id);\r
+            // skip whitespace\r
+            for (; i < line.size(); i++)\r
+            {\r
+              if (!isspace(line[i]))\r
+                break;\r
+            }\r
+            // now get the text part and add the message to the message map\r
+            std::string text = line.substr(i, line.size()-i);\r
+            m_messages[id] = message(m_files.size()-1, l, i, text);\r
+          }\r
+        }\r
+      }\r
+\r
+    void add_message(const std::string& id, const std::string& text)\r
+      throw()\r
+      {\r
+        m_messages[id] = message((unsigned)-1, 0, 0, text);\r
+      }\r
+\r
+    bool message_present(const std::string& id) const\r
+      throw()\r
+      {\r
+        return m_messages.find(id) != m_messages.end();\r
+      }\r
+\r
+    void push_supplement(const message_position& position,\r
+                         const std::string& message_id,\r
+                         const std::vector<std::string>& args)\r
+      {\r
+        m_supplement.push_back(pending_message(position,message_id,args));\r
+      }\r
+\r
+    void push_context(const message_position& position,\r
+                      const std::string& message_id,\r
+                      const std::vector<std::string>& args)\r
+      {\r
+        m_context.push_back(pending_message(position,message_id,args));\r
+      }\r
+\r
+    void pop_context(unsigned depth)\r
+      {\r
+        while (depth < m_context.size())\r
+          m_context.pop_back();\r
+      }\r
+\r
+    unsigned context_depth(void) const\r
+      {\r
+        return m_context.size();\r
+      }\r
+\r
+    std::vector<std::string> format_report(const message_position& position,\r
+                                           const std::string& message_id,\r
+                                           const std::string& status_id,\r
+                                           const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error)\r
+      {\r
+        // gathers everything together into a full multi-line report\r
+        std::vector<std::string> result;\r
+        // the first part of the report is the status message (info/warning/error/fatal)\r
+        result.push_back(format_id(position, message_id, status_id, args));\r
+        // now append any supplemental messages that have been stacked\r
+        // these are printed in FIFO order i.e. the order that they were added to the handler\r
+        for (std::list<pending_message>::iterator j = m_supplement.begin(); j != m_supplement.end(); j++)\r
+          result.push_back(format_id(j->position(),j->id(),supplement_id,j->args()));\r
+        // now discard any supplementary messages because they only persist until they are printed once\r
+        m_supplement.clear();\r
+        // now append any context messages that have been stacked\r
+        // these are printed in LIFO order i.e. closest context first\r
+        for (std::list<pending_message>::reverse_iterator i = m_context.rbegin(); i != m_context.rend(); i++)\r
+          result.push_back(format_id(i->position(),i->id(),context_id,i->args()));\r
+        return result;\r
+      }\r
+\r
+    std::string format_id(const message_position& position,\r
+                          const std::string& message_id,\r
+                          const std::string& status_id,\r
+                          const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error)\r
+      {\r
+        // This function creates a fully-formatted single-line message from a\r
+        // combination of the position format and the status format plus the message\r
+        // ID and its arguments. There are up to three levels of substitution\r
+        // required to do this.\r
+\r
+        // get the status format from the status_id\r
+        std::map<std::string,message>::iterator status_found = m_messages.find(status_id);\r
+        if (status_found == m_messages.end()) throw message_handler_id_error(status_id);\r
+\r
+        // similarly get the message format\r
+        std::map<std::string,message>::iterator message_found = m_messages.find(message_id);\r
+        if (message_found == m_messages.end()) throw message_handler_id_error(message_id);\r
+\r
+        // format the message contents\r
+        std::string message_text = format_message(message_found->second, args);\r
+\r
+        // now format the message in the status string (informational, warning etc).\r
+        std::vector<std::string> status_args;\r
+        status_args.push_back(message_text);\r
+        std::string result = format_message(status_found->second, status_args);\r
+\r
+        // finally, if the message is positional, format the status message in the positional string\r
+        if (position.valid())\r
+        {\r
+          // get the position format from the message set\r
+          std::map<std::string,message>::iterator position_found = m_messages.find(position_id);\r
+          if (position_found == m_messages.end()) throw message_handler_id_error(position_id);\r
+\r
+          // now format the message\r
+          std::vector<std::string> position_args;\r
+          position_args.push_back(result);\r
+          position_args.push_back(position.filename());\r
+          position_args.push_back(dformat("%u",position.line()));\r
+          position_args.push_back(dformat("%u",position.column()));\r
+          result = format_message(position_found->second, position_args);\r
+          // add the source file text and position if that option is on\r
+\r
+          if (m_show)\r
+          {\r
+            std::vector<std::string> show = position.show();\r
+            for (unsigned i = 0; i < show.size(); i++)\r
+            {\r
+              result += std::string("\n");\r
+              result += show[i];\r
+            }\r
+          }\r
+        }\r
+        return result;\r
+      }\r
+\r
+    std::string format_message(const message& mess,\r
+                               const std::vector<std::string>& args) \r
+      throw(message_handler_format_error)\r
+      {\r
+        // this function creates a formatted string from the stored message text and\r
+        // the arguments. Most of the work is done in format_string. However, if a\r
+        // formatting error is found, this function uses extra information stored in\r
+        // the message data structure to improve the reporting of the error\r
+        try\r
+        {\r
+          // This is the basic string formatter which performs parameter substitution\r
+          // into a message. Parameter placeholders are in the form @0, @1 etc, where\r
+          // the number is the index of the argument in the args vector. At present,\r
+          // no field codes are supported as in printf formats Algorithm: scan the\r
+          // input string and transfer it into the result. When an @nnn appears,\r
+          // substitute the relevant argument from the args vector. Throw an exception\r
+          // if its out of range. Also convert C-style escaped characters of the form\r
+          // \n.\r
+          std::string format = mess.text();\r
+          std::string result;\r
+          for (unsigned i = 0; i < format.size(); )\r
+          {\r
+            if (format[i] == '@')\r
+            {\r
+              // an argument substitution has been found - now find the argument number\r
+              if (i+1 == format.size()) throw message_handler_format_error(format, i);\r
+              i++;\r
+              // check for @@ which is an escaped form of '@'\r
+              if (format[i] == '@')\r
+              {\r
+                result += '@';\r
+                i++;\r
+              }\r
+              else\r
+              {\r
+                // there must be at least one digit in the substitution number\r
+                if (!isdigit(format[i])) throw message_handler_format_error(format,i);\r
+                unsigned a = 0;\r
+                for (; i < format.size() && isdigit(format[i]); i++)\r
+                {\r
+                  a *= 10;\r
+                  a += (format[i] - '0');\r
+                }\r
+                // check for an argument request out of the range of the set of arguments\r
+                if (a >= args.size()) throw message_handler_format_error(format,i-1);\r
+                result += args[a];\r
+              }\r
+            }\r
+            else if (format[i] == '\\')\r
+            {\r
+              // an escaped character has been found\r
+              if (i+1 == format.size()) throw message_handler_format_error(format, i);\r
+              i++;\r
+              // do the special ones first, then all the others just strip off the \ and leave the following characters\r
+              switch(format[i])\r
+              {\r
+              case '\\':\r
+                result += '\\';\r
+                break;\r
+              case 't':\r
+                result += '\t';\r
+                break;\r
+              case 'n':\r
+                result += '\n';\r
+                break;\r
+              case 'r':\r
+                result += '\r';\r
+                break;\r
+              case 'a':\r
+                result += '\a';\r
+                break;\r
+              default:\r
+                result += format[i];\r
+                break;\r
+              }\r
+              i++;\r
+            }\r
+            else\r
+            {\r
+              // plain text found - just append to the result\r
+              result += format[i++];\r
+            }\r
+          }\r
+          return result;\r
+        }\r
+        catch(message_handler_format_error& exception)\r
+        {\r
+          // if the message came from a message file, improve the error reporting by adding file positional information\r
+          // Also adjust the position from the start of the text (stored in the message field) to the column of the error\r
+          if (mess.index() != (unsigned)-1)\r
+            throw message_handler_format_error(\r
+              message_position(m_files[mess.index()], mess.line(), mess.column()+exception.offset()),\r
+              exception.format(),\r
+              exception.offset());\r
+          else\r
+            throw exception;\r
+        }\r
+      }\r
+\r
+  private:\r
+    message_handler_base_body(message_handler_base_body&);\r
+    message_handler_base_body& operator=(message_handler_base_body&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  message_handler_base::message_handler_base(bool show)\r
+    throw() :\r
+    m_base_body(new message_handler_base_body)\r
+  {\r
+    m_base_body->set_show(show);\r
+  }\r
+\r
+  message_handler_base::message_handler_base(const std::string& file, bool show)\r
+    throw(message_handler_read_error) :\r
+    m_base_body(new message_handler_base_body)\r
+  {\r
+    m_base_body->set_show(show);\r
+    add_message_file(file);\r
+  }\r
+\r
+  message_handler_base::message_handler_base(const std::vector<std::string>& files, bool show)\r
+    throw(message_handler_read_error) :\r
+    m_base_body(new message_handler_base_body)\r
+  {\r
+    m_base_body->set_show(show);\r
+    add_message_files(files);\r
+  }\r
+\r
+  message_handler_base::~message_handler_base(void)\r
+    throw()\r
+  {\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void message_handler_base::add_message_file(const std::string& file)\r
+    throw(message_handler_read_error)\r
+  {\r
+    m_base_body->add_message_file(file);\r
+  }\r
+\r
+  void message_handler_base::add_message_files(const std::vector<std::string>& files)\r
+    throw(message_handler_read_error)\r
+  {\r
+    for (unsigned i = 0; i < files.size(); i++)\r
+      add_message_file(files[i]);\r
+  }\r
+\r
+  void message_handler_base::add_message(const std::string& id, const std::string& text)\r
+    throw()\r
+  {\r
+    m_base_body->add_message(id,text);\r
+  }\r
+\r
+  bool message_handler_base::message_present(const std::string& id) const\r
+    throw()\r
+  {\r
+    return m_base_body->message_present(id);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void message_handler_base::set_information_format(const std::string& format) throw()\r
+  {\r
+    add_message(information_id, format);\r
+  }\r
+\r
+  void message_handler_base::set_warning_format(const std::string& format) throw()\r
+  {\r
+    add_message(warning_id, format);\r
+  }\r
+\r
+  void message_handler_base::set_error_format(const std::string& format) throw()\r
+  {\r
+    add_message(error_id, format);\r
+  }\r
+\r
+  void message_handler_base::set_fatal_format(const std::string& format) throw()\r
+  {\r
+    add_message(fatal_id, format);\r
+  }\r
+\r
+  void message_handler_base::set_position_format(const std::string& format) throw()\r
+  {\r
+    add_message(position_id, format);\r
+  }\r
+\r
+  void message_handler_base::set_context_format(const std::string& format) throw()\r
+  {\r
+    add_message(context_id, format);\r
+  }\r
+\r
+  void message_handler_base::set_supplement_format(const std::string& format) throw()\r
+  {\r
+    add_message(supplement_id, format);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  void message_handler_base::show_position(void)\r
+    throw()\r
+  {\r
+    m_base_body->set_show(true);\r
+  }\r
+\r
+  void message_handler_base::hide_position(void)\r
+    throw()\r
+  {\r
+    m_base_body->set_show(false);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // information messages\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const std::string& id,\r
+                                                                     const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return information_message(message_position(), id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return information_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const std::string& id,\r
+                                                                     const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return information_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const std::string& id,\r
+                                                                     const std::string& arg1,\r
+                                                                     const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return information_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const std::string& id,\r
+                                                                     const std::string& arg1,\r
+                                                                     const std::string& arg2,\r
+                                                                     const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return information_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const message_position& position,\r
+                                                                     const std::string& id,\r
+                                                                     const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return m_base_body->format_report(position, id, information_id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const message_position& position,\r
+                                                                     const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return information_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const message_position& position,\r
+                                                                     const std::string& id,\r
+                                                                     const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return information_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const message_position& position,\r
+                                                                     const std::string& id,\r
+                                                                     const std::string& arg1,\r
+                                                                     const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return information_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::information_message(const message_position& position,\r
+                                                                     const std::string& id,\r
+                                                                     const std::string& arg1,\r
+                                                                     const std::string& arg2,\r
+                                                                     const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return information_message(position, id, args);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // warning messages\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const std::string& id,\r
+                                                                 const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return warning_message(message_position(), id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return warning_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const std::string& id,\r
+                                                                 const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return warning_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const std::string& id,\r
+                                                                 const std::string& arg1,\r
+                                                                 const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return warning_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const std::string& id,\r
+                                                                 const std::string& arg1,\r
+                                                                 const std::string& arg2,\r
+                                                                 const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return warning_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const message_position& position,\r
+                                                                 const std::string& id,\r
+                                                                 const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return m_base_body->format_report(position, id, warning_id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const message_position& position,\r
+                                                                 const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return warning_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const message_position& position,\r
+                                                                 const std::string& id,\r
+                                                                 const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return warning_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const message_position& position,\r
+                                                                 const std::string& id,\r
+                                                                 const std::string& arg1,\r
+                                                                 const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return warning_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::warning_message(const message_position& position,\r
+                                                                 const std::string& id,\r
+                                                                 const std::string& arg1,\r
+                                                                 const std::string& arg2,\r
+                                                                 const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return warning_message(position, id, args);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // error messages\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const std::string& id,\r
+                                                               const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return error_message(message_position(), id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return error_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const std::string& id,\r
+                                                               const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return error_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return error_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2,\r
+                                                               const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return error_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return m_base_body->format_report(position, id, error_id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const message_position& position,\r
+                                                               const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return error_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return error_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return error_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::error_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2,\r
+                                                               const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return error_message(position, id, args);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // fatal messages\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const std::string& id,\r
+                                                               const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return fatal_message(message_position(), id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return fatal_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const std::string& id,\r
+                                                               const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return fatal_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return fatal_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2,\r
+                                                               const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return fatal_message(id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return m_base_body->format_report(position, id, fatal_id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const message_position& position,\r
+                                                               const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return fatal_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return fatal_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return fatal_message(position, id, args);\r
+  }\r
+\r
+  std::vector<std::string> message_handler_base::fatal_message(const message_position& position,\r
+                                                               const std::string& id,\r
+                                                               const std::string& arg1,\r
+                                                               const std::string& arg2,\r
+                                                               const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return fatal_message(position, id, args);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // supplemental messages\r
+\r
+  void message_handler_base::push_supplement(const std::string& id,\r
+                                             const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    push_supplement(message_position(), id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    push_supplement(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const std::string& id,\r
+                                             const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    push_supplement(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    push_supplement(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2,\r
+                                             const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    push_supplement(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const message_position& position,\r
+                                             const std::string& id,\r
+                                             const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    m_base_body->push_supplement(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const message_position& position,\r
+                                             const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    push_supplement(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const message_position& position,\r
+                                             const std::string& id,\r
+                                             const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    push_supplement(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const message_position& position,\r
+                                             const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    push_supplement(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_supplement(const message_position& position,\r
+                                             const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2,\r
+                                             const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    push_supplement(position, id, args);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // context\r
+\r
+  void message_handler_base::push_context (const std::string& id,\r
+                                           const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    push_context(message_position(), id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    push_context(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const std::string& id,\r
+                                           const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    push_context(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    push_context(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2,\r
+                                           const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    push_context(id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const message_position& position,\r
+                                           const std::string& id,\r
+                                           const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    m_base_body->push_context(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const message_position& position,\r
+                                           const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    push_context(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const message_position& position,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    push_context(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const message_position& position,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    push_context(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::push_context (const message_position& position,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2,\r
+                                           const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    push_context(position, id, args);\r
+  }\r
+\r
+  void message_handler_base::pop_context(void) throw()\r
+  {\r
+    m_base_body->pop_context(m_base_body->context_depth()-1);\r
+  }\r
+\r
+  void message_handler_base::pop_context(unsigned depth) throw()\r
+  {\r
+    m_base_body->pop_context(depth);\r
+  }\r
+\r
+  unsigned message_handler_base::context_depth(void) const\r
+    throw()\r
+  {\r
+    return m_base_body->context_depth();\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const std::string& id, const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    return auto_push_context(message_position(), id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const std::string& id)                                                 \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return auto_push_context(id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const std::string& id,\r
+                                                          const std::string& arg1)                         \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return auto_push_context(id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context (const std::string& id,\r
+                                                           const std::string& arg1,\r
+                                                           const std::string& arg2) \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return auto_push_context(id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const std::string& id,\r
+                                                          const std::string& arg1,\r
+                                                          const std::string& arg2,\r
+                                                          const std::string& arg3) \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return auto_push_context(id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const message_position& position,\r
+                                                          const std::string& id,\r
+                                                          const std::vector<std::string>& args)            \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    message_context result(*this);\r
+    m_base_body->push_context(position, id, args);\r
+    return result;\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const message_position& position,\r
+                                                          const std::string& id)             \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    return auto_push_context(position, id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const message_position& position,\r
+                                                          const std::string& id,\r
+                                                          const std::string& arg1)                         \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    return auto_push_context(position, id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const message_position& position,\r
+                                                          const std::string& id,\r
+                                                          const std::string& arg1,\r
+                                                          const std::string& arg2) \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    return auto_push_context(position, id, args);\r
+  }\r
+\r
+  message_context message_handler_base::auto_push_context(const message_position& position,\r
+                                                          const std::string& id,\r
+                                                          const std::string& arg1,\r
+                                                          const std::string& arg2,\r
+                                                          const std::string& arg3) \r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    std::vector<std::string> args;\r
+    args.push_back(arg1);\r
+    args.push_back(arg2);\r
+    args.push_back(arg3);\r
+    return auto_push_context(position, id, args);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // iostream-based derivative uses the above base class to generate messages then uses iostream to print them\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class message_handler_body\r
+  {\r
+  private:\r
+    std::ostream* m_device;                        // TextIO output device\r
+    unsigned m_limit;                              // error limit\r
+    unsigned m_errors;                             // error count\r
+\r
+  public:\r
+    message_handler_body(std::ostream& device, unsigned limit) :\r
+      m_device(&device), m_limit(limit), m_errors(0)\r
+      {\r
+      }\r
+\r
+    ~message_handler_body(void)\r
+      {\r
+        device().flush();\r
+      }\r
+\r
+    std::ostream& device(void)\r
+      {\r
+        return *m_device;\r
+      }\r
+\r
+    unsigned limit(void) const\r
+      {\r
+        return m_limit;\r
+      }\r
+\r
+    void set_limit(unsigned limit)\r
+      {\r
+        m_limit = limit;\r
+      }\r
+\r
+    unsigned count(void) const\r
+      {\r
+        return m_errors;\r
+      }\r
+\r
+    void set_count(unsigned count)\r
+      {\r
+        m_errors = count;\r
+      }\r
+\r
+    void error_increment(void)\r
+      {\r
+        ++m_errors;\r
+      }\r
+\r
+    bool limit_reached(void) const\r
+      {\r
+        return m_limit > 0 && m_errors >= m_limit;\r
+      }\r
+\r
+  private:\r
+    message_handler_body(const message_handler_body&);\r
+    message_handler_body& operator=(const message_handler_body&);\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  message_handler::message_handler(std::ostream& device, unsigned limit, bool show)\r
+    throw() :\r
+    message_handler_base(show), m_body(new message_handler_body(device, limit))\r
+  {\r
+  }\r
+\r
+  message_handler::message_handler(std::ostream& device,\r
+                                   const std::string& message_file, unsigned limit, bool show) \r
+    throw(message_handler_read_error) :\r
+    message_handler_base(message_file,show), m_body(new message_handler_body(device, limit))\r
+  {\r
+  }\r
+\r
+  message_handler::message_handler(std::ostream& device, const std::vector<std::string>& message_files, unsigned limit, bool show) \r
+    throw(message_handler_read_error) :\r
+    message_handler_base(message_files,show), m_body(new message_handler_body(device, limit))\r
+  {\r
+  }\r
+\r
+  message_handler::~message_handler(void)\r
+    throw()\r
+  {\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // error count\r
+\r
+  void message_handler::set_error_limit(unsigned error_limit)\r
+    throw()\r
+  {\r
+    m_body->set_limit(error_limit);\r
+  }\r
+\r
+  unsigned message_handler::error_limit(void) const\r
+    throw()\r
+  {\r
+    return m_body->limit();\r
+  }\r
+\r
+  void message_handler::reset_error_count(void)\r
+    throw()\r
+  {\r
+    m_body->set_count(0);\r
+  }\r
+\r
+  unsigned message_handler::error_count(void) const\r
+    throw()\r
+  {\r
+    return m_body->count();\r
+  }\r
+\r
+  std::ostream& message_handler::device(void)\r
+  {\r
+    return m_body->device();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // information messages\r
+\r
+  bool message_handler::information(const std::string& id,\r
+                                    const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(id, args);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(id);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const std::string& id,\r
+                                    const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(id, arg1);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const std::string& id,\r
+                                    const std::string& arg1,\r
+                                    const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(id, arg1, arg2);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const std::string& id,\r
+                                    const std::string& arg1,\r
+                                    const std::string& arg2,\r
+                                    const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(id, arg1, arg2, arg3);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const message_position& position,\r
+                                    const std::string& id,\r
+                                    const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(position, id, args);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const message_position& position,\r
+                                    const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(position, id);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const message_position& position,\r
+                                    const std::string& id,\r
+                                    const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(position, id, arg1);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const message_position& position,\r
+                                    const std::string& id,\r
+                                    const std::string& arg1,\r
+                                    const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(position, id, arg1, arg2);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::information(const message_position& position,\r
+                                    const std::string& id,\r
+                                    const std::string& arg1,\r
+                                    const std::string& arg2,\r
+                                    const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << information_message(position, id, arg1, arg2, arg3);\r
+    return true;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // warning messages\r
+\r
+  bool message_handler::warning(const std::string& id,\r
+                                const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(id, args);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(id);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const std::string& id,\r
+                                const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(id, arg1);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const std::string& id,\r
+                                const std::string& arg1,\r
+                                const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(id, arg1, arg2);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const std::string& id,\r
+                                const std::string& arg1,\r
+                                const std::string& arg2,\r
+                                const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(id, arg1, arg2, arg3);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const message_position& position,\r
+                                const std::string& id,\r
+                                const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(position, id, args);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const message_position& position,\r
+                                const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(position, id);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const message_position& position,\r
+                                const std::string& id,\r
+                                const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(position, id, arg1);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const message_position& position,\r
+                                const std::string& id,\r
+                                const std::string& arg1,\r
+                                const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(position, id, arg1, arg2);\r
+    return true;\r
+  }\r
+\r
+  bool message_handler::warning(const message_position& position,\r
+                                const std::string& id,\r
+                                const std::string& arg1,\r
+                                const std::string& arg2,\r
+                                const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error)\r
+  {\r
+    device() << warning_message(position, id, arg1, arg2, arg3);\r
+    return true;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // error messages\r
+\r
+  bool message_handler::error(const std::string& id,\r
+                              const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(id, args);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(id);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const std::string& id,\r
+                              const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(id, arg1);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(id, arg1, arg2);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2,\r
+                              const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(id, arg1, arg2, arg3);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(position, id, args);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const message_position& position,\r
+                              const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(position, id);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(position, id, arg1);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(position, id, arg1, arg2);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  bool message_handler::error(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2,\r
+                              const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error)\r
+  {\r
+    device() << error_message(position, id, arg1, arg2, arg3);\r
+    m_body->error_increment();\r
+    if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit());\r
+    return false;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // fatal messages\r
+\r
+  bool message_handler::fatal(const std::string& id,\r
+                              const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(id, args);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(id);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const std::string& id,\r
+                              const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(id, arg1);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(id, arg1, arg2);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2,\r
+                              const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(id, arg1, arg2, arg3);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::vector<std::string>& args)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(position, id, args);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const message_position& position,\r
+                              const std::string& id)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(position, id);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::string& arg1)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(position, id, arg1);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(position, id, arg1, arg2);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  bool message_handler::fatal(const message_position& position,\r
+                              const std::string& id,\r
+                              const std::string& arg1,\r
+                              const std::string& arg2,\r
+                              const std::string& arg3)\r
+    throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error)\r
+  {\r
+    device() << fatal_message(position, id, arg1, arg2, arg3);\r
+    throw message_handler_fatal_error(id);\r
+  }\r
+\r
+  ///////////////////////////////////////////////////////////////////////////////\r
+  // plain text\r
+\r
+  bool message_handler::plaintext(const std::string& text)\r
+  {\r
+    device() << text << std::endl;\r
+    return true;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/subsystems/message_handler.hpp b/src/stlplus/subsystems/message_handler.hpp
new file mode 100644 (file)
index 0000000..26b8ed6
--- /dev/null
@@ -0,0 +1,1015 @@
+#ifndef STLPLUS_MESSAGE_HANDLER\r
+#define STLPLUS_MESSAGE_HANDLER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A general-purpose message handler using a message file as the source of all text\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "subsystems_fixes.hpp"\r
+#include "smart_ptr.hpp"\r
+#include <iostream>\r
+#include <string>\r
+#include <vector>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Internals\r
+\r
+  class message_handler_base;\r
+  class message_handler_base_body;\r
+\r
+  class message_handler;\r
+  class message_handler_body;\r
+\r
+  class message_context_body;\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // an object representing a file position\r
+  // used for example when reporting errors when parsing a text file\r
+\r
+  class message_position\r
+  {\r
+  public:\r
+    message_position(void);\r
+    message_position(const std::string& filename, unsigned line, unsigned column);\r
+    ~message_position(void);\r
+\r
+    // access the elements of the position\r
+    const std::string& filename(void) const;\r
+    // line number in the range 1..n\r
+    // so line 0 means uninitialised\r
+    unsigned line(void) const;\r
+    // column number in the range 0..m-1\r
+    unsigned column(void) const;\r
+\r
+    // add a column offset to a position\r
+    message_position operator + (unsigned) const;\r
+    message_position& operator += (unsigned);\r
+\r
+    // tests for valid position\r
+    bool empty(void) const;\r
+    bool valid(void) const;\r
+\r
+    // vector of two strings\r
+    // - the first reproducing the source line\r
+    // - the second an arrow pointing to the correct column\r
+    // the vector will be empty if the position can't be found\r
+    std::vector<std::string> show(void) const;\r
+\r
+  private:\r
+    std::string m_filename;\r
+    unsigned m_line;\r
+    unsigned m_column;\r
+  };\r
+\r
+  std::string to_string(const message_position& where);\r
+\r
+  //////////////////////////////////////////////////////////////////////////////\r
+  // an object representing an message context\r
+  // used to control the context stack\r
+  // on initialisation, the message_context stores the state of the context stack\r
+  // on destruction it restores the state by popping any context that has been pushed since creation\r
+\r
+  class message_context\r
+  {\r
+  public:\r
+    message_context(message_handler_base& handler);\r
+\r
+    void set(message_handler_base& handler);\r
+    void pop(void);\r
+\r
+  private:\r
+    friend class message_context_body;\r
+    friend class message_handler_base;\r
+    smart_ptr_nocopy<message_context_body> m_body;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // exception classes which can be thrown by the message handler\r
+\r
+  // read_error is thrown if the message file read fails\r
+  class message_handler_read_error : public std::runtime_error\r
+  {\r
+  public:\r
+    message_handler_read_error(const message_position& position, const std::string& reason);\r
+    ~message_handler_read_error(void) throw();\r
+\r
+    const message_position& where(void) const;\r
+\r
+  private:\r
+    message_position m_position;\r
+  };\r
+\r
+  // format_error is thrown if a formatting error occurs trying to create the text for the message\r
+\r
+  class message_handler_format_error : public std::runtime_error\r
+  {\r
+  public:\r
+    message_handler_format_error(const std::string& format, unsigned offset);\r
+    message_handler_format_error(const message_position& pos, const std::string& format, unsigned offset);\r
+    ~message_handler_format_error(void) throw();\r
+\r
+    const message_position& where(void) const;\r
+    const std::string& format(void) const;\r
+    unsigned offset(void) const;\r
+\r
+  private:\r
+    message_position m_position;\r
+    std::string m_format;\r
+    unsigned m_offset;\r
+  };\r
+\r
+  // id_error is thrown if an error id is requested that could not be found in the message file\r
+\r
+  class message_handler_id_error : public std::runtime_error\r
+  {\r
+  public:\r
+    message_handler_id_error(const std::string& id);\r
+    ~message_handler_id_error(void) throw();\r
+\r
+    const std::string& id(void) const;\r
+\r
+  private:\r
+    std::string m_id;\r
+  };\r
+\r
+  // limit_error is thrown when the number of errors reaches the error limit\r
+\r
+  class message_handler_limit_error : public std::runtime_error\r
+  {\r
+  public:\r
+    message_handler_limit_error(unsigned limit);\r
+    ~message_handler_limit_error(void) throw();\r
+\r
+    unsigned limit(void) const;\r
+\r
+  private:\r
+    unsigned m_limit;   // the limit that was exceeded\r
+  };\r
+\r
+  // fatal_error is thrown when a fatal error is reported\r
+\r
+  class message_handler_fatal_error : public std::runtime_error\r
+  {\r
+  public:\r
+    message_handler_fatal_error(const std::string& id);\r
+    ~message_handler_fatal_error(void) throw();\r
+\r
+    const std::string& id(void) const;\r
+\r
+  private:\r
+    std::string m_id;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // base version returns message objects as vectors of strings\r
+  // - it is then up to the user to decide what to do with them\r
+  // - suitable for use in a GUI for example where the message is displayed in a dialog\r
+\r
+  class message_handler_base\r
+  {\r
+  public:\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // constructor\r
+\r
+    // The first form sets the show flag but doesn't load any message files.\r
+    // The second and third forms also read message file(s) by calling\r
+    // add_message_file and therefore can throw exceptions. The first form\r
+    // defers file reading to explicit calls of add_message_file so does not\r
+    // throw any exceptions.\r
+\r
+    // show determines whether the source file line containing the source of a problem should also be shown\r
+\r
+    message_handler_base(bool show = true)\r
+      throw();\r
+\r
+    message_handler_base(const std::string& message_file, bool show = true)\r
+      throw(message_handler_read_error);\r
+\r
+    message_handler_base(const std::vector<std::string>& message_files, bool show = true)\r
+      throw(message_handler_read_error);\r
+\r
+    virtual ~message_handler_base(void)\r
+      throw();\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // message file handling\r
+\r
+    // The message file format contains lines of the form:\r
+    //\r
+    // <id> <spaces> <text>\r
+    //\r
+    // In <id> is a unique mnemonic for the message. It starts with an\r
+    // alphabetic character and may contain alphanumerics and underscores only.\r
+    // The <spaces> can be one or more space or tab characters. The <text> is the\r
+    // remainder of the line and is plain text (not a quoted string). All lines\r
+    // starting with a non-alphabetic character are assumed to be comments and are\r
+    // ignored\r
+\r
+    // If the message file is missing the function throws read_error with no line\r
+    // number. If formatting errors were found in the file,then it throws a\r
+    // read_error with valid line information.\r
+\r
+    // Any number of message files can be added and they accumulate\r
+\r
+    void add_message_file(const std::string& message_file)\r
+      throw(message_handler_read_error);\r
+\r
+    void add_message_files(const std::vector<std::string>& message_files)\r
+      throw(message_handler_read_error);\r
+\r
+    void add_message(const std::string& id, const std::string& text)\r
+      throw();\r
+\r
+    bool message_present(const std::string& id) const\r
+      throw();\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // format control\r
+\r
+    // The status formats - that is, information/warning/error/fatal/context/supplement\r
+    // formats take a single argument which is the formatted message\r
+    // For example: "warning: @0"\r
+    //\r
+    // Messages may be printed as either simple or positional\r
+    //   simple:     just a text message, such as a progress report\r
+    //   positional: a message relating to a source file line\r
+    // The formatted message text is generated directly for simple messages\r
+    // However, for positional messages, this text is further substituted\r
+    // into a positional format string.\r
+    // The positional format string takes up to 4 arguments:\r
+    //   @0: simple message text\r
+    //   @1: filename\r
+    //   @2: line number\r
+    //   @3: column number\r
+    // You can miss out a part of this (e.g. the column number)\r
+    // by simply not including the argument number in the format string\r
+    // For example: "file: @1, line: @2: @0"\r
+    //\r
+    // The default formats are:\r
+    //   information: "@0"\r
+    //   warning:     "warning: @0"\r
+    //   error:       "error: @0"\r
+    //   fatal:       "FATAL: @0"\r
+    //   context:     "context: @0"\r
+    //   supplement:  "supplement: @0"\r
+    //\r
+    //   positional:  "\"@1\" (@2,@3) : @0"\r
+\r
+    void set_information_format(const std::string& format)\r
+      throw();\r
+\r
+    void set_warning_format(const std::string& format)\r
+      throw();\r
+\r
+    void set_error_format(const std::string& format)\r
+      throw();\r
+\r
+    void set_fatal_format(const std::string& format)\r
+      throw();\r
+\r
+    void set_context_format(const std::string& format)\r
+      throw();\r
+\r
+    void set_supplement_format(const std::string& format)\r
+      throw();\r
+\r
+    void set_position_format(const std::string& format)\r
+      throw();\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // source file position display control\r
+    // show_position indicates that the source file line containing the error\r
+    //   should be shown with the message on subsequent lines\r
+    // hide_position indicates that the source file line should not be shown\r
+\r
+    void show_position(void)\r
+      throw();\r
+\r
+    void hide_position(void)\r
+      throw();\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // Message formatting functions\r
+    // These functions return a vector of strings containing the completed message\r
+\r
+    // There are 6 classes of message: information, context, supplement, warning, error, fatal\r
+    // The 4 main classes are:\r
+    //   - information: progress messages, status messages etc.\r
+    //   - warning: a problem has been found but there is a sensible way of proceeding\r
+    //   - error: a problem has been found and the operation will fail\r
+    //            processing may continue but only to find further errors\r
+    //   - fatal: an internal (programming) error has been found and the operation is stopping NOW\r
+    // The remaining two always follow one of the above\r
+    //   - context: give stack-like information of the error context \r
+    //              e.g. if processing include files, the sequence of includes forms a stack\r
+    //   - supplement: give extra information of the error context\r
+    //              e.g. give the set of possible solutions to the problem\r
+\r
+    // There are 2 kinds of message: simple, positional\r
+    //   - simple: just a text message\r
+    //   - positional: a message relating to a source file and a specific position in that file\r
+    // This gives 8 variants. \r
+    // Note: a positional message with an empty position is treated as a simple message\r
+\r
+    // Messages can have arguments.\r
+    // All arguments are strings.\r
+    // For each variant there are 5 functions relating to different numbers of arguments.\r
+    //   - general form: takes any number of arguments as a vector of strings\r
+    //   - 0 arguments: takes no arguments\r
+    //   - 1 argument: allows a single argument\r
+    //   - 2 arguments: allows two arguments\r
+    //   - 3 arguments: allows three arguments\r
+    // For more than 3 arguments, use the general form\r
+\r
+    // information messages\r
+\r
+    // simple messages\r
+    std::vector<std::string> information_message(const std::string& id,\r
+                                                 const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const std::string& id,\r
+                                                 const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const std::string& id,\r
+                                                 const std::string& arg1,\r
+                                                 const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const std::string& id,\r
+                                                 const std::string& arg1,\r
+                                                 const std::string& arg2,\r
+                                                 const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional messages\r
+    std::vector<std::string> information_message(const message_position&,\r
+                                                 const std::string& id,\r
+                                                 const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const message_position&,\r
+                                                 const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const message_position&,\r
+                                                 const std::string& id,\r
+                                                 const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const message_position&,\r
+                                                 const std::string& id,\r
+                                                 const std::string& arg1,\r
+                                                 const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> information_message(const message_position&,\r
+                                                 const std::string& id,\r
+                                                 const std::string& arg1,\r
+                                                 const std::string& arg2,\r
+                                                 const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // warning messages\r
+\r
+    // simple messages\r
+    std::vector<std::string> warning_message(const std::string& id,\r
+                                             const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const std::string& id,\r
+                                             const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2,\r
+                                             const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional messages\r
+    std::vector<std::string> warning_message(const message_position&,\r
+                                             const std::string& id,\r
+                                             const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const message_position&,\r
+                                             const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const message_position&,\r
+                                             const std::string& id,\r
+                                             const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const message_position&,\r
+                                             const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> warning_message(const message_position&,\r
+                                             const std::string& id,\r
+                                             const std::string& arg1,\r
+                                             const std::string& arg2,\r
+                                             const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // error messages\r
+\r
+    // simple messages\r
+    std::vector<std::string> error_message(const std::string& id,\r
+                                           const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const std::string& id,\r
+                                           const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2,\r
+                                           const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional messages\r
+    std::vector<std::string> error_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const message_position&,\r
+                                           const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> error_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2,\r
+                                           const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // fatal messages\r
+    // Note that these do not throw the fatal_error exception because that would prevent the message being reported\r
+    // the caller should throw the exception after reporting the message\r
+\r
+    // simple messages\r
+    std::vector<std::string> fatal_message(const std::string& id,\r
+                                           const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const std::string& id,\r
+                                           const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2,\r
+                                           const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional messages\r
+    std::vector<std::string> fatal_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const message_position&,\r
+                                           const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    std::vector<std::string> fatal_message(const message_position&,\r
+                                           const std::string& id,\r
+                                           const std::string& arg1,\r
+                                           const std::string& arg2,\r
+                                           const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // supplement messages - these must be pushed *before* the message that they apply to\r
+\r
+    // simple messages\r
+    void push_supplement(const std::string& id,\r
+                         const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const std::string& id,\r
+                         const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const std::string& id,\r
+                         const std::string& arg1,\r
+                         const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const std::string& id,\r
+                         const std::string& arg1,\r
+                         const std::string& arg2,\r
+                         const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional messages\r
+    void push_supplement(const message_position&,\r
+                         const std::string& id,\r
+                         const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const message_position&,\r
+                         const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const message_position&,\r
+                         const std::string& id,\r
+                         const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const message_position&,\r
+                         const std::string& id,\r
+                         const std::string& arg1,\r
+                         const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_supplement(const message_position&,\r
+                         const std::string& id,\r
+                         const std::string& arg1,\r
+                         const std::string& arg2,\r
+                         const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // context stack - allows supplementary messages to be printed after each message showing where it came from\r
+    // for example, an message whilst inlining a function could be followed by a "function called from..." message\r
+\r
+    // simple context messages\r
+    void push_context(const std::string& id,\r
+                      const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const std::string& id,\r
+                      const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const std::string& id,\r
+                      const std::string& arg1,\r
+                      const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const std::string& id,\r
+                      const std::string& arg1,\r
+                      const std::string& arg2,\r
+                      const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional context messages\r
+    void push_context(const message_position&,\r
+                      const std::string& id,\r
+                      const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const message_position&,\r
+                      const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const message_position&,\r
+                      const std::string& id,\r
+                      const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const message_position&,\r
+                      const std::string& id,\r
+                      const std::string& arg1,\r
+                      const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    void push_context(const message_position&,\r
+                      const std::string& id,\r
+                      const std::string& arg1,\r
+                      const std::string& arg2,\r
+                      const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    unsigned context_depth(void) const\r
+      throw();\r
+\r
+    // remove the last level of context if there is one\r
+    void pop_context(void)\r
+      throw();\r
+    // remove context messages to the specified depth\r
+    void pop_context(unsigned)\r
+      throw();\r
+\r
+    // push the context and save it in the message_context handle. When the\r
+    // message_context handle goes out of scope, the context is popped\r
+    // automatically\r
+\r
+    // simple context messages\r
+    message_context auto_push_context(const std::string& id,\r
+                                      const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const std::string& id,\r
+                                      const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const std::string& id,\r
+                                      const std::string& arg1,\r
+                                      const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const std::string& id,\r
+                                      const std::string& arg1,\r
+                                      const std::string& arg2,\r
+                                      const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional context messages\r
+    message_context auto_push_context(const message_position&,\r
+                                      const std::string& id,\r
+                                      const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const message_position&,\r
+                                      const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const message_position&,\r
+                                      const std::string& id,\r
+                                      const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const message_position&,\r
+                                      const std::string& id,\r
+                                      const std::string& arg1,\r
+                                      const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    message_context auto_push_context(const message_position&,\r
+                                      const std::string& id,\r
+                                      const std::string& arg1,\r
+                                      const std::string& arg2,\r
+                                      const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+  private:\r
+    friend class message_handler_base_body;\r
+    smart_ptr_nocopy<message_handler_base_body> m_base_body;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // iostream-based derivative uses the above base class to generate messages then uses iostream to print them\r
+  // Note: since this is a public derivative, all message_handler_base operations are also available\r
+\r
+  class message_handler : public message_handler_base\r
+  {\r
+  public:\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // constructor\r
+\r
+    // The device is the output on which to print the error. For command-line tools\r
+    // it will be either std::cout (standard output) or std::cerr (standard error) from\r
+    // <iostream>.\r
+\r
+    // The second and third form also reads a message file by calling\r
+    // add_message_file and therefore can throw exceptions. The first form\r
+    // defers file reading to explicit calls of add_message_file so does not\r
+    // throw any exceptions.\r
+\r
+    // limit sets the error limit - zero disables this feature\r
+    // show determines whether the source file line containing the error should also be shown\r
+\r
+    message_handler(std::ostream& device,unsigned limit = 0,bool show = true) \r
+      throw();\r
+\r
+    message_handler(std::ostream& device,\r
+                    const std::string& message_file,unsigned limit = 0,bool show = true) \r
+      throw(message_handler_read_error);\r
+\r
+    message_handler(std::ostream& device,\r
+                    const std::vector<std::string>& message_files,unsigned limit = 0,bool show = true) \r
+      throw(message_handler_read_error);\r
+\r
+    ~message_handler(void)\r
+      throw();\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // error count and error limits\r
+\r
+    void set_error_limit(unsigned error_limit)\r
+      throw();\r
+\r
+    unsigned error_limit(void) const\r
+      throw();\r
+\r
+    void reset_error_count(void)\r
+      throw();\r
+\r
+    unsigned error_count(void) const\r
+      throw();\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // access the output device for whatever reason (for example, to ensure that\r
+    // text output goes wherever the messages go)\r
+\r
+    std::ostream& device(void);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // Message reporting functions\r
+    // These are based on the error formatting functions in the baseclass\r
+\r
+    // information messages\r
+\r
+    // simple messages\r
+    bool information(const std::string& id,\r
+                     const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const std::string& id,\r
+                     const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const std::string& id,\r
+                     const std::string& arg1,\r
+                     const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const std::string& id,\r
+                     const std::string& arg1,\r
+                     const std::string& arg2,\r
+                     const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional messages\r
+    bool information(const message_position&,\r
+                     const std::string& id,\r
+                     const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const message_position&,\r
+                     const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const message_position&,\r
+                     const std::string& id,\r
+                     const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const message_position&,\r
+                     const std::string& id,\r
+                     const std::string& arg1,\r
+                     const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool information(const message_position&,\r
+                     const std::string& id,\r
+                     const std::string& arg1,\r
+                     const std::string& arg2,\r
+                     const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // warning messages\r
+\r
+    // simple messages\r
+    bool warning(const std::string& id,\r
+                 const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const std::string& id,\r
+                 const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const std::string& id,\r
+                 const std::string& arg1,\r
+                 const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const std::string& id,\r
+                 const std::string& arg1,\r
+                 const std::string& arg2,\r
+                 const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // positional messages\r
+    bool warning(const message_position&,\r
+                 const std::string& id,\r
+                 const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const message_position&,\r
+                 const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const message_position&,\r
+                 const std::string& id,\r
+                 const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const message_position&,\r
+                 const std::string& id,\r
+                 const std::string& arg1,\r
+                 const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    bool warning(const message_position&,\r
+                 const std::string& id,\r
+                 const std::string& arg1,\r
+                 const std::string& arg2,\r
+                 const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error);\r
+\r
+    // error messages\r
+\r
+    // simple messages\r
+    bool error(const std::string& id,\r
+               const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const std::string& id,\r
+               const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2,\r
+               const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    // positional messages\r
+    bool error(const message_position&,\r
+               const std::string& id,\r
+               const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const message_position&,\r
+               const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const message_position&,\r
+               const std::string& id,\r
+               const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const message_position&,\r
+               const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    bool error(const message_position&,\r
+               const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2,\r
+               const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error);\r
+\r
+    // fatal messages\r
+    // These report the error and then always throw the fatal_error exception\r
+\r
+    // simple messages\r
+    bool fatal(const std::string& id,\r
+               const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const std::string& id,\r
+               const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2,\r
+               const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    // positional messages\r
+    bool fatal(const message_position&,\r
+               const std::string& id,\r
+               const std::vector<std::string>& args)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const message_position&,\r
+               const std::string& id)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const message_position&,\r
+               const std::string& id,\r
+               const std::string& arg1)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const message_position&,\r
+               const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    bool fatal(const message_position&,\r
+               const std::string& id,\r
+               const std::string& arg1,\r
+               const std::string& arg2,\r
+               const std::string& arg3)\r
+      throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error);\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // plain text output\r
+    // provides a simple way of outputting text from the program to the same device as the messages\r
+    // Each call of plaintext is treated as a line of text and has a newline appended\r
+\r
+    bool plaintext (const std::string& text);\r
+\r
+  private:\r
+    friend class message_handler_body;\r
+    smart_ptr_nocopy<message_handler_body> m_body;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
diff --git a/src/stlplus/subsystems/subsystems.hpp b/src/stlplus/subsystems/subsystems.hpp
new file mode 100644 (file)
index 0000000..2bc41b5
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef STLPLUS_SUBSYSTEMS\r
+#define STLPLUS_SUBSYSTEMS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Allows all the STLplus subsystems to be included in one go\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "cli_parser.hpp"\r
+#include "ini_manager.hpp"\r
+#include "library_manager.hpp"\r
+#include "message_handler.hpp"\r
+#include "timer.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/subsystems/subsystems_fixes.hpp b/src/stlplus/subsystems/subsystems_fixes.hpp
new file mode 100644 (file)
index 0000000..8fd9f91
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef STLPLUS_SUBSYSTEMS_FIXES\r
+#define STLPLUS_SUBSYSTEMS_FIXES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   Contains work arounds for OS or Compiler specific problems\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Unnecessary compiler warnings\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef _MSC_VER\r
+// Microsoft Visual Studio\r
+// shut up the following irritating warnings\r
+//   4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)\r
+//   4305 - VC6, identifier type was converted to a smaller type\r
+//   4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)\r
+//   4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it\r
+//   4290 - VC6, C++ exception specification ignored\r
+//   4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)\r
+//   4675 - VC7.1, "change" in function overload resolution _might_ have altered program\r
+//   4996 - VC8, 'xxxx' was declared deprecated\r
+#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996)\r
+#endif\r
+\r
+#ifdef __BORLANDC__\r
+// Borland\r
+// Shut up the following irritating warnings\r
+//   8026 - Functions with exception specifications are not expanded inline\r
+//   8027 - Functions with xxx are not expanded inline\r
+#pragma warn -8026\r
+#pragma warn -8027\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
diff --git a/src/stlplus/subsystems/timer.cpp b/src/stlplus/subsystems/timer.cpp
new file mode 100644 (file)
index 0000000..9faac0f
--- /dev/null
@@ -0,0 +1,59 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "timer.hpp"\r
+#include "dprintf.hpp"\r
+#include "time.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  timer::timer(void)\r
+  {\r
+    reset();\r
+  }\r
+\r
+  timer::~timer(void)\r
+  {\r
+  }\r
+\r
+  void timer::reset(void)\r
+  {\r
+    m_clock = clock();\r
+    m_time = time(0);\r
+  }\r
+\r
+  float timer::cpu(void) const\r
+  {\r
+    return ((float)(clock() - m_clock)) / ((float)CLOCKS_PER_SEC);\r
+  }\r
+\r
+  float timer::elapsed(void) const\r
+  {\r
+    return ((float)(time(0) - m_time));\r
+  }\r
+\r
+  std::string timer::text(void) const\r
+  {\r
+    return dformat("%4.2fs CPU, %s elapsed", cpu(), delaytime_string(time(0)-m_time).c_str());\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+  std::ostream& operator << (std::ostream& str, const timer& t)\r
+  {\r
+    return str << t.text();\r
+  }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
diff --git a/src/stlplus/subsystems/timer.hpp b/src/stlplus/subsystems/timer.hpp
new file mode 100644 (file)
index 0000000..a3ad38d
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef STLPLUS_TIMER\r
+#define STLPLUS_TIMER\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+//   A CPU timer encapsulated as a class. Measures the CPU time used since its\r
+//   construction and allows this cumulative time to be reported at any time.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "subsystems_fixes.hpp"\r
+#include <time.h>\r
+#include <string>\r
+#include <iostream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class timer\r
+  {\r
+  private:\r
+    clock_t m_clock;\r
+    time_t m_time;\r
+\r
+  public:\r
+    // constructor resets the timer to zero\r
+    timer(void);\r
+    ~timer(void);\r
+\r
+    // reset the timer to zero without destroying it\r
+    void reset(void);\r
+\r
+    // get the elapsed time in seconds, expressed as a float\r
+    float elapsed(void) const;\r
+    // get the CPU time in seconds, expressed as a float\r
+    float cpu(void) const;\r
+\r
+    // get a printable string representing the elapsed time and CPU time\r
+    std::string text(void) const;\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  // print the elapsed time and CPU time using the same representation as the text method\r
+  std::ostream& operator << (std::ostream&, const timer&);\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
index 64eee3d93a1f59aded19483aac796f073e47c5b4..caf96bb9a45c1f621d79a08683d62c0b52ad1871 100644 (file)
@@ -1,11 +1,10 @@
 
 //
 // Yoink
-// Compile this file with windres and link the object with the executable.
+// Compile this file with windres and link with the executable.
 //
 
 #include <winver.h>
-#include "config.h"
 
 1 VERSIONINFO
 FILEVERSION            VERSION_MAJOR,VERSION_MINOR,VERSION_REVISION,0
This page took 0.963457 seconds and 4 git commands to generate.