From: Charles McGarvey Date: Mon, 7 Jun 2010 16:33:23 +0000 (-0600) Subject: testing new non-autotools build system X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=commitdiff_plain;h=6b0a0d0efafe34d48ab344fca3b479553bd4e62c testing new non-autotools build system --- diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..75aec0a --- /dev/null +++ b/build/compile.sh @@ -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 +# .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 index 0000000..bf48496 --- /dev/null +++ b/build/config.guess @@ -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 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 ." + +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 + #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 /* 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 + + 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 + #include + + 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 + 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' /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 + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # 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 < +# include +#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 + 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 +# 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 < 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 index 0000000..4c0d959 --- /dev/null +++ b/build/config.sub @@ -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 . 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 ." + +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 index 0000000..c330d93 --- /dev/null +++ b/build/functions.sh @@ -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/\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 index 0000000..be27f7f --- /dev/null +++ b/build/install.sh @@ -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 index 0000000..d3a739c --- /dev/null +++ b/build/link.sh @@ -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 + diff --git a/configure.ac b/configure.ac index d45b756..a4f33cb 100644 --- a/configure.ac +++ b/configure.ac @@ -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 index 0000000..92d7529 --- /dev/null +++ b/data/rules.mk @@ -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)) +####################### + diff --git a/data/yoinkrc b/data/yoinkrc index f6f362c..d26ac4d 100644 --- a/data/yoinkrc +++ b/data/yoinkrc @@ -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 index 0000000..c654bbd --- /dev/null +++ b/doc/rules.mk @@ -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)) +####################### + diff --git a/doc/yoink.6.in b/doc/yoink.6.in index 24787e8..b730c6b 100644 --- a/doc/yoink.6.in +++ b/doc/yoink.6.in @@ -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 diff --git a/src/Animation.cc b/src/Animation.cc index ba34bc5..503f77a 100644 --- a/src/Animation.cc +++ b/src/Animation.cc @@ -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"); -} - diff --git a/src/Animation.hh b/src/Animation.hh index ba38601..3875b45 100644 --- a/src/Animation.hh +++ b/src/Animation.hh @@ -38,9 +38,6 @@ typedef boost::shared_ptr AnimationP; class Animation : public moof::resource { - class impl; - boost::shared_ptr 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_; }; diff --git a/src/Character.cc b/src/Character.cc index a857cbe..01b4753 100644 --- a/src/Character.cc +++ b/src/Character.cc @@ -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_; diff --git a/src/GameLayer.cc b/src/GameLayer.cc index 0498182..2518bdf 100644 --- a/src/GameLayer.cc +++ b/src/GameLayer.cc @@ -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)); } diff --git a/src/GameLayer.hh b/src/GameLayer.hh index c194e9f..37eb8e0 100644 --- a/src/GameLayer.hh +++ b/src/GameLayer.hh @@ -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(); diff --git a/src/Main.cc b/src/Main.cc index bb6b35b..2df3f1e 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -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 +#include + +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_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("mine"); + //moof::resource::add_type("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->get()->cool(); + //myAsset->get()->cool(); + ////myAsset->get()->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()->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(); diff --git a/src/Main.hh b/src/Main.hh index aa4a0d9..0a3c616 100644 --- a/src/Main.hh +++ b/src/Main.hh @@ -13,7 +13,7 @@ #define _MAIN_HH_ /** - * @file Main.hh + * \file Main.hh * This is where all the fun begins. */ diff --git a/src/Makefile.am b/src/Makefile.am index 61be610..7a680c8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,13 +5,266 @@ # +# +# 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 \ diff --git a/src/Scene.cc b/src/Scene.cc index 9510e36..2d8f120 100644 --- a/src/Scene.cc +++ b/src/Scene.cc @@ -195,7 +195,7 @@ struct Scene::impl : public moof::manager 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"); -} - diff --git a/src/Scene.hh b/src/Scene.hh index 9cebc3c..3c14b47 100644 --- a/src/Scene.hh +++ b/src/Scene.hh @@ -57,8 +57,6 @@ public: bool castRay(const moof::ray<2>& ray, std::list::contact>& hits) const; bool checkForCollision(Character& character); - - static bool find_path(std::string& name); }; diff --git a/src/TitleLayer.cc b/src/TitleLayer.cc index 6bc70f3..7a206c0 100644 --- a/src/TitleLayer.cc +++ b/src/TitleLayer.cc @@ -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(); } diff --git a/src/moof/cml/cml.h b/src/cml/cml.h similarity index 100% rename from src/moof/cml/cml.h rename to src/cml/cml.h diff --git a/src/moof/cml/constants.h b/src/cml/constants.h similarity index 100% rename from src/moof/cml/constants.h rename to src/cml/constants.h diff --git a/src/moof/cml/core/cml_assert.h b/src/cml/core/cml_assert.h similarity index 100% rename from src/moof/cml/core/cml_assert.h rename to src/cml/core/cml_assert.h diff --git a/src/moof/cml/core/cml_meta.h b/src/cml/core/cml_meta.h similarity index 100% rename from src/moof/cml/core/cml_meta.h rename to src/cml/core/cml_meta.h diff --git a/src/moof/cml/core/common.h b/src/cml/core/common.h similarity index 100% rename from src/moof/cml/core/common.h rename to src/cml/core/common.h diff --git a/src/moof/cml/core/dynamic_1D.h b/src/cml/core/dynamic_1D.h similarity index 100% rename from src/moof/cml/core/dynamic_1D.h rename to src/cml/core/dynamic_1D.h diff --git a/src/moof/cml/core/dynamic_2D.h b/src/cml/core/dynamic_2D.h similarity index 100% rename from src/moof/cml/core/dynamic_2D.h rename to src/cml/core/dynamic_2D.h diff --git a/src/moof/cml/core/external_1D.h b/src/cml/core/external_1D.h similarity index 100% rename from src/moof/cml/core/external_1D.h rename to src/cml/core/external_1D.h diff --git a/src/moof/cml/core/external_2D.h b/src/cml/core/external_2D.h similarity index 100% rename from src/moof/cml/core/external_2D.h rename to src/cml/core/external_2D.h diff --git a/src/moof/cml/core/fixed_1D.h b/src/cml/core/fixed_1D.h similarity index 100% rename from src/moof/cml/core/fixed_1D.h rename to src/cml/core/fixed_1D.h diff --git a/src/moof/cml/core/fixed_2D.h b/src/cml/core/fixed_2D.h similarity index 100% rename from src/moof/cml/core/fixed_2D.h rename to src/cml/core/fixed_2D.h diff --git a/src/moof/cml/core/fwd.h b/src/cml/core/fwd.h similarity index 100% rename from src/moof/cml/core/fwd.h rename to src/cml/core/fwd.h diff --git a/src/moof/cml/core/meta/common.h b/src/cml/core/meta/common.h similarity index 100% rename from src/moof/cml/core/meta/common.h rename to src/cml/core/meta/common.h diff --git a/src/moof/cml/core/meta/if.h b/src/cml/core/meta/if.h similarity index 100% rename from src/moof/cml/core/meta/if.h rename to src/cml/core/meta/if.h diff --git a/src/moof/cml/core/meta/switch.h b/src/cml/core/meta/switch.h similarity index 100% rename from src/moof/cml/core/meta/switch.h rename to src/cml/core/meta/switch.h diff --git a/src/moof/cml/defaults.h b/src/cml/defaults.h similarity index 100% rename from src/moof/cml/defaults.h rename to src/cml/defaults.h diff --git a/src/moof/cml/dynamic.h b/src/cml/dynamic.h similarity index 100% rename from src/moof/cml/dynamic.h rename to src/cml/dynamic.h diff --git a/src/moof/cml/et/array_promotions.h b/src/cml/et/array_promotions.h similarity index 100% rename from src/moof/cml/et/array_promotions.h rename to src/cml/et/array_promotions.h diff --git a/src/moof/cml/et/scalar_ops.h b/src/cml/et/scalar_ops.h similarity index 100% rename from src/moof/cml/et/scalar_ops.h rename to src/cml/et/scalar_ops.h diff --git a/src/moof/cml/et/scalar_promotions.h b/src/cml/et/scalar_promotions.h similarity index 100% rename from src/moof/cml/et/scalar_promotions.h rename to src/cml/et/scalar_promotions.h diff --git a/src/moof/cml/et/size_checking.h b/src/cml/et/size_checking.h similarity index 100% rename from src/moof/cml/et/size_checking.h rename to src/cml/et/size_checking.h diff --git a/src/moof/cml/et/tags.h b/src/cml/et/tags.h similarity index 100% rename from src/moof/cml/et/tags.h rename to src/cml/et/tags.h diff --git a/src/moof/cml/et/traits.h b/src/cml/et/traits.h similarity index 100% rename from src/moof/cml/et/traits.h rename to src/cml/et/traits.h diff --git a/src/moof/cml/external.h b/src/cml/external.h similarity index 100% rename from src/moof/cml/external.h rename to src/cml/external.h diff --git a/src/moof/cml/fixed.h b/src/cml/fixed.h similarity index 100% rename from src/moof/cml/fixed.h rename to src/cml/fixed.h diff --git a/src/moof/cml/mathlib/checking.h b/src/cml/mathlib/checking.h similarity index 100% rename from src/moof/cml/mathlib/checking.h rename to src/cml/mathlib/checking.h diff --git a/src/moof/cml/mathlib/coord_conversion.h b/src/cml/mathlib/coord_conversion.h similarity index 100% rename from src/moof/cml/mathlib/coord_conversion.h rename to src/cml/mathlib/coord_conversion.h diff --git a/src/moof/cml/mathlib/epsilon.h b/src/cml/mathlib/epsilon.h similarity index 100% rename from src/moof/cml/mathlib/epsilon.h rename to src/cml/mathlib/epsilon.h diff --git a/src/moof/cml/mathlib/frustum.h b/src/cml/mathlib/frustum.h similarity index 100% rename from src/moof/cml/mathlib/frustum.h rename to src/cml/mathlib/frustum.h diff --git a/src/moof/cml/mathlib/helper.h b/src/cml/mathlib/helper.h similarity index 100% rename from src/moof/cml/mathlib/helper.h rename to src/cml/mathlib/helper.h diff --git a/src/moof/cml/mathlib/interpolation.h b/src/cml/mathlib/interpolation.h similarity index 100% rename from src/moof/cml/mathlib/interpolation.h rename to src/cml/mathlib/interpolation.h diff --git a/src/moof/cml/mathlib/mathlib.h b/src/cml/mathlib/mathlib.h similarity index 100% rename from src/moof/cml/mathlib/mathlib.h rename to src/cml/mathlib/mathlib.h diff --git a/src/moof/cml/mathlib/matrix_basis.h b/src/cml/mathlib/matrix_basis.h similarity index 100% rename from src/moof/cml/mathlib/matrix_basis.h rename to src/cml/mathlib/matrix_basis.h diff --git a/src/moof/cml/mathlib/matrix_concat.h b/src/cml/mathlib/matrix_concat.h similarity index 100% rename from src/moof/cml/mathlib/matrix_concat.h rename to src/cml/mathlib/matrix_concat.h diff --git a/src/moof/cml/mathlib/matrix_misc.h b/src/cml/mathlib/matrix_misc.h similarity index 100% rename from src/moof/cml/mathlib/matrix_misc.h rename to src/cml/mathlib/matrix_misc.h diff --git a/src/moof/cml/mathlib/matrix_ortho.h b/src/cml/mathlib/matrix_ortho.h similarity index 100% rename from src/moof/cml/mathlib/matrix_ortho.h rename to src/cml/mathlib/matrix_ortho.h diff --git a/src/moof/cml/mathlib/matrix_projection.h b/src/cml/mathlib/matrix_projection.h similarity index 100% rename from src/moof/cml/mathlib/matrix_projection.h rename to src/cml/mathlib/matrix_projection.h diff --git a/src/moof/cml/mathlib/matrix_rotation.h b/src/cml/mathlib/matrix_rotation.h similarity index 100% rename from src/moof/cml/mathlib/matrix_rotation.h rename to src/cml/mathlib/matrix_rotation.h diff --git a/src/moof/cml/mathlib/matrix_transform.h b/src/cml/mathlib/matrix_transform.h similarity index 100% rename from src/moof/cml/mathlib/matrix_transform.h rename to src/cml/mathlib/matrix_transform.h diff --git a/src/moof/cml/mathlib/matrix_translation.h b/src/cml/mathlib/matrix_translation.h similarity index 100% rename from src/moof/cml/mathlib/matrix_translation.h rename to src/cml/mathlib/matrix_translation.h diff --git a/src/moof/cml/mathlib/misc.h b/src/cml/mathlib/misc.h similarity index 100% rename from src/moof/cml/mathlib/misc.h rename to src/cml/mathlib/misc.h diff --git a/src/moof/cml/mathlib/picking.h b/src/cml/mathlib/picking.h similarity index 100% rename from src/moof/cml/mathlib/picking.h rename to src/cml/mathlib/picking.h diff --git a/src/moof/cml/mathlib/projection.h b/src/cml/mathlib/projection.h similarity index 100% rename from src/moof/cml/mathlib/projection.h rename to src/cml/mathlib/projection.h diff --git a/src/moof/cml/mathlib/quaternion_basis.h b/src/cml/mathlib/quaternion_basis.h similarity index 100% rename from src/moof/cml/mathlib/quaternion_basis.h rename to src/cml/mathlib/quaternion_basis.h diff --git a/src/moof/cml/mathlib/quaternion_rotation.h b/src/cml/mathlib/quaternion_rotation.h similarity index 100% rename from src/moof/cml/mathlib/quaternion_rotation.h rename to src/cml/mathlib/quaternion_rotation.h diff --git a/src/moof/cml/mathlib/typedef.h b/src/cml/mathlib/typedef.h similarity index 100% rename from src/moof/cml/mathlib/typedef.h rename to src/cml/mathlib/typedef.h diff --git a/src/moof/cml/mathlib/vector_angle.h b/src/cml/mathlib/vector_angle.h similarity index 100% rename from src/moof/cml/mathlib/vector_angle.h rename to src/cml/mathlib/vector_angle.h diff --git a/src/moof/cml/mathlib/vector_misc.h b/src/cml/mathlib/vector_misc.h similarity index 100% rename from src/moof/cml/mathlib/vector_misc.h rename to src/cml/mathlib/vector_misc.h diff --git a/src/moof/cml/mathlib/vector_ortho.h b/src/cml/mathlib/vector_ortho.h similarity index 100% rename from src/moof/cml/mathlib/vector_ortho.h rename to src/cml/mathlib/vector_ortho.h diff --git a/src/moof/cml/mathlib/vector_transform.h b/src/cml/mathlib/vector_transform.h similarity index 100% rename from src/moof/cml/mathlib/vector_transform.h rename to src/cml/mathlib/vector_transform.h diff --git a/src/moof/cml/matrix.h b/src/cml/matrix.h similarity index 100% rename from src/moof/cml/matrix.h rename to src/cml/matrix.h diff --git a/src/moof/cml/matrix/class_ops.h b/src/cml/matrix/class_ops.h similarity index 100% rename from src/moof/cml/matrix/class_ops.h rename to src/cml/matrix/class_ops.h diff --git a/src/moof/cml/matrix/determinant.h b/src/cml/matrix/determinant.h similarity index 100% rename from src/moof/cml/matrix/determinant.h rename to src/cml/matrix/determinant.h diff --git a/src/moof/cml/matrix/dynamic.h b/src/cml/matrix/dynamic.h similarity index 100% rename from src/moof/cml/matrix/dynamic.h rename to src/cml/matrix/dynamic.h diff --git a/src/moof/cml/matrix/external.h b/src/cml/matrix/external.h similarity index 100% rename from src/moof/cml/matrix/external.h rename to src/cml/matrix/external.h diff --git a/src/moof/cml/matrix/fixed.h b/src/cml/matrix/fixed.h similarity index 100% rename from src/moof/cml/matrix/fixed.h rename to src/cml/matrix/fixed.h diff --git a/src/moof/cml/matrix/inverse.h b/src/cml/matrix/inverse.h similarity index 100% rename from src/moof/cml/matrix/inverse.h rename to src/cml/matrix/inverse.h diff --git a/src/moof/cml/matrix/lu.h b/src/cml/matrix/lu.h similarity index 100% rename from src/moof/cml/matrix/lu.h rename to src/cml/matrix/lu.h diff --git a/src/moof/cml/matrix/matop_macros.h b/src/cml/matrix/matop_macros.h similarity index 100% rename from src/moof/cml/matrix/matop_macros.h rename to src/cml/matrix/matop_macros.h diff --git a/src/moof/cml/matrix/matrix_comparison.h b/src/cml/matrix/matrix_comparison.h similarity index 100% rename from src/moof/cml/matrix/matrix_comparison.h rename to src/cml/matrix/matrix_comparison.h diff --git a/src/moof/cml/matrix/matrix_expr.h b/src/cml/matrix/matrix_expr.h similarity index 100% rename from src/moof/cml/matrix/matrix_expr.h rename to src/cml/matrix/matrix_expr.h diff --git a/src/moof/cml/matrix/matrix_functions.h b/src/cml/matrix/matrix_functions.h similarity index 100% rename from src/moof/cml/matrix/matrix_functions.h rename to src/cml/matrix/matrix_functions.h diff --git a/src/moof/cml/matrix/matrix_mul.h b/src/cml/matrix/matrix_mul.h similarity index 100% rename from src/moof/cml/matrix/matrix_mul.h rename to src/cml/matrix/matrix_mul.h diff --git a/src/moof/cml/matrix/matrix_ops.h b/src/cml/matrix/matrix_ops.h similarity index 100% rename from src/moof/cml/matrix/matrix_ops.h rename to src/cml/matrix/matrix_ops.h diff --git a/src/moof/cml/matrix/matrix_print.h b/src/cml/matrix/matrix_print.h similarity index 100% rename from src/moof/cml/matrix/matrix_print.h rename to src/cml/matrix/matrix_print.h diff --git a/src/moof/cml/matrix/matrix_promotions.h b/src/cml/matrix/matrix_promotions.h similarity index 100% rename from src/moof/cml/matrix/matrix_promotions.h rename to src/cml/matrix/matrix_promotions.h diff --git a/src/moof/cml/matrix/matrix_rowcol.h b/src/cml/matrix/matrix_rowcol.h similarity index 100% rename from src/moof/cml/matrix/matrix_rowcol.h rename to src/cml/matrix/matrix_rowcol.h diff --git a/src/moof/cml/matrix/matrix_traits.h b/src/cml/matrix/matrix_traits.h similarity index 100% rename from src/moof/cml/matrix/matrix_traits.h rename to src/cml/matrix/matrix_traits.h diff --git a/src/moof/cml/matrix/matrix_transpose.h b/src/cml/matrix/matrix_transpose.h similarity index 100% rename from src/moof/cml/matrix/matrix_transpose.h rename to src/cml/matrix/matrix_transpose.h diff --git a/src/moof/cml/matrix/matrix_unroller.h b/src/cml/matrix/matrix_unroller.h similarity index 100% rename from src/moof/cml/matrix/matrix_unroller.h rename to src/cml/matrix/matrix_unroller.h diff --git a/src/moof/cml/matvec/matvec_mul.h b/src/cml/matvec/matvec_mul.h similarity index 100% rename from src/moof/cml/matvec/matvec_mul.h rename to src/cml/matvec/matvec_mul.h diff --git a/src/moof/cml/matvec/matvec_promotions.h b/src/cml/matvec/matvec_promotions.h similarity index 100% rename from src/moof/cml/matvec/matvec_promotions.h rename to src/cml/matvec/matvec_promotions.h diff --git a/src/moof/cml/quaternion.h b/src/cml/quaternion.h similarity index 100% rename from src/moof/cml/quaternion.h rename to src/cml/quaternion.h diff --git a/src/moof/cml/quaternion/conjugate.h b/src/cml/quaternion/conjugate.h similarity index 100% rename from src/moof/cml/quaternion/conjugate.h rename to src/cml/quaternion/conjugate.h diff --git a/src/moof/cml/quaternion/inverse.h b/src/cml/quaternion/inverse.h similarity index 100% rename from src/moof/cml/quaternion/inverse.h rename to src/cml/quaternion/inverse.h diff --git a/src/moof/cml/quaternion/quaternion.h b/src/cml/quaternion/quaternion.h similarity index 100% rename from src/moof/cml/quaternion/quaternion.h rename to src/cml/quaternion/quaternion.h diff --git a/src/moof/cml/quaternion/quaternion_comparison.h b/src/cml/quaternion/quaternion_comparison.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_comparison.h rename to src/cml/quaternion/quaternion_comparison.h diff --git a/src/moof/cml/quaternion/quaternion_dot.h b/src/cml/quaternion/quaternion_dot.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_dot.h rename to src/cml/quaternion/quaternion_dot.h diff --git a/src/moof/cml/quaternion/quaternion_expr.h b/src/cml/quaternion/quaternion_expr.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_expr.h rename to src/cml/quaternion/quaternion_expr.h diff --git a/src/moof/cml/quaternion/quaternion_functions.h b/src/cml/quaternion/quaternion_functions.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_functions.h rename to src/cml/quaternion/quaternion_functions.h diff --git a/src/moof/cml/quaternion/quaternion_mul.h b/src/cml/quaternion/quaternion_mul.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_mul.h rename to src/cml/quaternion/quaternion_mul.h diff --git a/src/moof/cml/quaternion/quaternion_ops.h b/src/cml/quaternion/quaternion_ops.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_ops.h rename to src/cml/quaternion/quaternion_ops.h diff --git a/src/moof/cml/quaternion/quaternion_print.h b/src/cml/quaternion/quaternion_print.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_print.h rename to src/cml/quaternion/quaternion_print.h diff --git a/src/moof/cml/quaternion/quaternion_promotions.h b/src/cml/quaternion/quaternion_promotions.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_promotions.h rename to src/cml/quaternion/quaternion_promotions.h diff --git a/src/moof/cml/quaternion/quaternion_traits.h b/src/cml/quaternion/quaternion_traits.h similarity index 100% rename from src/moof/cml/quaternion/quaternion_traits.h rename to src/cml/quaternion/quaternion_traits.h diff --git a/src/moof/cml/quaternion/quatop_macros.h b/src/cml/quaternion/quatop_macros.h similarity index 100% rename from src/moof/cml/quaternion/quatop_macros.h rename to src/cml/quaternion/quatop_macros.h diff --git a/src/moof/cml/util.h b/src/cml/util.h similarity index 100% rename from src/moof/cml/util.h rename to src/cml/util.h diff --git a/src/moof/cml/vector.h b/src/cml/vector.h similarity index 100% rename from src/moof/cml/vector.h rename to src/cml/vector.h diff --git a/src/moof/cml/vector/class_ops.h b/src/cml/vector/class_ops.h similarity index 100% rename from src/moof/cml/vector/class_ops.h rename to src/cml/vector/class_ops.h diff --git a/src/moof/cml/vector/dynamic.h b/src/cml/vector/dynamic.h similarity index 100% rename from src/moof/cml/vector/dynamic.h rename to src/cml/vector/dynamic.h diff --git a/src/moof/cml/vector/external.h b/src/cml/vector/external.h similarity index 100% rename from src/moof/cml/vector/external.h rename to src/cml/vector/external.h diff --git a/src/moof/cml/vector/fixed.h b/src/cml/vector/fixed.h similarity index 100% rename from src/moof/cml/vector/fixed.h rename to src/cml/vector/fixed.h diff --git a/src/moof/cml/vector/vecop_macros.h b/src/cml/vector/vecop_macros.h similarity index 100% rename from src/moof/cml/vector/vecop_macros.h rename to src/cml/vector/vecop_macros.h diff --git a/src/moof/cml/vector/vector_comparison.h b/src/cml/vector/vector_comparison.h similarity index 100% rename from src/moof/cml/vector/vector_comparison.h rename to src/cml/vector/vector_comparison.h diff --git a/src/moof/cml/vector/vector_expr.h b/src/cml/vector/vector_expr.h similarity index 100% rename from src/moof/cml/vector/vector_expr.h rename to src/cml/vector/vector_expr.h diff --git a/src/moof/cml/vector/vector_functions.h b/src/cml/vector/vector_functions.h similarity index 100% rename from src/moof/cml/vector/vector_functions.h rename to src/cml/vector/vector_functions.h diff --git a/src/moof/cml/vector/vector_ops.h b/src/cml/vector/vector_ops.h similarity index 100% rename from src/moof/cml/vector/vector_ops.h rename to src/cml/vector/vector_ops.h diff --git a/src/moof/cml/vector/vector_print.h b/src/cml/vector/vector_print.h similarity index 100% rename from src/moof/cml/vector/vector_print.h rename to src/cml/vector/vector_print.h diff --git a/src/moof/cml/vector/vector_products.h b/src/cml/vector/vector_products.h similarity index 100% rename from src/moof/cml/vector/vector_products.h rename to src/cml/vector/vector_products.h diff --git a/src/moof/cml/vector/vector_promotions.h b/src/cml/vector/vector_promotions.h similarity index 100% rename from src/moof/cml/vector/vector_promotions.h rename to src/cml/vector/vector_promotions.h diff --git a/src/moof/cml/vector/vector_traits.h b/src/cml/vector/vector_traits.h similarity index 100% rename from src/moof/cml/vector/vector_traits.h rename to src/cml/vector/vector_traits.h diff --git a/src/moof/cml/vector/vector_unroller.h b/src/cml/vector/vector_unroller.h similarity index 100% rename from src/moof/cml/vector/vector_unroller.h rename to src/cml/vector/vector_unroller.h diff --git a/src/moof/camera.cc b/src/moof/camera.cc index 4a4e4bf..093b7d1 100644 --- a/src/moof/camera.cc +++ b/src/moof/camera.cc @@ -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 index 0000000..161a8af --- /dev/null +++ b/src/moof/debug.hh @@ -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 // exit + +#include + + +#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_ + diff --git a/src/moof/hash.hh b/src/moof/hash.hh index 27cc650..0963912 100644 --- a/src/moof/hash.hh +++ b/src/moof/hash.hh @@ -19,7 +19,7 @@ #include -#include +#include namespace moof { diff --git a/src/moof/image.cc b/src/moof/image.cc index f2f0c09..93ef6d6 100644 --- a/src/moof/image.cc +++ b/src/moof/image.cc @@ -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 diff --git a/src/moof/image.hh b/src/moof/image.hh index 9757270..614e614 100644 --- a/src/moof/image.hh +++ b/src/moof/image.hh @@ -58,8 +58,6 @@ public: void set_as_icon() const; - static bool find_path(std::string& name); - private: diff --git a/src/moof/interpolator.hh b/src/moof/interpolator.hh index 937ca8a..0a7aa01 100644 --- a/src/moof/interpolator.hh +++ b/src/moof/interpolator.hh @@ -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_); } } diff --git a/src/moof/log.hh b/src/moof/log.hh index 2679b37..f65d571 100644 --- a/src/moof/log.hh +++ b/src/moof/log.hh @@ -23,21 +23,6 @@ #include -#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 { diff --git a/src/moof/math.hh b/src/moof/math.hh index 9a0c8eb..1c09d6e 100644 --- a/src/moof/math.hh +++ b/src/moof/math.hh @@ -21,7 +21,7 @@ #include -#include +#include #if HAVE_CONFIG_H #include "config.h" diff --git a/src/moof/modal_dialog.hh b/src/moof/modal_dialog.hh index 65bc61f..01dfc62 100644 --- a/src/moof/modal_dialog.hh +++ b/src/moof/modal_dialog.hh @@ -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); diff --git a/src/moof/packet.cc b/src/moof/packet.cc index 0440b7d..4ae5311 100644 --- a/src/moof/packet.cc +++ b/src/moof/packet.cc @@ -9,8 +9,6 @@ * **************************************************************************/ -#include "../config.h" - #include #if HAVE_BYTESWAP_H #include diff --git a/src/moof/packet.hh b/src/moof/packet.hh index 03c519f..55dd96c 100644 --- a/src/moof/packet.hh +++ b/src/moof/packet.hh @@ -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 { diff --git a/src/moof/resource.cc b/src/moof/resource.cc index 3512878..ac200f7 100644 --- a/src/moof/resource.cc +++ b/src/moof/resource.cc @@ -9,17 +9,178 @@ * **************************************************************************/ +#include + #include +#include + +#include -#include "log.hh" +#include "hash.hh" #include "resource.hh" +#if HAVE_CONFIG_H +#include "../config.h" +#endif + +#if USE_HOTLOADING +#include +#include +#endif + +#ifndef BUF_SIZE +#define BUF_SIZE 4096 +#endif + namespace moof { + +static std::vector search_paths_; + +typedef boost::weak_ptr resource_weakptr; +static hash resource_table_; // static member -std::vector resource::search_paths_; +resource::type_lookup_ptr resource::type_lookup_; + + +#if USE_HOTLOADING +static hash 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::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::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::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::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& 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& 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 preList; - boost::split(preList, temp_prefix, boost::is_any_of(":")); -#else - std::vector preList; - boost::split(preList, prefix, boost::is_any_of(":")); + //boost::replace_all(path, "/", "\\"); #endif - std::vector postList; - boost::split(postList, extension, boost::is_any_of(":")); - std::vector::iterator it; for (it = search_paths_.begin(); it != search_paths_.end(); ++it) { - std::vector::iterator jt; - for (jt = preList.begin(); jt != preList.end(); ++jt) - { - std::vector::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 diff --git a/src/moof/resource.hh b/src/moof/resource.hh index ed8a7d0..0f79757 100644 --- a/src/moof/resource.hh +++ b/src/moof/resource.hh @@ -18,23 +18,39 @@ */ #include +#include +#include #include #include +#include +#include + +#include + +#if HAVE_CONFIG_H +#include "../config.h" +#endif + namespace moof { +class resource; +typedef boost::shared_ptr 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 + 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); + (*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 + explicit resource(T* ptr) : + resource_(ptr), + typeinfo_(const_cast(&typeid(T))), + unloader_(new specific_unloader(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 + 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 + T* get() const + { + if (check()) 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_ptr; + + template + 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_ptr; + + template + 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 type_lookup; + typedef boost::shared_ptr 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 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(); + } + + + /** + * 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(); + } + + /** + * 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()); + } + + + /** + * 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 search_paths_; + resource_ptr resource_; }; diff --git a/src/moof/rules.mk b/src/moof/rules.mk new file mode 100644 index 0000000..aa2c749 --- /dev/null +++ b/src/moof/rules.mk @@ -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)) +####################### + diff --git a/src/moof/socket.hh b/src/moof/socket.hh index f4d2c82..c592fdb 100644 --- a/src/moof/socket.hh +++ b/src/moof/socket.hh @@ -24,11 +24,7 @@ #include #include -#if HAVE_FCNTL_H #include -#else -#error No alternative to fcntl implemented yet. -#endif #if defined(_WIN32) #include @@ -44,7 +40,7 @@ #include #endif -#include +#include #include #include @@ -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; } diff --git a/src/moof/sound.cc b/src/moof/sound.cc index 7482de7..d5b67bd 100644 --- a/src/moof/sound.cc +++ b/src/moof/sound.cc @@ -33,11 +33,31 @@ namespace moof { +class impl +{ +public: + + impl() + { + //log_info("registering ogg resource handler"); + resource::register_type("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); } diff --git a/src/moof/sound.hh b/src/moof/sound.hh index 5a60f0c..a0dd103 100644 --- a/src/moof/sound.hh +++ b/src/moof/sound.hh @@ -29,28 +29,22 @@ namespace moof { class sound; -typedef boost::shared_ptr sound_ptr; - +typedef resource_handle sound_handle; class sound_stream; -typedef boost::shared_ptr sound_stream_ptr; +typedef resource_handle 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(); }; diff --git a/src/moof/texture.cc b/src/moof/texture.cc index f8687f0..94d792d 100644 --- a/src/moof/texture.cc +++ b/src/moof/texture.cc @@ -105,10 +105,9 @@ class texture::impl : public manager 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 diff --git a/src/moof/texture.hh b/src/moof/texture.hh index 0ea1952..a53dcd1 100644 --- a/src/moof/texture.hh +++ b/src/moof/texture.hh @@ -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; diff --git a/src/moof/timer.cc b/src/moof/timer.cc index 2736256..e31e49f 100644 --- a/src/moof/timer.cc +++ b/src/moof/timer.cc @@ -15,19 +15,15 @@ #include -#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::max(); -std::map timer::gTimers; +scalar timer::next_expiration_ = std::numeric_limits::max(); +hash 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(id_, this)); + timers_.insert(std::pair(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::iterator it; - scalar nextFire = std::numeric_limits::max(); + scalar next_fire = std::numeric_limits::max(); - for (it = gTimers.begin(); it != gTimers.end(); ++it) + hash::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::iterator it; - - if (gNextFire > t) return; + if (next_expiration_ > t) return; - for (it = gTimers.begin(); it != gTimers.end(); ++it) + hash::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() { diff --git a/src/moof/timer.hh b/src/moof/timer.hh index d7a411b..a86f445 100644 --- a/src/moof/timer.hh +++ b/src/moof/timer.hh @@ -17,54 +17,151 @@ * Functions for measuring time in a friendly unit. */ -#include - #include #include +#include #include 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 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 gTimers; + static scalar next_expiration_; + static hash timers_; }; diff --git a/src/moof/video.cc b/src/moof/video.cc index 32278cc..aa49b03 100644 --- a/src/moof/video.cc +++ b/src/moof/video.cc @@ -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 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 index 0000000..e452ae4 --- /dev/null +++ b/src/rules.mk @@ -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 index 0000000..dad518e --- /dev/null +++ b/src/stlplus/README.txt @@ -0,0 +1,19 @@ +This directory is for implementing STLplus as a library collection. The +libraries are to be found in the directories: + + containers + persistence + portability + strings + subsystems + +The documentation is in the 'docs' directory and starts with index.html. + +To build the STLplus3 library collection, use the project files supplied at +this level - 'Makefile' for gcc, 'stlplus3.sln' for Visual Studio 9 (2008), +'stlplus3.dsw' for Visual Studio 6 (and 7 or 8). + +The 'source' directory is provided with script files that allow the library +collection to be merged into one large library - termed the monolithic build. +See source/README.txt for instructions. + diff --git a/src/moof/stlplus/containers.hpp b/src/stlplus/containers/containers.hpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/containers.hpp rename to src/stlplus/containers/containers.hpp diff --git a/src/moof/stlplus/containers_fixes.hpp b/src/stlplus/containers/containers_fixes.hpp old mode 100755 new mode 100644 similarity index 79% rename from src/moof/stlplus/containers_fixes.hpp rename to src/stlplus/containers/containers_fixes.hpp index 618ef84..ad29f99 --- a/src/moof/stlplus/containers_fixes.hpp +++ b/src/stlplus/containers/containers_fixes.hpp @@ -19,7 +19,6 @@ #ifdef _MSC_VER // Microsoft Visual Studio // shut up the following irritating warnings -// 4275 - VC6, exported class was derived from a class that was not exported // 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger) // 4305 - VC6, identifier type was converted to a smaller type // 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger) @@ -29,33 +28,16 @@ // 4355 - VC6, 'this' : used in base member initializer list // 4675 - VC7.1, "change" in function overload resolution _might_ have altered program // 4996 - VC8, 'xxxx' was declared deprecated -#pragma warning(disable: 4275 4786 4305 4503 4309 4290 4800 4355 4675 4996) +#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4355 4675 4996) #endif #ifdef __BORLANDC__ // Borland // Shut up the following irritating warnings -// 8008 - Condition is always true. -// Whenever the compiler encounters a constant comparison that (due to -// the nature of the value being compared) is always true or false, it -// issues this warning and evaluates the condition at compile time. // 8026 - Functions with exception specifications are not expanded inline // 8027 - Functions with xxx are not expanded inline -// 8060 - Possibly incorrect assignment. -// This warning is generated when the compiler encounters an assignment -// operator as the main operator of a conditional expression (part of -// an if, while, or do-while statement). This is usually a -// typographical error for the equality operator. -// 8066 - Unreachable code. -// A break, continue, goto, or return statement was not followed by a -// label or the end of a loop or function. The compiler checks while, -// do, and for loops with a constant test condition, and attempts to -// recognize loops that can't fall through. -#pragma warn -8008 #pragma warn -8026 #pragma warn -8027 -#pragma warn -8060 -#pragma warn -8066 #endif //////////////////////////////////////////////////////////////////////////////// @@ -114,7 +96,7 @@ // - version 7 (.NET) (compiler v.13) requires a typename in a parameter specification but supports all // - version 8 (2005) (compiler v.14) requires parameters and templates, supports all #ifdef _MSC_VER -#if _MSC_VER < 1300 +#if _MSC_VER <= 1200 #undef TYPENAME #define TYPENAME #endif @@ -129,5 +111,22 @@ #endif #endif +//////////////////////////////////////////////////////////////////////////////// +// Member templates +// e.g. a template function in a template class + +// Not all compilers support them - this fix can be used to disable member +// templates for compilers that don't. Unfortunately that means that some +// functionality will be missing for those compilers. + +#define STLPLUS_MEMBER_TEMPLATES + +// Visual Studio v6 (compiler version 12) does not support them +#ifdef _MSC_VER +#if _MSC_VER <= 1200 +#undef STLPLUS_MEMBER_TEMPLATES +#endif +#endif + //////////////////////////////////////////////////////////////////////////////// #endif diff --git a/src/stlplus/containers/copy_functors.hpp b/src/stlplus/containers/copy_functors.hpp new file mode 100644 index 0000000..c82ee9f --- /dev/null +++ b/src/stlplus/containers/copy_functors.hpp @@ -0,0 +1,66 @@ +#ifndef STLPLUS_COPY_FUNCTORS +#define STLPLUS_COPY_FUNCTORS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// The function constructor classes below are used by the smart_ptr and the +// simple_ptr classes. They provide three (well ok, two) copying mechanisms. +// These classes have been separated from the smart_ptr header by DJDM, as +// the simple_ptr classes now also use them. + +//////////////////////////////////////////////////////////////////////////////// +#include "containers_fixes.hpp" +#include "exceptions.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // copy functors implementing the three possible copy semantics + + // constructor_copy uses the copy constructor of the object - used for simple types + + template + class constructor_copy + { + public: + T* operator() (const T& from) throw() + { + return new T(from); + } + }; + + // clone_copy uses the clone method of the object - used for polymorphic types + + template + class clone_copy + { + public: + T* operator() (const T& from) throw() + { + return from.clone(); + } + }; + + // no_copy throws an exception - used for types that cannot be copied + + template + class no_copy + { + public: + T* operator() (const T& from) throw(illegal_copy) + { + throw illegal_copy("no_copy functor called"); + return 0; + } + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/moof/stlplus/digraph.hpp b/src/stlplus/containers/digraph.hpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/digraph.hpp rename to src/stlplus/containers/digraph.hpp diff --git a/src/moof/stlplus/digraph.tpp b/src/stlplus/containers/digraph.tpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/digraph.tpp rename to src/stlplus/containers/digraph.tpp diff --git a/src/moof/stlplus/exceptions.hpp b/src/stlplus/containers/exceptions.hpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/exceptions.hpp rename to src/stlplus/containers/exceptions.hpp diff --git a/src/moof/stlplus/foursome.hpp b/src/stlplus/containers/foursome.hpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/foursome.hpp rename to src/stlplus/containers/foursome.hpp diff --git a/src/moof/stlplus/foursome.tpp b/src/stlplus/containers/foursome.tpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/foursome.tpp rename to src/stlplus/containers/foursome.tpp diff --git a/src/moof/stlplus/hash.hpp b/src/stlplus/containers/hash.hpp old mode 100755 new mode 100644 similarity index 92% rename from src/moof/stlplus/hash.hpp rename to src/stlplus/containers/hash.hpp index 05d3b7d..1962481 --- a/src/moof/stlplus/hash.hpp +++ b/src/stlplus/containers/hash.hpp @@ -14,6 +14,7 @@ #include "exceptions.hpp" #include "safe_iterator.hpp" #include +#include namespace stlplus { @@ -151,10 +152,14 @@ namespace stlplus iterator insert(const K& key); // remove a key/data pair from the hash table - bool erase(const K& key); + // as in map, this returns the number of elements erased + size_type erase(const K& key); + // remove an element from the hash table using an iterator + // as in map, returns an iterator to the next element + iterator erase(iterator it); // remove all elements from the hash table void erase(void); - // provide the std::map equivalent clear function + // map equivalent of above void clear(void); // find a key and return an iterator to it @@ -176,6 +181,10 @@ namespace stlplus const_iterator end(void) const; iterator end(void); + // diagnostic report shows the number of items in each bin so can be used + // to diagnose effectiveness of hash functions + void debug_report(std::ostream&) const; + // internals private: friend class hash_element; diff --git a/src/moof/stlplus/hash.tpp b/src/stlplus/containers/hash.tpp old mode 100755 new mode 100644 similarity index 82% rename from src/moof/stlplus/hash.tpp rename to src/stlplus/containers/hash.tpp index bcb5bc5..9a1e4e9 --- a/src/moof/stlplus/hash.tpp +++ b/src/stlplus/containers/hash.tpp @@ -6,6 +6,7 @@ // License: BSD License, see ../docs/license.html //////////////////////////////////////////////////////////////////////////////// +#include namespace stlplus { @@ -22,13 +23,13 @@ namespace stlplus hash_element* m_next; unsigned m_hash; - hash_element(const hash* owner, const K& key, const T& data, unsigned hash) : - m_master(owner,this), m_value(key,data), m_next(0), m_hash(hash) + hash_element(const hash* owner, const K& key, const T& data, unsigned hash) : + m_master(owner,this), m_value(key,data), m_next(0), m_hash(hash) { } - hash_element(const hash* owner, const std::pair& value, unsigned hash) : - m_master(owner,this), m_value(value), m_next(0), m_hash(hash) + hash_element(const hash* owner, const std::pair& value, unsigned hash) : + m_master(owner,this), m_value(value), m_next(0), m_hash(hash) { } @@ -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 template - bool hash::erase(const K& key) + unsigned hash::erase(const K& key) { unsigned hash_value_full = H()(key); unsigned bin = hash_value_full % m_bins; @@ -465,9 +466,43 @@ namespace stlplus delete current; // remember to maintain the size count m_size--; - return true; + return 1; } - return false; + return 0; + } + + // remove an element from the hash table using an iterator (std::map equivalent) + template + TYPENAME hash::iterator hash::erase(TYPENAME hash::iterator it) + { + // work out what the next iterator is in order to return it later + TYPENAME hash::iterator next(it); + ++next; + // we now need to find where this item is - made difficult by the use of + // single-linked lists which means I have to search through the bin from + // the top in order to unlink from the list. + unsigned hash_value_full = it.node()->m_hash; + unsigned bin = hash_value_full % m_bins; + // scan the list for this element + // need to keep a previous pointer because the lists are single-linked + hash_element* previous = 0; + for (hash_element* current = m_values[bin]; current; previous = current, current = current->m_next) + { + // direct test on the address of the element + if (current != it.node()) continue; + // found this iterator, so unhook the element from the list + if (previous) + previous->m_next = current->m_next; + else + m_values[bin] = current->m_next; + // destroy it + delete current; + current = 0; + // remember to maintain the size count + m_size--; + break; + } + return next; } template @@ -569,6 +604,54 @@ namespace stlplus return hash_iterator >(this); } + template + void hash::debug_report(std::ostream& str) const + { + // calculate some stats first + unsigned occupied = 0; + unsigned min_in_bin = m_size; + unsigned max_in_bin = 0; + for (unsigned i = 0; i < m_bins; i++) + { + if (m_values[i]) occupied++; + unsigned count = 0; + for (hash_element* item = m_values[i]; item; item = item->m_next) count++; + if (count > max_in_bin) max_in_bin = count; + if (count < min_in_bin) min_in_bin = count; + } + // now print the table + str << "------------------------------------------------------------------------" << std::endl; + str << "| size: " << m_size << std::endl; + str << "| bins: " << m_bins << std::endl; + str << "| loading: " << loading() << " "; + if (m_rehash) + str << "auto-rehash at " << m_rehash << std::endl; + else + str << "manual rehash" << std::endl; + str << "| occupied: " << occupied + << std::fixed << " (" << (100.0*(float)occupied/(float)m_bins) << "%)" << std::scientific + << ", min = " << min_in_bin << ", max = " << max_in_bin << std::endl; + str << "|-----------------------------------------------------------------------" << std::endl; + str << "| bin 0 1 2 3 4 5 6 7 8 9" << std::endl; + str << "| ---------------------------------------------------------------"; + for (unsigned j = 0; j < m_bins; j++) + { + if (j % 10 == 0) + { + str << std::endl; + str << "| " << std::setw(6) << std::right << (j/10*10) << std::left << " |"; + } + unsigned count = 0; + for (hash_element* item = m_values[j]; item; item = item->m_next) count++; + if (!count) + str << " ."; + else + str << std::setw(6) << std::right << count << std::left; + } + str << std::endl; + str << "------------------------------------------------------------------------" << std::endl; + } + //////////////////////////////////////////////////////////////////////////////// } // end namespace stlplus diff --git a/src/moof/stlplus/matrix.hpp b/src/stlplus/containers/matrix.hpp old mode 100755 new mode 100644 similarity index 96% rename from src/moof/stlplus/matrix.hpp rename to src/stlplus/containers/matrix.hpp index fd0a512..b76a998 --- a/src/moof/stlplus/matrix.hpp +++ b/src/stlplus/containers/matrix.hpp @@ -11,6 +11,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "containers_fixes.hpp" +#include namespace stlplus { diff --git a/src/moof/stlplus/matrix.tpp b/src/stlplus/containers/matrix.tpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/matrix.tpp rename to src/stlplus/containers/matrix.tpp diff --git a/src/moof/stlplus/ntree.hpp b/src/stlplus/containers/ntree.hpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/ntree.hpp rename to src/stlplus/containers/ntree.hpp diff --git a/src/moof/stlplus/ntree.tpp b/src/stlplus/containers/ntree.tpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/ntree.tpp rename to src/stlplus/containers/ntree.tpp diff --git a/src/moof/stlplus/safe_iterator.hpp b/src/stlplus/containers/safe_iterator.hpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/safe_iterator.hpp rename to src/stlplus/containers/safe_iterator.hpp diff --git a/src/moof/stlplus/safe_iterator.tpp b/src/stlplus/containers/safe_iterator.tpp old mode 100755 new mode 100644 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 index 0000000..08fff03 --- /dev/null +++ b/src/stlplus/containers/simple_ptr.hpp @@ -0,0 +1,264 @@ +#ifndef STLPLUS_SIMPLE_PTR +#define STLPLUS_SIMPLE_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Daniel Milton, Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Daniel Milton, Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A smart pointer is a memory-managing pointer to an object. If you like, it +// is a zero-dimensional container. + +// Assignment of smart pointers result in multiple aliases of the same object. +// The term alias is used to differentiate from conventional pointers because +// the semantics are different. + +// Aliases can be turned into copies if the pointed-to class supports copying. + +// These simple_ptr classes from DJDM have slightly different semantics than +// the smart_ptr classes of AJR. There are no cross-pointer side effects +// that occur when the pointer is cleared. The clear() function is effectively +// equivalent to the clear_unique() function of the smart_ptr. The only way +// that a "referenced" object will be deleted is if all simple_ptr's that +// reference the object are cleared (by deletion, manual clearing or reassignment). + +// Also, the simple pointer cannot contain a reference to a shared null pointer +// (which occurs as a side-effect of clearing a multiply referenced object in +// the smart_ptr classes). Which means that if you have a null simple_ptr, then +// the assignment of any other null simple_ptr will NOT reassign the reference of +// any other simple_ptr. Hence, the simple_ptr class acts a little more like a +// normal pointer (with fewer side effects), with the added bonus of containment. + +// Due to the way that the simple_ptr contains the data, it also allows the +// addition of various casting functions, while still keeping the managed data +// containment functionality of the underlying object. This means that you can +// have two simple_ptr's of different template types, both pointing to the same +// data (if the differing template types are derivatives of each other). + +// The base class is simple_ptr_base which defines the common interface. Then +// there are three subclasses which have the same interface but different copy +// semantics: + +// - simple_ptr for simple types and classes which have copy constructors +// - simple_ptr_clone for polymorphic class hierarchies which are copied using a clone method +// - simple_ptr_nocopy for any class that cannot or should not be copied + +//////////////////////////////////////////////////////////////////////////////// +#include "containers_fixes.hpp" +#include "exceptions.hpp" +#include "copy_functors.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Base class + //////////////////////////////////////////////////////////////////////////////// + + template + class simple_ptr_base + { + public: + ////////////////////////////////////////////////////////////////////////////// + // member type definitions + + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; + typedef C value_copy; + + ////////////////////////////////////////////////////////////////////////////// + // constructors and destructors + + // create a null pointer + simple_ptr_base(void); + + // create a pointer containing a *copy* of the object using the template parameter C + // this copy is taken because the pointer class maintains a dynamically allocated object + // and the T& may not be (usually is not) dynamically allocated + explicit simple_ptr_base(const T& data) throw(illegal_copy); + + // create a pointer containing a dynamically created object + // Note: the object must be allocated *by the user* with new + // constructor form - must be called in the form smart_ptr_base x(new type(args)) + explicit simple_ptr_base(T* data); + + // copy constructor implements aliasing so no copy is made + // note that the copy constructor should NOT be explicit, as this breaks + // the returning of pointer objects from functions (at least within GCC 4.4) + simple_ptr_base(const simple_ptr_base& r); + + // assignment operator - required, else the output of GCC suffers segmentation faults + simple_ptr_base& operator=(const simple_ptr_base& r); + + // destructor decrements the reference count and delete only when the last reference is destroyed + ~simple_ptr_base(void); + + ////////////////////////////////////////////////////////////////////////////// + // logical tests to see if there is anything contained in the pointer since it can be null + + // there are two forms:explicit and implicit + // implicit: if(!r) or if(r) + // explicit: if(r.null()) or if(r.present()) + operator bool(void) const; + bool operator!(void) const; + bool present(void) const; + bool null(void) const; + + ////////////////////////////////////////////////////////////////////////////// + // dereference operators and functions + + // dereference the smart pointer to get the object - use in the form *p1 + T& operator*(void) throw(null_dereference); + const T& operator*(void) const throw(null_dereference); + + // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() + T* operator->(void) throw(null_dereference); + const T* operator->(void) const throw(null_dereference); + + ////////////////////////////////////////////////////////////////////////////// + // explicit function forms of the above assignment and dereference operators + + // set the value - note that this does a copy using the C template parameter + void set_value(const T& data) throw(illegal_copy); + // get the value + T& value(void) throw(null_dereference); + const T& value(void) const throw(null_dereference); + + // set the pointer + // deletes the previous pointer and adopts the passed pointer instead + // Note: the object must be allocated *by the user* with new + // Warning: it is very easy to break the memory management with this operation + void set(T* data = 0); + // get the pointer + T* pointer(void); + const T* pointer(void) const; + + ////////////////////////////////////////////////////////////////////////////// + // functions to manage aliases + + // make this an alias of the passed object + void alias(const simple_ptr_base&); + + // test whether two pointers point to the same object(known as aliasing the object) + // used in the form if(a.aliases(b)) + bool aliases(const simple_ptr_base&) const; + + // find the number of aliases - used when you need to know whether an + // object is still referred to from elsewhere (rare!) + unsigned alias_count(void) const; + + // clear the reference to the object, but only delete the object if there are no + // other references to that object. Hence, this does not affect other pointers + // that are pointing to the same object. + void clear(void); + + // This is just an alias of the clear() function, provided for completeness of + // the interface when acting as a replacement for the smart_ptr classes + void clear_unique(void); + + ////////////////////////////////////////////////////////////////////////////// + // functions that involve copying + + // these functions use the copy functor passed as the template parameter C + // to copy the object with the right copy semantics. If the copy functor + // is no_copy, an exception will be thrown. + + // make this pointer unique with respect to any other references to the same object + // if this pointer is already unique, it does nothing - otherwise it copies the object + void make_unique(void) throw(illegal_copy); + + // make this pointer a unique copy of the parameter + // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2 + void copy(const simple_ptr_base&) throw(illegal_copy); + + ////////////////////////////////////////////////////////////////////////////// + // functions that involve casting + +#ifdef STLPLUS_MEMBER_TEMPLATES + + // dynamic cast of underlying pointer to a derived/parent + template simple_ptr_base dyn_cast(void) const; + + // static cast of underlying pointer to a derived/parent + template simple_ptr_base stat_cast(void) const; + + // cast of underlying pointer to a base - while keeping the same ref-counted object + template simple_ptr_base cast(void) const; + +#endif + + ////////////////////////////////////////////////////////////////////////////// + + protected: + T* m_pointer; + unsigned* m_count; + + public: + // internal use only - had to make them public because they need to be + // accessed by routines that could not be made friends + // can't have a handle due to the way the simple pointer stores it's data + // in separate counter and pointer objects + unsigned* _count(void) const; + T* _pointer(void) const; + void _make_alias(T* pointer, unsigned* count); + + private: + void increment(void); + bool decrement(void); + }; + + //////////////////////////////////////////////////////////////////////////////// + // simple_ptr for simple types and classes which have copy constructors + + template + class simple_ptr : public simple_ptr_base > + { + public: + simple_ptr(void) {} + explicit simple_ptr(const T& data) : simple_ptr_base >(data) {} + explicit simple_ptr(T* data) : simple_ptr_base >(data) {} + simple_ptr& operator=(const T& data) {set_value(data); return *this;} + simple_ptr& operator=(T* data) {set(data); return *this;} + ~simple_ptr(void) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + // smart_ptr_clone for polymorphic class hierarchies which have a clone method + + template + class simple_ptr_clone : public simple_ptr_base > + { + public: + simple_ptr_clone(void) {} + explicit simple_ptr_clone(const T& data) : simple_ptr_base >(data) {} + explicit simple_ptr_clone(T* data) : simple_ptr_base >(data) {} + simple_ptr_clone& operator=(const T& data) {set_value(data); return *this;} + simple_ptr_clone& operator=(T* data) {set(data); return *this;} + ~simple_ptr_clone(void) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + // smart_ptr_nocopy for any class that cannot or should not be copied + + template + class simple_ptr_nocopy : public simple_ptr_base > + { + public: + simple_ptr_nocopy(void) {} + explicit simple_ptr_nocopy(const T& data) : simple_ptr_base >(data) {} + explicit simple_ptr_nocopy(T* data) : simple_ptr_base >(data) {} + simple_ptr_nocopy& operator=(const T& data) {set_value(data); return *this;} + simple_ptr_nocopy& operator=(T* data) {set(data); return *this;} + ~simple_ptr_nocopy(void) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "simple_ptr.tpp" +#endif diff --git a/src/stlplus/containers/simple_ptr.tpp b/src/stlplus/containers/simple_ptr.tpp new file mode 100644 index 0000000..13f5596 --- /dev/null +++ b/src/stlplus/containers/simple_ptr.tpp @@ -0,0 +1,338 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Daniel Milton +// Copyright: (c) Daniel Milton 2002-2009 + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // simple_ptr_base class + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + // constructors, assignments and destructors + + // create a null pointer + template + simple_ptr_base::simple_ptr_base(void) : + m_pointer(0), + m_count(new unsigned(1)) + { + } + + // create a pointer containing a *copy* of the object pointer + template + simple_ptr_base::simple_ptr_base(const T& data) throw(illegal_copy) : + m_pointer(C()(data)), + m_count(new unsigned(1)) + { + } + + // create a pointer containing a dynamically created object + // Note: the object must be allocated *by the user* with new + // constructor form - must be called in the form simple_ptr x(new type(args)) + template + simple_ptr_base::simple_ptr_base(T* data) : + m_pointer(data), + m_count(new unsigned(1)) + { + } + + // copy constructor implements counted referencing - no copy is made + template + simple_ptr_base::simple_ptr_base(const simple_ptr_base& r) : + m_pointer(r.m_pointer), + m_count(r.m_count) + { + increment(); + } + + // assignment operator - required, else the output of GCC suffers segmentation faults + template + simple_ptr_base& simple_ptr_base::operator=(const simple_ptr_base& r) + { + alias(r); + return *this; + } + + // destructor decrements the reference count and delete only when the last reference is destroyed + template + simple_ptr_base::~simple_ptr_base(void) + { + if(decrement()) + { + delete m_pointer; + delete m_count; + } + } + + ////////////////////////////////////////////////////////////////////////////// + // logical tests to see if there is anything contained in the pointer since it can be null + + template + bool simple_ptr_base::null(void) const + { + return m_pointer==0; + } + + template + bool simple_ptr_base::present(void) const + { + return m_pointer!=0; + } + + template + bool simple_ptr_base::operator!(void) const + { + return m_pointer==0; + } + + template + simple_ptr_base::operator bool(void) const + { + return m_pointer!=0; + } + + ////////////////////////////////////////////////////////////////////////////// + // dereference operators and functions + + template + T& simple_ptr_base::operator*(void) throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); + return *m_pointer; + } + + template + const T& simple_ptr_base::operator*(void) const throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); + return *m_pointer; + } + + template + T* simple_ptr_base::operator->(void) throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->"); + return m_pointer; + } + + template + const T* simple_ptr_base::operator->(void) const throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->"); + return m_pointer; + } + + ////////////////////////////////////////////////////////////////////////////// + // explicit function forms of the above assignment dereference operators + + template + void simple_ptr_base::set_value(const T& data) throw(illegal_copy) + { + set(C()(data)); + } + + template + T& simple_ptr_base::value(void) throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); + return *m_pointer; + } + + template + const T& simple_ptr_base::value(void) const throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); + return *m_pointer; + } + + template + void simple_ptr_base::set(T* data) + { + unsigned& count = *m_count; + if (count<=1) + delete m_pointer; + else + { + --count; + m_count = new unsigned(1); + } + m_pointer = data; + } + + template + T* simple_ptr_base::pointer(void) + { + return m_pointer; + } + + template + const T* simple_ptr_base::pointer(void) const + { + return m_pointer; + } + + //////////////////////////////////////////////////////////////////////////////// + // functions to manage counted referencing + + template + void simple_ptr_base::increment(void) + { + ++(*m_count); + } + + template + bool simple_ptr_base::decrement(void) + { + unsigned& count = *m_count; + --count; + return count == 0; + } + + // make this an alias of the passed object + template + void simple_ptr_base::alias(const simple_ptr_base& r) + { + // make it alias-copy safe - this means that I don't try to do the + // assignment if r is either the same object or an alias of it + if (m_pointer==r.m_pointer) return; + if(decrement()) { + delete m_pointer; + delete m_count; + } + m_pointer = r.m_pointer; + m_count = r.m_count; + increment(); + } + + template + bool simple_ptr_base::aliases(const simple_ptr_base& r) const + { + return m_count == r.m_count; + } + + template + unsigned simple_ptr_base::alias_count(void) const + { + return *m_count; + } + + template + void simple_ptr_base::clear(void) + { + set(0); + } + + template + void simple_ptr_base::clear_unique(void) + { + set(0); // no difference between clear and clear_unique with the simple_ptr + } + + template + void simple_ptr_base::make_unique(void) throw(illegal_copy) + { + unsigned& count = *m_count; + if (count <= 1) return; + --count; + if (m_pointer) m_pointer = C()(*m_pointer); + m_count = new unsigned(1); + } + + template + void simple_ptr_base::copy(const simple_ptr_base& data) throw(illegal_copy) + { + alias(data); + make_unique(); + } + +#ifdef STLPLUS_MEMBER_TEMPLATES + + // dynamic cast of underlying pointer to a derived/parent + template + template + simple_ptr_base simple_ptr_base::dyn_cast(void) const + { + simple_ptr_base rtn; + rtn.m_pointer = dynamic_cast(m_pointer); + if (rtn.m_pointer) { + delete rtn.m_count; + rtn.m_count = m_count; + rtn.increment(); + } + return rtn; + } + + // static cast of underlying pointer to a derived/parent + template + template + simple_ptr_base simple_ptr_base::stat_cast(void) const + { + simple_ptr_base rtn; + rtn.m_pointer = static_cast(m_pointer); + if (rtn.m_pointer) { + delete rtn.m_count; + rtn.m_count = m_count; + rtn.increment(); + } + return rtn; + } + + // cast of underlying pointer to a base - while keeping the same ref-counted object + template + template + simple_ptr_base simple_ptr_base::cast(void) const + { + simple_ptr_base rtn; + rtn.m_pointer = (T2*)m_pointer; + if (rtn.m_pointer) { + delete rtn.m_count; + rtn.m_count = m_count; + rtn.increment(); + } + return rtn; + } + +#endif + + // internal function for distinguishing unique simple_ptr objects + // used for example in persistence routines + + template + unsigned* simple_ptr_base::_count(void) const + { + return m_count; + } + + template + T* simple_ptr_base::_pointer(void) const + { + return m_pointer; + } + + template + void simple_ptr_base::_make_alias(T* pointer, unsigned* count) + { + // make it alias-copy safe - this means that I don't try to do the + // assignment if r is either the same object or an alias of it + if (m_count != count) + { + if(decrement()) + { + delete m_pointer; + delete m_count; + } + m_pointer = pointer; + m_count = count; + increment(); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + diff --git a/src/moof/stlplus/smart_ptr.hpp b/src/stlplus/containers/smart_ptr.hpp old mode 100755 new mode 100644 similarity index 83% rename from src/moof/stlplus/smart_ptr.hpp rename to src/stlplus/containers/smart_ptr.hpp index d1395f5..2d37e2e --- a/src/moof/stlplus/smart_ptr.hpp +++ b/src/stlplus/containers/smart_ptr.hpp @@ -8,7 +8,7 @@ // License: BSD License, see ../docs/license.html // A smart pointer is a memory-managing pointer to an object. If you like, it -// is a zero-dimensional container. +// is a zero-dimensional container. // Assignment of smart pointers result in multiple aliases of the same object. // The term alias is used to differentiate from conventional pointers because @@ -27,6 +27,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "containers_fixes.hpp" #include "exceptions.hpp" +#include "copy_functors.hpp" #include #include @@ -61,7 +62,7 @@ namespace stlplus smart_ptr_base(void); // create a pointer containing a *copy* of the object using the template parameter C - // this copy is taken because the pointer class maintains a dynamically allocated object + // this copy is taken because the pointer class maintains a dynamically allocated object // and the T& may not be (usually is not) dynamically allocated explicit smart_ptr_base(const T& data) throw(illegal_copy); @@ -71,7 +72,12 @@ namespace stlplus explicit smart_ptr_base(T* data); // copy constructor implements aliasing so no copy is made - explicit smart_ptr_base(const smart_ptr_base& r); + // note that the copy constructor should NOT be explicit, as this breaks + // the returning of pointer objects from functions (at least within GCC 4.4) + smart_ptr_base(const smart_ptr_base& r); + + // assignment operator - required, else the output of GCC suffers segmentation faults + smart_ptr_base& operator=(const smart_ptr_base& r); // destructor decrements the reference count and delete only when the last reference is destroyed ~smart_ptr_base(void); @@ -159,48 +165,8 @@ namespace stlplus public: // internal use only - had to make them public because they need to be // accessed by routines that could not be made friends - void* handle(void) const; - void make_alias(void* handle); - }; - - //////////////////////////////////////////////////////////////////////////////// - // copy functors implementing the three possible copy semantics - - // constructor_copy uses the copy constructor of the object - used for simple types - - template - class constructor_copy - { - public: - T* operator() (const T& from) throw() - { - return new T(from); - } - }; - - // clone_copy uses the clone method of the object - used for polymorphic types - - template - class clone_copy - { - public: - T* operator() (const T& from) throw() - { - return from.clone(); - } - }; - - // no_copy throws an exception - used for types that cannot be copied - - template - class no_copy - { - public: - T* operator() (const T& from) throw(illegal_copy) - { - throw illegal_copy("no_copy functor called"); - return 0; - } + smart_ptr_holder* _handle(void) const; + void _make_alias(smart_ptr_holder* handle); }; //////////////////////////////////////////////////////////////////////////////// @@ -214,7 +180,7 @@ namespace stlplus explicit smart_ptr(const T& data) : smart_ptr_base >(data) {} explicit smart_ptr(T* data) : smart_ptr_base >(data) {} smart_ptr& operator=(const T& data) {set_value(data); return *this;} - smart_ptr& operator=(const smart_ptr& r) {alias(r); return *this;} + smart_ptr& operator=(T* data) {set(data); return *this;} ~smart_ptr(void) {} }; @@ -229,7 +195,7 @@ namespace stlplus explicit smart_ptr_clone(const T& data) : smart_ptr_base >(data) {} explicit smart_ptr_clone(T* data) : smart_ptr_base >(data) {} smart_ptr_clone& operator=(const T& data) {set_value(data); return *this;} - smart_ptr_clone& operator=(const smart_ptr_clone& r) {alias(r); return *this;} + smart_ptr_clone& operator=(T* data) {set(data); return *this;} ~smart_ptr_clone(void) {} }; @@ -244,7 +210,7 @@ namespace stlplus explicit smart_ptr_nocopy(const T& data) : smart_ptr_base >(data) {} explicit smart_ptr_nocopy(T* data) : smart_ptr_base >(data) {} smart_ptr_nocopy& operator=(const T& data) {set_value(data); return *this;} - smart_ptr_nocopy& operator=(const smart_ptr_nocopy& r) {alias(r); return *this;} + smart_ptr_nocopy& operator=(T* data) {set(data); return *this;} ~smart_ptr_nocopy(void) {} }; diff --git a/src/moof/stlplus/smart_ptr.tpp b/src/stlplus/containers/smart_ptr.tpp old mode 100755 new mode 100644 similarity index 91% rename from src/moof/stlplus/smart_ptr.tpp rename to src/stlplus/containers/smart_ptr.tpp index cb1b8bb..ce72da2 --- a/src/moof/stlplus/smart_ptr.tpp +++ b/src/stlplus/containers/smart_ptr.tpp @@ -33,7 +33,7 @@ namespace stlplus } public: - smart_ptr_holder(T* p = 0) : + smart_ptr_holder(T* p = 0) : m_count(1), m_data(p) { } @@ -139,6 +139,14 @@ namespace stlplus m_holder->increment(); } + // assignment operator - required, else the output of GCC suffers segmentation faults + template + smart_ptr_base& smart_ptr_base::operator=(const smart_ptr_base& r) + { + alias(r); + return *this; + } + // destructor decrements the reference count and delete only when the last reference is destroyed template smart_ptr_base::~smart_ptr_base(void) @@ -253,14 +261,7 @@ namespace stlplus template void smart_ptr_base::alias(const smart_ptr_base& r) { - // make it alias-copy safe - this means that I don't try to do the - // assignment if r is either the same object or an alias of it - // if (m_holder == r.m_holder) return; - // if (m_holder->decrement()) - // delete m_holder; - // m_holder = r.m_holder; - // m_holder->increment(); - make_alias(r.m_holder); + _make_alias(r.m_holder); } template @@ -319,15 +320,16 @@ namespace stlplus // used for example in persistence routines template - void* smart_ptr_base::handle(void) const + smart_ptr_holder* smart_ptr_base::_handle(void) const { return m_holder; } template - void smart_ptr_base::make_alias(void* handle) + void smart_ptr_base::_make_alias(smart_ptr_holder* r_holder) { - smart_ptr_holder* r_holder = (smart_ptr_holder*)handle; + // make it alias-copy safe - this means that I don't try to do the + // assignment if r is either the same object or an alias of it if (m_holder != r_holder) { if (m_holder->decrement()) diff --git a/src/moof/stlplus/triple.hpp b/src/stlplus/containers/triple.hpp old mode 100755 new mode 100644 similarity index 100% rename from src/moof/stlplus/triple.hpp rename to src/stlplus/containers/triple.hpp diff --git a/src/moof/stlplus/triple.tpp b/src/stlplus/containers/triple.tpp old mode 100755 new mode 100644 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 index 0000000..548a999 --- /dev/null +++ b/src/stlplus/messages/stlplus_messages.txt @@ -0,0 +1,14 @@ +# messages required by the CLI parser (see cli_parser.hpp) +CLI_VALUE_MISSING option @0 requires a value - end of line was reached instead +CLI_UNRECOGNISED_OPTION @0 is not a recognised option for this command +CLI_NO_VALUES argument @0 is invalid - this program doesn't take command-line arguments +CLI_USAGE_PROGRAM usage:\n\t@0 { arguments } +CLI_USAGE_DEFINITIONS arguments: +CLI_USAGE_VALUES values: +CLI_USAGE_VALUE_RESULT \t@0 : from @1 +CLI_USAGE_SWITCH_RESULT \t-@0 : from @1 +CLI_USAGE_OPTION_RESULT \t-@0 @1 : from @2 +CLI_INI_HEADER configuration files: +CLI_INI_FILE_PRESENT \t@0 +CLI_INI_FILE_ABSENT \t@0 (not found) +CLI_INI_VARIABLE unknown variable "@0" found in Ini file diff --git a/src/stlplus/persistence/persistence.hpp b/src/stlplus/persistence/persistence.hpp new file mode 100644 index 0000000..6a47838 --- /dev/null +++ b/src/stlplus/persistence/persistence.hpp @@ -0,0 +1,22 @@ +#ifndef STLPLUS_PERSISTENCE +#define STLPLUS_PERSISTENCE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Header that includes all the persistence routines in one go + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistent_contexts.hpp" +#include "persistent_shortcuts.hpp" +#include "persistent_basic.hpp" +#include "persistent_pointers.hpp" +#include "persistent_stl.hpp" +#include "persistent_stlplus.hpp" + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistence_fixes.hpp b/src/stlplus/persistence/persistence_fixes.hpp new file mode 100644 index 0000000..d540ade --- /dev/null +++ b/src/stlplus/persistence/persistence_fixes.hpp @@ -0,0 +1,42 @@ +#ifndef STLPLUS_PERSISTENCE_FIXES +#define STLPLUS_PERSISTENCE_FIXES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Contains work arounds for OS or Compiler specific problems + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Unnecessary compiler warnings +//////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +// Microsoft Visual Studio +// shut up the following irritating warnings +// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger) +// 4305 - VC6, identifier type was converted to a smaller type +// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger) +// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it +// 4290 - VC6, C++ exception specification ignored +// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning) +// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program +// 4996 - VC8, 'xxxx' was declared deprecated +#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996) +#endif + +#ifdef __BORLANDC__ +// Borland +// Shut up the following irritating warnings +// 8026 - Functions with exception specifications are not expanded inline +// 8027 - Functions with xxx are not expanded inline +#pragma warn -8026 +#pragma warn -8027 +#endif + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent.hpp b/src/stlplus/persistence/persistent.hpp new file mode 100644 index 0000000..7ddfe4e --- /dev/null +++ b/src/stlplus/persistence/persistent.hpp @@ -0,0 +1,37 @@ +#ifndef STLPLUS_PERSISTENT +#define STLPLUS_PERSISTENT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Interface class inherited by classes using the interface approach to polymorphism + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistence_fixes.hpp" +#include "persistent_exceptions.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + class dump_context; + class restore_context; + + class persistent + { + public: + virtual void dump(dump_context&) const throw(persistent_dump_failed) = 0; + virtual void restore(restore_context&) throw(persistent_restore_failed) = 0; + virtual persistent* clone(void) const = 0; + virtual ~persistent(void) {} + }; + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_basic.hpp b/src/stlplus/persistence/persistent_basic.hpp new file mode 100644 index 0000000..2ffc30f --- /dev/null +++ b/src/stlplus/persistence/persistent_basic.hpp @@ -0,0 +1,22 @@ +#ifndef STLPLUS_PERSISTENT_BASIC +#define STLPLUS_PERSISTENT_BASIC +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of basic types + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include "persistent_bool.hpp" +#include "persistent_cstring.hpp" +#include "persistent_enum.hpp" +#include "persistent_float.hpp" +#include "persistent_int.hpp" + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_bitset.hpp b/src/stlplus/persistence/persistent_bitset.hpp new file mode 100644 index 0000000..3f684e4 --- /dev/null +++ b/src/stlplus/persistence/persistent_bitset.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_PERSISTENT_BITSET +#define STLPLUS_PERSISTENT_BITSET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL bitset + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_bitset(dump_context&, const std::bitset& data) throw(persistent_dump_failed); + + template + void restore_bitset(restore_context&, std::bitset& data) throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_bitset.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_bitset.tpp b/src/stlplus/persistence/persistent_bitset.tpp new file mode 100644 index 0000000..e6b4d54 --- /dev/null +++ b/src/stlplus/persistence/persistent_bitset.tpp @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // format: data msB first, packed into bytes with lowest index at the byte's lsb + + // Note: the interface does not provide access to the internal storage and yet + // to be efficient the std::bitset must be packed as bytes. Thus I have to do it the + // hard way. + + template + void dump_bitset(dump_context& context, const std::bitset& data) + throw(persistent_dump_failed) + { + size_t bits = data.size(); + size_t bytes = (bits+7)/8; + for (size_t B = bytes; B--; ) + { + unsigned char ch = 0; + for (size_t b = 0; b < 8; b++) + { + size_t bit = B*8+b; + if (bit < bits && data.test(bit)) + ch |= (0x01 << b); + } + dump_unsigned_char(context,ch); + } + } + + template + void restore_bitset(restore_context& context, std::bitset& data) + throw(persistent_restore_failed) + { + size_t bits = data.size(); + size_t bytes = (bits+7)/8; + for (size_t B = bytes; B--; ) + { + unsigned char ch = 0; + restore_unsigned_char(context,ch); + for (size_t b = 0; b < 8; b++) + { + size_t bit = B*8+b; + if (bit >= bits) break; + data.set(bit, ch & (0x01 << b) ? true : false); + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_bool.cpp b/src/stlplus/persistence/persistent_bool.cpp new file mode 100644 index 0000000..dde1828 --- /dev/null +++ b/src/stlplus/persistence/persistent_bool.cpp @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_bool.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +// bool is dumped and restored as an unsigned char +void stlplus::dump_bool(stlplus::dump_context& context, const bool& data) throw(stlplus::persistent_dump_failed) +{ + context.put((unsigned char)data); +} + +void stlplus::restore_bool(restore_context& context, bool& data) throw(stlplus::persistent_restore_failed) +{ + data = context.get() != 0; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_bool.hpp b/src/stlplus/persistence/persistent_bool.hpp new file mode 100644 index 0000000..5fbe237 --- /dev/null +++ b/src/stlplus/persistence/persistent_bool.hpp @@ -0,0 +1,27 @@ +#ifndef STLPLUS_PERSISTENT_BOOL +#define STLPLUS_PERSISTENT_BOOL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of bool + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + void dump_bool(dump_context&, const bool& data) throw(persistent_dump_failed); + void restore_bool(restore_context&, bool& data) throw(persistent_restore_failed); + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/persistence/persistent_callback.hpp b/src/stlplus/persistence/persistent_callback.hpp new file mode 100644 index 0000000..034f43e --- /dev/null +++ b/src/stlplus/persistence/persistent_callback.hpp @@ -0,0 +1,53 @@ +#ifndef STLPLUS_PERSISTENT_CALLBACK +#define STLPLUS_PERSISTENT_CALLBACK +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence for pointers to polymorphic classes using the callback approach. + +// This works on a set of classes. Each subclass has a set of callback +// (non-method) functions that enable create/dump/restore operations. Each +// subclass must be registered with the persistence dump/restore context so +// that the system knows how to handle it. + +// This approach is suited to classes that cannot be modified to add +// persistence methods. See persistent_interface for a more C++-like way of +// handling polymorphism. + +// Objects are always dumped/restored as pointers to the superclass T. + +// Multiple pointers to the same object are handled in the same way as for +// simple pointers + +// Only classes registered with the context can be dumped and restored as +// polymorphic types - see dump_context::register_callback and +// restore_context::register_callback. Attempting to use any unrecognised class +// will throw an exception. + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_callback(dump_context&, const T* const data) + throw(persistent_dump_failed); + + template + void restore_callback(restore_context&, T*& data) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_callback.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_callback.tpp b/src/stlplus/persistence/persistent_callback.tpp new file mode 100644 index 0000000..0773dad --- /dev/null +++ b/src/stlplus/persistence/persistent_callback.tpp @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Polymorphous classes using the callback approach + +// format: magic [ key data ] + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_callback(dump_context& context, const T* const data) + throw(persistent_dump_failed) + { + try + { + // register the address and get the magic key for it + std::pair mapping = context.pointer_map(data); + dump_unsigned(context,mapping.second); + // if the address is null, then that is all that we need to do + // however, if it is non-null and this is the first sight of the address, dump the contents + if (data && !mapping.first) + { + // callback method - get the callback data and perform the dump + // this will throw persistent_illegal_type if not recognised, thus the try block + dump_context::callback_data callback = context.lookup_callback(typeid(*data)); + // dump the magic key for the type + dump_unsigned(context, callback.first); + // now call the callback that dumps the subclass + callback.second(context,data); + } + } + catch (const persistent_illegal_type& except) + { + // convert this to a simpler dump failed exception + throw persistent_dump_failed(except.what()); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void restore_callback(restore_context& context, T*& data) + throw(persistent_restore_failed) + { + try + { + // first delete any previous object pointed to since the restore creates the object of the right subclass + if (data) + { + delete data; + data = 0; + } + // get the magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // now lookup the magic key to see if this pointer has already been restored + // null pointers are always flagged as already restored + std::pair address = context.pointer_map(magic); + if (address.first) + { + // seen before, so simply map it to the existing address + data = (T*)address.second; + } + else + { + // now restore the magic key that denotes the particular subclass + unsigned key = 0; + restore_unsigned(context, key); + // callback approach + // call the create callback to create an object of the right type + // then call the restore callback to get the contents + // this will throw persistent_illegal_type if not recognised - this is caught below + restore_context::callback_data callbacks = context.lookup_callback(key); + data = (T*)callbacks.first(); + // add this pointer to the set of already seen objects + // note that the address is mapped before it is dumped so that self-referential structures dump correctly + context.pointer_add(magic,data); + callbacks.second(context,data); + } + } + catch (const persistent_illegal_type& exception) + { + // convert this to a simpler dump failed exception + throw persistent_restore_failed(exception.what()); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_complex.hpp b/src/stlplus/persistence/persistent_complex.hpp new file mode 100644 index 0000000..4c02215 --- /dev/null +++ b/src/stlplus/persistence/persistent_complex.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_PERSISTENT_COMPLEX +#define STLPLUS_PERSISTENT_COMPLEX +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Set of persistence routines for the STL classes + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_complex(dump_context&, const std::complex& data, D dump_fn) throw(persistent_dump_failed); + + template + void restore_complex(restore_context&, std::complex& data, R restore_fn) throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_complex.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_complex.tpp b/src/stlplus/persistence/persistent_complex.tpp new file mode 100644 index 0000000..7db9bc4 --- /dev/null +++ b/src/stlplus/persistence/persistent_complex.tpp @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_complex(dump_context& context, const std::complex& data, D dump_fn) + throw(persistent_dump_failed) + { + dump_fn(context,data.real()); + dump_fn(context,data.imag()); + } + + template + void restore_complex(restore_context& context, std::complex& data, R restore_fn) + throw(persistent_restore_failed) + { + T re, im; + restore_fn(context,re); + restore_fn(context,im); + data = std::complex(re, im); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_contexts.cpp b/src/stlplus/persistence/persistent_contexts.cpp new file mode 100644 index 0000000..008fa9f --- /dev/null +++ b/src/stlplus/persistence/persistent_contexts.cpp @@ -0,0 +1,434 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_contexts.hpp" +#include "persistent.hpp" +#include +#include +#include + +namespace stlplus +{ + + + //////////////////////////////////////////////////////////////////////////////// + // File format version + + // This relates to the layout of basic types - if I change the file layout of, + // say, int or vector, then this will change + + // Early versions of the persistence routines did not have this - they are no longer supported + // - Change from version 1 to 2: changed the persistent representation of inf + + unsigned char PersistentVersion = 2; + + //////////////////////////////////////////////////////////////////////////////// + // avoid creating dependencies on other libraries + + static std::string to_string(int number) + { + // use sprintf in a very controlled way that cannot overrun + char* buffer = new char[50]; + sprintf(buffer, "%i", number); + std::string result = buffer; + delete buffer; + return result; + } + + static bool little_endian(void) + { + // TODO - find a compile-time way of doing this + int sample = 1; + char* sample_bytes = (char*)&sample; + return sample_bytes[0] != 0; + } + + //////////////////////////////////////////////////////////////////////////////// + // dump context classes + //////////////////////////////////////////////////////////////////////////////// + + class dump_context_body + { + public: + typedef std::map magic_map; + typedef std::map callback_map; + typedef std::map interface_map; + + unsigned m_max_key; + unsigned char m_version; + bool m_little_endian; + std::ostream* m_device; + magic_map m_pointers; + callback_map m_callbacks; + interface_map m_interfaces; + + dump_context_body(std::ostream& device, unsigned char version) throw(persistent_dump_failed) : + m_max_key(0), m_version(version), m_little_endian(stlplus::little_endian()), m_device(&device) + { + // write the version number as a single byte + put(version); + // map a null pointer onto magic number zero + m_pointers[0] = 0; + // test whether the version number is supported + if (m_version != 1 && m_version != 2) + throw persistent_dump_failed(std::string("wrong version: ") + to_string(m_version)); + } + + void put(unsigned char data) throw(persistent_dump_failed) + { + if (!m_device->put(data)) + throw persistent_dump_failed(std::string("output device error")); + } + + const std::ostream& device(void) const + { + return *m_device; + } + + unsigned char version(void) const + { + return m_version; + } + + bool little_endian(void) const + { + return m_little_endian; + } + + std::pair pointer_map(const void* const pointer) + { + magic_map::iterator found = m_pointers.find(pointer); + if (found == m_pointers.end()) + { + // add a new mapping + unsigned magic = m_pointers.size(); + m_pointers[pointer] = magic; + return std::pair(false,magic); + } + // return the old mapping + return std::pair(true,found->second); + } + + unsigned register_callback(const std::type_info& info, dump_context::dump_callback callback) + { + std::string key = info.name(); + unsigned data = ++m_max_key; + m_callbacks[key] = std::make_pair(data,callback); + return data; + } + + bool is_callback(const std::type_info& info) const + { + return m_callbacks.find(info.name()) != m_callbacks.end(); + } + + dump_context::callback_data lookup_callback(const std::type_info& info) const throw(persistent_illegal_type) + { + std::string key = info.name(); + callback_map::const_iterator found = m_callbacks.find(key); + if (found == m_callbacks.end()) + throw persistent_illegal_type(key); + return found->second; + } + + unsigned register_interface(const std::type_info& info) + { + std::string key = info.name(); + unsigned data = ++m_max_key; + m_interfaces[key] = data; + return data; + } + + bool is_interface(const std::type_info& info) const + { + return m_interfaces.find(info.name()) != m_interfaces.end(); + } + + unsigned lookup_interface(const std::type_info& info) const throw(persistent_illegal_type) + { + std::string key = info.name(); + interface_map::const_iterator found = m_interfaces.find(key); + if (found == m_interfaces.end()) + throw persistent_illegal_type(key); + return found->second; + } + }; + + //////////////////////////////////////////////////////////////////////////////// + + dump_context::dump_context(std::ostream& device, unsigned char version) throw(persistent_dump_failed) : m_body(0) + { + m_body = new dump_context_body(device,version); + } + + dump_context::~dump_context(void) + { + delete m_body; + } + + void dump_context::put(unsigned char data) throw(persistent_dump_failed) + { + m_body->put(data); + } + + const std::ostream& dump_context::device(void) const + { + return m_body->device(); + } + + unsigned char dump_context::version(void) const + { + return m_body->version(); + } + + bool dump_context::little_endian(void) const + { + return m_body->little_endian(); + } + + std::pair dump_context::pointer_map(const void* const pointer) + { + return m_body->pointer_map(pointer); + } + + unsigned dump_context::register_callback(const std::type_info& info, dump_context::dump_callback callback) + { + return m_body->register_callback(info,callback); + } + + bool dump_context::is_callback(const std::type_info& info) const + { + return m_body->is_callback(info); + } + + dump_context::callback_data dump_context::lookup_callback(const std::type_info& info) const throw(persistent_illegal_type) + { + return m_body->lookup_callback(info); + } + + unsigned dump_context::register_interface(const std::type_info& info) + { + return m_body->register_interface(info); + } + + bool dump_context::is_interface(const std::type_info& info) const + { + return m_body->is_interface(info); + } + + unsigned dump_context::lookup_interface(const std::type_info& info) const throw(persistent_illegal_type) + { + return m_body->lookup_interface(info); + } + + void dump_context::register_all(dump_context::installer installer) + { + if (installer) installer(*this); + } + + //////////////////////////////////////////////////////////////////////////////// + // restore context classes + //////////////////////////////////////////////////////////////////////////////// + + class restore_context_body + { + public: + typedef persistent* persistent_ptr; + typedef std::map magic_map; + typedef std::map callback_map; + typedef std::map interface_map; + + unsigned m_max_key; + unsigned char m_version; + bool m_little_endian; + std::istream* m_device; + magic_map m_pointers; + callback_map m_callbacks; + interface_map m_interfaces; + + restore_context_body(std::istream& device) throw(persistent_restore_failed) : + m_max_key(0), m_little_endian(stlplus::little_endian()), m_device(&device) + { + // map a null pointer onto magic number zero + m_pointers[0] = 0; + // get the dump version and see if we support it + m_version = (unsigned char)get(); + if (m_version != 1 && m_version != 2) + throw persistent_restore_failed(std::string("wrong version: ") + to_string(m_version)); + } + + ~restore_context_body(void) + { + // need to delete all interfaces + // I used to use smart_ptr_clone for storing them but I want to disconnect as many dependencies as possible + for (unsigned i = 0; i < m_interfaces.size(); i++) + delete m_interfaces[i]; + } + + const std::istream& device(void) const + { + return *m_device; + } + + unsigned char version(void) const + { + return m_version; + } + + bool little_endian(void) const + { + return m_little_endian; + } + + int get(void) throw(persistent_restore_failed) + { + int result = m_device->get(); + if (!m_device->good()) + throw persistent_restore_failed(std::string("device error or premature end of file")); + return result; + } + + std::pair pointer_map(unsigned magic) + { + magic_map::iterator found = m_pointers.find(magic); + if (found == m_pointers.end()) + { + // this magic number has never been seen before + return std::pair(false,0); + } + return std::pair(true,found->second); + } + + void pointer_add(unsigned magic, void* new_pointer) + { + m_pointers[magic] = new_pointer; + } + + unsigned register_callback(restore_context::create_callback create, restore_context::restore_callback restore) + { + unsigned key = ++m_max_key; + m_callbacks[key] = std::make_pair(create,restore); + return key; + } + + bool is_callback(unsigned key) const + { + return m_callbacks.find(key) != m_callbacks.end(); + } + + restore_context::callback_data lookup_callback(unsigned key) const throw(persistent_illegal_type) + { + callback_map::const_iterator found = m_callbacks.find(key); + if (found == m_callbacks.end()) + throw persistent_illegal_type(key); + return found->second; + } + + unsigned register_interface(persistent* sample) + { + unsigned key = ++m_max_key; + m_interfaces[key] = sample; + return key; + } + + bool is_interface(unsigned key) const + { + return m_interfaces.find(key) != m_interfaces.end(); + } + + persistent* lookup_interface(unsigned key) const throw(persistent_illegal_type) + { + interface_map::const_iterator found = m_interfaces.find(key); + if (found == m_interfaces.end()) + throw persistent_illegal_type(key); + return found->second; + } + }; + + //////////////////////////////////////////////////////////////////////////////// + + restore_context::restore_context(std::istream& device) throw(persistent_restore_failed) : + m_body(0) + { + m_body = new restore_context_body(device); + } + + restore_context::~restore_context(void) + { + delete m_body; + } + + const std::istream& restore_context::device(void) const + { + return m_body->device(); + } + + unsigned char restore_context::version(void) const + { + return m_body->version(); + } + + bool restore_context::little_endian(void) const + { + return m_body->little_endian(); + } + + int restore_context::get(void) throw(persistent_restore_failed) + { + return m_body->get(); + } + + std::pair restore_context::pointer_map(unsigned magic) + { + return m_body->pointer_map(magic); + } + + void restore_context::pointer_add(unsigned magic, void* new_pointer) + { + m_body->pointer_add(magic,new_pointer); + } + + unsigned restore_context::register_callback(restore_context::create_callback create, restore_context::restore_callback restore) + { + return m_body->register_callback(create,restore); + } + + bool restore_context::is_callback(unsigned key) const + { + return m_body->is_callback(key); + } + + restore_context::callback_data restore_context::lookup_callback(unsigned key) const throw(persistent_illegal_type) + { + return m_body->lookup_callback(key); + } + + unsigned restore_context::register_interface(persistent* sample) + { + return m_body->register_interface(sample); + } + + bool restore_context::is_interface(unsigned key) const + { + return m_body->is_interface(key); + } + + persistent* restore_context::lookup_interface(unsigned key) const throw(persistent_illegal_type) + { + return m_body->lookup_interface(key); + } + + void restore_context::register_all(restore_context::installer installer) + { + if (installer) installer(*this); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + diff --git a/src/stlplus/persistence/persistent_contexts.hpp b/src/stlplus/persistence/persistent_contexts.hpp new file mode 100644 index 0000000..dfef17b --- /dev/null +++ b/src/stlplus/persistence/persistent_contexts.hpp @@ -0,0 +1,156 @@ +#ifndef STLPLUS_PERSISTENT_CONTEXTS +#define STLPLUS_PERSISTENT_CONTEXTS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Core context classes used to control the persistent dump/restore operations + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistence_fixes.hpp" +#include "persistent.hpp" +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Internals + + class dump_context_body; + class restore_context_body; + + //////////////////////////////////////////////////////////////////////////////// + // The format version number currently supported + //////////////////////////////////////////////////////////////////////////////// + + extern unsigned char PersistentVersion; + + //////////////////////////////////////////////////////////////////////////////// + // dump_context controls the formatting of a persistent dump + //////////////////////////////////////////////////////////////////////////////// + + class dump_context + { + friend class persistent; + public: + ////////////////////////////////////////////////////////////////////////////// + + // device must be in binary mode + dump_context(std::ostream& device, unsigned char version = PersistentVersion) throw(persistent_dump_failed); + ~dump_context(void); + + // low level output used to dump a byte + void put(unsigned char data) throw(persistent_dump_failed); + + // access the device, for example to check the error status + const std::ostream& device(void) const; + + // recover the version number of the dumped output + unsigned char version(void) const; + + // test whether the current platform uses little-endian or big-endian addressing of bytes + // this is used in dump/restore of integers and is exported so that other routines can use it + bool little_endian(void) const; + + // Assist functions for Pointers + // the return pair value is a flag saying whether this is a new pointer and the magic key to dump to file + std::pair pointer_map(const void* const pointer); + + // Assist functions for Polymorphous classes (i.e. subclasses) using callback approach + typedef void (*dump_callback)(dump_context&,const void*); + unsigned register_callback(const std::type_info& info, dump_callback); + bool is_callback(const std::type_info& info) const; + typedef std::pair callback_data; + callback_data lookup_callback(const std::type_info&) const throw(persistent_illegal_type); + + // Assist functions for Polymorphous classes (i.e. subclasses) using interface approach + unsigned register_interface(const std::type_info& info); + bool is_interface(const std::type_info& info) const; + unsigned lookup_interface(const std::type_info&) const throw(persistent_illegal_type); + + // Register all Polymorphous classes using either approach by calling an installer callback + typedef void (*installer)(dump_context&); + void register_all(installer); + + private: + friend class dump_context_body; + dump_context_body* m_body; + + // disallow copying by making assignment and copy constructor private + dump_context(const dump_context&); + dump_context& operator=(const dump_context&); + }; + + //////////////////////////////////////////////////////////////////////////////// + // restore_context controls the reading of the persistent data during a restore + + class restore_context + { + friend class persistent; + public: + ////////////////////////////////////////////////////////////////////////////// + + // device must be in binary mode + restore_context(std::istream& device) throw(persistent_restore_failed); + ~restore_context(void); + + // low level input used to restore a byte + int get(void) throw(persistent_restore_failed); + + // access the device, for example to check the error status + const std::istream& device(void) const; + + // access the version number of the input being restored + unsigned char version(void) const; + + // test whether the current platform uses little-endian or big-endian addressing of bytes + // this is used in dump/restore of integers + bool little_endian(void) const; + + // Assist functions for Pointers + std::pair pointer_map(unsigned magic); + void pointer_add(unsigned magic, void* new_pointer); + + // Assist functions for Polymorphous classes using the callback approach + typedef void* (*create_callback)(void); + typedef void (*restore_callback)(restore_context&,void*); + unsigned register_callback(create_callback,restore_callback); + bool is_callback(unsigned) const; + typedef std::pair callback_data; + callback_data lookup_callback(unsigned) const throw(persistent_illegal_type); + + // Assist functions for Polymorphous classes using the interface approach + unsigned register_interface(persistent*); + bool is_interface(unsigned) const; + persistent* lookup_interface(unsigned) const throw(persistent_illegal_type); + + // Register all Polymorphous classes using either approach by calling an installer callback + typedef void (*installer)(restore_context&); + void register_all(installer); + + private: + friend class restore_context_body; + restore_context_body* m_body; + + typedef std::pair interface_data; + + // disallow copying by making assignment and copy constructor private + restore_context(const restore_context&); + restore_context& operator=(const restore_context&); + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_cstring.cpp b/src/stlplus/persistence/persistent_cstring.cpp new file mode 100644 index 0000000..a829b9a --- /dev/null +++ b/src/stlplus/persistence/persistent_cstring.cpp @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistent_cstring.hpp" +#include "persistent_int.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// +// Null-terminated char arrays +// Format: address [ size data ] + +void stlplus::dump_cstring(stlplus::dump_context& context, const char* data) throw(stlplus::persistent_dump_failed) +{ + // register the address and get the magic key for it + std::pair mapping = context.pointer_map(data); + stlplus::dump_unsigned(context,mapping.second); + // if the address is null, then that is all that we need to do + // however, if it is non-null and this is the first sight of the address, dump the contents + if (data && !mapping.first) + { + unsigned size = strlen(data); + stlplus::dump_unsigned(context,size); + for (unsigned i = 0; i < size; i++) + stlplus::dump_char(context,data[i]); + } +} + +void stlplus::restore_cstring(restore_context& context, char*& data) throw(stlplus::persistent_restore_failed) +{ + // destroy any previous contents + if (data) + { + delete[] data; + data = 0; + } + // get the magic key + unsigned magic = 0; + stlplus::restore_unsigned(context,magic); + // now lookup the magic key to see if this pointer has already been restored + // null pointers are always flagged as already restored + std::pair address = context.pointer_map(magic); + if (!address.first) + { + // this pointer has never been seen before and is non-null + // restore the string + unsigned size = 0; + stlplus::restore_unsigned(context,size); + data = new char[size+1]; + for (unsigned i = 0; i < size; i++) + stlplus::restore_char(context,data[i]); + data[size] = '\0'; + // add this pointer to the set of already seen objects + context.pointer_add(magic,data); + } +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_cstring.hpp b/src/stlplus/persistence/persistent_cstring.hpp new file mode 100644 index 0000000..8e3e94a --- /dev/null +++ b/src/stlplus/persistence/persistent_cstring.hpp @@ -0,0 +1,40 @@ +#ifndef STLPLUS_PERSISTENT_CSTRING +#define STLPLUS_PERSISTENT_CSTRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of C-style char* strings + +// These are handled differently to other pointer types + +// Warning! This means that pointers to char cannot be supported, since there +// is no type difference between a pointer to char and a C-style array of char. + +// Warning! The restore deletes any old value of the data parameter and +// allocates a new char* which is (just) big enough and assigns it to the data +// field. This is because there is no way of knowing how long a char* is so +// the passed parameter is not safe to use. The allocation is done using +// standard new. If the data field is non-null on entry it will be deleted by +// standard delete. Best to make it null in the first place. + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + void dump_cstring(dump_context&, const char* data) throw(persistent_dump_failed); + void restore_cstring(restore_context&, char*& data) throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_deque.hpp b/src/stlplus/persistence/persistent_deque.hpp new file mode 100644 index 0000000..2a47180 --- /dev/null +++ b/src/stlplus/persistence/persistent_deque.hpp @@ -0,0 +1,32 @@ +#ifndef STLPLUS_PERSISTENT_DEQUE +#define STLPLUS_PERSISTENT_DEQUE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of the STL deque + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_deque(dump_context&, const std::deque& data, D dump_fn) throw(persistent_dump_failed); + + template + void restore_deque(restore_context&, std::deque& data, R restore_fn) throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_deque.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_deque.tpp b/src/stlplus/persistence/persistent_deque.tpp new file mode 100644 index 0000000..26854ce --- /dev/null +++ b/src/stlplus/persistence/persistent_deque.tpp @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_deque(dump_context& context, const std::deque& data, D dump_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (typename std::deque::const_iterator i = data.begin(); i != data.end(); i++) + dump_fn(context,*i); + } + + template + void restore_deque(restore_context& context, std::deque& data, R restore_fn) + throw(persistent_restore_failed) + { + data.clear(); + unsigned size = 0; + restore_unsigned(context,size); + for (unsigned i = 0; i < size; i++) + { + data.push_back(T()); + restore_fn(context,data.back()); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_digraph.hpp b/src/stlplus/persistence/persistent_digraph.hpp new file mode 100644 index 0000000..fcc57a0 --- /dev/null +++ b/src/stlplus/persistence/persistent_digraph.hpp @@ -0,0 +1,56 @@ +#ifndef STLPLUS_PERSISTENT_DIGRAPH +#define STLPLUS_PERSISTENT_DIGRAPH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STLplus digraph + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "digraph.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + // digraph + + template + void dump_digraph(dump_context&, const digraph& data, DN dump_node, DA dump_arc) + throw(persistent_dump_failed); + + template + void restore_digraph(restore_context&, digraph& data, RN restore_node, RA restore_arc) + throw(persistent_restore_failed); + + // node iterator + + template + void dump_digraph_iterator(dump_context& str, const digraph_iterator& data) + throw(persistent_dump_failed); + + template + void restore_digraph_iterator(restore_context& str, digraph_iterator& data) + throw(persistent_restore_failed); + + // arc iterator + + template + void dump_digraph_arc_iterator(dump_context& str, const digraph_arc_iterator& data) + throw(persistent_dump_failed); + + template + void restore_digraph_arc_iterator(restore_context& str, digraph_arc_iterator& data) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_digraph.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_digraph.tpp b/src/stlplus/persistence/persistent_digraph.tpp new file mode 100644 index 0000000..3ff94a5 --- /dev/null +++ b/src/stlplus/persistence/persistent_digraph.tpp @@ -0,0 +1,153 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" +#include "persistent_xref.hpp" + +namespace stlplus +{ + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_digraph(dump_context& context, const digraph& data, + DN dump_node, DA dump_arc) + throw(persistent_dump_failed) + { + // dump a magic key to the address of the graph for use in persistence of iterators + // and register it as a dumped address + std::pair mapping = context.pointer_map(&data); + if (mapping.first) throw persistent_dump_failed("digraph: already dumped this graph"); + dump_unsigned(context,mapping.second); + // dump the nodes + dump_unsigned(context,data.size()); + for (typename digraph::const_iterator node = data.begin(); node != data.end(); node++) + { + // nodes are keyed by the magic key to the node address + // this key is then used in dumping the arc from/to pointers + std::pair node_mapping = context.pointer_map(node.node()); + if (node_mapping.first) throw persistent_dump_failed("digraph: already dumped this node"); + dump_unsigned(context,node_mapping.second); + // finally, dump the node contents + dump_node(context,*node); + } + // dump the arcs + dump_unsigned(context,data.arc_size()); + for (typename digraph::const_arc_iterator arc = data.arc_begin(); arc != data.arc_end(); arc++) + { + // dump the magic key to the arc address + // this is used by iterator persistence too + std::pair arc_mapping = context.pointer_map(arc.node()); + if (arc_mapping.first) throw persistent_dump_failed("digraph: already dumped this arc"); + dump_unsigned(context,arc_mapping.second); + // now dump the from/to pointers as cross-references + dump_xref(context,data.arc_from(arc).node()); + dump_xref(context,data.arc_to(arc).node()); + // now dump the arc's data + dump_arc(context,*arc); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void restore_digraph(restore_context& context, digraph& data, + RN restore_node, RA restore_arc) + throw(persistent_restore_failed) + { + data.clear(); + // restore the graph's magic key and map it onto the graph's address + // this is used in the persistence of iterators + unsigned magic = 0; + restore_unsigned(context,magic); + context.pointer_add(magic,&data); + // restore the nodes + unsigned nodes = 0; + restore_unsigned(context, nodes); + for (unsigned n = 0; n < nodes; n++) + { + unsigned node_magic = 0; + restore_unsigned(context,node_magic); + // create a new node and map the magic key onto the new address + typename digraph::iterator node = data.insert(NT()); + context.pointer_add(node_magic,node.node()); + // now restore the user's data + restore_node(context,*node); + } + // restore the arcs + unsigned arcs = 0; + restore_unsigned(context, arcs); + for (unsigned a = 0; a < arcs; a++) + { + unsigned arc_magic = 0; + restore_unsigned(context,arc_magic); + // restore the from and to cross-references + digraph_node* from = 0; + digraph_node* to = 0; + restore_xref(context,from); + restore_xref(context,to); + // create an arc with these from/to pointers + digraph_arc_iterator arc = + data.arc_insert(digraph_iterator(from), + digraph_iterator(to)); + context.pointer_add(arc_magic,arc.node()); + // restore the user data + restore_arc(context,*arc); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_digraph_iterator(dump_context& context, + const digraph_iterator& data) + throw(persistent_dump_failed) + { + dump_xref(context,data.owner()); + dump_xref(context,data.node()); + } + + template + void restore_digraph_iterator(restore_context& context, + digraph_iterator& data) + throw(persistent_restore_failed) + { + digraph* owner = 0; + digraph_node* node = 0; + restore_xref(context,owner); + restore_xref(context,node); + data = digraph_iterator(node); + data.assert_owner(owner); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_digraph_arc_iterator(dump_context& context, + const digraph_arc_iterator& data) + throw(persistent_dump_failed) + { + dump_xref(context,data.owner()); + dump_xref(context,data.node()); + } + + template + void restore_digraph_arc_iterator(restore_context& context, + digraph_arc_iterator& data) + throw(persistent_restore_failed) + { + digraph* owner = 0; + digraph_arc* arc = 0; + restore_xref(context,owner); + restore_xref(context,arc); + data = digraph_arc_iterator(arc); + data.assert_owner(owner); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_enum.hpp b/src/stlplus/persistence/persistent_enum.hpp new file mode 100644 index 0000000..ddce1b4 --- /dev/null +++ b/src/stlplus/persistence/persistent_enum.hpp @@ -0,0 +1,31 @@ +#ifndef STLPLUS_PERSISTENT_ENUM +#define STLPLUS_PERSISTENT_ENUM +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of enumeration types + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_enum(dump_context&, const T& data) throw(persistent_dump_failed); + + template + void restore_enum(restore_context&, T& data) throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_enum.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_enum.tpp b/src/stlplus/persistence/persistent_enum.tpp new file mode 100644 index 0000000..9778688 --- /dev/null +++ b/src/stlplus/persistence/persistent_enum.tpp @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // enumeration types + + template + void dump_enum(dump_context& context, const T& data) + throw(persistent_dump_failed) + { + dump_unsigned(context,(unsigned)data); + } + + template + void restore_enum(restore_context& context, T& data) + throw(persistent_restore_failed) + { + unsigned value = 0; + restore_unsigned(context, value); + data = (T)value; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_exceptions.cpp b/src/stlplus/persistence/persistent_exceptions.cpp new file mode 100644 index 0000000..d233826 --- /dev/null +++ b/src/stlplus/persistence/persistent_exceptions.cpp @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistent_exceptions.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +static std::string to_string(int number) +{ + // use sprintf in a very controlled way that cannot overrun + char* buffer = new char[50]; + sprintf(buffer, "%i", number); + std::string result = buffer; + delete buffer; + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// exceptions + +stlplus::persistent_illegal_type::persistent_illegal_type(const std::string& type) throw() : + std::logic_error(std::string("illegal type: ") + type) +{ +} + +stlplus::persistent_illegal_type::persistent_illegal_type(unsigned key) throw() : + std::logic_error(std::string("illegal key: ") + to_string((int)key)) +{ +} + +stlplus::persistent_illegal_type::~persistent_illegal_type(void) throw() +{ +} + +//////////////////////////////////////////////////////////////////////////////// + +stlplus::persistent_dump_failed::persistent_dump_failed(const std::string& message) throw() : + std::runtime_error(std::string("dump failed: ") + message) +{ +} + +stlplus::persistent_dump_failed::~persistent_dump_failed(void) throw() +{ +} + +//////////////////////////////////////////////////////////////////////////////// + +stlplus::persistent_restore_failed::persistent_restore_failed(const std::string& message) throw() : + std::runtime_error(std::string("restore failed: ") + message) +{ +} + +stlplus::persistent_restore_failed::~persistent_restore_failed(void) throw() +{ +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_exceptions.hpp b/src/stlplus/persistence/persistent_exceptions.hpp new file mode 100644 index 0000000..2e2a861 --- /dev/null +++ b/src/stlplus/persistence/persistent_exceptions.hpp @@ -0,0 +1,53 @@ +#ifndef STLPLUS_PERSISTENT_EXCEPTIONS +#define STLPLUS_PERSISTENT_EXCEPTIONS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Exceptions thrown by persistence routines + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + // exception thrown if you try to dump or restore an illegal polymorphic type + class persistent_illegal_type : public std::logic_error + { + public: + persistent_illegal_type(const std::string& type) throw(); + persistent_illegal_type(unsigned key) throw(); + ~persistent_illegal_type(void) throw(); + }; + + // exception thrown if a dump fails for any reason - but typically because the output stream couldn't take the data + class persistent_dump_failed : public std::runtime_error + { + public: + persistent_dump_failed(const std::string& message) throw(); + ~persistent_dump_failed(void) throw(); + }; + + // exception thrown if you try to restore from an out of date or unrecognised byte stream + class persistent_restore_failed : public std::runtime_error + { + public: + persistent_restore_failed(const std::string& message) throw(); + ~persistent_restore_failed(void) throw(); + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/persistence/persistent_float.cpp b/src/stlplus/persistence/persistent_float.cpp new file mode 100644 index 0000000..59a6678 --- /dev/null +++ b/src/stlplus/persistence/persistent_float.cpp @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_float.hpp" + +//////////////////////////////////////////////////////////////////////////////// +// Macro for mapping either endian data onto little-endian addressing to make +// my life easier in writing this code! I think better in little-endian mode +// so the macro does nothing in that mode but maps little-endian onto +// big-endian addressing in big-endian mode +// TODO - make this compile-time configurable + +#define INDEX(index) ((context.little_endian()) ? (index) : ((bytes) - (index) - 1)) + +///////////////////////////////////////////////////////////////////// +// floating point types +// format: {size}{byte}*size +// ordering is msB first + +// this uses a similar mechanism to integer dumps. However, it is not clear how +// the big-endian and little-endian argument applies to multi-word data so +// this may need reworking by splitting into words and then bytes. + +namespace stlplus +{ + + static void dump_float(stlplus::dump_context& context, unsigned bytes, unsigned char* data) + throw(stlplus::persistent_dump_failed) + { + unsigned i = bytes; + // put the size + context.put((unsigned char)i); + // and put the bytes + while(i--) + context.put(data[INDEX(i)]); + } + + static void restore_float(stlplus::restore_context& context, unsigned bytes, unsigned char* data) + throw(stlplus::persistent_restore_failed) + { + // get the dumped size from the file + unsigned dumped_bytes = (unsigned)context.get(); + // get the bytes from the file + unsigned i = dumped_bytes; + while(i--) + { + int ch = context.get(); + if (i < bytes) + data[INDEX(i)] = (unsigned char)ch; + } + // however, if the dumped size was different I don't know how to map the formats, so give an error + if (dumped_bytes != bytes) + throw stlplus::persistent_restore_failed(std::string("size mismatch")); + } + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// + // exported functions which simply call the low-level byte-dump and byte-restore routines above + +void stlplus::dump_float(stlplus::dump_context& context, const float& data) throw(stlplus::persistent_dump_failed) +{ + stlplus::dump_float(context, sizeof(float), (unsigned char*)&data); +} + +void stlplus::restore_float(restore_context& context, float& data) throw(stlplus::persistent_restore_failed) +{ + stlplus::restore_float(context, sizeof(float), (unsigned char*)&data); +} + +void stlplus::dump_double(stlplus::dump_context& context, const double& data) throw(stlplus::persistent_dump_failed) +{ + stlplus::dump_float(context, sizeof(double), (unsigned char*)&data); +} + +void stlplus::restore_double(restore_context& context, double& data) throw(stlplus::persistent_restore_failed) +{ + stlplus::restore_float(context, sizeof(double), (unsigned char*)&data); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_float.hpp b/src/stlplus/persistence/persistent_float.hpp new file mode 100644 index 0000000..4a01e30 --- /dev/null +++ b/src/stlplus/persistence/persistent_float.hpp @@ -0,0 +1,37 @@ +#ifndef STLPLUS_PERSISTENT_FLOAT +#define STLPLUS_PERSISTENT_FLOAT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of floating-point types + +// Note: despite years and years of IEEE standardisation, not all +// architectures use IEEE-standard representations of floating-point numbers. +// Therefore a binary dump is not necessarily portable between platforms. +// Solving this is (currently) beyond the scope of the STLplus project. + +// If you want to be strictly portable to all platforms, do not dump/restore float + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + void dump_float(dump_context&, const float& data) throw(persistent_dump_failed); + void restore_float(restore_context&, float& data) throw(persistent_restore_failed); + + void dump_double(dump_context&, const double& data) throw(persistent_dump_failed); + void restore_double(restore_context&, double& data) throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_foursome.hpp b/src/stlplus/persistence/persistent_foursome.hpp new file mode 100644 index 0000000..b326a85 --- /dev/null +++ b/src/stlplus/persistence/persistent_foursome.hpp @@ -0,0 +1,36 @@ +#ifndef STLPLUS_PERSISTENT_FOURSOME +#define STLPLUS_PERSISTENT_FOURSOME +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL foursome + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "foursome.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_foursome(dump_context&, const stlplus::foursome& data, + D1 dump_fn1, D2 dump_fn2, D3 dump_fn3, D4 dump_fn4) + throw(persistent_dump_failed); + + template + void restore_foursome(restore_context&, stlplus::foursome& data, + R1 restore_fn1, R2 restore_fn2, R3 restore_fn3, R4 restore_fn4) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_foursome.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_foursome.tpp b/src/stlplus/persistence/persistent_foursome.tpp new file mode 100644 index 0000000..cfcf6ae --- /dev/null +++ b/src/stlplus/persistence/persistent_foursome.tpp @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_foursome(dump_context& context, const foursome& data, + D1 dump_fn1, D2 dump_fn2, D3 dump_fn3, D4 dump_fn4) + throw(persistent_dump_failed) + { + dump_fn1(context,data.first); + dump_fn2(context,data.second); + dump_fn3(context,data.third); + dump_fn4(context,data.fourth); + } + + template + void restore_foursome(restore_context& context, foursome& data, + R1 restore_fn1, R2 restore_fn2, R3 restore_fn3, R4 restore_fn4) + throw(persistent_restore_failed) + { + restore_fn1(context,data.first); + restore_fn2(context,data.second); + restore_fn3(context,data.third); + restore_fn4(context,data.fourth); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_hash.hpp b/src/stlplus/persistence/persistent_hash.hpp new file mode 100644 index 0000000..623f285 --- /dev/null +++ b/src/stlplus/persistence/persistent_hash.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PERSISTENT_HASH +#define STLPLUS_PERSISTENT_HASH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence for STLplus hash + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "hash.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_hash(dump_context&, const hash& data, DK key_dump_fn, DT val_dump_fn) + throw(persistent_dump_failed); + + template + void restore_hash(restore_context&, hash& data, RK key_restore_fn, RT val_restore_fn) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_hash.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_hash.tpp b/src/stlplus/persistence/persistent_hash.tpp new file mode 100644 index 0000000..9fe2dbc --- /dev/null +++ b/src/stlplus/persistence/persistent_hash.tpp @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_hash(dump_context& context, const hash& data, DK key_fn, DT val_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (typename hash::const_iterator i = data.begin(); i != data.end(); i++) + { + key_fn(context,i->first); + val_fn(context,i->second); + } + } + + template + void restore_hash(restore_context& context, hash& data, RK key_fn, RT val_fn) + throw(persistent_restore_failed) + { + data.erase(); + unsigned size = 0; + restore_unsigned(context,size); + for (unsigned j = 0; j < size; j++) + { + K key; + key_fn(context,key); + val_fn(context,data[key]); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_inf.cpp b/src/stlplus/persistence/persistent_inf.cpp new file mode 100644 index 0000000..29637c9 --- /dev/null +++ b/src/stlplus/persistence/persistent_inf.cpp @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded to break the dependency on the portability library +#ifndef NO_STLPLUS_INF + +#include "persistent_int.hpp" +#include "persistent_string.hpp" +#include "persistent_inf.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +void stlplus::dump_inf(stlplus::dump_context& context, const stlplus::inf& data) + throw(stlplus::persistent_dump_failed) +{ + // don't support dumping of old versions + if (context.version() < 2) + throw stlplus::persistent_dump_failed(std::string("stlplus::inf::dump: wrong version")); + // just dump the string + stlplus::dump_string(context,data.get_bytes()); +} + +//////////////////////////////////////////////////////////////////////////////// + +void stlplus::restore_inf(stlplus::restore_context& context, stlplus::inf& data) + throw(stlplus::persistent_restore_failed) +{ + if (context.version() < 1) + throw stlplus::persistent_restore_failed(std::string("stlplus::inf::restore: wrong version")); + if (context.version() == 1) + { + // old-style restore relies on the word size being the same - 32-bits - on all platforms + // this can be restored on such machines but is not portable to 64-bit machines + std::string value; + unsigned bits = 0; + stlplus::restore_unsigned(context,bits); + unsigned words = (bits+7)/32; + // inf was dumped msB first + for (unsigned i = words; i--; ) + { + // restore a word + unsigned word = 0; + stlplus::restore_unsigned(context,word); + // now extract the bytes + unsigned char* byte_ptr = (unsigned char*)(&word); + for (unsigned b = 4; b--; ) + value.insert(value.begin(),byte_ptr[context.little_endian() ? b : 3 - b]); + } + data.set_bytes(value); + } + else + { + // new-style dump just uses the string persistence + std::string value; + stlplus::restore_string(context,value); + data.set_bytes(value); + } +} +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_inf.hpp b/src/stlplus/persistence/persistent_inf.hpp new file mode 100644 index 0000000..2b456d9 --- /dev/null +++ b/src/stlplus/persistence/persistent_inf.hpp @@ -0,0 +1,25 @@ +#ifndef STLPLUS_PERSISTENT_INF +#define STLPLUS_PERSISTENT_INF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of stlplus infinite integer type - inf + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "inf.hpp" + +namespace stlplus +{ + + void dump_inf(dump_context&, const inf& data) throw(persistent_dump_failed); + void restore_inf(restore_context&, inf& data) throw(persistent_restore_failed); + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/persistence/persistent_int.cpp b/src/stlplus/persistence/persistent_int.cpp new file mode 100644 index 0000000..87d403a --- /dev/null +++ b/src/stlplus/persistence/persistent_int.cpp @@ -0,0 +1,223 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +//////////////////////////////////////////////////////////////////////////////// +// Macro for mapping either endian data onto little-endian addressing to make +// my life easier in writing this code! I think better in little-endian mode +// so the macro does nothing in that mode but maps little-endian onto +// big-endian addressing in big-endian mode +// TODO - make this compile-time configurable so it's more efficient + +#define INDEX(index) ((context.little_endian()) ? (index) : ((bytes) - (index) - 1)) + +//////////////////////////////////////////////////////////////////////////////// +// Integer types +// format: {size}{byte}*size +// size can be zero! +// +// A major problem is that integer types may be different sizes on different +// machines or even with different compilers on the same machine (though I +// haven't come across that yet). Neither the C nor the C++ standards specify +// the size of integer types. Dumping an int on one machine might dump 16 +// bits. Restoring it on another machine might try to restore 32 bits. These +// functions must therefore handle different type sizes. It does this by +// writing the size to the file as well as the data, so the restore can +// therefore know how many bytes to restore independent of the type size. +// +// In fact, the standard does not even specify the size of char (true! And +// mind-numbingly stupid...). However, to be able to do anything at all, I've +// had to assume that a char is 1 byte. + +static void dump_unsigned(stlplus::dump_context& context, unsigned bytes, unsigned char* data) + throw(stlplus::persistent_dump_failed) +{ + // first skip zero bytes - this may reduce the data to zero bytes long + unsigned i = bytes; + while(i >= 1 && data[INDEX(i-1)] == 0) + i--; + // put the remaining size + context.put((unsigned char)i); + // and put the bytes + while(i--) + context.put(data[INDEX(i)]); +} + +static void dump_signed(stlplus::dump_context& context, unsigned bytes, unsigned char* data) + throw(stlplus::persistent_dump_failed) +{ + // first skip all-zero or all-one bytes but only if doing so does not change the sign + unsigned i = bytes; + if (data[INDEX(i-1)] < 128) + { + // positive number so discard leading zeros but only if the following byte is positive + while(i >= 2 && data[INDEX(i-1)] == 0 && data[INDEX(i-2)] < 128) + i--; + } + else + { + // negative number so discard leading ones but only if the following byte is negative + while(i >= 2 && data[INDEX(i-1)] == 255 && data[INDEX(i-2)] >= 128) + i--; + } + // put the remaining size + context.put((unsigned char)i); + // and put the bytes + while(i--) + context.put(data[INDEX(i)]); +} + +static void restore_unsigned(stlplus::restore_context& context, unsigned bytes, unsigned char* data) + throw(stlplus::persistent_restore_failed) +{ + // get the dumped size from the file + unsigned dumped_bytes = (unsigned)context.get(); + // zero fill any empty space + unsigned i = bytes; + for (; i > dumped_bytes; i--) + data[INDEX(i-1)] = 0; + // restore the dumped bytes but discard any that don't fit + while(i--) + { + int ch = context.get(); + if (i < bytes) + data[INDEX(i)] = (unsigned char)ch; + else + throw stlplus::persistent_restore_failed(std::string("integer overflow")); + } +} + +static void restore_signed(stlplus::restore_context& context, unsigned bytes, unsigned char* data) + throw(stlplus::persistent_restore_failed) +{ + // get the dumped size from the file + unsigned dumped_bytes = (unsigned)context.get(); + // restore the dumped bytes but discard any that don't fit + unsigned i = dumped_bytes; + while(i--) + { + int ch = context.get(); + if (i < bytes) + data[INDEX(i)] = (unsigned char)ch; + else + throw stlplus::persistent_restore_failed(std::string("integer overflow")); + } + // sign extend if the dumped integer was smaller + if (dumped_bytes < bytes) + { + if (data[INDEX(dumped_bytes-1)] < 128) + { + // positive so zero fill + for (i = dumped_bytes; i < bytes; i++) + data[INDEX(i)] = 0; + } + else + { + // negative so one fill + for (i = dumped_bytes; i < bytes; i++) + data[INDEX(i)] = 0xff; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// exported functions + +// char is dumped and restored as an unsigned char because the signedness of char is not defined and can vary +void stlplus::dump_char(stlplus::dump_context& context, const char& data) throw(stlplus::persistent_dump_failed) +{ + context.put((unsigned char)data); +} + +void stlplus::restore_char(restore_context& context, char& data) throw(stlplus::persistent_restore_failed) +{ + data = (char)(unsigned char)context.get(); +} + +void stlplus::dump_signed_char(stlplus::dump_context& context, const signed char& data) throw(stlplus::persistent_dump_failed) +{ + context.put((unsigned char)data); +} + +void stlplus::restore_signed_char(restore_context& context, signed char& data) throw(stlplus::persistent_restore_failed) +{ + data = (signed char)(unsigned char)context.get(); +} + +void stlplus::dump_unsigned_char(stlplus::dump_context& context, const unsigned char& data) throw(stlplus::persistent_dump_failed) +{ + context.put((unsigned char)data); +} + +void stlplus::restore_unsigned_char(restore_context& context, unsigned char& data) throw(stlplus::persistent_restore_failed) +{ + data = (signed char)(unsigned char)context.get(); +} + +void stlplus::dump_short(stlplus::dump_context& context, const short& data) throw(stlplus::persistent_dump_failed) +{ + ::dump_signed(context, sizeof(short), (unsigned char*)&data); +} + +void stlplus::restore_short(restore_context& context, short& data) throw(stlplus::persistent_restore_failed) +{ + ::restore_signed(context, sizeof(short),(unsigned char*)&data); +} + +void stlplus::dump_unsigned_short(stlplus::dump_context& context, const unsigned short& data) throw(stlplus::persistent_dump_failed) +{ + ::dump_unsigned(context, sizeof(unsigned short), (unsigned char*)&data); +} + +void stlplus::restore_unsigned_short(restore_context& context, unsigned short& data) throw(stlplus::persistent_restore_failed) +{ + ::restore_unsigned(context, sizeof(unsigned short),(unsigned char*)&data); +} + +void stlplus::dump_int(stlplus::dump_context& context, const int& data) throw(stlplus::persistent_dump_failed) +{ + ::dump_signed(context, sizeof(int), (unsigned char*)&data); +} + +void stlplus::restore_int(restore_context& context, int& data) throw(stlplus::persistent_restore_failed) +{ + ::restore_signed(context, sizeof(int),(unsigned char*)&data); +} + +void stlplus::dump_unsigned(stlplus::dump_context& context, const unsigned& data) throw(stlplus::persistent_dump_failed) +{ + ::dump_unsigned(context, sizeof(unsigned), (unsigned char*)&data); +} + +void stlplus::restore_unsigned(restore_context& context, unsigned& data) throw(stlplus::persistent_restore_failed) +{ + ::restore_unsigned(context, sizeof(unsigned),(unsigned char*)&data); +} + +void stlplus::dump_long(stlplus::dump_context& context, const long& data) throw(stlplus::persistent_dump_failed) +{ + ::dump_signed(context, sizeof(long), (unsigned char*)&data); +} + +void stlplus::restore_long(restore_context& context, long& data) throw(stlplus::persistent_restore_failed) +{ + ::restore_signed(context, sizeof(long),(unsigned char*)&data); +} + +void stlplus::dump_unsigned_long(stlplus::dump_context& context, const unsigned long& data) throw(stlplus::persistent_dump_failed) +{ + ::dump_unsigned(context, sizeof(unsigned long), (unsigned char*)&data); +} + +void stlplus::restore_unsigned_long(restore_context& context, unsigned long& data) throw(stlplus::persistent_restore_failed) +{ + ::restore_unsigned(context, sizeof(unsigned long),(unsigned char*)&data); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_int.hpp b/src/stlplus/persistence/persistent_int.hpp new file mode 100644 index 0000000..404d1c5 --- /dev/null +++ b/src/stlplus/persistence/persistent_int.hpp @@ -0,0 +1,50 @@ +#ifndef STLPLUS_PERSISTENT_INT +#define STLPLUS_PERSISTENT_INT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of basic integer types + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +namespace stlplus +{ + + void dump_char(dump_context&, const char& data) throw(persistent_dump_failed); + void restore_char(restore_context&, char& data) throw(persistent_restore_failed); + + void dump_signed_char(dump_context&, const signed char& data) throw(persistent_dump_failed); + void restore_signed_char(restore_context&, signed char& data) throw(persistent_restore_failed); + + void dump_unsigned_char(dump_context&, const unsigned char& data) throw(persistent_dump_failed); + void restore_unsigned_char(restore_context&, unsigned char& data) throw(persistent_restore_failed); + + void dump_short(dump_context&, const short& data) throw(persistent_dump_failed); + void restore_short(restore_context&, short& data) throw(persistent_restore_failed); + + void dump_unsigned_short(dump_context&, const unsigned short& data) throw(persistent_dump_failed); + void restore_unsigned_short(restore_context&, unsigned short& data) throw(persistent_restore_failed); + + void dump_int(dump_context&, const int& data) throw(persistent_dump_failed); + void restore_int(restore_context&, int& data) throw(persistent_restore_failed); + + void dump_unsigned(dump_context&, const unsigned& data) throw(persistent_dump_failed); + void restore_unsigned(restore_context&, unsigned& data) throw(persistent_restore_failed); + + void dump_long(dump_context&, const long& data) throw(persistent_dump_failed); + void restore_long(restore_context&, long& data) throw(persistent_restore_failed); + + void dump_unsigned_long(dump_context&, const unsigned long& data) throw(persistent_dump_failed); + void restore_unsigned_long(restore_context&, unsigned long& data) throw(persistent_restore_failed); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/persistence/persistent_interface.hpp b/src/stlplus/persistence/persistent_interface.hpp new file mode 100644 index 0000000..366954c --- /dev/null +++ b/src/stlplus/persistence/persistent_interface.hpp @@ -0,0 +1,54 @@ +#ifndef STLPLUS_PERSISTENT_INTERFACE +#define STLPLUS_PERSISTENT_INTERFACE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence for pointers to polymorphic classes using the interface approach. + +// This works on a set of classes derived from a common superclass called +// persistent which is declared as an interface. Each subclass has a set of +// methods that enable clone/dump/restore operations. Each subclass must be +// registered with the persistence dump/restore context so that the system +// knows how to dump it. + +// This approach is suited to classes that can be modified to add persistence +// methods. See persistent_callback for a non-invasive way of handling +// polymorphism. + +// Objects are always dumped/restored as pointers to the superclass T. + +// Multiple pointers to the same object are handled in the same way as for +// simple pointers + +// Only classes registered with the context can be dumped and restored as +// polymorphic types - see dump_context::register_interface and +// restore_context::register_interface. Attempting to use any unrecognised class +// will throw an exception. + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "persistent.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_interface(dump_context&, const T* const data) + throw(persistent_dump_failed); + + template + void restore_interface(restore_context&, T*& data) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_interface.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_interface.tpp b/src/stlplus/persistence/persistent_interface.tpp new file mode 100644 index 0000000..a684aa3 --- /dev/null +++ b/src/stlplus/persistence/persistent_interface.tpp @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Polymorphous classes using the interface approach + +// format: magic [ key data ] + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_interface(dump_context& context, const T* const data) + throw(persistent_dump_failed) + { + try + { + // register the address and get the magic key for it + std::pair mapping = context.pointer_map(data); + dump_unsigned(context,mapping.second); + // if the address is null, then that is all that we need to do + // however, if it is non-null and this is the first sight of the address, dump the contents + if (data && !mapping.first) + { + // interface method + // the lookup just finds the magic key and the type has a dump method + // this will throw persistent_illegal_type if the type is not registered + unsigned key = context.lookup_interface(typeid(*data)); + // dump the magic key for the type + dump_unsigned(context, key); + // now call the dump method defined by the interface + data->dump(context); + } + } + catch (const persistent_illegal_type& except) + { + // convert this to a simpler dump failed exception + throw persistent_dump_failed(except.what()); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void restore_interface(restore_context& context, T*& data) + throw(persistent_restore_failed) + { + try + { + // first delete any previous object pointed to since the restore creates the object of the right subclass + if (data) + { + delete data; + data = 0; + } + // get the magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // now lookup the magic key to see if this pointer has already been restored + // null pointers are always flagged as already restored + std::pair address = context.pointer_map(magic); + if (address.first) + { + // seen before, so simply map it to the existing address + data = (T*)address.second; + } + else + { + // now restore the magic key that denotes the particular subclass + unsigned key = 0; + restore_unsigned(context, key); + // interface approach + // first clone the sample object stored in the map - lookup_interface can throw persistent_illegal_type + data = (T*)(context.lookup_interface(key)->clone()); + // add this pointer to the set of already seen objects + // do this before restoring the object so that self-referential structures restore correctly + context.pointer_add(magic,data); + // now restore the contents using the object's method + data->restore(context); + } + } + catch (const persistent_illegal_type& exception) + { + // convert this to a simpler dump failed exception + throw persistent_restore_failed(exception.what()); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_list.hpp b/src/stlplus/persistence/persistent_list.hpp new file mode 100644 index 0000000..b95d767 --- /dev/null +++ b/src/stlplus/persistence/persistent_list.hpp @@ -0,0 +1,32 @@ +#ifndef STLPLUS_PERSISTENT_LIST +#define STLPLUS_PERSISTENT_LIST +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL list + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_list(dump_context&, const std::list& data, D dump_fn) throw(persistent_dump_failed); + + template + void restore_list(restore_context&, std::list& data, R restore_fn) throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_list.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_list.tpp b/src/stlplus/persistence/persistent_list.tpp new file mode 100644 index 0000000..aaf696f --- /dev/null +++ b/src/stlplus/persistence/persistent_list.tpp @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_list(dump_context& context, const std::list& data, D dump_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (typename std::list::const_iterator i = data.begin(); i != data.end(); i++) + dump_fn(context,*i); + } + + template + void restore_list(restore_context& context, std::list& data, R restore_fn) + throw(persistent_restore_failed) + { + data.clear(); + unsigned size = 0; + restore_unsigned(context,size); + for (unsigned i = 0; i < size; i++) + { + data.push_back(T()); + restore_fn(context,data.back()); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_map.hpp b/src/stlplus/persistence/persistent_map.hpp new file mode 100644 index 0000000..4f9449f --- /dev/null +++ b/src/stlplus/persistence/persistent_map.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PERSISTENT_MAP +#define STLPLUS_PERSISTENT_MAP +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence for STL map + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_map(dump_context&, const std::map& data, DK key_dump_fn, DT val_dump_fn) + throw(persistent_dump_failed); + + template + void restore_map(restore_context&, std::map& data, RK key_restore_fn, RT val_restore_fn) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_map.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_map.tpp b/src/stlplus/persistence/persistent_map.tpp new file mode 100644 index 0000000..b1af42c --- /dev/null +++ b/src/stlplus/persistence/persistent_map.tpp @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_map(dump_context& context, const std::map& data, DK key_fn, DT val_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (typename std::map::const_iterator i = data.begin(); i != data.end(); i++) + { + key_fn(context,i->first); + val_fn(context,i->second); + } + } + + template + void restore_map(restore_context& context, std::map& data, RK key_fn, RT val_fn) + throw(persistent_restore_failed) + { + data.clear(); + unsigned size = 0; + restore_unsigned(context,size); + for (unsigned j = 0; j < size; j++) + { + K key; + key_fn(context,key); + val_fn(context,data[key]); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_matrix.hpp b/src/stlplus/persistence/persistent_matrix.hpp new file mode 100644 index 0000000..2f727a2 --- /dev/null +++ b/src/stlplus/persistence/persistent_matrix.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PERSISTENT_MATRIX +#define STLPLUS_PERSISTENT_MATRIX +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STLplus matrix + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "matrix.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_matrix(dump_context&, const matrix& data, DT dump_fn) + throw(persistent_dump_failed); + + template + void restore_matrix(restore_context&, matrix& data, RT restore_fn) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_matrix.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_matrix.tpp b/src/stlplus/persistence/persistent_matrix.tpp new file mode 100644 index 0000000..ac81f85 --- /dev/null +++ b/src/stlplus/persistence/persistent_matrix.tpp @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_matrix(dump_context& context, const matrix& data, + DT dump_fn) + throw(persistent_dump_failed) + { + unsigned rows = data.rows(); + unsigned cols = data.columns(); + dump_unsigned(context, rows); + dump_unsigned(context, cols); + for (unsigned r = 0; r < rows; r++) + for (unsigned c = 0; c < cols; c++) + dump_fn(context, data(r,c)); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void restore_matrix(restore_context& context, matrix& data, + RT restore_fn) + throw(persistent_restore_failed) + { + unsigned rows = 0; + restore_unsigned(context, rows); + unsigned cols = 0; + restore_unsigned(context, cols); + data.resize(rows,cols); + for (unsigned r = 0; r < rows; r++) + for (unsigned c = 0; c < cols; c++) + restore_fn(context, data(r,c)); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_multimap.hpp b/src/stlplus/persistence/persistent_multimap.hpp new file mode 100644 index 0000000..0ad0716 --- /dev/null +++ b/src/stlplus/persistence/persistent_multimap.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PERSISTENT_MULTIMAP +#define STLPLUS_PERSISTENT_MULTIMAP +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL multimap + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_multimap(dump_context&, const std::multimap& data, DK key_dump_fn, DT val_dump_fn) + throw(persistent_dump_failed); + + template + void restore_multimap(restore_context&, std::multimap& data, RK key_restore_fn, RT val_restore_fn) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_multimap.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_multimap.tpp b/src/stlplus/persistence/persistent_multimap.tpp new file mode 100644 index 0000000..5f96a3d --- /dev/null +++ b/src/stlplus/persistence/persistent_multimap.tpp @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_multimap(dump_context& context, const std::multimap& data, DK key_fn, DT val_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (typename std::multimap::const_iterator i = data.begin(); i != data.end(); i++) + { + key_fn(context,i->first); + val_fn(context,i->second); + } + } + + template + void restore_multimap(restore_context& context, std::multimap& data, RK key_fn, RT val_fn) + throw(persistent_restore_failed) + { + data.clear(); + unsigned size = 0; + restore_unsigned(context,size); + for (unsigned j = 0; j < size; j++) + { + K key; + key_fn(context,key); + typename std::multimap::iterator v = data.insert(std::pair(key,T())); + val_fn(context,v->second); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_multiset.hpp b/src/stlplus/persistence/persistent_multiset.hpp new file mode 100644 index 0000000..d451a8e --- /dev/null +++ b/src/stlplus/persistence/persistent_multiset.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PERSISTENT_MULTISET +#define STLPLUS_PERSISTENT_MULTISET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL multiset + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_multiset(dump_context&, const std::multiset& data, D dump_fn) + throw(persistent_dump_failed); + + template + void restore_multiset(restore_context&, std::multiset& data, R restore_fn) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_multiset.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_multiset.tpp b/src/stlplus/persistence/persistent_multiset.tpp new file mode 100644 index 0000000..0a31353 --- /dev/null +++ b/src/stlplus/persistence/persistent_multiset.tpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_multiset(dump_context& context, const std::multiset& data, D dump_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (typename std::multiset::const_iterator i = data.begin(); i != data.end(); i++) + dump_fn(context,*i); + } + + template + void restore_multiset(restore_context& context, std::multiset& data, R restore_fn) + throw(persistent_restore_failed) + { + data.clear(); + unsigned size = 0; + restore_unsigned(context,size); + typename std::multiset::iterator i = data.begin(); + for (unsigned j = 0; j < size; j++) + { + K key; + restore_fn(context,key); + // inserting using an iterator is O(n) rather than O(n*log(n)) + i = data.insert(i, key); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_ntree.hpp b/src/stlplus/persistence/persistent_ntree.hpp new file mode 100644 index 0000000..635da50 --- /dev/null +++ b/src/stlplus/persistence/persistent_ntree.hpp @@ -0,0 +1,66 @@ +#ifndef STLPLUS_PERSISTENT_NTREE +#define STLPLUS_PERSISTENT_NTREE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STLplus ntree + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "ntree.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + // ntree + + template + void dump_ntree(dump_context&, const ntree& data, D dump_fn) + throw(persistent_dump_failed); + + template + void restore_ntree(restore_context&, ntree& data, R restore_fn) + throw(persistent_restore_failed); + + // iterator + + template + void dump_ntree_iterator(dump_context&, const ntree_iterator&) + throw(persistent_dump_failed); + + template + void restore_ntree_iterator(restore_context&, ntree_iterator&) + throw(persistent_restore_failed); + + // prefix iterator + + template + void dump_ntree_prefix_iterator(dump_context&, const ntree_prefix_iterator&) + throw(persistent_dump_failed); + + template + void restore_ntree_prefix_iterator(restore_context&, ntree_prefix_iterator&) + throw(persistent_restore_failed); + + // postfix iterator + + template + void dump_ntree_postfix_iterator(dump_context&, const ntree_postfix_iterator&) + throw(persistent_dump_failed); + + template + void restore_ntree_postfix_iterator(restore_context&, ntree_postfix_iterator&) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_ntree.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_ntree.tpp b/src/stlplus/persistence/persistent_ntree.tpp new file mode 100644 index 0000000..d50ba17 --- /dev/null +++ b/src/stlplus/persistence/persistent_ntree.tpp @@ -0,0 +1,174 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_bool.hpp" +#include "persistent_int.hpp" +#include "persistent_xref.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_ntree_r(dump_context& context, + const ntree& tree, + const TYPENAME ntree::const_iterator& node, + D dump_fn) + throw(persistent_dump_failed) + { + // the magic key of the ntree_node is dumped as well as the contents - this is used in iterator persistence + std::pair node_mapping = context.pointer_map(node.node()); + if (node_mapping.first) throw persistent_dump_failed("ntree: already dumped this node"); + dump_unsigned(context,node_mapping.second); + // now dump the contents + dump_fn(context,*node); + // dump the number of children + unsigned children = tree.children(node); + dump_unsigned(context,children); + // recurse on the children + for (unsigned i = 0; i < children; i++) + dump_ntree_r(context,tree,tree.child(node,i),dump_fn); + } + + template + void dump_ntree(dump_context& context, + const ntree& tree, + D dump_fn) + throw(persistent_dump_failed) + { + // dump a magic key to the address of the tree for use in persistence of iterators + // and register it as a dumped address + std::pair mapping = context.pointer_map(&tree); + if (mapping.first) throw persistent_dump_failed("ntree: already dumped this tree"); + dump_unsigned(context,mapping.second); + // now dump the tree contents - start with a flag to indicate whether the tree is empty + dump_bool(context, tree.empty()); + // now recursively dump the contents + if (!tree.empty()) + dump_ntree_r(context,tree,tree.root(),dump_fn); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void restore_ntree_r(restore_context& context, + ntree& tree, + const TYPENAME ntree::iterator& node, + R restore_fn) + throw(persistent_restore_failed) + { + // restore the node magic key, check whether it has been used before and add it to the set of known addresses + unsigned node_magic = 0; + restore_unsigned(context,node_magic); + std::pair node_mapping = context.pointer_map(node_magic); + if (node_mapping.first) throw persistent_restore_failed("ntree: restored this tree node already"); + context.pointer_add(node_magic,node.node()); + // now restore the node contents + restore_fn(context,*node); + // restore the number of children + unsigned children = 0; + restore_unsigned(context,children); + // recurse on each child + for (unsigned i = 0; i < children; i++) + { + typename ntree::iterator child = tree.insert(node,i,T()); + restore_ntree_r(context,tree,child,restore_fn); + } + } + + template + void restore_ntree(restore_context& context, + ntree& tree, + R restore_fn) + throw(persistent_restore_failed) + { + tree.erase(); + // restore the tree's magic key and map it onto the tree's address + // this is used in the persistence of iterators + unsigned magic = 0; + restore_unsigned(context,magic); + context.pointer_add(magic,&tree); + // now restore the contents + bool empty = true; + restore_bool(context, empty); + if (!empty) + { + typename ntree::iterator node = tree.insert(T()); + restore_ntree_r(context,tree,node,restore_fn); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_ntree_iterator(dump_context& context, + const ntree_iterator& data) + throw(persistent_dump_failed) + { + data.assert_valid(); + dump_xref(context,data.owner()); + dump_xref(context,data.node()); + } + + template + void restore_ntree_iterator(restore_context& context, + ntree_iterator& data) + throw(persistent_restore_failed) + { + const ntree* owner = 0; + ntree_node* node = 0; + restore_xref(context,owner); + restore_xref(context,node); + data = ntree_iterator(node->m_master); + data.assert_valid(owner); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_ntree_prefix_iterator(dump_context& context, + const ntree_prefix_iterator& data) + throw(persistent_dump_failed) + { + dump_ntree_iterator(context,data.iterator()); + } + + template + void restore_ntree_prefix_iterator(restore_context& context, + ntree_prefix_iterator& data) + throw(persistent_restore_failed) + { + ntree_iterator iterator; + restore_ntree_iterator(context,iterator); + data = ntree_prefix_iterator(iterator); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_ntree_postfix_iterator(dump_context& context, + const ntree_postfix_iterator& data) + throw(persistent_dump_failed) + { + dump_ntree_iterator(context,data.iterator()); + } + + template + void restore_ntree_postfix_iterator(restore_context& context, + ntree_postfix_iterator& data) + throw(persistent_restore_failed) + { + ntree_iterator iterator; + restore_ntree_iterator(context,iterator); + data = ntree_postfix_iterator(iterator); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_pair.hpp b/src/stlplus/persistence/persistent_pair.hpp new file mode 100644 index 0000000..7d23107 --- /dev/null +++ b/src/stlplus/persistence/persistent_pair.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PERSISTENT_PAIR +#define STLPLUS_PERSISTENT_PAIR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL pair + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_pair(dump_context&, const std::pair& data, D1 dump_fn1, D2 dump_fn2) + throw(persistent_dump_failed); + + template + void restore_pair(restore_context&, std::pair& data, R1 restore_fn1, R2 restore_fn2) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_pair.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_pair.tpp b/src/stlplus/persistence/persistent_pair.tpp new file mode 100644 index 0000000..aa118cf --- /dev/null +++ b/src/stlplus/persistence/persistent_pair.tpp @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_pair.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_pair(dump_context& context, const std::pair& data, D1 dump_fn1, D2 dump_fn2) + throw(persistent_dump_failed) + { + dump_fn1(context,data.first); + dump_fn2(context,data.second); + } + + template + void restore_pair(restore_context& context, std::pair& data, R1 restore_fn1, R2 restore_fn2) + throw(persistent_restore_failed) + { + restore_fn1(context,data.first); + restore_fn2(context,data.second); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_pointer.hpp b/src/stlplus/persistence/persistent_pointer.hpp new file mode 100644 index 0000000..02d0951 --- /dev/null +++ b/src/stlplus/persistence/persistent_pointer.hpp @@ -0,0 +1,47 @@ +#ifndef STLPLUS_PERSISTENT_POINTER +#define STLPLUS_PERSISTENT_POINTER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence for pointers to persistent objects + +// Warning! The pointer must be a dynamically-allocated type, since the +// implementation uses new/delete + +// Multiple pointers to the same object *will* be restored as multiple pointers +// to the same object. The object is dumped only the first time it is +// encountered along with a "magic key". Subsequent pointers to the same object +// cause only the magic key to be dumped. On restore, the object is only +// restored once and the magic keys are matched up so that the other pointers +// now point to the restored object. + +// Supports null pointers too! If the data field to restore is null and the +// file format non-null, allocates a new T(). If the data field is non-null and +// the file format is null, deletes it and sets it null + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_pointer(dump_context&, const T* const data, D dump_fn) + throw(persistent_dump_failed); + + template + void restore_pointer(restore_context&, T*& data, R restore_fn) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_pointer.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_pointer.tpp b/src/stlplus/persistence/persistent_pointer.tpp new file mode 100644 index 0000000..c760b17 --- /dev/null +++ b/src/stlplus/persistence/persistent_pointer.tpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// format: magic_key [ data ] + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_pointer(dump_context& context, const T* const data, D dump_fn) + throw(persistent_dump_failed) + { + // register the address and get the magic key for it + std::pair mapping = context.pointer_map(data); + dump_unsigned(context,mapping.second); + // if the address is null, then that is all that we need to do + // however, if it is non-null and this is the first sight of the address, dump the contents + // note that the address is mapped before it is dumped so that self-referential structures dump correctly + if (data && !mapping.first) + dump_fn(context,*data); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void restore_pointer(restore_context& context, T*& data, R restore_fn) + throw(persistent_restore_failed) + { + if (data) + { + delete data; + data = 0; + } + // get the magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // now lookup the magic key to see if this pointer has already been restored + // null pointers are always flagged as already restored + std::pair address = context.pointer_map(magic); + if (address.first) + { + // seen before, so simply assign the old address + data = (T*)address.second; + } + else + { + // this pointer has never been seen before and is non-null + data = new T(); + // add this pointer to the set of already seen objects + // do this before restoring the object so that self-referential structures restore correctly + context.pointer_add(magic,data); + // now restore it + restore_fn(context,*data); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_pointers.hpp b/src/stlplus/persistence/persistent_pointers.hpp new file mode 100644 index 0000000..7e84759 --- /dev/null +++ b/src/stlplus/persistence/persistent_pointers.hpp @@ -0,0 +1,21 @@ +#ifndef STLPLUS_PERSISTENT_POINTERS +#define STLPLUS_PERSISTENT_POINTERS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of all pointer types + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +#include "persistent_pointer.hpp" +#include "persistent_xref.hpp" +#include "persistent_callback.hpp" +#include "persistent_interface.hpp" + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_set.hpp b/src/stlplus/persistence/persistent_set.hpp new file mode 100644 index 0000000..a2f8c7a --- /dev/null +++ b/src/stlplus/persistence/persistent_set.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PERSISTENT_SET +#define STLPLUS_PERSISTENT_SET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Set of persistence routines for the STL classes + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_set(dump_context&, const std::set& data, D dump_fn) + throw(persistent_dump_failed); + + template + void restore_set(restore_context&, std::set& data, R restore_fn) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_set.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_set.tpp b/src/stlplus/persistence/persistent_set.tpp new file mode 100644 index 0000000..007d068 --- /dev/null +++ b/src/stlplus/persistence/persistent_set.tpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_set(dump_context& context, const std::set& data, D dump_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (typename std::set::const_iterator i = data.begin(); i != data.end(); i++) + dump_fn(context,*i); + } + + template + void restore_set(restore_context& context, std::set& data, R restore_fn) + throw(persistent_restore_failed) + { + data.clear(); + unsigned size = 0; + restore_unsigned(context,size); + typename std::set::iterator i = data.begin(); + for (unsigned j = 0; j < size; j++) + { + K key; + restore_fn(context,key); + // inserting using an iterator is O(n) rather than O(n*log(n)) + i = data.insert(i, key); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_shortcuts.hpp b/src/stlplus/persistence/persistent_shortcuts.hpp new file mode 100644 index 0000000..beb54d0 --- /dev/null +++ b/src/stlplus/persistence/persistent_shortcuts.hpp @@ -0,0 +1,70 @@ +#ifndef STLPLUS_PERSISTENT_SHORTCUTS +#define STLPLUS_PERSISTENT_SHORTCUTS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Short-cut functions for dumping and restoring to common targets. These do +// the whole dump operation in a single function call. + +// They take as their second template argument a dump or restore functor which +// is then called to perform the dump/restore operation. + +// They use an installer callback function to install any polymorphic type +// handlers required prior to performing the dump/restore. If there are no +// polymorphic types used in the data structure, then the callback can be set +// to null (i.e. 0). + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // arbitrary IOStream device + // must be in binary mode + + template + void dump_to_device(const T& source, std::ostream& result, D dump_fn, dump_context::installer installer) + throw(persistent_dump_failed); + + template + void restore_from_device(std::istream& source, T& result, R restore_fn, restore_context::installer installer) + throw(persistent_restore_failed); + + //////////////////////////////////////////////////////////////////////////////// + // string IO device + + template + void dump_to_string(const T& source, std::string& result, D dump_fn, dump_context::installer installer) + throw(persistent_dump_failed); + + template + void restore_from_string(const std::string& source, T& result, R restore_fn, restore_context::installer installer) + throw(persistent_restore_failed); + + //////////////////////////////////////////////////////////////////////////////// + // file IO device + + template + void dump_to_file(const T& source, const std::string& filename, D dump_fn, dump_context::installer installer) + throw(persistent_dump_failed); + + template + void restore_from_file(const std::string& filename, T& result, R restore_fn, restore_context::installer installer) + throw(persistent_restore_failed); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_shortcuts.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_shortcuts.tpp b/src/stlplus/persistence/persistent_shortcuts.tpp new file mode 100644 index 0000000..9b5aac5 --- /dev/null +++ b/src/stlplus/persistence/persistent_shortcuts.tpp @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_to_device(const T& source, std::ostream& result, D dump_fn, + dump_context::installer installer) + throw(persistent_dump_failed) + { + dump_context context(result); + context.register_all(installer); + dump_fn(context, source); + } + + template + void restore_from_device(std::istream& source, T& result, R restore_fn, + restore_context::installer installer) + throw(persistent_restore_failed) + { + restore_context context(source); + context.register_all(installer); + restore_fn(context, result); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_to_string(const T& source, std::string& result, D dump_fn, + dump_context::installer installer) + throw(persistent_dump_failed) + { + std::ostringstream output(std::ios_base::out | std::ios_base::binary); + dump_to_device(source, output, dump_fn, installer); + result = output.str(); + } + + template + void restore_from_string(const std::string& source, T& result, R restore_fn, + restore_context::installer installer) + throw(persistent_restore_failed) + { + std::istringstream input(source, std::ios_base::in | std::ios_base::binary); + restore_from_device(input, result, restore_fn, installer); + } + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_to_file(const T& source, const std::string& filename, D dump_fn, + dump_context::installer installer) + throw(persistent_dump_failed) + { + std::ofstream output(filename.c_str(), std::ios_base::out | std::ios_base::binary); + dump_to_device(source, output, dump_fn, installer); + } + + template + void restore_from_file(const std::string& filename, T& result, R restore_fn, + restore_context::installer installer) + throw(persistent_restore_failed) + { + std::ifstream input(filename.c_str(), std::ios_base::in | std::ios_base::binary); + restore_from_device(input, result, restore_fn, installer); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_simple_ptr.hpp b/src/stlplus/persistence/persistent_simple_ptr.hpp new file mode 100644 index 0000000..ccf4260 --- /dev/null +++ b/src/stlplus/persistence/persistent_simple_ptr.hpp @@ -0,0 +1,58 @@ +#ifndef STLPLUS_PERSISTENT_SIMPLE_PTR +#define STLPLUS_PERSISTENT_SIMPLE_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STLplus simple_ptr + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "simple_ptr.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + // simple_ptr - uses dump/restore_pointer on the contents + + template + void dump_simple_ptr(dump_context&, const simple_ptr& data, DE dump_element) + throw(persistent_dump_failed); + + template + void restore_simple_ptr(restore_context&, simple_ptr& data, RE restore_element) + throw(persistent_restore_failed); + + // simple_ptr_clone using the polymorphic callback approach - uses dump/restore_callback on the contents + + template + void dump_simple_ptr_clone_callback(dump_context&, const simple_ptr_clone& data) + throw(persistent_dump_failed); + + template + void restore_simple_ptr_clone_callback(restore_context&, simple_ptr_clone& data) + throw(persistent_restore_failed); + + // simple_ptr_clone using the interface approach - uses dump/restore_interface on the contents + + template + void dump_simple_ptr_clone_interface(dump_context&, const simple_ptr_clone& data) + throw(persistent_dump_failed); + + template + void restore_simple_ptr_clone_interface(restore_context&, simple_ptr_clone& data) + throw(persistent_restore_failed); + + // simple_ptr_nocopy is not made persistent because if it is uncopyable, it must be undumpable + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_simple_ptr.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_simple_ptr.tpp b/src/stlplus/persistence/persistent_simple_ptr.tpp new file mode 100644 index 0000000..36f88bd --- /dev/null +++ b/src/stlplus/persistence/persistent_simple_ptr.tpp @@ -0,0 +1,144 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" +#include "persistent_pointer.hpp" +#include "persistent_callback.hpp" +#include "persistent_interface.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_simple_ptr(dump_context& context, const simple_ptr& data, + DE dump_element) + throw(persistent_dump_failed) + { + // Many smart pointers can point to the same object. + // I could have used the address of the object to differentiate, + // but that would not have differentiated between different null smart pointers + // so I use the address of the count to differentiate between different objects. + // get a magic key for the substructure - this also returns a flag saying whether its been seen before + std::pair mapping = context.pointer_map(data._count()); + // dump the magic key for the count + dump_unsigned(context,mapping.second); + // dump the contents always - this is because I need to rely on the pointer routines dumping a second magic key + // use the existing routines for ordinary pointers to dump the contents + dump_pointer(context,data._pointer(),dump_element); + } + + template + void restore_simple_ptr(restore_context& context, simple_ptr& data, + RE restore_element) + throw(persistent_restore_failed) + { + // get the old counter magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // lookup this magic number to see if we have seen this already + std::pair mapping = context.pointer_map(magic); + if (mapping.first) + { + // this holder has already been restored + // now restore the object and rely on the pointer routines to return the existing object + T* value = 0; + restore_pointer(context,value,restore_element); + // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it + data._make_alias(value, (unsigned*)mapping.second); + } + else + { + // this is the first contact with this holder + // make sure this smart pointer is unique to prevent side-effects + data.clear_unique(); + // map the magic key onto this structure's holder + // do this before restoring the object so that self-referential structures restore correctly + context.pointer_add(magic,data._count()); + // now restore the object + T* value = 0; + restore_pointer(context,value,restore_element); + // and add it to the pointer + data.set(value); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // simple_ptr_clone using callbacks + + template + void dump_simple_ptr_clone_callback(dump_context& context, const simple_ptr_clone& data) + throw(persistent_dump_failed) + { + std::pair mapping = context.pointer_map(data._count()); + dump_unsigned(context,mapping.second); + dump_callback(context,data._pointer()); + } + + template + void restore_simple_ptr_clone_callback(restore_context& context, simple_ptr_clone& data) + throw(persistent_restore_failed) + { + unsigned magic = 0; + restore_unsigned(context,magic); + std::pair mapping = context.pointer_map(magic); + if (mapping.first) + { + T* value = 0; + restore_callback(context,value); + data._make_alias(value, (unsigned*)mapping.second); + } + else + { + data.clear_unique(); + context.pointer_add(magic,data._count()); + T* value = 0; + restore_callback(context,value); + data.set(value); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // simple_ptr_clone using interface + + template + void dump_simple_ptr_clone_interface(dump_context& context, const simple_ptr_clone& data) + throw(persistent_dump_failed) + { + std::pair mapping = context.pointer_map(data._count()); + dump_unsigned(context,mapping.second); + dump_interface(context,data._pointer()); + } + + template + void restore_simple_ptr_clone_interface(restore_context& context, simple_ptr_clone& data) + throw(persistent_restore_failed) + { + unsigned magic = 0; + restore_unsigned(context,magic); + std::pair mapping = context.pointer_map(magic); + if (mapping.first) + { + T* value = 0; + restore_interface(context,value); + data._make_alias(value, (unsigned*)mapping.second); + } + else + { + data.clear_unique(); + context.pointer_add(magic,data._count()); + T* value = 0; + restore_interface(context,value); + data.set(value); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_smart_ptr.hpp b/src/stlplus/persistence/persistent_smart_ptr.hpp new file mode 100644 index 0000000..5f37725 --- /dev/null +++ b/src/stlplus/persistence/persistent_smart_ptr.hpp @@ -0,0 +1,58 @@ +#ifndef STLPLUS_PERSISTENT_SMART_PTR +#define STLPLUS_PERSISTENT_SMART_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STLplus smart_ptr + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "smart_ptr.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + // smart_ptr - uses dump/restore_pointer on the contents + + template + void dump_smart_ptr(dump_context&, const smart_ptr& data, DE dump_element) + throw(persistent_dump_failed); + + template + void restore_smart_ptr(restore_context&, smart_ptr& data, RE restore_element) + throw(persistent_restore_failed); + + // smart_ptr_clone using the polymorphic callback approach - uses dump/restore_callback on the contents + + template + void dump_smart_ptr_clone_callback(dump_context&, const smart_ptr_clone& data) + throw(persistent_dump_failed); + + template + void restore_smart_ptr_clone_callback(restore_context&, smart_ptr_clone& data) + throw(persistent_restore_failed); + + // smart_ptr_clone using the interface approach - uses dump/restore_interface on the contents + + template + void dump_smart_ptr_clone_interface(dump_context&, const smart_ptr_clone& data) + throw(persistent_dump_failed); + + template + void restore_smart_ptr_clone_interface(restore_context&, smart_ptr_clone& data) + throw(persistent_restore_failed); + + // smart_ptr_nocopy is not made persistent because if it is uncopyable, it must be undumpable + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_smart_ptr.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_smart_ptr.tpp b/src/stlplus/persistence/persistent_smart_ptr.tpp new file mode 100644 index 0000000..2ce5315 --- /dev/null +++ b/src/stlplus/persistence/persistent_smart_ptr.tpp @@ -0,0 +1,176 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" +#include "persistent_pointer.hpp" +#include "persistent_callback.hpp" +#include "persistent_interface.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_smart_ptr(dump_context& context, const smart_ptr& data, + DE dump_element) + throw(persistent_dump_failed) + { + // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases + // Many smart pointers can point to the same object. + // I could have used the address of the object to differentiate, + // but that would not have differentiated between different null smart pointers + // so I use the address of the substructure to differentiate between different objects. + // get a magic key for the substructure - this also returns a flag saying whether its been seen before + std::pair mapping = context.pointer_map(data._handle()); + // dump the magic key + dump_unsigned(context,mapping.second); + // dump the contents but only if this is the first time this object has been seen + // use the existing routines for ordinary pointers to dump the contents + if (!mapping.first) + dump_pointer(context,data.pointer(),dump_element); + } + + template + void restore_smart_ptr(restore_context& context, smart_ptr& data, + RE restore_element) + throw(persistent_restore_failed) + { + // get the old substructure magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // lookup this magic number to see if we have seen this already + std::pair mapping = context.pointer_map(magic); + if (mapping.first) + { + // this holder has already been restored + // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it + data._make_alias((smart_ptr_holder*)mapping.second); + } + else + { + // this is the first contact with this holder + // make sure this smart pointer is unique to prevent side-effects + data.clear_unique(); + // map the magic key onto this structure's holder + // do this before restoring the object so that self-referential structures restore correctly + context.pointer_add(magic,data._handle()); + // now restore the object + T* value = 0; + restore_pointer(context,value,restore_element); + data.set(value); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // smart_ptr_clone using callbacks + + template + void dump_smart_ptr_clone_callback(dump_context& context, const smart_ptr_clone& data) + throw(persistent_dump_failed) + { + // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases + // Many smart pointers can point to the same object. + // I could have used the address of the object to differentiate, + // but that would not have differentiated between different null smart pointers + // so I use the address of the substructure to differentiate between different objects. + // get a magic key for the substructure - this also returns a flag saying whether its been seen before + std::pair mapping = context.pointer_map(data._handle()); + // dump the magic key + dump_unsigned(context,mapping.second); + // dump the contents but only if this is the first time this object has been seen + // use the existing routines for ordinary pointers to dump the contents + if (!mapping.first) + dump_callback(context,data.pointer()); + } + + template + void restore_smart_ptr_clone_callback(restore_context& context, smart_ptr_clone& data) + throw(persistent_restore_failed) + { + // get the old substructure magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // lookup this magic number to see if we have seen this already + std::pair mapping = context.pointer_map(magic); + if (mapping.first) + { + // this holder has already been restored + // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it + data._make_alias((smart_ptr_holder*)mapping.second); + } + else + { + // this is the first contact with this holder + // make sure this smart pointer is unique to prevent side-effects + data.clear_unique(); + // map the magic key onto this structure's holder + // do this before restoring the object so that self-referential structures restore correctly + context.pointer_add(magic,data._handle()); + // now restore the object + T* value = 0; + restore_callback(context,value); + data.set(value); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // smart_ptr_clone using interface + + template + void dump_smart_ptr_clone_interface(dump_context& context, const smart_ptr_clone& data) + throw(persistent_dump_failed) + { + // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases + // Many smart pointers can point to the same object. + // I could have used the address of the object to differentiate, + // but that would not have differentiated between different null smart pointers + // so I use the address of the substructure to differentiate between different objects. + // get a magic key for the substructure - this also returns a flag saying whether its been seen before + std::pair mapping = context.pointer_map(data._handle()); + // dump the magic key + dump_unsigned(context,mapping.second); + // dump the contents but only if this is the first time this object has been seen + // use the existing routines for ordinary pointers to dump the contents + if (!mapping.first) + dump_interface(context,data.pointer()); + } + + template + void restore_smart_ptr_clone_interface(restore_context& context, smart_ptr_clone& data) + throw(persistent_restore_failed) + { + // get the old substructure magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // lookup this magic number to see if we have seen this already + std::pair mapping = context.pointer_map(magic); + if (mapping.first) + { + // this holder has already been restored + // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it + data._make_alias((smart_ptr_holder*)mapping.second); + } + else + { + // this is the first contact with this holder + // make sure this smart pointer is unique to prevent side-effects + data.clear_unique(); + // map the magic key onto this structure's holder + // do this before restoring the object so that self-referential structures restore correctly + context.pointer_add(magic,data._handle()); + // now restore the object + T* value = 0; + restore_interface(context,value); + data.set(value); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_stl.hpp b/src/stlplus/persistence/persistent_stl.hpp new file mode 100644 index 0000000..bc2f32b --- /dev/null +++ b/src/stlplus/persistence/persistent_stl.hpp @@ -0,0 +1,26 @@ +#ifndef STLPLUS_PERSISTENT_STL +#define STLPLUS_PERSISTENT_STL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Set of persistence routines for the STL classes + +//////////////////////////////////////////////////////////////////////////////// + +#include "persistent_bitset.hpp" +#include "persistent_complex.hpp" +#include "persistent_deque.hpp" +#include "persistent_list.hpp" +#include "persistent_map.hpp" +#include "persistent_multimap.hpp" +#include "persistent_multiset.hpp" +#include "persistent_pair.hpp" +#include "persistent_set.hpp" +#include "persistent_string.hpp" +#include "persistent_vector.hpp" + +#endif diff --git a/src/stlplus/persistence/persistent_stlplus.hpp b/src/stlplus/persistence/persistent_stlplus.hpp new file mode 100644 index 0000000..a3cb476 --- /dev/null +++ b/src/stlplus/persistence/persistent_stlplus.hpp @@ -0,0 +1,31 @@ +#ifndef STLPLUS_PERSISTENT_STLPLUS +#define STLPLUS_PERSISTENT_STLPLUS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Set of persistence routines for the STLplus classes + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded to break the dependency on the containers library +#ifndef NO_STLPLUS_CONTAINERS +#include "persistent_digraph.hpp" +#include "persistent_foursome.hpp" +#include "persistent_hash.hpp" +#include "persistent_matrix.hpp" +#include "persistent_ntree.hpp" +#include "persistent_smart_ptr.hpp" +#include "persistent_triple.hpp" +#endif + +// can be excluded to break the dependency on the portability library +#ifndef NO_STLPLUS_INF +#include "persistent_inf.hpp" +#endif + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/persistence/persistent_string.cpp b/src/stlplus/persistence/persistent_string.cpp new file mode 100644 index 0000000..80940d1 --- /dev/null +++ b/src/stlplus/persistence/persistent_string.cpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_string.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +void stlplus::dump_string(stlplus::dump_context& context, const std::string& data) + throw(stlplus::persistent_dump_failed) +{ + stlplus::dump_basic_string(context, data, stlplus::dump_char); +} + +void stlplus::restore_string(stlplus::restore_context& context, std::string& data) + throw(stlplus::persistent_restore_failed) +{ + stlplus::restore_basic_string(context, data, stlplus::restore_char); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_string.hpp b/src/stlplus/persistence/persistent_string.hpp new file mode 100644 index 0000000..ff065c7 --- /dev/null +++ b/src/stlplus/persistence/persistent_string.hpp @@ -0,0 +1,48 @@ +#ifndef STLPLUS_PERSISTENT_STRING +#define STLPLUS_PERSISTENT_STRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence for STL strings + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + // basic_string + + template + void dump_basic_string(dump_context&, const std::basic_string& data, D dump_fn) + throw(persistent_dump_failed); + + template + void restore_basic_string(restore_context&, std::basic_string& data, R restore_fn) + throw(persistent_restore_failed); + + // string + + void dump_string(dump_context&, const std::string& data) + throw(persistent_dump_failed); + + void restore_string(restore_context&, std::string& data) + throw(persistent_restore_failed); + + + // Note: persistence of wstring not supported because it is too weakly defined and messy + // decide on a byte-wide encoding of wide strings (e.g. UTF8) and use the string persistence on that + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_string.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_string.tpp b/src/stlplus/persistence/persistent_string.tpp new file mode 100644 index 0000000..c78f7e8 --- /dev/null +++ b/src/stlplus/persistence/persistent_string.tpp @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // STL strings + + template + void dump_basic_string(dump_context& context, const std::basic_string& data, D dump_fn) + throw(persistent_dump_failed) + { + unsigned size = data.size(); + dump_unsigned(context, size); + for (unsigned i = 0; i < size; i++) + { + charT ch = data[i]; + dump_fn(context,ch); + } + } + + template + void restore_basic_string(restore_context& context, std::basic_string& data, R restore_fn) + throw(persistent_restore_failed) + { + data.erase(); + unsigned size = 0; + restore_unsigned(context, size); + for (unsigned i = 0; i < size; i++) + { + charT ch; + restore_fn(context,ch); + data += ch; + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_triple.hpp b/src/stlplus/persistence/persistent_triple.hpp new file mode 100644 index 0000000..d9e30d6 --- /dev/null +++ b/src/stlplus/persistence/persistent_triple.hpp @@ -0,0 +1,36 @@ +#ifndef STLPLUS_PERSISTENT_TRIPLE +#define STLPLUS_PERSISTENT_TRIPLE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL triple + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include "triple.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_triple(dump_context&, const stlplus::triple& data, + D1 dump_fn1, D2 dump_fn2, D3 dump_fn3) + throw(persistent_dump_failed); + + template + void restore_triple(restore_context&, stlplus::triple& data, + R1 restore_fn1, R2 restore_fn2, R3 restore_fn3) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_triple.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_triple.tpp b/src/stlplus/persistence/persistent_triple.tpp new file mode 100644 index 0000000..9c3c978 --- /dev/null +++ b/src/stlplus/persistence/persistent_triple.tpp @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_triple(dump_context& context, const triple& data, + D1 dump_fn1, D2 dump_fn2, D3 dump_fn3) + throw(persistent_dump_failed) + { + dump_fn1(context,data.first); + dump_fn2(context,data.second); + dump_fn3(context,data.third); + } + + template + void restore_triple(restore_context& context, triple& data, + R1 restore_fn1, R2 restore_fn2, R3 restore_fn3) + throw(persistent_restore_failed) + { + restore_fn1(context,data.first); + restore_fn2(context,data.second); + restore_fn3(context,data.third); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_vector.cpp b/src/stlplus/persistence/persistent_vector.cpp new file mode 100644 index 0000000..b983d37 --- /dev/null +++ b/src/stlplus/persistence/persistent_vector.cpp @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_vector.hpp" + +//////////////////////////////////////////////////////////////////////////////// +// specialisation for a vector of bool which has a different implementation to a vector of anything else + +void stlplus::dump_vector_bool(stlplus::dump_context& context, const std::vector& data) + throw(stlplus::persistent_dump_failed) +{ + stlplus::dump_unsigned(context,data.size()); + unsigned size = data.size(); + unsigned bytes = ((size + 7) / 8); + for (unsigned b = 0; b < bytes; b++) + { + unsigned char byte = 0; + unsigned char mask = 1; + for (unsigned e = 0; e < 8; e++) + { + unsigned i = b*8 + e; + if (i >= size) break; + if (data[i]) byte |= mask; + mask <<= 1; + } + context.put(byte); + } +} + +void stlplus::restore_vector_bool(stlplus::restore_context& context, std::vector& data) + throw(stlplus::persistent_restore_failed) +{ + unsigned size = 0; + stlplus::restore_unsigned(context,size); + data.resize(size); + unsigned bytes = ((size + 7) / 8); + for (unsigned b = 0; b < bytes; b++) + { + unsigned char byte = context.get(); + unsigned char mask = 1; + for (unsigned e = 0; e < 8; e++) + { + unsigned i = b*8 + e; + if (i >= size) break; + data[i] = ((byte & mask) != 0); + mask <<= 1; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_vector.hpp b/src/stlplus/persistence/persistent_vector.hpp new file mode 100644 index 0000000..fd108aa --- /dev/null +++ b/src/stlplus/persistence/persistent_vector.hpp @@ -0,0 +1,44 @@ +#ifndef STLPLUS_PERSISTENT_VECTOR +#define STLPLUS_PERSISTENT_VECTOR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence of STL vector + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + // vector + + template + void dump_vector(dump_context&, const std::vector& data, D dump_fn) + throw(persistent_dump_failed); + + template + void restore_vector(restore_context&, std::vector& data, R restore_fn) + throw(persistent_restore_failed); + + // specialism for vector + + void dump_vector_bool(dump_context&, const std::vector& data) + throw(persistent_dump_failed); + + void restore_vector_bool(restore_context&, std::vector& data) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_vector.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_vector.tpp b/src/stlplus/persistence/persistent_vector.tpp new file mode 100644 index 0000000..4ad69b6 --- /dev/null +++ b/src/stlplus/persistence/persistent_vector.tpp @@ -0,0 +1,39 @@ + +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_vector(dump_context& context, const std::vector& data, D dump_fn) + throw(persistent_dump_failed) + { + dump_unsigned(context,data.size()); + for (unsigned i = 0; i < data.size(); i++) + dump_fn(context,data[i]); + } + + template + void restore_vector(restore_context& context, std::vector& data, R restore_fn) + throw(persistent_restore_failed) + { + unsigned size = 0; + restore_unsigned(context,size); + data.resize(size); + for (unsigned i = 0; i < size; i++) + restore_fn(context,data[i]); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_xref.hpp b/src/stlplus/persistence/persistent_xref.hpp new file mode 100644 index 0000000..c41e28d --- /dev/null +++ b/src/stlplus/persistence/persistent_xref.hpp @@ -0,0 +1,48 @@ +#ifndef STLPLUS_PERSISTENT_XREF +#define STLPLUS_PERSISTENT_XREF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Persistence for cross-references to persistent objects + +// A cross-reference is a pointer to an object that has definitely been dumped +// already by one of dump_pointer, dump_interface or dump_callback, i.e. by +// one of the dump routines for pointers to objects. + +// These are typically used in data structures as back-pointers or pointers +// between nodes. + +// For example, you may have a tree with cross links. Dump the tree as the +// primary data structure first, then dump the cross links as cross-references +// afterwards. The whole tree must be dumped before any cross-references to +// ensure that all cross-references are known to the persistence system. + +// These functions will throw an exception if the cross-reference points to +// something not dumped before. + +//////////////////////////////////////////////////////////////////////////////// +#include "persistence_fixes.hpp" +#include "persistent_contexts.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void dump_xref(dump_context&, const T* const data) + throw(persistent_dump_failed); + + template + void restore_xref(restore_context&, T*& data) + throw(persistent_restore_failed); + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#include "persistent_xref.tpp" +#endif diff --git a/src/stlplus/persistence/persistent_xref.tpp b/src/stlplus/persistence/persistent_xref.tpp new file mode 100644 index 0000000..e12639d --- /dev/null +++ b/src/stlplus/persistence/persistent_xref.tpp @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// format: magic_key [ data ] + +//////////////////////////////////////////////////////////////////////////////// +#include "persistent_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void dump_xref(dump_context& context, const T* const data) + throw(persistent_dump_failed) + { + // register the address and get the magic key for it + std::pair mapping = context.pointer_map(data); + // if this is the first view of this pointer, simply throw an exception + if (!mapping.first) throw persistent_dump_failed("tried to dump a cross-reference not seen before"); + // otherwise, just dump the magic key + dump_unsigned(context,mapping.second); + } + + ////////////////////////////////////////////////////////////////////////////////> + + template + void restore_xref(restore_context& context, T*& data) + throw(persistent_restore_failed) + { + // Note: I do not try to delete the old data because this is a cross-reference + // get the magic key + unsigned magic = 0; + restore_unsigned(context,magic); + // now lookup the magic key to see if this pointer has already been restored + // null pointers are always flagged as already restored + std::pair address = context.pointer_map(magic); + // if this is the first view of this pointer, simply throw an exception + if (!address.first) throw persistent_restore_failed("tried to restore a cross-reference not seen before"); + // seen before, so simply assign the old address + data = (T*)address.second; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/build.cpp b/src/stlplus/portability/build.cpp new file mode 100644 index 0000000..6a9cd64 --- /dev/null +++ b/src/stlplus/portability/build.cpp @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "build.hpp" +#include "version.hpp" +#include "dprintf.hpp" +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // report the platform-specific details of this build + + std::string build(void) + { + //////////////////////////////////////////////////////////////////////////////// + // work out the platform + +#ifdef _WIN32 + std::string platform("Windows"); +#else + // at present there are no variations between different Unix platforms so + // they all map onto the generic Unix platform + std::string platform("Generic Unix"); +#endif + + //////////////////////////////////////////////////////////////////////////////// + // work out the compiler + +#if defined __GNUC__ + std::string compiler(dformat("gcc v%s",__VERSION__)); +#elif defined _MSC_VER + std::string compiler(dformat("MSVC v%0.2f",((float)_MSC_VER)/100.0)); +#elif defined __BORLANDC__ + std::string compiler(dformat("Borland v%d.%d",__BORLANDC__/256,__BORLANDC__/16%16)); +#else + std::string compiler("unknown compiler"); +#endif + + //////////////////////////////////////////////////////////////////////////////// + // work out the kind of build + // there are two variants - debug and release + +#ifndef NDEBUG + std::string variant("debug"); +#else + std::string variant("release"); +#endif + + return std::string("STLplus v") + version() + ", " + platform + ", " + compiler + ", " + variant; + } + +//////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/portability/build.hpp b/src/stlplus/portability/build.hpp new file mode 100644 index 0000000..b105f19 --- /dev/null +++ b/src/stlplus/portability/build.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_BUILD +#define STLPLUS_BUILD +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Provides a printable representation of the build characteristics in the form: + +// version, platform, compiler, variant + +// Where +// version is the version of STLplus +// platform is the target operating system +// compiler is the compilation system and version that the function was compiled with +// variant is the kind of build - debug or release + +// Example: +// STLplus version 3.0, Generic Unix, gcc v3.4, debug + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include + +namespace stlplus +{ + + std::string build(void); + +} +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/portability/debug.cpp b/src/stlplus/portability/debug.cpp new file mode 100644 index 0000000..562518e --- /dev/null +++ b/src/stlplus/portability/debug.cpp @@ -0,0 +1,187 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +#include "debug.hpp" +#include "dprintf.hpp" +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + static std::string format(const char* file, int line, const char* function, const char* message) + { + return dformat("%s:%d:%s: assertion failed: %s", + (file ? file : ""), + line, + (function ? function : "") , + (message ? message : "")); + } + + //////////////////////////////////////////////////////////////////////////////// + + assert_failed::assert_failed(const char* file, int line, const char* function, const char* message) + throw() : + std::logic_error(format(file, line, function, message)) + { + } + + assert_failed::~assert_failed(void) throw() + { + } + + //////////////////////////////////////////////////////////////////////////////// + + static unsigned _debug_depth = 0; + static bool _debug_global = false; + static bool _debug_set = false; + static bool _debug_recurse = false; + static bool _debug_read = false; + static char* _debug_match = 0; + static const debug_trace* debug_last = 0; + + void debug_global(const char* file, int line, const char* function, bool state) + { + _debug_global = state; + fprintf(stderr, "%s:%i:[%i]%s ", file, line, _debug_depth, function ? function : ""); + fprintf(stderr, "debug global : %s\n", _debug_global ? "on" : "off"); + } + + void debug_assert_fail(const char* file, int line, const char* function, const char* test) + throw(assert_failed) + { + fprintf(stderr, "%s:%i:[%i]%s: assertion failed: %s\n", + file, line, _debug_depth, function ? function : "", test); + if (debug_last) debug_last->stackdump(); + throw assert_failed(file, line, function, test); + } + + //////////////////////////////////////////////////////////////////////////////// + + debug_trace::debug_trace(const char* f, int l, const char* fn) : + m_file(f), m_line(l), m_function(fn ? fn : ""), + m_depth(0), m_last(debug_last), m_dbg(false), m_old(false) + { + if (!_debug_read) + { + _debug_match = getenv("DEBUG"); + _debug_recurse = getenv("DEBUG_LOCAL") == 0; + _debug_read = true; + } + m_dbg = _debug_set || (_debug_match && (!_debug_match[0] || (strcmp(_debug_match, m_file) == 0))); + m_old = _debug_set; + if (m_dbg && _debug_recurse) + _debug_set = true; + m_depth = ++_debug_depth; + debug_last = this; + if (debug()) report(std::string("entering ") + (m_function ? m_function : "")); + } + + debug_trace::~debug_trace(void) + { + if (debug()) report("leaving"); + --_debug_depth; + _debug_set = m_old; + debug_last = m_last; + } + + const char* debug_trace::file(void) const + { + return m_file; + } + + int debug_trace::line(void) const + { + return m_line; + } + + bool debug_trace::debug(void) const + { + return m_dbg || _debug_global; + } + + void debug_trace::debug_on(int l, bool recurse) + { + m_dbg = true; + m_old = _debug_set; + if (recurse) + _debug_set = true; + report(l, std::string("debug on") + (recurse ? " recursive" : "")); + } + + void debug_trace::debug_off(int l) + { + if (debug()) report(l, std::string("debug off")); + m_dbg = false; + _debug_set = m_old; + } + + void debug_trace::prefix(int l) const + { + fprintf(stderr, "%s:%i:[%i]%s ", m_file, l, m_depth, m_function ? m_function : ""); + } + + void debug_trace::do_report(int l, const std::string& message) const + { + prefix(l); + fprintf(stderr, "%s\n", message.c_str()); + fflush(stderr); + } + + void debug_trace::do_report(const std::string& message) const + { + do_report(m_line, message); + } + + void debug_trace::report(int l, const std::string& message) const + { + do_report(l, message); + } + + void debug_trace::report(const std::string& message) const + { + report(m_line, message); + } + + void debug_trace::error(int l, const std::string& message) const + { + do_report(l, "ERROR: " + message); + } + + void debug_trace::error(const std::string& message) const + { + error(m_line, message); + } + + void debug_trace::stackdump(int l, const std::string& message) const + { + do_report(l, message); + stackdump(); + } + + void debug_trace::stackdump(const std::string& message) const + { + stackdump(m_line, message); + } + + void debug_trace::stackdump(void) const + { + for (const debug_trace* item = this; item; item = item->m_last) + item->do_report("...called from here"); + } + + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/debug.hpp b/src/stlplus/portability/debug.hpp new file mode 100644 index 0000000..a7aafd6 --- /dev/null +++ b/src/stlplus/portability/debug.hpp @@ -0,0 +1,128 @@ +#ifndef STLPLUS_DEBUG +#define STLPLUS_DEBUG +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Set of simple debug utilities, all of which are switched off by the +// NDEBUG compiler directive + +//////////////////////////////////////////////////////////////////////////////// + +#include "portability_fixes.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// Problem with missing __FUNCTION__ macro +//////////////////////////////////////////////////////////////////////////////// +// this macro is used in debugging but was missing in Visual Studio prior to version 7 +// it also has a different name in Borland + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#define __FUNCTION__ 0 +#endif + +#ifdef __BORLANDC__ +#define __FUNCTION__ __FUNC__ +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Exception thrown if an assertion fails + +namespace stlplus +{ + + class assert_failed : public std::logic_error + { + public: + assert_failed(const char* file, int line, const char* function, const char* message) throw(); + ~assert_failed(void) throw(); + }; + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// + // The macros used in debugging + +#ifndef NDEBUG + +#define DEBUG_TRACE stlplus::debug_trace stlplus_debug_trace(__FILE__,__LINE__,__FUNCTION__) +#define IF_DEBUG(stmts) {if (stlplus_debug_trace.debug()){stlplus_debug_trace.prefix(__LINE__);stmts;}} +#define DEBUG_REPORT(str) IF_DEBUG(stlplus_debug_trace.report(__LINE__,str)) +#define DEBUG_ERROR(str) stlplus_debug_trace.error(__LINE__,str) +#define DEBUG_STACKDUMP(str) stlplus_debug_trace.stackdump(__LINE__,str) +#define DEBUG_ON stlplus_debug_trace.debug_on(__LINE__,true) +#define DEBUG_ON_LOCAL stlplus_debug_trace.debug_on(__LINE__,false) +#define DEBUG_ON_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,true) +#define DEBUG_OFF_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,false) +#define DEBUG_OFF stlplus_debug_trace.debug_off(__LINE__) +#define DEBUG_ASSERT(test) if (!(test))stlplus::debug_assert_fail(__FILE__,__LINE__,__FUNCTION__,#test) + +#else + +#define DEBUG_TRACE +#define IF_DEBUG(stmts) +#define DEBUG_REPORT(str) +#define DEBUG_ERROR(str) +#define DEBUG_STACKDUMP(str) +#define DEBUG_ON +#define DEBUG_ON_LOCAL +#define DEBUG_ON_GLOBAL +#define DEBUG_OFF_GLOBAL +#define DEBUG_OFF +#define DEBUG_ASSERT(test) + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// infrastructure - don't use directly + +namespace stlplus +{ + + void debug_global(const char* file, int line, const char* function, bool state = true); + void debug_assert_fail(const char* file, int line, const char* function, const char* test) throw(assert_failed); + + class debug_trace + { + public: + debug_trace(const char* f, int l, const char* fn); + ~debug_trace(void); + const char* file(void) const; + int line(void) const; + bool debug(void) const; + void debug_on(int l, bool recurse); + void debug_off(int l); + void prefix(int l) const; + void report(int l, const std::string& message) const; + void report(const std::string& message) const; + void error(int l, const std::string& message) const; + void error(const std::string& message) const; + void stackdump(int l, const std::string& message) const; + void stackdump(const std::string& message) const; + void stackdump(void) const; + + private: + const char* m_file; + int m_line; + const char* m_function; + unsigned m_depth; + const debug_trace* m_last; + bool m_dbg; + bool m_old; + void do_report(int l, const std::string& message) const; + void do_report(const std::string& message) const; + + // make this class uncopyable + debug_trace(const debug_trace&); + debug_trace& operator = (const debug_trace&); + }; + +} // end namespace stlplus + + //////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/portability/dprintf.cpp b/src/stlplus/portability/dprintf.cpp new file mode 100644 index 0000000..f03217e --- /dev/null +++ b/src/stlplus/portability/dprintf.cpp @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Notes: + +// Feb 2007: Rewritten in terms of platform-specific fixes to the +// buffer-overflow problem. Using native functions for this has the added +// benefit of giving access to other features of the C-runtime such as Unicode +// support. + +//////////////////////////////////////////////////////////////////////////////// + +#include "dprintf.hpp" +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + +//////////////////////////////////////////////////////////////////////////////// + + int vdprintf(std::string& formatted, const char* format, va_list args) + { +#ifdef MSWINDOWS + int length = 0; + char* buffer = 0; + for(int buffer_length = 256; ; buffer_length*=2) + { + buffer = (char*)malloc(buffer_length); + if (!buffer) return -1; + length = _vsnprintf(buffer, buffer_length-1, format, args); + if (length >= 0) + { + buffer[length] = 0; + formatted += std::string(buffer); + free(buffer); + break; + } + free(buffer); + } + return length; +#else + char* buffer = 0; + int length = vasprintf(&buffer, format, args); + if (!buffer) return -1; + if (length >= 0) + formatted += std::string(buffer); + free(buffer); + return length; +#endif + } + + int dprintf(std::string& formatted, const char* format, ...) + { + va_list args; + va_start(args, format); + int result = vdprintf(formatted, format, args); + va_end(args); + return result; + } + + std::string vdformat(const char* format, va_list args) throw(std::invalid_argument) + { + std::string formatted; + int length = vdprintf(formatted, format, args); + if (length < 0) throw std::invalid_argument("dprintf"); + return formatted; + } + + std::string dformat(const char* format, ...) throw(std::invalid_argument) + { + std::string formatted; + va_list args; + va_start(args, format); + int length = vdprintf(formatted, format, args); + va_end(args); + if (length < 0) throw std::invalid_argument("dprintf"); + return formatted; + } + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/dprintf.hpp b/src/stlplus/portability/dprintf.hpp new file mode 100644 index 0000000..f7de78b --- /dev/null +++ b/src/stlplus/portability/dprintf.hpp @@ -0,0 +1,122 @@ +#ifndef STLPLUS_DPRINTF +#define STLPLUS_DPRINTF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Provides an sprintf-like function acting on STL strings. The 'd' in dprintf +// stands for "dynamic" in that the string is a dynamic string whereas a char* +// buffer would be static (in size that is, not static in C terms). + +// The obvious solution to the problem of in-memory formatted output is to use +// sprintf(), but this is a potentially dangerous operation since it will quite +// happily charge off the end of the string it is printing to and thereby +// corrupt memory. This kind of buffer-overflow vulnerability is the source of +// most security failures exploited by virus-writers. It means that sprintf +// should *never* be used and should be made obsolete. + +// In any case, using arbitrary-sized fixed-length buffers is not part of any +// quality-orientated design philosophy. + +// Most operating systems now have a safe version of sprintf, but this is +// non-standard. The functions in this file are platform-independent interfaces +// to the underlying safe implementation. + +// I would like to make this set of functions obsolete too, since I believe the +// C runtime should be deprecated in favour of C++ runtime which uses dynamic +// strings and can handle exceptions. However, there is as yet no C++ +// equivalent functionality to some of the string-handling available through +// the printf-like functions, so it has to stay for now. + +// int dprintf (std::string& buffer, const char* format, ...); + +// Formats the message by appending to the std::string buffer according to +// the formatting codes in the format string. The return int is the number +// of characters generated by this call, i.e. the increase in the length of +// the std::string. + +// int vdprintf (std::string& buffer, const char* format, va_list args); + +// As above, but using a pre-initialised va_args argument list. Useful for +// nesting dprintf calls within variable argument functions. + +// std::string dformat (const char* format, ...); + +// Similar to dprintf() above, except the result is formatted into a new +// std::string which is returned by the function. Very useful for inline +// calls within an iostream expression. + +// e.g. cout << "Total: " << dformat("%6i",t) << endl; + +// std::string vdformat (const char* format, va_list); + +// As above, but using a pre-initialised va_args argument list. Useful for nesting +// dformat calls within variable argument functions. + +// The format string supports the following format codes as in the C runtime library: + +// % [ flags ] [ field ] [ . precision ] [ modifier ] [ conversion ] + +// flags: +// - - left justified +// + - print sign for +ve numbers +// ' ' - leading space where + sign would be +// 0 - leading zeros to width of field +// # - alternate format + +// field: +// a numeric argument specifying the field width - default = 0 +// * means take the next va_arg as the field width - if negative then left justify + +// precision: +// a numeric argument the meaning of which depends on the conversion - +// - %s - max characters from a string - default = strlen() +// - %e, %f - decimal places to be displayed - default = 6 +// - %g - significant digits to be displayed - default = 6 +// - all integer conversions - minimum digits to display - default = 0 +// * means take the next va_arg as the field width - if negative then left justify + +// modifier: +// h - short or unsigned short +// l - long or unsigned long +// L - long double + +// conversions: +// d, i - short/int/long as decimal +// u - short/int/long as unsigned decimal +// o - short/int/long as unsigned octal - # adds leading 0 +// x, X - short/int/long as unsigned hexadecimal - # adds leading 0x +// c - char +// s - char* +// f - double/long double as fixed point +// e, E - double/long double as floating point +// g, G - double/long double as fixed point/floating point depending on value +// p - void* as unsigned hexadecimal +// % - literal % +// n - int* as recipient of length of formatted string so far + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + // format by appending to a string and return the increase in length + // if there is an error, return a negative number and leave the string unchanged + int dprintf (std::string& formatted, const char* format, ...); + int vdprintf (std::string& formatted, const char* format, va_list args); + + // format into a new string and return the result + // if there is an error, throw an exception + std::string dformat (const char* format, ...) throw(std::invalid_argument); + std::string vdformat (const char* format, va_list) throw(std::invalid_argument); + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/dynaload.cpp b/src/stlplus/portability/dynaload.cpp new file mode 100644 index 0000000..cdd1848 --- /dev/null +++ b/src/stlplus/portability/dynaload.cpp @@ -0,0 +1,184 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "dynaload.hpp" + +#ifdef MSWINDOWS +#include +#else +#include +#endif + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef MSWINDOWS + +static std::string last_error(void) +{ + // get the last error code - if none, return the empty string + DWORD err = GetLastError(); + if (err == 0) return std::string(); + // get the system message for this error code + char* message; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + 0, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&message, + 0,0); + std::string result = message; + LocalFree(message); + // the error message is for some perverse reason newline terminated - remove this + if (result[result.size()-1] == '\n') + result.erase(result.end()-1); + if (result[result.size()-1] == '\r') + result.erase(result.end()-1); + return result; +} + +#else + +static std::string last_error(void) +{ + return std::string(dlerror()); +} + +#endif + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // library management + + // construct the object but do not load + dynaload::dynaload(void) : m_handle(0) + { + } + + // construct and load + dynaload::dynaload(const std::string& library) : m_handle(0) + { + load(library); + } + + // destroy and unload if loaded + dynaload::~dynaload(void) + { + unload(); + } + + // load the library - return success or fail + bool dynaload::load(const std::string& library) + { +#ifdef MSWINDOWS + m_handle = (void*)LoadLibrary(library.c_str()); +#elif defined(CYGWIN) + m_handle = dlopen(library.c_str(),RTLD_NOW); +#else + std::string full_library = std::string("lib") + library + std::string(".so"); + m_handle = dlopen(full_library.c_str(),RTLD_NOW); +#endif + if (!m_handle) + { + m_error = load_error; + m_text = last_error(); + } + return loaded(); + } + + // unload the library if loaded + bool dynaload::unload(void) + { + if (!loaded()) return false; +#ifdef MSWINDOWS + int status = FreeLibrary((HINSTANCE)m_handle) ? 0 : 1; +#else + int status = dlclose(m_handle); +#endif + if (status != 0) + { + m_error = unload_error; + m_text = last_error(); + } + return status == 0; + } + + // test whether the library is loaded + bool dynaload::loaded(void) const + { + return m_handle != 0; + } + + //////////////////////////////////////////////////////////////////////////// + // symbol management + + // test whether a function is exported by the library + // does not set the error flag if fails + bool dynaload::present(const std::string& name) + { + if (!loaded()) return false; +#ifdef MSWINDOWS + void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str()); +#else + void* fn = dlsym(m_handle,name.c_str()); +#endif + return fn != 0; + } + + // get the function as a generic pointer + void* dynaload::symbol(const std::string& name) + { + if (!loaded()) return 0; +#ifdef MSWINDOWS + void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str()); +#else + void* fn = dlsym(m_handle,name.c_str()); +#endif + if (!fn) + { + m_error = symbol_error; + m_text = last_error(); + } + return fn; + } + + //////////////////////////////////////////////////////////////////////////// + // error management + + // test whether there has been an error + bool dynaload::error(void) const + { + return m_error != no_error; + } + + // clear an error once it has been handled (or ignored) + void dynaload::clear_error(void) + { + m_error = no_error; + m_text = std::string(); + } + + // get the type of the error as indicated by the enum error_t + dynaload::error_t dynaload::error_type(void) const + { + return m_error; + } + + // get the text of the error as provided by the OS + std::string dynaload::error_text(void) const + { + return m_text; + } + + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/dynaload.hpp b/src/stlplus/portability/dynaload.hpp new file mode 100644 index 0000000..dafaf24 --- /dev/null +++ b/src/stlplus/portability/dynaload.hpp @@ -0,0 +1,86 @@ +#ifndef STLPLUS_DYNALOAD +#define STLPLUS_DYNALOAD +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A portable interface to the dynamic loader - i.e. the system for loading +// dynamic libraries or shared libraries during the running of a program, +// rather than by linking + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include + +namespace stlplus +{ + + ////////////////////////////////////////////////////////////////////////////// + // dynaload class manages a dynamic loadable library and unloads it on destruction + + class dynaload + { + public: + + //////////////////////////////////////////////////////////////////////////// + // library management + + // construct the object but do not load + dynaload(void); + + // construct and load + dynaload(const std::string& library); + + // destroy and unload if loaded + ~dynaload(void); + + // load the library - return success or fail + bool load(const std::string& library); + + // unload the library if loaded + bool unload(void); + + // test whether the library is loaded + bool loaded(void) const; + + //////////////////////////////////////////////////////////////////////////// + // symbol management + + // test whether a function is exported by the library + bool present(const std::string& name); + + // get the function as a generic pointer + void* symbol(const std::string& name); + + //////////////////////////////////////////////////////////////////////////// + // error management + + // enum values to indicate type of error + enum error_t {no_error, load_error, unload_error, symbol_error}; + + // test whether there has been an error + bool error(void) const; + + // clear an error once it has been handled (or ignored) + void clear_error(void); + + // get the type of the error as indicated by the enum error_t + error_t error_type(void) const; + + // get the text of the error as provided by the OS + std::string error_text(void) const; + + //////////////////////////////////////////////////////////////////////////// + + private: + void* m_handle; + error_t m_error; + std::string m_text; + }; + +} + +#endif diff --git a/src/stlplus/portability/file_system.cpp b/src/stlplus/portability/file_system.cpp new file mode 100644 index 0000000..983d984 --- /dev/null +++ b/src/stlplus/portability/file_system.cpp @@ -0,0 +1,1058 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// This is a portable interface to the file system. + +// The idea is that you write all file system access code using these functions, +// which are ported to all platforms that we are interested in. Therefore your +// code is inherently portable. + +// Native Windows version: switched on by macro _WIN32 which is defined by VC++/Borland/Mingw compilers +// Unix/Gnu version: default variant, no compiler directives are required but _WIN32 must be absent +// Cygwin/Gnu version: as Unix version but with additional support for Windows drive letters + +//////////////////////////////////////////////////////////////////////////////// +#include "file_system.hpp" +#include "wildcard.hpp" +#include +#include +#include +#include +#include + +#ifdef MSWINDOWS +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + +//////////////////////////////////////////////////////////////////////////////// +// definitions of separators + +#ifdef MSWINDOWS + static const char* separator_set = "\\/"; + static const char preferred_separator = '\\'; +#else + static const char* separator_set = "/"; + static const char preferred_separator = '/'; +#endif + + static bool is_separator (char ch) + { + for (int i = 0; separator_set[i]; i++) + { + if (separator_set[i] == ch) + return true; + } + return false; + } + +//////////////////////////////////////////////////////////////////////////////// +// implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive + +#ifdef MSWINDOWS + + static std::string lowercase(const std::string& val) + { + std::string text = val; + for (unsigned i = 0; i < text.size(); i++) + text[i] = tolower(text[i]); + return text; + } + +#endif + + bool path_compare(const std::string& l, const std::string& r) + { +#ifdef MSWINDOWS + return lowercase(l) == lowercase(r); +#else + return l == r; +#endif + } + +//////////////////////////////////////////////////////////////////////////////// +// Internal data structure used to hold the different parts of a filespec + + class file_specification + { + private: + bool m_relative; // true = relative, false = absolute + std::string m_drive; // drive - drive letter (e.g. "c:") or the path for an UNC (e.g. "\\somewhere") + // empty if not known or on Unix + std::vector m_path; // the subdirectory path to follow from the drive + std::string m_filename; // the filename + public: + file_specification(void) : m_relative(false) {} + ~file_specification(void) {} + + bool initialise_folder(const std::string& spec); + bool initialise_file(const std::string& spec); + bool simplify(void); + bool make_absolute(const std::string& root = folder_current_full()); + bool make_absolute(const file_specification& root); + bool make_relative(const std::string& root = folder_current_full()); + bool make_relative(const file_specification& root); + bool relative(void) const {return m_relative;} + bool absolute(void) const {return !relative();} + void set_relative(void) {m_relative = true;} + void set_absolute(void) {m_relative = false;} + + const std::string& drive(void) const {return m_drive;} + std::string& drive(void) {return m_drive;} + void set_drive(const std::string& drive) {m_drive = drive;} + + const std::vector& path(void) const {return m_path;} + std::vector& path(void) {return m_path;} + void set_path(const std::vector& path) {m_path = path;} + + void add_subpath(const std::string& subpath) {m_path.push_back(subpath);} + unsigned subpath_size(void) const {return m_path.size();} + const std::string& subpath_element(unsigned i) const {return m_path[i];} + void subpath_erase(unsigned i) {m_path.erase(m_path.begin()+i);} + + const std::string& file(void) const {return m_filename;} + std::string& file(void) {return m_filename;} + void set_file(const std::string& file) {m_filename = file;} + + std::string image(void) const; + }; + + bool file_specification::initialise_folder(const std::string& folder_spec) + { + std::string spec = folder_spec; + m_relative = true; + m_drive.erase(); + m_path.clear(); + m_filename.erase(); + unsigned i = 0; +#ifdef MSWINDOWS + // first split off the drive letter or UNC prefix on Windows + if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':') + { + // found a drive letter + i = 2; + m_drive = spec.substr(0, 2); + m_relative = false; + // if there is a drive but no path or a relative path, get the current + // path for this drive and prepend it to the path + if (i == spec.size() || !is_separator(spec[i])) + { + // getdcwd requires the drive number (1..26) not the letter (A..Z) + char path [MAX_PATH+1]; + int drivenum = toupper(m_drive[0]) - 'A' + 1; + if (_getdcwd(drivenum, path, MAX_PATH+1)) + { + // the path includes the drive so we have the drive info twice + // need to prepend this absolute path to the spec such that any remaining relative path is still retained + if (!is_separator(path[strlen(path)-1])) spec.insert(2, 1, preferred_separator); + spec.insert(2, path+2); + } + else + { + // non-existent drive - fill in just the root directory + spec.insert(2, 1, preferred_separator); + } + } + } + else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1])) + { + // found an UNC prefix + i = 2; + // find the end of the prefix by scanning for the next seperator or the end of the spec + while (i < spec.size() && !is_separator(spec[i])) i++; + m_drive = spec.substr(0, i); + m_relative = false; + } +#endif +#ifdef CYGWIN + // first split off the drive letter or UNC prefix on Windows - the Cygwin environment supports these too + if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':') + { + // found a drive letter + i = 2; + m_drive = spec.substr(0, 2); + m_relative = false; + // if there is a drive but no path or a relative path, get the current + // path for this drive and prepend it to the path + if (i == spec.size() || !is_separator(spec[i])) + { + // non-existent drive - fill in just the root directory + spec.insert(2, 1, preferred_separator); + } + } + else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1])) + { + // found an UNC prefix + i = 2; + // find the end of the prefix by scanning for the next seperator or the end of the spec + while (i < spec.size() && !is_separator(spec[i])) i++; + m_drive = spec.substr(0, i); + m_relative = false; + } +#endif + // check whether the path is absolute or relative and discard the leading / if absolute + if (i < spec.size() && is_separator(spec[i])) + { + m_relative = false; + i++; +#ifdef MSWINDOWS + // if there's no drive, fill it in on Windows since absolute paths must have a drive + if (m_drive.empty()) + { + m_drive += (char)(_getdrive() - 1 + 'A'); + m_drive += ':'; + } +#endif + } + // now extract the path elements - note that a trailing / is not significant since /a/b/c/ === /a/b/c + // also note that the leading / has been discarded - all paths are relative + // if absolute() is set, then paths are relative to the drive, else they are relative to the current path + unsigned start = i; + while(i <= spec.size()) + { + if (i == spec.size()) + { + // path element terminated by the end of the string + // discard this element if it is zero length because that represents the trailing / + if (i != start) + m_path.push_back(spec.substr(start, i-start)); + } + else if (is_separator(spec[i])) + { + // path element terminated by a separator + m_path.push_back(spec.substr(start, i-start)); + start = i+1; + } + i++; + } + // TODO - some error handling? + return true; + } + + bool file_specification::initialise_file(const std::string& spec) + { + m_filename.erase(); + // remove last element as the file and then treat the rest as a folder + unsigned i = spec.size(); + while (--i) + { + if (is_separator(spec[i])) + break; +#ifdef MSWINDOWS + // on windoze you can say a:fred.txt so the colon separates the path from the filename + else if (i == 1 && spec[i] == ':') + break; +#endif + } + bool result = initialise_folder(spec.substr(0,i+1)); + m_filename = spec.substr(i+1,spec.size()-i-1); + // TODO - some error handling? + return result; + } + + bool file_specification::simplify(void) + { + // simplify the path by removing unnecessary . and .. entries - Note that zero-length entries are treated like . + for (unsigned i = 0; i < m_path.size(); ) + { + if (m_path[i].empty() || m_path[i].compare(".") == 0) + { + // found . or null + // these both mean do nothing - so simply delete this element + m_path.erase(m_path.begin()+i); + } + else if (m_path[i].compare("..") == 0) + { + // found .. + if (i == 0 && !m_relative) + { + // up from the root does nothing so can be deleted + m_path.erase(m_path.begin()+i); + i++; + } + else if (i == 0 || m_path[i-1].compare("..") == 0) + { + // the first element of a relative path or the previous element is .. then keep it + i++; + } + else + { + // otherwise delete this element and the previous one + m_path.erase(m_path.begin()+i); + m_path.erase(m_path.begin()+i-1); + i--; + } + } + // keep all other elements + else + i++; + } + // TODO - error checking? + return true; + } + + bool file_specification::make_absolute(const std::string& root) + { + // test whether already an absolute path in which case there's nothing to do + if (absolute()) return true; + // now simply call the other version of make_absolute + file_specification rootspec; + rootspec.initialise_folder(root); + return make_absolute(rootspec); + } + + bool file_specification::make_absolute(const file_specification& rootspec) + { + // test whether already an absolute path in which case there's nothing to do + if (absolute()) return true; + // initialise the result with the root and make the root absolute + file_specification result = rootspec; + result.make_absolute(); + // now append this's relative path and filename to the root's absolute path + for (unsigned i = 0; i < subpath_size(); i++) + result.add_subpath(subpath_element(i)); + result.set_file(file()); + // now the result is the absolute path, so transfer it to this + *this = result; + // and simplify to get rid of any unwanted .. or . elements + simplify(); + return true; + } + + bool file_specification::make_relative(const std::string& root) + { + // test whether already an relative path in which case there's nothing to do + if (relative()) return true; + // now simply call the other version of make_relative + file_specification rootspec; + rootspec.initialise_folder(root); + return make_relative(rootspec); + } + + bool file_specification::make_relative(const file_specification& rootspec) + { + // test whether already an relative path in which case there's nothing to do + if (relative()) return true; + // initialise the result with the root and make the root absolute + file_specification absolute_root = rootspec; + absolute_root.make_absolute(); + // now compare elements of the absolute root with elements of this to find the common path + // if the drives are different, no conversion can take place and the result must be absolute, else clear the drive + if (!path_compare(drive(), absolute_root.drive())) return true; + set_drive(""); + // first remove leading elements that are identical to the corresponding element in root + unsigned i = 0; + while(subpath_size() > 0 && + i < absolute_root.subpath_size() && + path_compare(subpath_element(0), absolute_root.subpath_element(i))) + { + subpath_erase(0); + i++; + } + // now add a .. prefix for every element in root that is different from this + while (i < absolute_root.subpath_size()) + { + m_path.insert(m_path.begin(), ".."); + i++; + } + set_relative(); + return true; + } + + std::string file_specification::image(void) const + { + std::string result = m_drive; + if (absolute()) + result += preferred_separator; + if (!m_path.empty()) + { + for (unsigned i = 0; i < m_path.size(); i++) + { + if (i != 0) result += std::string(1,preferred_separator); + result += m_path[i]; + } + } + else if (relative()) + result += '.'; + // add a trailing / to the last directory element + if (result.empty() || !is_separator(result[result.size()-1])) + result += preferred_separator; + if (!m_filename.empty()) + result += m_filename; + return result; + } + +//////////////////////////////////////////////////////////////////////////////// +// classifying functions + +#ifdef MSWINDOWS +// file type tests are not defined for some reason on Windows despite them providing the stat() function! +#define R_OK 4 +#define W_OK 2 +#endif + + bool is_present (const std::string& thing) + { + // strip off any trailing separator because that will cause the stat function to fail + std::string path = thing; + if (!path.empty() && is_separator(path[path.size()-1])) + path.erase(path.size()-1,1); + // now test if this thing exists using the built-in stat function + struct stat buf; + return stat(path.c_str(), &buf) == 0; + } + + bool is_folder (const std::string& thing) + { + // strip off any trailing separator because that will cause the stat function to fail + std::string path = thing; + if (!path.empty() && is_separator(path[path.size()-1])) + path.erase(path.size()-1,1); + // now test if this thing exists using the built-in stat function and if so, is it a folder + struct stat buf; + if (!(stat(path.c_str(), &buf) == 0)) {return false;} + return (buf.st_mode & S_IFDIR) != 0; + } + + bool is_file (const std::string& thing) + { + // strip off any trailing separator because that will cause the stat function to fail + std::string path = thing; + if (!path.empty() && is_separator(path[path.size()-1])) + path.erase(path.size()-1,1); + // now test if this thing exists using the built-in stat function and if so, is it a file + struct stat buf; + if (!(stat(path.c_str(), &buf) == 0)) {return false;} + return (buf.st_mode & S_IFREG) != 0; + } + +//////////////////////////////////////////////////////////////////////////////// +// file functions + + bool file_exists (const std::string& filespec) + { + return is_file(filespec); + } + + bool file_readable (const std::string& filespec) + { + // a file is readable if it exists and can be read + if (!file_exists(filespec)) return false; + return access(filespec.c_str(),R_OK)==0; + } + + bool file_writable (const std::string& filespec) + { + // 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 + if (is_present(filespec)) + { + if (!is_file(filespec)) return false; + return access(filespec.c_str(),W_OK)==0; + } + std::string dir = folder_part(filespec); + if (dir.empty()) dir = "."; + return folder_writable(dir); + } + + size_t file_size (const std::string& filespec) + { + struct stat buf; + if (!(stat(filespec.c_str(), &buf) == 0)) return 0; + return buf.st_size; + } + + bool file_delete (const std::string& filespec) + { + if (!is_file(filespec)) return false; + return remove(filespec.c_str())==0; + } + + bool file_rename (const std::string& old_filespec, const std::string& new_filespec) + { + if (!is_file(old_filespec)) return false; + return rename(old_filespec.c_str(), new_filespec.c_str())==0; + } + + bool file_copy (const std::string& old_filespec, const std::string& new_filespec) + { + if (!is_file(old_filespec)) return false; + // do an exact copy - to do this, use binary mode + bool result = true; + FILE* old_file = fopen(old_filespec.c_str(),"rb"); + FILE* new_file = fopen(new_filespec.c_str(),"wb"); + if (!old_file) + result = false; + else if (!new_file) + result = false; + else + { + for (int byte = getc(old_file); byte != EOF; byte = getc(old_file)) + putc(byte,new_file); + } + if (old_file) fclose(old_file); + if (new_file) fclose(new_file); + return result; + } + + bool file_move (const std::string& old_filespec, const std::string& new_filespec) + { + // try to move the file by renaming - if that fails then do a copy and delete the original + if (file_rename(old_filespec, new_filespec)) + return true; + if (!file_copy(old_filespec, new_filespec)) + return false; + // I'm not sure what to do if the delete fails - is that an error? + // I've made it an error and then delete the copy so that the original state is recovered + if (file_delete(old_filespec)) + return true; + file_delete(new_filespec); + return false; + } + + time_t file_created (const std::string& filespec) + { + struct stat buf; + if (!(stat(filespec.c_str(), &buf) == 0)) return 0; + return buf.st_ctime; + } + + time_t file_modified (const std::string& filespec) + { + struct stat buf; + if (!(stat(filespec.c_str(), &buf) == 0)) return 0; + return buf.st_mtime; + } + + time_t file_accessed (const std::string& filespec) + { + struct stat buf; + if (!(stat(filespec.c_str(), &buf) == 0)) return 0; + return buf.st_atime; + } + + std::string create_filespec (const std::string& directory, const std::string& filename) + { + std::string result = directory; + // if directory is empty then no directory part will be added + // add trailing slash if the directory was specified and does not have a trailing slash + if (!result.empty() && !is_separator(result[result.size()-1])) + result += preferred_separator; + // if filename is null or empty, nothing will be added so the path is then a directory path + result += filename; + return result; + } + + std::string create_filespec (const std::string& directory, const std::string& basename, const std::string& extension) + { + return create_filespec(directory, create_filename(basename, extension)); + } + + std::string create_filename(const std::string& basename, const std::string& extension) + { + std::string name = basename; + // extension is optional - so the dot is also optional + if (!extension.empty()) + { + if (extension[0] != '.') name += '.'; + name += extension; + } + return name; + } + +//////////////////////////////////////////////////////////////////////////////// +// folder functions + + bool folder_create (const std::string& directory) + { +#ifdef MSWINDOWS + return mkdir(directory.c_str()) == 0; +#else + return mkdir(directory.c_str(), 0777) == 0; +#endif + } + + bool folder_exists (const std::string& directory) + { + return is_folder(directory); + } + + bool folder_readable (const std::string& directory) + { + // a folder is readable if it exists and has read access + std::string dir = directory; + if (dir.empty()) dir = "."; + if (!folder_exists(dir)) return false; + return access(dir.c_str(),R_OK)==0; + } + + bool folder_writable (const std::string& directory) + { + // a folder is writable if it exists and has write access + std::string dir = directory; + if (dir.empty()) dir = "."; + if (!folder_exists(dir)) return false; + return access(dir.c_str(),W_OK)==0; + } + + bool folder_delete (const std::string& directory, bool recurse) + { + std::string dir = directory; + if (dir.empty()) dir = "."; + if (!folder_exists(dir)) return false; + bool result = true; + // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself + if (recurse) + { + std::vector subdirectories = folder_subdirectories(dir); + for (std::vector::size_type d = 0; d < subdirectories.size(); ++d) + if (!folder_delete(folder_down(dir,subdirectories[d]),true)) + result = false; + std::vector files = folder_files(dir); + for (std::vector::size_type f = 0; f < files.size(); ++f) + if (!file_delete(create_filespec(dir, files[f]))) + result = false; + } + if (rmdir(dir.c_str())!=0) result = false; + return result; + } + + bool folder_rename (const std::string& old_directory, const std::string& new_directory) + { + if (!folder_exists(old_directory)) return false; + return rename(old_directory.c_str(), new_directory.c_str())==0; + } + + bool folder_empty(const std::string& directory) + { + std::string dir = directory.empty() ? std::string(".") : directory; + bool result = true; +#ifdef MSWINDOWS + std::string wildcard = create_filespec(dir, "*.*"); + long handle = -1; + _finddata_t fileinfo; + for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0)) + { + std::string strentry = fileinfo.name; + if (strentry.compare(".")!=0 && strentry.compare("..")!=0) + { + result = false; + break; + } + } + _findclose(handle); +#else + DIR* d = opendir(dir.c_str()); + if (d) + { + for (dirent* entry = readdir(d); entry; entry = readdir(d)) + { + std::string strentry = entry->d_name; + if (strentry.compare(".")!=0 && strentry.compare("..")!=0) + { + result = false; + break; + } + } + closedir(d); + } +#endif + return result; + } + + bool folder_set_current(const std::string& folder) + { + if (!folder_exists(folder)) + return false; +#ifdef MSWINDOWS + // Windose implementation - this returns non-zero for success + return (SetCurrentDirectoryA(folder.c_str()) != 0); +#else + // Unix implementation - this returns zero for success + return (chdir(folder.c_str()) == 0); +#endif + } + + std::string folder_current (void) + { + return "."; + } + + std::string folder_current_full(void) + { + // It's not clear from the documentation whether the buffer for a path should be one byte longer + // than the maximum path length to allow for the null termination, so I have made it so anyway +#ifdef MSWINDOWS + char abspath [MAX_PATH+1]; + return std::string(_fullpath(abspath, ".", MAX_PATH+1)); +#else + char pathname [MAXPATHLEN+1]; + getcwd(pathname,MAXPATHLEN+1); + return std::string(pathname); +#endif + } + + std::string folder_down (const std::string& directory, const std::string& subdirectory) + { + file_specification spec; + spec.initialise_folder(directory); + spec.add_subpath(subdirectory); + return spec.image(); + } + + std::string folder_up (const std::string& directory, unsigned levels) + { + file_specification spec; + spec.initialise_folder(directory); + for (unsigned i = 0; i < levels; i++) + spec.add_subpath(".."); + spec.simplify(); + return spec.image(); + } + + std::vector folder_subdirectories (const std::string& directory) + { + return folder_wildcard(directory, "*", true, false); + } + + std::vector folder_files (const std::string& directory) + { + return folder_wildcard(directory, "*", false, true); + } + + std::vector folder_all(const std::string& directory) + { + return folder_wildcard(directory, "*", true, true); + } + + std::vector folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files) + { + std::string dir = directory.empty() ? std::string(".") : directory; + std::vector results; +#ifdef MSWINDOWS + std::string wildcard = create_filespec(dir, wild); + long handle = -1; + _finddata_t fileinfo; + for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0)) + { + std::string strentry = fileinfo.name; + if (strentry.compare(".")!=0 && strentry.compare("..")!=0) + if ((subdirs && (fileinfo.attrib & _A_SUBDIR)) || (files && !(fileinfo.attrib & _A_SUBDIR))) + results.push_back(strentry); + } + _findclose(handle); +#else + DIR* d = opendir(dir.c_str()); + if (d) + { + for (dirent* entry = readdir(d); entry; entry = readdir(d)) + { + std::string strentry = entry->d_name; + if (strentry.compare(".")!=0 && strentry.compare("..")!=0) + { + std::string subpath = create_filespec(dir, strentry); + if (((subdirs && is_folder(subpath)) || (files && is_file(subpath))) && (wildcard(wild, strentry))) + results.push_back(strentry); + } + } + closedir(d); + } +#endif + return results; + } + + std::string folder_home (void) + { + if (getenv("HOME")) + return std::string(getenv("HOME")); +#ifdef MSWINDOWS + if (getenv("HOMEDRIVE") || getenv("HOMEPATH")) + return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH")); + return "C:\\"; +#else + if (getenv("USER")) + return folder_down("/home", std::string(getenv("USER"))); + if (getenv("USERNAME")) + return folder_down("/home", std::string(getenv("USERNAME"))); + return ""; +#endif + } + +//////////////////////////////////////////////////////////////////////////////// +// path functions convert between full and relative paths + + bool is_full_path(const std::string& path) + { + file_specification spec; + spec.initialise_folder(path.empty() ? std::string(".") : path); + return spec.absolute(); + } + + bool is_relative_path(const std::string& path) + { + file_specification spec; + spec.initialise_folder(path.empty() ? std::string(".") : path); + return spec.relative(); + } + + static std::string full_path(const std::string& root, const std::string& path) + { + // convert path to a full path using root as the start point for relative paths + // decompose the path and test whether it is already an absolute path, in which case just return it + file_specification spec; + spec.initialise_folder(path.empty() ? std::string(".") : path); + if (spec.absolute()) return spec.image(); + // okay, so the path is relative after all, so we need to combine it with the root path + // decompose the root path and check whether it is relative + file_specification rootspec; + rootspec.initialise_folder(root.empty() ? std::string(".") : root); + if (rootspec.relative()) + rootspec.make_absolute(); + // Now do the conversion of the path relative to the root + spec.make_absolute(rootspec); + return spec.image(); + } + + static std::string relative_path(const std::string& root, const std::string& path) + { + // convert path to a relative path, using the root path as its starting point + // first convert both paths to full paths relative to CWD + file_specification rootspec; + rootspec.initialise_folder(root.empty() ? std::string(".") : root); + if (rootspec.relative()) + rootspec.make_absolute(); + file_specification spec; + spec.initialise_folder(path.empty() ? std::string(".") : path); + if (spec.relative()) + spec.make_absolute(); + // now make path spec relative to the root spec + spec.make_relative(rootspec); + return spec.image(); + } + + std::string folder_to_path (const std::string& path, const std::string& directory) + { + return full_path(path, directory); + } + + std::string filespec_to_path (const std::string& path, const std::string& spec) + { + return create_filespec(folder_to_path(path, folder_part(spec)),filename_part(spec)); + } + + std::string folder_to_path(const std::string& folder) + { + return folder_to_path(folder_current(), folder); + } + + std::string filespec_to_path(const std::string& filespec) + { + return filespec_to_path(folder_current(), filespec); + } + + std::string folder_to_relative_path(const std::string& root, const std::string& folder) + { + return relative_path(root, folder); + } + + std::string filespec_to_relative_path(const std::string& root, const std::string& spec) + { + return create_filespec(folder_to_relative_path(root, folder_part(spec)),filename_part(spec)); + } + + std::string folder_to_relative_path(const std::string& folder) + { + return folder_to_relative_path(folder_current(), folder); + } + + std::string filespec_to_relative_path(const std::string& filespec) + { + return filespec_to_relative_path(folder_current(), filespec); + } + + std::string folder_append_separator(const std::string& folder) + { + std::string result = folder; + if (result.empty() || !is_separator(result[result.size()-1])) + result += preferred_separator; + return result; + } + +//////////////////////////////////////////////////////////////////////////////// + + std::string basename_part (const std::string& spec) + { + std::string fname = filename_part(spec); + // scan back through filename until a '.' is found and remove suffix + // the whole filename is the basename if there is no '.' + std::string::size_type i = fname.find_last_of('.'); + // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension + if (i != 0 && i != std::string::npos) + fname.erase(i, fname.size()-i); + return fname; + } + + std::string filename_part (const std::string& spec) + { + // scan back through filename until a preferred_separator is found and remove prefix; + // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename + unsigned i = spec.size(); + while (i--) + { + if (is_separator(spec[i])) + return spec.substr(i+1,spec.size()-i-1); + } + return spec; + } + + std::string extension_part (const std::string& spec) + { + std::string fname = filename_part(spec); + // scan back through filename until a '.' is found and remove prefix; + std::string::size_type i = fname.find_last_of('.'); + // observe Unix convention that a dot at the start of a filename is part of the name, not the extension; + if (i != 0 && i != std::string::npos) + fname.erase(0, i+1); + else + fname.erase(); + return fname; + } + + std::string folder_part (const std::string& spec) + { + // scan back through filename until a separator is found and remove prefix + // if there is no separator, remove the whole + unsigned i = spec.size(); + while (i--) + { + if (is_separator(spec[i])) + return spec.substr(0,i); + } + return std::string(); + } + + std::vector filespec_elements(const std::string& filespec) + { + file_specification spec; + spec.initialise_file(filespec); + std::vector result = spec.path(); + if (!spec.drive().empty()) result.insert(result.begin(),spec.drive()); + if (!spec.file().empty()) result.push_back(spec.file()); + return result; + } + + std::vector folder_elements(const std::string& folder) + { + file_specification spec; + spec.initialise_folder(folder); + std::vector result = spec.path(); + if (!spec.drive().empty()) result.insert(result.begin(),spec.drive()); + return result; + } + +//////////////////////////////////////////////////////////////////////////////// +// mimic the command lookup used by the shell + +// Windows looks at the following locations: +// 1) application root +// 2) current directory +// 3) 32-bit system directory +// 4) 16-bit system directory +// 5) windows system directory +// 6) %path% +// currently only (2) and (6) has been implemented although many system folders are on the path anyway +// also implement the implied .exe extension on commands with no path (see CreateProcess documentation) +// TODO - PATHEXT handling to find non-exe executables + + std::string path_lookup (const std::string& command) + { + std::string path = std::string(".") + PATH_SPLITTER + getenv("PATH"); + return lookup(command, path); + } + + std::string lookup (const std::string& command, const std::string& path, const std::string& splitter) + { + // first check whether the command is already a path and check whether it exists + if (!folder_part(command).empty()) + { + if (file_exists(command)) + return command; + } + else + { + // command is just a name - so do path lookup + // split the path into its elements + std::vector paths; + if (!path.empty()) + { + for(std::string::size_type offset = 0;;) + { + std::string::size_type found = path.find(splitter, offset); + if (found != std::string::npos) + { + paths.push_back(path.substr(offset, found-offset)); + offset = found + splitter.size(); + } + else + { + paths.push_back(path.substr(offset, path.size()-offset)); + break; + } + } + } + // now lookup each path to see if it its the matching one + for (unsigned i = 0; i < paths.size(); i++) + { + std::string spec = create_filespec(paths[i], command); + if (file_exists(spec)) + { + return spec; + } + } + } +#ifdef MSWINDOWS + // if there is no extension, try recursing on each possible extension + // TODO iterate through PATHEXT + if (extension_part(command).empty()) + return lookup(create_filespec(folder_part(command), basename_part(command), "exe"), path, splitter); +#endif + // if path lookup failed, return empty string to indicate error + return std::string(); + } + +//////////////////////////////////////////////////////////////////////////////// + + std::string install_path(const std::string& argv0) + { + std::string bin_directory = folder_part(argv0); + if (bin_directory.empty()) + { + // do path lookup to find the executable path + bin_directory = folder_part(path_lookup(argv0)); + } + return bin_directory; + } + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/file_system.hpp b/src/stlplus/portability/file_system.hpp new file mode 100644 index 0000000..374e86d --- /dev/null +++ b/src/stlplus/portability/file_system.hpp @@ -0,0 +1,201 @@ +#ifndef STLPLUS_FILE_SYSTEM +#define STLPLUS_FILE_SYSTEM +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Simplified access to the File system + +// All file system access and filename manipulation should be done +// with this package. Then it is only necessary to port this package +// to port all file handling. + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // implement string comparison of paths - Unix is case-sensitive, Windows is case-insensitive + + bool path_compare(const std::string& l, const std::string& r); + + //////////////////////////////////////////////////////////////////////////////// + // classifying functions + + // test for whether there's something (i.e. folder or file) with this name + bool is_present(const std::string& thing); + // test for whether there's something present and its a folder + bool is_folder(const std::string& thing); + // test for whether there's something present and its a file + bool is_file(const std::string& thing); + + //////////////////////////////////////////////////////////////////////////////// + // file functions + + // tests whether there's a file of this name + bool file_exists(const std::string& filespec); + // tests whether the file is readable - i.e. exists and has read mode set + bool file_readable(const std::string& filespec); + // tests whether file is writable - either it exists and is writable or doesn't exist but is in a writable folder + bool file_writable(const std::string& filespec); + // the size of the file in bytes - 0 if doesn't exist + size_t file_size(const std::string& filespec); + // delete the file - returns true if the delete succeeded + bool file_delete(const std::string& filespec); + // rename the file - returns true if the rename succeeded + bool file_rename (const std::string& old_filespec, const std::string& new_filespec); + // make an exact copy of the file - returns true if it succeeded + bool file_copy (const std::string& old_filespec, const std::string& new_filespec); + // move the file - tries to rename, if that fails, tries to copy - returns true if either of these succeeded + bool file_move (const std::string& old_filespec, const std::string& new_filespec); + + // get the file's time stamps as a time_t - see the stlplus time.hpp package + + // time the file was originally created + time_t file_created(const std::string& filespec); + // time the file was last modified + time_t file_modified(const std::string& filespec); + // time the file was accessed + time_t file_accessed(const std::string& filespec); + + // platform-specific string handling to combine a directory and filename into a path + + // combine a folder with a filename (basename.extension) + std::string create_filespec(const std::string& folder, const std::string& filename); + // combine a folder, a basename and an extension - extension does not need the . + std::string create_filespec(const std::string& folder, const std::string& basename, const std::string& extension); + // combine a basename and an extension - extension does not need the . + std::string create_filename(const std::string& basename, const std::string& extension); + + //////////////////////////////////////////////////////////////////////////////// + // folder functions + + // craete a folder - returns true if successful + bool folder_create(const std::string& folder); + // tests for whether the folder exists, i.e. there is something of that name and its a folder + bool folder_exists(const std::string& folder); + // test whether the folder contents are readable + bool folder_readable(const std::string& folder); + // tests whether the folder can be written to - for example to create a new file + bool folder_writable(const std::string& folder); + // delete the folder, optionally deleting the contents first - only succeeds if everything could be deleted + bool folder_delete(const std::string& folder, bool recurse = false); + // rename the folder - this probably only works within a disk/partition + bool folder_rename (const std::string& old_directory, const std::string& new_directory); + // test whether the folder is empty (of files) + bool folder_empty(const std::string& folder); + + // set the current folder + bool folder_set_current(const std::string& folder); + + // platform-specific string handling to retrieve some special locations + // these do not check whether the folder exists, they just process strings + + // get the current folder + std::string folder_current(void); + // get the current folder as a full path + std::string folder_current_full(void); + // get the home folder - $HOME or %HOMEDRIVE%%HOMEPATH% + std::string folder_home(void); + // go down a level in the folder hierarchy + std::string folder_down(const std::string& folder, const std::string& subfolder); + // go up a level in the folder hierarchy + std::string folder_up(const std::string& folder, unsigned levels = 1); + + // get folder contents + + // the set of all subdirectories + std::vector folder_subdirectories(const std::string& folder); + // the set of all files + std::vector folder_files(const std::string& folder); + // the set of all folders and files + std::vector folder_all(const std::string& folder); + // the set of all folder contents matching a wildcard string + // if folders is true, include folders; if files is true, include files + std::vector folder_wildcard(const std::string& folder, + const std::string& wildcard, + bool folders = true, + bool files = true); + + //////////////////////////////////////////////////////////////////////////////// + // path functions + + // string manipulations of paths + + // test whether a string represents a full path or a relative one + bool is_full_path(const std::string& path); + bool is_relative_path(const std::string& path); + + // convert to a full path relative to the root path + std::string folder_to_path(const std::string& root, const std::string& folder); + std::string filespec_to_path(const std::string& root, const std::string& filespec); + + // convert to a full path relative to the current working directory + std::string folder_to_path(const std::string& folder); + std::string filespec_to_path(const std::string& filespec); + + // convert to a relative path relative to the root path + std::string folder_to_relative_path(const std::string& root, const std::string& folder); + std::string filespec_to_relative_path(const std::string& root, const std::string& filespec); + + // convert to a relative path relative to the current working directory + std::string folder_to_relative_path(const std::string& folder); + std::string filespec_to_relative_path(const std::string& filespec); + + // append a folder separator to the path to make it absolutely clear that it is a folder + std::string folder_append_separator(const std::string& folder); + + //////////////////////////////////////////////////////////////////////////////// + // access functions split a filespec into its elements + + // get the basename - that is, the name of the file without folder or extension parts + std::string basename_part(const std::string& filespec); + // get the filename - that is, the name of the file without folder part but with extension + std::string filename_part(const std::string& filespec); + // get the extension - that is the part of the filename after the . (and excluding the .) + std::string extension_part(const std::string& filespec); + // get the folder part - that is the filespec with the filename removed + std::string folder_part(const std::string& filespec); + + // split a path into a vector of elements - i.e. split at the folder separator + std::vector folder_elements(const std::string& folder); + std::vector filespec_elements(const std::string& filespec); + + //////////////////////////////////////////////////////////////////////////////// + // Path lookup functions + +#ifdef MSWINDOWS +#define PATH_SPLITTER ";" +#else +#define PATH_SPLITTER ":" +#endif + + // The lookup normally carried out by the shell to find a command in a + // directory in the PATH. Give this function the name of a command and it + // will return the full path. It returns an empty string on failure. + std::string path_lookup (const std::string& command); + + // Generalised form of the above, takes a second argument + // - the list to search. This can be used to do other path lookups, + // such as LD_LIBRARY_PATH. The third argument specifies the splitter - + // the default value of PATH_SPLITTER is appropriate for environment variables. + std::string lookup (const std::string& file, const std::string& path, const std::string& splitter = PATH_SPLITTER); + + // utility function for finding the folder that contains the current executable + // the argument is the argv[0] parameter passed to main + std::string install_path(const std::string& argv0); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/inf.cpp b/src/stlplus/portability/inf.cpp new file mode 100644 index 0000000..80b0475 --- /dev/null +++ b/src/stlplus/portability/inf.cpp @@ -0,0 +1,1482 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// The integer is represented as a sequence of bytes. They are stored such that +// element 0 is the lsB, which makes sense when seen as an integer offset but +// is counter-intuitive when you think that a string is usually read from left +// to right, 0 to size-1, in which case the lsB is on the *left*. + +// This solution is compatible with 32-bit and 64-bit machines with either +// little-endian or big-endian representations of integers. + +// Problem: I'm using std::string, which is an array of char. However, char is +// not well-defined - it could be signed or unsigned. + +// In fact, there's no requirement for a char to even be one byte - it can be +// any size of one byte or more. However, it's just impossible to make any +// progress with that naffness (thanks to the C non-standardisation committee) +// and the practice is that char on every platform/compiler I've ever come +// across is that char = byte. + +// The algorithms here use unsigned char to represent bit-patterns so I have to +// be careful to type-cast from char to unsigned char a lot. I use a typedef to +// make life easier. + +//////////////////////////////////////////////////////////////////////////////// +#include "inf.hpp" +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // choose a sensible C type for a byte + + typedef unsigned char byte; + + //////////////////////////////////////////////////////////////////////////////// + // local functions + + // removes leading bytes that don't contribute to the value to create the minimum string representation + static void reduce_string(std::string& data) + { + while(data.size() > 1 && + ((byte(data[data.size()-1]) == byte(0) && byte(data[data.size()-2]) < byte(128)) || + (byte(data[data.size()-1]) == byte(255) && byte(data[data.size()-2]) >= byte(128)))) + { + data.erase(data.end()-1); + } + } + + // generic implementations of type conversions from integer type to internal representation + // data: integer value for conversion + // result: internal representation + + template + static void convert_from_signed(const T& data, std::string& result) + { + result.erase(); + bool lsb_first = little_endian(); + byte* address = (byte*)&data; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = (lsb_first ? i : (sizeof(T) - i - 1)); + result.append(1,address[offset]); + } + reduce_string(result); + } + + template + static void convert_from_unsigned(const T& data, std::string& result) + { + result.erase(); + bool lsb_first = little_endian(); + byte* address = (byte*)&data; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = (lsb_first ? i : (sizeof(T) - i - 1)); + result.append(1,address[offset]); + } + // inf is signed - so there is a possible extra sign bit to add + result.append(1,std::string::value_type(0)); + reduce_string(result); + } + + // generic implementations of type conversions from internal representation to an integer type + // data : string representation of integer + // result: integer result of conversion + // return: flag indicating success - false = overflow + + template + bool convert_to_signed(const std::string& data, T& result) + { + bool lsb_first = little_endian(); + byte* address = (byte*)&result; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = lsb_first ? i : (sizeof(T) - i - 1); + if (i < data.size()) + address[offset] = byte(data[i]); + else if (data.empty() || (byte(data[data.size()-1]) < byte(128))) + address[offset] = byte(0); + else + address[offset] = byte(255); + } + return data.size() <= sizeof(T); + } + + template + bool convert_to_unsigned(const std::string& data, T& result) + { + bool lsb_first = little_endian(); + byte* address = (byte*)&result; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = lsb_first ? i : (sizeof(T) - i - 1); + if (i < data.size()) + address[offset] = byte(data[i]); + else + address[offset] = byte(0); + } + return data.size() <= sizeof(T); + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions to string + + static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static int from_char [] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + static void convert_to_string(const stlplus::inf& data, std::string& result, unsigned radix = 10) + throw(std::invalid_argument) + { + // only support the C-style radixes plus 0b for binary + if (radix != 2 && radix != 8 && radix != 10 && radix != 16) + throw std::invalid_argument("invalid radix value"); + inf local_i = data; + // untangle all the options + bool binary = radix == 2; + bool octal = radix == 8; + bool hex = radix == 16; + // the C representations for binary, octal and hex use 2's-complement representation + // all other represenations use sign-magnitude + if (hex || octal || binary) + { + // bit-pattern representation + // this is the binary representation optionally shown in octal or hex + // first generate the binary by masking the bits + for (unsigned j = local_i.bits(); j--; ) + result += (local_i.bit(j) ? '1' : '0'); + // the result is now the full width of the type - e.g. int will give a 32-bit result + // now interpret this as either binary, octal or hex and add the prefix + if (binary) + { + // trim down to the smallest string that preserves the value + while (true) + { + // do not trim to less than 1 bit (sign only) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // add the prefix + result.insert((std::string::size_type)0, "0b"); + } + else if (octal) + { + // the result is currently binary + // trim down to the smallest string that preserves the value + while (true) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() % 3 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to octal + std::string octal_result; + for (unsigned i = 0; i < result.size()/3; i++) + { + // yuck - ugly or what? + if (result[i*3] == '0') + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '0'; + else + octal_result += '1'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '2'; + else + octal_result += '3'; + } + } + else + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '4'; + else + octal_result += '5'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '6'; + else + octal_result += '7'; + } + } + } + result = octal_result; + // add the prefix + result.insert((std::string::size_type)0, "0"); + } + else + { + // similar to octal + while (true) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // pad to a multiple of 4 characters + while (result.size() % 4 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to hex + std::string hex_result; + for (unsigned i = 0; i < result.size()/4; i++) + { + // yuck - ugly or what? + if (result[i*4] == '0') + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '0'; + else + hex_result += '1'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '2'; + else + hex_result += '3'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '4'; + else + hex_result += '5'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '6'; + else + hex_result += '7'; + } + } + } + else + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '8'; + else + hex_result += '9'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'a'; + else + hex_result += 'b'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += 'c'; + else + hex_result += 'd'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'e'; + else + hex_result += 'f'; + } + } + } + } + result = hex_result; + // add the prefix + result.insert((std::string::size_type)0, "0x"); + } + } + else + { + // convert to sign-magnitude + // the representation is: + // [sign]magnitude + bool negative = local_i.negative(); + local_i.abs(); + // create a representation of the magnitude by successive division + inf inf_radix(radix); + do + { + std::pair divided = local_i.divide(inf_radix); + unsigned remainder = divided.second.to_unsigned(); + char digit = to_char[remainder]; + result.insert((std::string::size_type)0, 1, digit); + local_i = divided.first; + } + while(!local_i.zero()); + // add the prefixes + // add a sign only for negative values + if (negative) + result.insert((std::string::size_type)0, 1, '-'); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions FROM string + + void convert_from_string(const std::string& str, inf& result, unsigned radix = 10) throw(std::invalid_argument) + { + result = 0; + // only support the C-style radixes plus 0b for binary + // a radix of 0 means deduce the radix from the input - assume 10 + if (radix != 0 && radix != 2 && radix != 8 && radix != 10 && radix != 16) + throw std::invalid_argument("invalid radix value"); + unsigned i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by the C prefix + // Note: a leading zero is the C-style prefix for octal - I only make this + // override the default when the default radix is not specified + // first check for a C-style prefix + bool c_style = false; + if (i < str.size() && str[i] == '0') + { + // binary or hex + if (i+1 < str.size() && tolower(str[i+1]) == 'x') + { + c_style = true; + radix = 16; + i += 2; + } + else if (i+1 < str.size() && tolower(str[i+1]) == 'b') + { + c_style = true; + radix = 2; + i += 2; + } + else if (radix == 0) + { + c_style = true; + radix = 8; + i += 1; + } + } + if (radix == 0) + radix = 10; + if (c_style) + { + // the C style formats are bit patterns not integer values - these need + // to be sign-extended to get the right value + std::string binary; + if (radix == 2) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += '0'; + break; + case '1': + binary += '1'; + break; + default: + throw std::invalid_argument("invalid binary character in string " + str); + } + } + } + else if (radix == 8) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += "000"; + break; + case '1': + binary += "001"; + break; + case '2': + binary += "010"; + break; + case '3': + binary += "011"; + break; + case '4': + binary += "100"; + break; + case '5': + binary += "101"; + break; + case '6': + binary += "110"; + break; + case '7': + binary += "111"; + break; + default: + throw std::invalid_argument("invalid octal character in string " + str); + } + } + } + else + { + for (unsigned j = i; j < str.size(); j++) + { + switch(tolower(str[j])) + { + case '0': + binary += "0000"; + break; + case '1': + binary += "0001"; + break; + case '2': + binary += "0010"; + break; + case '3': + binary += "0011"; + break; + case '4': + binary += "0100"; + break; + case '5': + binary += "0101"; + break; + case '6': + binary += "0110"; + break; + case '7': + binary += "0111"; + break; + case '8': + binary += "1000"; + break; + case '9': + binary += "1001"; + break; + case 'a': + binary += "1010"; + break; + case 'b': + binary += "1011"; + break; + case 'c': + binary += "1100"; + break; + case 'd': + binary += "1101"; + break; + case 'e': + binary += "1110"; + break; + case 'f': + binary += "1111"; + break; + default: + throw std::invalid_argument("invalid hex character in string " + str); + } + } + } + // now convert the value + result.resize(binary.size()); + for (unsigned j = 0; j < binary.size(); j++) + result.preset(binary.size() - j - 1, binary[j] == '1'); + } + else + { + // sign-magnitude representation + // now scan for a sign and find whether this is a negative number + bool negative = false; + if (i < str.size()) + { + switch (str[i]) + { + case '-': + negative = true; + i++; + break; + case '+': + i++; + break; + } + } + for (; i < str.size(); i++) + { + result *= inf(radix); + unsigned char ascii = (unsigned char)str[i]; + int ch = from_char[ascii] ; + if (ch == -1) + throw std::invalid_argument("invalid decimal character in string " + str); + result += inf(ch); + } + if (negative) + result.negate(); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // constructors - mostly implemented in terms of the assignment operators + + inf::inf(void) + { + // void constructor initialises to zero - represented as a single-byte value containing zero + m_data.append(1,std::string::value_type(0)); + } + + inf::inf(short r) + { + operator=(r); + } + + inf::inf(unsigned short r) + { + operator=(r); + } + + inf::inf(int r) + { + operator=(r); + } + + inf::inf(unsigned r) + { + operator=(r); + } + + inf::inf(long r) + { + operator=(r); + } + + inf::inf(unsigned long r) + { + operator=(r); + } + + inf::inf (const std::string& r) throw(std::invalid_argument) + { + operator=(r); + } + + inf::inf(const inf& r) + { +#ifdef __BORLANDC__ + // work round bug in Borland compiler - copy constructor fails if string + // contains null characters, so do my own copy + for (unsigned i = 0; i < r.m_data.size(); i++) + m_data += r.m_data[i]; +#else + m_data = r.m_data; +#endif + } + + //////////////////////////////////////////////////////////////////////////////// + + inf::~inf(void) + { + } + + //////////////////////////////////////////////////////////////////////////////// + // assignments convert from iteger types to internal representation + + inf& inf::operator = (short r) + { + convert_from_signed(r, m_data); + return *this; + } + + inf& inf::operator = (unsigned short r) + { + convert_from_unsigned(r, m_data); + return *this; + } + + inf& inf::operator = (int r) + { + convert_from_signed(r, m_data); + return *this; + } + + inf& inf::operator = (unsigned r) + { + convert_from_unsigned(r, m_data); + return *this; + } + + inf& inf::operator = (long r) + { + convert_from_signed(r, m_data); + return *this; + } + + inf& inf::operator = (unsigned long r) + { + convert_from_unsigned(r, m_data); + return *this; + } + + inf& inf::operator = (const std::string& r) throw(std::invalid_argument) + { + convert_from_string(r, *this); + return *this; + } + + inf& inf::operator = (const inf& r) + { + m_data = r.m_data; + return *this; + } + + //////////////////////////////////////////////////////////////////////////////// + + short inf::to_short(bool truncate) const throw(std::overflow_error) + { + short result = 0; + if (!convert_to_signed(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_short"); + return result; + } + + unsigned short inf::to_unsigned_short(bool truncate) const throw(std::overflow_error) + { + unsigned short result = 0; + if (!convert_to_unsigned(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_unsigned_short"); + return result; + } + + int inf::to_int(bool truncate) const throw(std::overflow_error) + { + int result = 0; + if (!convert_to_signed(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_int"); + return result; + } + + unsigned inf::to_unsigned(bool truncate) const throw(std::overflow_error) + { + unsigned result = 0; + if (!convert_to_unsigned(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_unsigned"); + return result; + } + + long inf::to_long(bool truncate) const throw(std::overflow_error) + { + long result = 0; + if (!convert_to_signed(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_long"); + return result; + } + + unsigned long inf::to_unsigned_long(bool truncate) const throw(std::overflow_error) + { + unsigned long result = 0; + if (!convert_to_unsigned(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_unsigned_long"); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // resize the inf regardless of the data + + void inf::resize(unsigned bits) + { + if (bits == 0) bits = 1; + unsigned bytes = (bits+7)/8; + byte extend = negative() ? byte(255) : byte (0); + while(bytes > m_data.size()) + m_data.append(1,extend); + } + + // reduce the bit count to the minimum needed to preserve the value + + void inf::reduce(void) + { + reduce_string(m_data); + } + + //////////////////////////////////////////////////////////////////////////////// + // the number of significant bits in the number + + unsigned inf::bits (void) const + { + // The number of significant bits in the integer value - this is the number + // of indexable bits less any redundant sign bits at the msb + // This does not assume that the inf has been reduced to its minimum form + unsigned result = indexable_bits(); + bool sign = bit(result-1); + while (result > 1 && (sign == bit(result-2))) + result--; + return result; + } + + unsigned inf::size(void) const + { + return bits(); + } + + unsigned inf::indexable_bits (void) const + { + return 8 * unsigned(m_data.size()); + } + + //////////////////////////////////////////////////////////////////////////////// + // bitwise operations + + bool inf::bit (unsigned index) const throw(std::out_of_range) + { + if (index >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::bit")); + // first split the offset into byte offset and bit offset + unsigned byte_offset = index/8; + unsigned bit_offset = index%8; + return (byte(m_data[byte_offset]) & (byte(1) << bit_offset)) != 0; + } + + bool inf::operator [] (unsigned index) const throw(std::out_of_range) + { + return bit(index); + } + + void inf::set (unsigned index) throw(std::out_of_range) + { + if (index >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::set")); + // first split the offset into byte offset and bit offset + unsigned byte_offset = index/8; + unsigned bit_offset = index%8; + m_data[byte_offset] |= (byte(1) << bit_offset); + } + + void inf::clear (unsigned index) throw(std::out_of_range) + { + if (index >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::clear")); + // first split the offset into byte offset and bit offset + unsigned byte_offset = index/8; + unsigned bit_offset = index%8; + m_data[byte_offset] &= (~(byte(1) << bit_offset)); + } + + void inf::preset (unsigned index, bool value) throw(std::out_of_range) + { + if (value) + set(index); + else + clear(index); + } + + inf inf::slice(unsigned low, unsigned high) const throw(std::out_of_range) + { + if (low >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::slice: low index")); + if (high >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::slice: high index")); + inf result; + if (high >= low) + { + // create a result the right size and filled with sign bits + std::string::size_type result_size = (high-low+1+7)/8; + result.m_data.erase(); + byte extend = bit(high) ? byte(255) : byte (0); + while (result.m_data.size() < result_size) + result.m_data.append(1,extend); + // now set the relevant bits + for (unsigned i = low; i <= high; i++) + result.preset(i-low, bit(i)); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // testing operations + + bool inf::negative (void) const + { + return bit(indexable_bits()-1); + } + + bool inf::natural (void) const + { + return !negative(); + } + + bool inf::positive (void) const + { + return natural() && !zero(); + } + + bool inf::zero (void) const + { + for (std::string::size_type i = 0; i < m_data.size(); i++) + if (m_data[i] != 0) + return false; + return true; + } + + bool inf::non_zero (void) const + { + return !zero(); + } + + bool inf::operator ! (void) const + { + return zero(); + } + + //////////////////////////////////////////////////////////////////////////////// + // comparison operators + + bool inf::operator == (const inf& r) const + { + // Two infs are equal if they are numerically equal, even if they are + // different sizes (i.e. they could be non-reduced values). + // This makes life a little more complicated than if I could assume that values were reduced. + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = bytes; i--; ) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + if (l_byte != r_byte) + return false; + } + return true; + } + + bool inf::operator != (const inf& r) const + { + return !operator==(r); + } + + bool inf::operator < (const inf& r) const + { + // This could be implemented in terms of subtraction. However, it can be + // simplified since there is no need to calculate the accurate difference, + // just the direction of the difference. I compare from msB down and as + // soon as a byte difference is found, that defines the ordering. The + // problem is that in 2's-complement, all negative values are greater than + // all natural values if you just do a straight unsigned comparison. I + // handle this by doing a preliminary test for different signs. + + // For example, a 3-bit signed type has the coding: + // 000 = 0 + // ... + // 011 = 3 + // 100 = -4 + // ... + // 111 = -1 + + // So, for natural values, the ordering of the integer values is the + // ordering of the bit patterns. Similarly, for negative values, the + // ordering of the integer values is the ordering of the bit patterns + // However, the bit patterns for the negative values are *greater than* + // the natural values. This is a side-effect of the naffness of + // 2's-complement representation + + // first handle the case of comparing two values with different signs + bool l_sign = negative(); + bool r_sign = r.negative(); + if (l_sign != r_sign) + { + // one argument must be negative and the other natural + // the left is less if it is the negative one + return l_sign; + } + // the arguments are the same sign + // so the ordering is a simple unsigned byte-by-byte comparison + // However, this is complicated by the possibility that the values could be different lengths + byte l_extend = l_sign ? byte(255) : byte (0); + byte r_extend = r_sign ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = bytes; i--; ) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + if (l_byte != r_byte) + return l_byte < r_byte; + } + // if I get here, the two are equal, so that is not less-than + return false; + } + + bool inf::operator <= (const inf& r) const + { + return !(r < *this); + } + + bool inf::operator > (const inf& r) const + { + return r < *this; + } + + bool inf::operator >= (const inf& r) const + { + return !(*this < r); + } + + //////////////////////////////////////////////////////////////////////////////// + // logical operators + + inf& inf::invert (void) + { + for (std::string::size_type i = 0; i < m_data.size(); i++) + m_data[i] = ~m_data[i]; + return *this; + } + + inf inf::operator ~ (void) const + { + inf result(*this); + result.invert(); + return result; + } + + inf& inf::operator &= (const inf& r) + { + // bitwise AND is extended to the length of the largest argument + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = 0; i < bytes; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + byte result = l_byte & r_byte; + if (i < m_data.size()) + m_data[i] = result; + else + m_data.append(1,result); + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator & (const inf& r) const + { + inf result(*this); + result &= r; + return result; + } + + inf& inf::operator |= (const inf& r) + { + // bitwise OR is extended to the length of the largest argument + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = 0; i < bytes; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + byte result = l_byte | r_byte; + if (i < m_data.size()) + m_data[i] = result; + else + m_data.append(1,result); + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator | (const inf& r) const + { + inf result(*this); + result |= r; + return result; + } + + inf& inf::operator ^= (const inf& r) + { + // bitwise XOR is extended to the length of the largest argument + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = 0; i < bytes; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + byte result = l_byte ^ r_byte; + if (i < m_data.size()) + m_data[i] = result; + else + m_data.append(1,result); + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator ^ (const inf& r) const + { + inf result(*this); + result ^= r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // shift operators all preserve the value by increasing the word size + + inf& inf::operator <<= (unsigned shift) + { + // left shift is a shift towards the msb, with 0s being shifted in at the lsb + // split this into a byte shift followed by a bit shift + + // first expand the value to be big enough for the result + std::string::size_type new_size = (indexable_bits() + shift + 7) / 8; + byte extend = negative() ? byte(255) : byte (0); + while (m_data.size() < new_size) + m_data.append(1,extend); + // now do the byte shift + unsigned byte_shift = shift/8; + if (byte_shift > 0) + { + for (std::string::size_type b = new_size; b--; ) + m_data[b] = (b >= byte_shift) ? m_data[b-byte_shift] : byte(0); + } + // and finally the bit shift + unsigned bit_shift = shift%8; + if (bit_shift > 0) + { + for (std::string::size_type b = new_size; b--; ) + { + byte current = byte(m_data[b]); + byte previous = b > 0 ? m_data[b-1] : byte(0); + m_data[b] = (current << bit_shift) | (previous >> (8 - bit_shift)); + } + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator << (unsigned shift) const + { + inf result(*this); + result <<= shift; + return result; + } + + inf& inf::operator >>= (unsigned shift) + { + // right shift is a shift towards the lsb, with sign bits being shifted in at the msb + // split this into a byte shift followed by a bit shift + + // a byte of sign bits + byte extend = negative() ? byte(255) : byte (0); + // do the byte shift + unsigned byte_shift = shift/8; + if (byte_shift > 0) + { + for (std::string::size_type b = 0; b < m_data.size(); b++) + m_data[b] = (b + byte_shift < m_data.size()) ? m_data[b+byte_shift] : extend; + } + // and finally the bit shift + unsigned bit_shift = shift%8; + if (bit_shift > 0) + { + for (std::string::size_type b = 0; b < m_data.size(); b++) + { + byte current = byte(m_data[b]); + byte next = ((b+1) < m_data.size()) ? m_data[b+1] : extend; + byte shifted = (current >> bit_shift) | (next << (8 - bit_shift)); + m_data[b] = shifted; + } + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator >> (unsigned shift) const + { + inf result(*this); + result >>= shift; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // negation operators + + inf& inf::negate (void) + { + // do 2's-complement negation + // equivalent to inversion plus one + invert(); + operator += (inf(1)); + return *this; + } + + inf inf::operator - (void) const + { + inf result(*this); + result.negate(); + return result; + } + + inf& inf::abs(void) + { + if (negative()) negate(); + return *this; + } + + inf abs(const inf& i) + { + inf result = i; + result.abs(); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // addition operators + + inf& inf::operator += (const inf& r) + { + // do 2's-complement addition + // Note that the addition can give a result that is larger than either argument + byte carry = 0; + std::string::size_type max_size = maximum(m_data.size(),r.m_data.size()); + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + for (std::string::size_type i = 0; i < max_size; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + // calculate the addition in a type that is bigger than a byte in order to catch the carry-out + unsigned short result = ((unsigned short)(l_byte)) + ((unsigned short)(r_byte)) + carry; + // now truncate the result to get the lsB + if (i < m_data.size()) + m_data[i] = byte(result); + else + m_data.append(1,byte(result)); + // and capture the carry out by grabbing the second byte of the result + carry = byte(result >> 8); + } + // if the result overflowed or underflowed, add an extra byte to catch it + unsigned short result = ((unsigned short)(l_extend)) + ((unsigned short)(r_extend)) + carry; + if (byte(result) != (negative() ? byte(255) : byte(0))) + m_data.append(1,byte(result)); + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator + (const inf& r) const + { + inf result(*this); + result += r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // subtraction operators + + inf& inf::operator -= (const inf& r) + { + // subtraction is defined in terms of negation and addition + inf negated = -r; + operator += (negated); + return *this; + } + + inf inf::operator - (const inf& r) const + { + inf result(*this); + result -= r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // multiplication operators + + inf& inf::operator *= (const inf& r) + { + // 2's complement multiplication + // one day I'll do a more efficient version than this based on the underlying representation + inf left(*this); + inf right = r; + // make the right value natural but preserve its sign for later + bool right_negative = right.negative(); + right.abs(); + // implemented as a series of conditional additions + operator = (0); + // left.resize(right.bits() + left.bits() - 1); + left <<= right.bits()-1; + for (unsigned i = right.bits(); i--; ) + { + if (right[i]) + operator += (left); + left >>= 1; + } + if (right_negative) + negate(); + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator * (const inf& r) const + { + inf result(*this); + result *= r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // division and remainder operators + + std::pair inf::divide(const inf& right) const throw(divide_by_zero) + { + if (right.zero()) + throw divide_by_zero("stlplus::inf::divide"); + inf numerator(*this); + inf denominator = right; + // make the numerator natural but preserve the sign for later + bool numerator_negative = numerator.negative(); + numerator.abs(); + // same with the denominator + bool denominator_negative = denominator.negative(); + denominator.abs(); + // the quotient and remainder will form the result + // start with the quotiont zero and the remainder equal to the whole of the + // numerator, then do trial subtraction from this + inf quotient; + inf remainder = numerator; + // there's nothing more to do if the numerator is smaller than the denominator + // but otherwise do the division + if (numerator.bits() >= denominator.bits()) + { + // make the quotient big enough to take the result + quotient.resize(numerator.bits()); + // start with the numerator shifted to the far left + unsigned shift = numerator.bits() - denominator.bits(); + denominator <<= shift; + // do the division by repeated subtraction, + for (unsigned i = shift+1; i--; ) + { + if (remainder >= denominator) + { + remainder -= denominator; + quotient.set(i); + } + denominator >>= 1; + } + } + // now adjust the signs + // x/(-y) == (-x)/y == -(x/y) + if (numerator_negative != denominator_negative) + quotient.negate(); + quotient.reduce(); + // x%(-y) == x%y and (-x)%y == -(x%y) + if (numerator_negative) + remainder.negate(); + remainder.reduce(); + return std::pair(quotient,remainder); + } + + inf& inf::operator /= (const inf& r) throw(divide_by_zero) + { + std::pair result = divide(r); + operator=(result.first); + return *this; + } + + inf inf::operator / (const inf& r) const throw(divide_by_zero) + { + std::pair result = divide(r); + return result.first; + } + + inf& inf::operator %= (const inf& r) throw(divide_by_zero) + { + std::pair result = divide(r); + operator=(result.second); + return *this; + } + + inf inf::operator % (const inf& r) const throw(divide_by_zero) + { + std::pair result = divide(r); + return result.second; + } + + //////////////////////////////////////////////////////////////////////////////// + // prefix (void) and postfix (int) operators + + inf& inf::operator ++ (void) + { + operator += (inf(1)); + return *this; + } + + inf inf::operator ++ (int) + { + inf old(*this); + operator += (inf(1)); + return old; + } + + inf& inf::operator -- (void) + { + operator -= (inf(1)); + return *this; + } + + inf inf::operator -- (int) + { + inf old(*this); + operator -= (inf(1)); + return old; + } + + //////////////////////////////////////////////////////////////////////////////// + // string representation and I/O routines + + std::string inf::to_string(unsigned radix) const + throw(std::invalid_argument) + { + std::string result; + convert_to_string(*this, result, radix); + return result; + } + + inf& inf::from_string(const std::string& value, unsigned radix) + throw(std::invalid_argument) + { + convert_from_string(value, *this, radix); + return *this; + } + + std::ostream& operator << (std::ostream& str, const inf& i) + { + try + { + // get radix + unsigned radix = 10; + if (str.flags() & std::ios_base::oct) + radix = 8; + if (str.flags() & std::ios_base::hex) + radix = 16; + // the field width is handled by iostream, so I don't need to handle it as well + // generate the string representation then print it + str << i.to_string(radix); + } + catch(const std::invalid_argument) + { + str.setstate(std::ios_base::badbit); + } + return str; + } + + std::istream& operator >> (std::istream& str, inf& i) + { + try + { + // get radix + unsigned radix = 10; + if (str.flags() & std::ios_base::oct) + radix = 8; + if (str.flags() & std::ios_base::hex) + radix = 16; + // now get the string image of the value + std::string image; + str >> image; + // and convert to inf + i.from_string(image, radix); + } + catch(const std::invalid_argument) + { + str.setstate(std::ios_base::badbit); + } + return str; + } + + //////////////////////////////////////////////////////////////////////////////// + // diagnostic dump + // just convert to hex + + std::string inf::image_debug(void) const + { + // create this dump in the human-readable form, i.e. msB to the left + std::string result = "0x"; + for (std::string::size_type i = m_data.size(); i--; ) + { + byte current = m_data[i]; + byte msB = (current & byte(0xf0)) >> 4; + result += to_char[msB]; + byte lsB = (current & byte(0x0f)); + result += to_char[lsB]; + } + return result; + } + + const std::string& inf::get_bytes(void) const + { + return m_data; + } + + void inf::set_bytes(const std::string& data) + { + m_data = data; + } + +} // end namespace stlplus diff --git a/src/stlplus/portability/inf.hpp b/src/stlplus/portability/inf.hpp new file mode 100644 index 0000000..f28541a --- /dev/null +++ b/src/stlplus/portability/inf.hpp @@ -0,0 +1,228 @@ +#ifndef STLPLUS_INF +#define STLPLUS_INF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// An infinite-precision integer class. This allows calculations on large +// integers to be performed without overflow. + +// this class can throw the following exceptions: +// std::out_of_range +// std::overflow_error +// std::invalid_argument +// stlplus::divide_by_zero // why doesn't std have this? +// all of these are derivations of the baseclass: +// std::logic_error +// So you can catch all of them by catching the baseclass + +// Warning: inf was never intended to be fast, it is just for programs which +// need a bit of infinite-precision integer arithmetic. For high-performance +// processing, use the Gnu Multi-Precision (GMP) library. The inf type is just +// easier to integrate and is already ported to all platforms and compilers +// that STLplus is ported to. + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include "portability_exceptions.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + +//////////////////////////////////////////////////////////////////////////////// + + class inf + { + public: + + ////////////////////////////////////////////////////////////////////////////// + // constructors and assignments initialise the inf + + // the void constructor initialises to zero, the others initialise to the + // value of the C integer type or the text value contained in the string + + inf(void); + explicit inf(short); + explicit inf(unsigned short); + explicit inf(int); + explicit inf(unsigned); + explicit inf(long); + explicit inf(unsigned long); + explicit inf(const std::string&) throw(std::invalid_argument); + inf(const inf&); + + ~inf(void); + + // assignments with equivalent behaviour to the constructors + + inf& operator = (short); + inf& operator = (unsigned short); + inf& operator = (int); + inf& operator = (unsigned); + inf& operator = (long); + inf& operator = (unsigned long); + inf& operator = (const std::string&) throw(std::invalid_argument); + inf& operator = (const inf&); + + ////////////////////////////////////////////////////////////////////////////// + // conversions back to the C types + // truncate: controls the behaviour when the value is too long for the result + // true: truncate the value + // false: throw an exception + + short to_short(bool truncate = true) const throw(std::overflow_error); + unsigned short to_unsigned_short(bool truncate = true) const throw(std::overflow_error); + + int to_int(bool truncate = true) const throw(std::overflow_error); + unsigned to_unsigned(bool truncate = true) const throw(std::overflow_error); + + long to_long(bool truncate = true) const throw(std::overflow_error); + unsigned long to_unsigned_long(bool truncate = true) const throw(std::overflow_error); + + ////////////////////////////////////////////////////////////////////////////// + // bitwise manipulation + + void resize(unsigned bits); + void reduce(void); + + // the number of significant bits in the value + unsigned bits (void) const; + unsigned size (void) const; + + // the number of bits that can be accessed by the bit() method (=bits() rounded up to the next byte) + unsigned indexable_bits(void) const; + + bool bit (unsigned index) const throw(std::out_of_range); + bool operator [] (unsigned index) const throw(std::out_of_range); + + void set (unsigned index) throw(std::out_of_range); + void clear (unsigned index) throw(std::out_of_range); + void preset (unsigned index, bool value) throw(std::out_of_range); + + inf slice(unsigned low, unsigned high) const throw(std::out_of_range); + + ////////////////////////////////////////////////////////////////////////////// + // tests for common values or ranges + + bool negative (void) const; + bool natural (void) const; + bool positive (void) const; + bool zero (void) const; + bool non_zero (void) const; + + // tests used in if(i) and if(!i) +// operator bool (void) const; + bool operator ! (void) const; + + ////////////////////////////////////////////////////////////////////////////// + // comparisons + + bool operator == (const inf&) const; + bool operator != (const inf&) const; + bool operator < (const inf&) const; + bool operator <= (const inf&) const; + bool operator > (const inf&) const; + bool operator >= (const inf&) const; + + ////////////////////////////////////////////////////////////////////////////// + // bitwise logic operations + + inf& invert (void); + inf operator ~ (void) const; + + inf& operator &= (const inf&); + inf operator & (const inf&) const; + + inf& operator |= (const inf&); + inf operator | (const inf&) const; + + inf& operator ^= (const inf&); + inf operator ^ (const inf&) const; + + inf& operator <<= (unsigned shift); + inf operator << (unsigned shift) const; + + inf& operator >>= (unsigned shift); + inf operator >> (unsigned shift) const; + + ////////////////////////////////////////////////////////////////////////////// + // arithmetic operations + + inf& negate (void); + inf operator - (void) const; + + inf& abs(void); + friend inf abs(const inf&); + + inf& operator += (const inf&); + inf operator + (const inf&) const; + + inf& operator -= (const inf&); + inf operator - (const inf&) const; + + inf& operator *= (const inf&); + inf operator * (const inf&) const; + + inf& operator /= (const inf&) throw(divide_by_zero); + inf operator / (const inf&) const throw(divide_by_zero); + + inf& operator %= (const inf&) throw(divide_by_zero); + inf operator % (const inf&) const throw(divide_by_zero); + + // combined division operator - returns the result pair(quotient,remainder) in one go + std::pair divide(const inf&) const throw(divide_by_zero); + + ////////////////////////////////////////////////////////////////////////////// + // pre- and post- increment and decrement + + inf& operator ++ (void); + inf operator ++ (int); + inf& operator -- (void); + inf operator -- (int); + + ////////////////////////////////////////////////////////////////////////////// + // string representation and I/O + + std::string image_debug(void) const; + + // conversion to a string representation + // radix must be 10, 2, 8 or 16 + std::string to_string(unsigned radix = 10) const + throw(std::invalid_argument); + + // conversion from a string + // radix == 0 - radix is deduced from the input - assumed 10 unless number is prefixed by 0b, 0 or 0x + // however, you can specify the radix to be 10, 2, 8 or 16 to force that interpretation + inf& from_string(const std::string&, unsigned radix = 0) + throw(std::invalid_argument); + + ////////////////////////////////////////////////////////////////////////////// + private: + std::string m_data; + public: + const std::string& get_bytes(void) const; + void set_bytes(const std::string&); + }; + + //////////////////////////////////////////////////////////////////////////////// + // redefine friends for gcc v4.1 + + inf abs(const inf&); + + //////////////////////////////////////////////////////////////////////////////// + + std::ostream& operator << (std::ostream&, const inf&); + std::istream& operator >> (std::istream&, inf&); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/ip_sockets.cpp b/src/stlplus/portability/ip_sockets.cpp new file mode 100644 index 0000000..6dc1fad --- /dev/null +++ b/src/stlplus/portability/ip_sockets.cpp @@ -0,0 +1,961 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Contains all the platform-specific socket handling used by the TCP and UDP classes + +// TODO - any conversion required to support IPv6 + +//////////////////////////////////////////////////////////////////////////////// + +#include "ip_sockets.hpp" +#include "dprintf.hpp" +#include + +#ifdef MSWINDOWS +// Windoze-specific includes +#include +#define ERRNO WSAGetLastError() +#define HERRNO WSAGetLastError() +#define IOCTL ioctlsocket +#define CLOSE closesocket +#define SHUT_RDWR SD_BOTH +#define EINPROGRESS WSAEINPROGRESS +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNRESET WSAECONNRESET +#define SOCKLEN_T int +#else +// Generic Unix includes +// fix for older versions of Darwin? +#define _BSD_SOCKLEN_T_ int +#include +#include +#include +#include +#include +#include +#include +#include +#define INVALID_SOCKET -1 +#define ERRNO errno +#define HERRNO h_errno +#define SOCKET int +#define SOCKET_ERROR -1 +#define IOCTL ::ioctl +#define CLOSE ::close +#define SOCKLEN_T socklen_t +#ifdef SOLARIS +// Sun put some definitions in a different place +#include +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Utilities + + // get an operating-system error message given an error code + static std::string error_string(int error) + { + std::string result = "error " + dformat("%d",error); +#ifdef MSWINDOWS + char* message = 0; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + 0, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // "User default language" + (LPTSTR)&message, + 0,0); + if (message) + { + result = message; + LocalFree(message); + } + // the error message is for some perverse reason newline terminated - remove this + if (result[result.size()-1] == '\n') + result.erase(result.end()-1); + if (result[result.size()-1] == '\r') + result.erase(result.end()-1); +#else + char* message = strerror(error); + if (message && message[0]) + result = message; +#endif + return result; + } + + // convert address:port into a sockaddr + static void convert_address(unsigned long address, unsigned short port, sockaddr& sa) + { + sa.sa_family = AF_INET; + unsigned short network_port = htons(port); + memcpy(&sa.sa_data[0], &network_port, sizeof(network_port)); + unsigned long network_address = htonl(address); + memcpy(&sa.sa_data[2], &network_address, sizeof(network_address)); + } + +// // convert host:port into a sockaddr +// static void convert_host(hostent& host, unsigned short port, sockaddr& sa) +// { +// sa.sa_family = host.h_addrtype; +// unsigned short network_port = htons(port); +// memcpy(&sa.sa_data[0], &network_port, sizeof(network_port)); +// memcpy(&sa.sa_data[2], host.h_addr, host.h_length); +// } + + // convert sockaddr to address:port + static void convert_sockaddr(const sockaddr& sa, unsigned long& address, unsigned short& port) + { + unsigned short network_port = 0; + memcpy(&network_port, &sa.sa_data[0], sizeof(network_port)); + port = ntohs(network_port); + unsigned long network_address = 0; + memcpy(&network_address, &sa.sa_data[2], sizeof(network_address)); + address = ntohl(network_address); + } + + //////////////////////////////////////////////////////////////////////////////// + // Initialisation + // Windows requires that Winsock is initialised before use and closed after + // These routines initialise once on first use and close on the destruction of the last object using it + // on non-windows platforms, I still increment/decrement the sockets count variable for diagnostic purposes + + static int sockets_count = 0; + + static int sockets_init(void) + { + int error = 0; + if (sockets_count++ == 0) + { +#ifdef MSWINDOWS + WSAData winsock_info; + // request Winsock 2.0 or higher + error = WSAStartup(MAKEWORD(2,0),&winsock_info); +#endif + } + return error; + } + + static int sockets_close(void) + { + int error = 0; + if (--sockets_count == 0) + { +#ifdef MSWINDOWS + if (WSACleanup() == SOCKET_ERROR) + error = ERRNO; +#endif + } + return error; + } + + //////////////////////////////////////////////////////////////////////////////// + // Socket Implementation - common code to manipulate a TCP socket + + class IP_socket_internals + { + private: + IP_socket_type m_type; + SOCKET m_socket; + unsigned long m_remote_address; + unsigned short m_remote_port; + mutable int m_error; + mutable std::string m_message; + unsigned m_count; + + // disable copying of the internals + IP_socket_internals(const IP_socket_internals&); + IP_socket_internals& operator=(const IP_socket_internals&); + + public: + + //////////////////////////////////////////////////////////////////////////// + // PIMPL alias counting + + void increment(void) + { + ++m_count; + } + + bool decrement(void) + { + --m_count; + return m_count == 0; + } + + //////////////////////////////////////////////////////////////////////////// + // constructors/destructors + + // construct an invalid socket + IP_socket_internals(void) : m_type(undefined_socket_type), m_socket(INVALID_SOCKET), m_error(0), m_count(1) + { + set_error(sockets_init()); + } + + // close on destroy + ~IP_socket_internals(void) + { + close(); + set_error(sockets_close()); + } + + //////////////////////////////////////////////////////////////////////////// + // initialisation, connection + + bool initialised(void) const + { + return m_socket != INVALID_SOCKET; + } + + // attach this object to a pre-opened socket + bool set(SOCKET socket, unsigned long remote_address, unsigned short remote_port) + { + if (initialised()) close(); + clear_error(); + m_socket = socket; + m_remote_address = remote_address; + m_remote_port = remote_port; + return true; + } + + // create a raw socket attached to this object + bool initialise(IP_socket_type type) + { + if (initialised()) close(); + clear_error(); + if ((type != TCP) && (type != UDP)) + { + set_error(-1, "Illegal socket type"); + return false; + } + // create an anonymous socket + m_socket = ::socket(AF_INET, ((type == TCP) ? SOCK_STREAM : SOCK_DGRAM), 0); + if (m_socket == INVALID_SOCKET) + { + set_error(ERRNO); + close(); + return false; + } + // record the type on success only + m_type = type; + // set the socket into non-blocking mode + unsigned long nonblocking = 1; + if (IOCTL(m_socket, FIONBIO, &nonblocking) == SOCKET_ERROR) + { + set_error(ERRNO); + return false; + } + return true; + } + + // function for performing IP lookup (i.e. gethostbyname) + // could be standalone but making it a member means that it can use the socket's error handler + // - remote_address: IP name or number + // - returns the IP address as a number - zero if there's an error + unsigned long ip_lookup(const std::string& remote_address) + { + unsigned long result = 0; + // Lookup the IP address to convert it into a host record + // this DOES lookup IP address names as well (not according to MS help !!) + // TODO - convert this to use ::getaddrinfo - ::gethostbyname is deprecated + hostent* host_info = ::gethostbyname(remote_address.c_str()); + if (!host_info) + { + set_error(HERRNO); + return 0; + } + // extract the address from the host info + unsigned long network_address = 0; + memcpy(&network_address, host_info->h_addr, host_info->h_length); + result = ntohl(network_address); + return result; + } + + // tests whether a socket is ready for communication + bool select(bool readable, bool writeable, unsigned wait) + { + if (!initialised()) return false; + // set up the readable set + fd_set readable_set; + fd_set* readable_set_ptr = 0; + if (readable) + { + FD_ZERO(&readable_set); + FD_SET(m_socket,&readable_set); + readable_set_ptr = &readable_set; + } + // set up the writeable set + fd_set writeable_set; + fd_set* writeable_set_ptr = 0; + if (writeable) + { + FD_ZERO(&writeable_set); + FD_SET(m_socket,&writeable_set); + writeable_set_ptr = &writeable_set; + } + // TODO - check the error set and lookup the error? + fd_set* error_set_ptr = 0; + // set up the timout value + // Note: a null pointer implements a blocking select + // a pointer to a zero value implements a zero-wait poll + // a pointer to a positive value implements a poll with a timeout + // I currently only implement polling with timeout which may be zero - no blocking + timeval timeout; + timeval* timeout_ptr = 0; + timeout.tv_sec = wait/1000000; + timeout.tv_usec = wait%1000000; + timeout_ptr = &timeout; + // now test the socket + int select_result = ::select(m_socket+1, readable_set_ptr, writeable_set_ptr, error_set_ptr, timeout_ptr); + switch(select_result) + { + case SOCKET_ERROR: + // select failed with an error - trap the error + set_error(ERRNO); + return false; + case 0: + // timeout exceeded without a connection appearing + return false; + default: + // at least one connection is pending + // TODO - do we need to do the extra socket options checking on Posix? + // TODO - does this connect in any way to the error_set above? + return true; + } + } + + // bind the socket to a port so that it can receive from specific address + bool bind(unsigned long remote_address, unsigned short local_port) + { + if (!initialised()) return false; + // name the socket and bind it to a port - this is a requirement for a server + sockaddr server; + convert_address(INADDR_ANY, local_port, server); + if (::bind(m_socket, &server, sizeof(server)) == SOCKET_ERROR) + { + set_error(ERRNO); + close(); + return false; + } + return true; + } + + // bind the socket to a port so that it can receive from any address + bool bind_any(unsigned short local_port) + { + return bind(INADDR_ANY, local_port); + } + + // set this socket up to be a listening port + // must have been bound to a local port already + // - length of backlog queue to manage - may be zero + // - returns success status + bool listen(unsigned short queue) + { + if (!initialised()) return false; + // set the port to listen for incoming connections + if (::listen(m_socket, (int)queue) == SOCKET_ERROR) + { + set_error(ERRNO); + close(); + return false; + } + return true; + } + + // test whether there's an incoming connection on the socket + // only applicable if it has been set up as a listening port + bool accept_ready(unsigned wait) + { + // the test for a connection being ready is the same as the test for whether the socket is readable + // see documentation for select + return select(true, false, wait); + } + + // accept a connection on the socket + // only applicable if it has been set up as a listening port + // - returns socket filled in with the accepted connection's details - or with the error fields set + IP_socket accept(void) + { + if (!initialised()) return IP_socket(); + IP_socket result; + // accept the connection, at the same time getting the address of the connecting client + sockaddr saddress; + SOCKLEN_T saddress_length = sizeof(saddress); + SOCKET socket = ::accept(m_socket, &saddress, &saddress_length); + if (socket == INVALID_SOCKET) + { + // only set the result socket with an error + result.m_impl->set_error(ERRNO); + return result; + } + // extract the contents of the address + unsigned long remote_address = 0; + unsigned short remote_port = 0; + convert_sockaddr(saddress, remote_address, remote_port); + result.m_impl->set(socket, remote_address, remote_port); + return result; + } + + // client connect to a server + // - remote_address: IP number of remote address to connect to + // - remote_port: port to connect to + bool connect(unsigned long remote_address, unsigned short remote_port) + { + if (!initialised()) return false; + // fill in the connection data structure + sockaddr connect_data; + convert_address(remote_address, remote_port, connect_data); + // connect binds the socket to a local address + // if connectionless it simply sets the default remote address + // if connectioned it makes the connection + if (::connect(m_socket, &connect_data, sizeof(connect_data)) == SOCKET_ERROR) + { + // the socket is non-blocking, so connect will almost certainly fail with EINPROGRESS which is not an error + // only catch real errors + int error = ERRNO; + if (error != EINPROGRESS && error != EWOULDBLOCK) + { + set_error(error); + return false; + } + } + // extract the remote connection details for local storage + convert_sockaddr(connect_data, m_remote_address, m_remote_port); + return true; + } + + // test whether a socket is connected and ready to communicate + bool connected(unsigned wait) + { + if (!initialised()) return false; + // Linux and Windows docs say test with select for whether socket is + // writable. However, a problem has been reported with Linux whereby + // the OS will report a socket as writable when it isn't + // first use the select method + if (!select(false, true, wait)) + return false; +#ifdef MSWINDOWS + // Windows needs no further processing - select method works + return true; +#else + // Posix version needs further checking using the socket options + // DJDM: socket has returned EINPROGRESS on the first attempt at connection + // it has also returned that it can be written to + // we must now ask it if it has actually connected - using getsockopt + int error = 0; + socklen_t serror = sizeof(int); + if (::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &error, &serror)==0) + // handle the error value - one of them means that the socket has connected + if (!error || error == EISCONN) + return true; + return false; +#endif + } + + bool close(void) + { + bool result = true; + if (initialised()) + { + if (shutdown(m_socket,SHUT_RDWR) == SOCKET_ERROR) + { + set_error(ERRNO); + result = false; + } + if (CLOSE(m_socket) == SOCKET_ERROR) + { + set_error(ERRNO); + result = false; + } + } + m_socket = INVALID_SOCKET; + m_remote_address = 0; + m_remote_port = 0; + return result; + } + + //////////////////////////////////////////////////////////////////////////// + // sending/receiving + + bool send_ready(unsigned wait) + { + // determines whether the socket is ready to send by testing whether it is writable + return select(false, true, wait); + } + + bool send (std::string& data) + { + if (!initialised()) return false; + // send the data - this will never block but may not send all the data + int bytes = ::send(m_socket, data.c_str(), data.size(), 0); + if (bytes == SOCKET_ERROR) + { + set_error(ERRNO); + return false; + } + // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent + data.erase(0,bytes); + return true; + } + + bool send_packet(std::string& data, unsigned long address = 0, unsigned short port = 0) + { + if (!initialised()) return false; + // if no address specified, rely on the socket having been connected (can I test this?) + // so use the standard send, otherwise use the sendto function + int bytes = 0; + if (!address) + { + bytes = ::send(m_socket, data.c_str(), data.size(), 0); + } + else + { + sockaddr saddress; + convert_address(address, port, saddress); + bytes = ::sendto(m_socket, data.c_str(), data.size(), 0, &saddress, sizeof(saddress)); + } + if (bytes == SOCKET_ERROR) + { + set_error(ERRNO); + return false; + } + // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent + data.erase(0,bytes); + return true; + } + + bool receive_ready(unsigned wait) + { + // determines whether the socket is ready to receive by testing whether it is readable + return select(true, false, wait); + } + + bool receive (std::string& data) + { + if (!initialised()) return false; + // determine how much data is available to read + unsigned long bytes = 0; + if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR) + { + set_error(ERRNO); + return false; + } + // get the data up to the amount claimed to be present - this is non-blocking + char* buffer = new char[bytes+1]; + int read = ::recv(m_socket, buffer, bytes, 0); + if (read == SOCKET_ERROR) + { + delete[] buffer; + set_error(ERRNO); + close(); + return false; + } + if (read == 0) + { + // TODO - check whether this is an appropriate conditon to close the socket + close(); + } + else + { + // this is binary data so copy the bytes including nulls + data.append(buffer,read); + } + delete[] buffer; + return true; + } + + bool receive_packet(std::string& data, unsigned long& address, unsigned short& port) + { + if (!initialised()) return false; + // determine how much data is available to read + unsigned long bytes = 0; + if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR) + { + set_error(ERRNO); + return false; + } + // get the data up to the amount claimed to be present - this is non-blocking + // also get the sender's details + char* buffer = new char[bytes+1]; + sockaddr saddress; + SOCKLEN_T saddress_length = sizeof(saddress); + int read = ::recvfrom(m_socket, buffer, bytes, 0, &saddress, &saddress_length); + if (read == SOCKET_ERROR) + { + // UDP connection reset means that a previous sent failed to deliver cos the address was unknown + // this is NOT an error with the sending server socket, which IS still usable + int error = ERRNO; + if (error != ECONNRESET) + { + delete[] buffer; + set_error(error); + close(); + return false; + } + } + // this is binary data so copy the bytes including nulls + data.append(buffer,read); + // also retrieve the sender's details + convert_sockaddr(saddress, address, port); + delete[] buffer; + return true; + } + + bool receive_packet(std::string& data) + { + // call the above and then discard the address details + unsigned long address = 0; + unsigned short port = 0; + return receive_packet(data, address, port); + } + + //////////////////////////////////////////////////////////////////////////// + // informational + + IP_socket_type type(void) const + { + return m_type; + } + + unsigned short local_port(void) const + { + if (!initialised()) return 0; + sockaddr saddress; + SOCKLEN_T saddress_length = sizeof(saddress); + if (::getsockname(m_socket, &saddress, &saddress_length) != 0) + { + set_error(ERRNO); + return 0; + } + unsigned long address = 0; + unsigned short port = 0; + convert_sockaddr(saddress, address, port); + return port; + } + + unsigned long remote_address(void) const + { + return m_remote_address; + } + + unsigned short remote_port(void) const + { + return m_remote_port; + } + + //////////////////////////////////////////////////////////////////////////// + // error handling + + void set_error (int error, const char* message = 0) const + { + if (error != 0) + { + m_error = error; + if (message && (message[0] != 0)) + m_message = message; + else + m_message = error_string(error); + } + } + + int error(void) const + { + return m_error; + } + + void clear_error (void) const + { + m_error = 0; + m_message.erase(); + } + + std::string message(void) const + { + return m_message; + } + + }; + + //////////////////////////////////////////////////////////////////////////////// + // Socket - common code to manipulate a socket + + // create an uninitialised socket + IP_socket::IP_socket(void) : m_impl(new IP_socket_internals) + { + } + + // create an initialised socket + // - type: create either a TCP or UDP socket - if neither, creates an uninitialised socket + IP_socket::IP_socket(IP_socket_type type) : m_impl(new IP_socket_internals) + { + initialise(type); + } + + // destroy the socket, closing it if open + IP_socket::~IP_socket(void) + { + if (m_impl->decrement()) + delete m_impl; + } + + //////////////////////////////////////////////////////////////////////////// + // copying is implemented as aliasing + + IP_socket::IP_socket(const IP_socket& right) : m_impl(0) + { + // make this an alias of right + m_impl = right.m_impl; + m_impl->increment(); + } + + IP_socket& IP_socket::operator=(const IP_socket& right) + { + // make self-copy safe + if (m_impl == right.m_impl) return *this; + // first dealias the existing implementation + if (m_impl->decrement()) + delete m_impl; + // now make this an alias of right + m_impl = right.m_impl; + m_impl->increment(); + return *this; + } + + //////////////////////////////////////////////////////////////////////////// + // initialisation, connection + + // initialise the socket + // - type: create either a TCP or UDP socket + // - returns success status + bool IP_socket::initialise(IP_socket_type type) + { + return m_impl->initialise(type); + } + + // test whether this is an initialised socket + // - returns whether this is initialised + bool IP_socket::initialised(void) const + { + return m_impl->initialised(); + } + + // close, i.e. disconnect the socket + // - returns a success flag + bool IP_socket::close(void) + { + return m_impl->close(); + } + + // function for performing IP lookup (i.e. gethostbyname) + // could be standalone but making it a member means that it can use the socket's error handler + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - returns the IP address as a long integer - zero if there's an error + unsigned long IP_socket::ip_lookup(const std::string& remote_address) + { + return m_impl->ip_lookup(remote_address); + } + + // test whether a socket is ready to communicate + // - readable: test whether socket is ready to read + // - writeable: test whether a socket is ready to write + // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait + // returns false if not ready or error - use error() method to tell - true if ready + bool IP_socket::select(bool readable, bool writeable, unsigned timeout) + { + return m_impl->select(readable, writeable, timeout); + } + + // bind the socket to a port so that it can receive from specific address - typically used by a client + // - remote_address: IP number of remote server to send/receive to/from + // - local_port: port on local machine to bind to the address + // - returns success flag + bool IP_socket::bind(unsigned long remote_address, unsigned short local_port) + { + return m_impl->bind(remote_address, local_port); + } + + // bind the socket to a port so that it can receive from any address - typically used by a server + // - local_port: port on local machine to bind to the address + // - returns success flag + bool IP_socket::bind_any(unsigned short local_port) + { + return m_impl->bind_any(local_port); + } + + // initialise a socket and set this socket up to be a listening port + // - queue: length of backlog queue to manage - may be zero + // - returns success status + bool IP_socket::listen(unsigned short queue) + { + return m_impl->listen(queue); + } + + // test for a connection on the object's socket - only applicable if it has been set up as a listening port + // - returns true if a connection is ready to be accepted + bool IP_socket::accept_ready(unsigned timeout) const + { + return m_impl->accept_ready(timeout); + } + + // accept a connection on the object's socket - only applicable if it has been set up as a listening port + // - returns the connection as a new socket + IP_socket IP_socket::accept(void) + { + return m_impl->accept(); + } + + // client connect to a server + // - address: IP number already lookup up with ip_lookup + // - port: port to connect to + // - returns a success flag + bool IP_socket::connect(unsigned long address, unsigned short port) + { + return m_impl->connect(address, port); + } + + // test whether a socket is connected and ready to communicate, returns on successful connect or timeout + // - timeout: how long to wait in microseconds if not connected yet + // - returns success flag + bool IP_socket::connected(unsigned timeout) + { + return m_impl->connected(timeout); + } + + //////////////////////////////////////////////////////////////////////////// + // sending/receiving + + // test whether a socket is connected and ready to send data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + bool IP_socket::send_ready(unsigned timeout) + { + return m_impl->send_ready(timeout); + } + + // send data through the socket - if the data is long only part of it may + // be sent. The sent part is removed from the data, so the same string can + // be sent again and again until it is empty. + // - data: string containing data to be sent - any data successfully sent is removed + // - returns success flag + bool IP_socket::send (std::string& data) + { + return m_impl->send(data); + } + + // send data through a connectionless (UDP) socket + // the data will be sent as a single packet + // - packet: string containing data to be sent - any data successfully sent is removed + // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote + // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote + // - returns success flag + bool IP_socket::send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port) + { + return m_impl->send_packet(packet, remote_address, remote_port); + } + + // send data through a connectionless (UDP) socket + // the data will be sent as a single packet + // only works if the socket has been connected to remote + // - packet: string containing data to be sent - any data successfully sent is removed + // - returns success flag + bool IP_socket::send_packet(std::string& packet) + { + return m_impl->send_packet(packet); + } + + // test whether a socket is connected and ready to receive data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + bool IP_socket::receive_ready(unsigned timeout) + { + return m_impl->receive_ready(timeout); + } + + // receive data through a connection-based (TCP) socket + // if the data is long only part of it may be received. The received data + // is appended to the string, building it up in stages, so the same string + // can be received again and again until all information has been + // received. + // - data: string receiving data from socket - any data successfully received is appended + // - returns success flag + bool IP_socket::receive (std::string& data) + { + return m_impl->receive(data); + } + + // receive data through a connectionless (UDP) socket + // - packet: string receiving data from socket - any data successfully received is appended + // - remote_address: returns the address of the remote host received from + // - remote_port: returns the port of the remote host received from + // - returns success flag + bool IP_socket::receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port) + { + return m_impl->receive_packet(packet, remote_address, remote_port); + } + + // variant of above which does not give back the address and port of the sender + // receive data through a connectionless (UDP) socket + // - packet: string receiving data from socket - any data successfully received is appended + // - returns success flag + bool IP_socket::receive_packet(std::string& packet) + { + return m_impl->receive_packet(packet); + } + + //////////////////////////////////////////////////////////////////////////// + // informational + + IP_socket_type IP_socket::type(void) const + { + return m_impl->type(); + } + + unsigned short IP_socket::local_port(void) const + { + return m_impl->local_port(); + } + + unsigned long IP_socket::remote_address(void) const + { + return m_impl->remote_address(); + } + + unsigned short IP_socket::remote_port(void) const + { + return m_impl->remote_port(); + } + + //////////////////////////////////////////////////////////////////////////// + // error handling + + void IP_socket::set_error (int error, const std::string& message) const + { + m_impl->set_error(error, message.c_str()); + } + + void IP_socket::clear_error (void) const + { + m_impl->clear_error(); + } + + int IP_socket::error(void) const + { + return m_impl->error(); + } + + std::string IP_socket::message(void) const + { + return m_impl->message(); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/ip_sockets.hpp b/src/stlplus/portability/ip_sockets.hpp new file mode 100644 index 0000000..ac86ebc --- /dev/null +++ b/src/stlplus/portability/ip_sockets.hpp @@ -0,0 +1,233 @@ +#ifndef STLPLUS_IP_SOCKET +#define STLPLUS_IP_SOCKET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A platform-independent (Unix and Windows anyway) interface to Internet-Protocol sockets + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include + +namespace stlplus +{ + + ////////////////////////////////////////////////////////////////////////////// + // internals + // use a PIMPL interface to hide the platform-specifics in the implementation + + class IP_socket_internals; + + //////////////////////////////////////////////////////////////////////////// + // Types of socket supported + + enum IP_socket_type {undefined_socket_type = -1, TCP = 0, UDP = 1}; + + ////////////////////////////////////////////////////////////////////////////// + // Socket class + + class IP_socket + { + public: + + //////////////////////////////////////////////////////////////////////////// + // constructors/destructors + + // create an uninitialised socket + IP_socket(void); + + // create an initialised socket + // - type: create either a TCP or UDP socket + IP_socket(IP_socket_type type); + + // destroy the socket, closing it if open + ~IP_socket(void); + + // copying is implemented as aliasing + IP_socket(const IP_socket&); + IP_socket& operator=(const IP_socket&); + + //////////////////////////////////////////////////////////////////////////// + // initialisation + + // initialise the socket + // - type: create either a TCP or UDP socket + // - returns success status + bool initialise(IP_socket_type type); + + // test whether this is an initialised socket + // - returns whether this is initialised + bool initialised(void) const; + + // close, i.e. disconnect the socket + // - returns a success flag + bool close(void); + + ////////////////////////////////////////////////////////////////////////////// + // Socket configuration + + // function for performing IP lookup (i.e. gethostbyname) + // could be standalone but making it a member means that it can use the socket's error handler + // i.e. if this fails, the sockets error code will be set - clear it to use the socket again + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - returns the IP address as a long integer - zero if there's an error + unsigned long ip_lookup(const std::string& remote_address); + + // test whether a socket is ready to communicate + // - readable: test whether socket is ready to read + // - writeable: test whether a socket is ready to write + // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait + // returns false if not ready or error - use error() method to tell - true if ready + bool select(bool readable, bool writeable, unsigned timeout = 0); + + // bind the socket to a port so that it can receive from specific address - typically used by a client + // - remote_address: IP number of remote server to send/receive to/from + // - local_port: port on local machine to bind to the address + // - returns success flag + bool bind(unsigned long remote_address, unsigned short local_port); + + // bind the socket to a port so that it can receive from any address - typically used by a server + // - local_port: port on local machine to bind to the address + // - returns success flag + bool bind_any(unsigned short local_port); + + // set this socket up to be a listening port + // socket must be bound to a port already + // - queue: length of backlog queue to manage - may be zero meaning no queue + // - returns success status + bool listen(unsigned short queue = 0); + + // test for a connection on the socket + // only applicable if it has been set up as a listening port + // - timeout: how long to wait in microseconds if not connected yet + // - returns true if a connection is ready to be accepted + bool accept_ready(unsigned timeout = 0) const; + + // accept a connection on the socket + // only applicable if it has been set up as a listening port + // - returns the connection as a new socket + IP_socket accept(void); + + // create a connection - usually used by a client + // TCP: client connect to a remote server + // UDP: setup remote address and port for sends + // - remote_address: IP number already looked up using ip_lookup + // - remote_port: port to connect to + // - returns a success flag + bool connect(unsigned long remote_address, unsigned short remote_port); + + // test whether a socket is connected and ready to communicate, returns on successful connect or timeout + // - timeout: how long to wait in microseconds if not connected yet + // - returns true if connected and ready to communicate, false if not ready or error + bool connected(unsigned timeout = 0); + + //////////////////////////////////////////////////////////////////////////// + // sending/receiving + + // test whether a socket is connected and ready to send data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + bool send_ready(unsigned timeout = 0); + + // send data through a connection-based (TCP) socket + // if the data is long only part of it may be sent. The sent part is + // removed from the data, so the same string can be sent again and again + // until it is empty. + // - data: string containing data to be sent - any data successfully sent is removed + // - returns success flag + bool send(std::string& data); + + // send data through a connectionless (UDP) socket + // the data will be sent as a single packet + // - packet: string containing data to be sent - any data successfully sent is removed + // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote + // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote + // - returns success flag + bool send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port); + + // send data through a connectionless (UDP) socket + // the data will be sent as a single packet + // only works if the socket has been connected to remote + // - packet: string containing data to be sent - any data successfully sent is removed + // - returns success flag + bool send_packet(std::string& packet); + + // test whether a socket is connected and ready to receive data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + bool receive_ready(unsigned wait = 0); + + // receive data through a connection-based (TCP) socket + // if the data is long only part of it may be received. The received data + // is appended to the string, building it up in stages, so the same string + // can be received again and again until all information has been + // received. + // - data: string receiving data from socket - any data successfully received is appended + // - returns success flag + bool receive(std::string& data); + + // receive data through a connectionless (UDP) socket + // - packet: string receiving data from socket - any data successfully received is appended + // - remote_address: returns the address of the remote host received from + // - remote_port: returns the port of the remote host received from + // - returns success flag + bool receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port); + + // variant of above which does not give back the address and port of the sender + // receive data through a connectionless (UDP) socket + // - packet: string receiving data from socket - any data successfully received is appended + // - returns success flag + bool receive_packet(std::string& packet); + + //////////////////////////////////////////////////////////////////////////// + // informational + + // gets the type of socket + // - returns undefined_socket_type, TCP or UDP + IP_socket_type type(void) const; + + // the local port number of the connection + // returns the port number, 0 if not bound to a port + unsigned short local_port(void) const; + + // the remote address of the connection + // returns the address, 0 if not connected + unsigned long remote_address(void) const; + + // the remote port number of the connection + // returns the port number, 0 if not connected to a port + unsigned short remote_port(void) const; + + //////////////////////////////////////////////////////////////////////////// + // error handling + // errors are set internally + // an error code of 0 is the test for no error, don't rely on the message being an empty string + // an error code != 0 means an error, then there will be a message explaining the error + + // indicate an error - mostly used internally, you can set your own errors - use a negative code + void set_error (int error, const std::string& message) const; + + // if an error is set it stays set - so you must clear it before further operations + void clear_error (void) const; + + // get the error code of the last error + int error(void) const; + + // get the explanatory message of the last error + std::string message(void) const; + + //////////////////////////////////////////////////////////////////////////// + + private: + friend class IP_socket_internals; + IP_socket_internals* m_impl; + }; + + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/portability.hpp b/src/stlplus/portability/portability.hpp new file mode 100644 index 0000000..942c182 --- /dev/null +++ b/src/stlplus/portability/portability.hpp @@ -0,0 +1,28 @@ +#ifndef STLPLUS_PORTABILITY +#define STLPLUS_PORTABILITY +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Allows all the STLplus portability packages to be included in one go + +//////////////////////////////////////////////////////////////////////////////// + +#include "build.hpp" +#include "debug.hpp" +#include "dprintf.hpp" +#include "dynaload.hpp" +#include "file_system.hpp" +#include "inf.hpp" +#include "subprocesses.hpp" +#include "tcp_sockets.hpp" +#include "udp_sockets.hpp" +#include "time.hpp" +#include "version.hpp" +#include "wildcard.hpp" + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/portability/portability_exceptions.hpp b/src/stlplus/portability/portability_exceptions.hpp new file mode 100644 index 0000000..c66b7bf --- /dev/null +++ b/src/stlplus/portability/portability_exceptions.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_PORTABILITY_EXCEPTIONS +#define STLPLUS_PORTABILITY_EXCEPTIONS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Adds missing arithmetic exceptions used in this library but missing from std + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // thrown by division when the divisor is zero + // This is a subclass of std::logic_error so can be caught by a generic catch clause for the superclass + + class divide_by_zero : public std::logic_error { + public: + divide_by_zero (const std::string& what_arg): std::logic_error (what_arg) { } + }; + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/portability_fixes.cpp b/src/stlplus/portability/portability_fixes.cpp new file mode 100644 index 0000000..e26ee28 --- /dev/null +++ b/src/stlplus/portability/portability_fixes.cpp @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" + +#ifdef MSWINDOWS +#include "windows.h" +#endif + +//////////////////////////////////////////////////////////////////////////////// +// problems with missing functions +//////////////////////////////////////////////////////////////////////////////// + +#ifdef MSWINDOWS +unsigned sleep(unsigned seconds) +{ + Sleep(1000*seconds); + // should return remaining time if interrupted - however Windoze Sleep cannot be interrupted + return 0; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Function for establishing endian-ness +//////////////////////////////////////////////////////////////////////////////// + +bool stlplus::little_endian(void) +{ + int sample = 1; + char* sample_bytes = (char*)&sample; + return sample_bytes[0] != 0; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/portability/portability_fixes.hpp b/src/stlplus/portability/portability_fixes.hpp new file mode 100644 index 0000000..b6a030c --- /dev/null +++ b/src/stlplus/portability/portability_fixes.hpp @@ -0,0 +1,127 @@ +#ifndef STLPLUS_PORTABILITY_FIXES +#define STLPLUS_PORTABILITY_FIXES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Contains work arounds for OS or Compiler specific problems to try to make +// them look more alike + +// It is strongly recommended that this header be included as the first +// #include in every source file + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Problem with MicroSoft defining two different macros to identify Windows +//////////////////////////////////////////////////////////////////////////////// + +#if defined(_WIN32) || defined(_WIN32_WCE) +#define MSWINDOWS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Problems with unnecessary or unfixable compiler warnings +//////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +// Microsoft Visual Studio +// shut up the following irritating warnings +// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger) +// 4305 - VC6, identifier type was converted to a smaller type +// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger) +// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it +// 4290 - VC6, C++ exception specification ignored +// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning) +// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program +// 4996 - VC8, 'xxxx' was declared deprecated +#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996) +#endif + +#ifdef __BORLANDC__ +// Borland +// Shut up the following irritating warnings +// 8026 - Functions with exception specifications are not expanded inline +// 8027 - Functions with xxx are not expanded inline +#pragma warn -8026 +#pragma warn -8027 +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Problems with redefinition of min/max in various different versions of library headers +//////////////////////////////////////////////////////////////////////////////// + +// The Windows headers define macros called max/min which conflict with the templates std::max and std::min. +// So, to avoid conflicts, MS removed the std::max/min rather than fixing the problem! +// From Visual Studio .NET (SV7, compiler version 13.00) the STL templates have been added correctly. +// For MFC compatibility, only undef min and max in non-MFC programs - some bits of MFC +// use macro min/max in headers. + +// I've created extra template function definitions minimum/maximum that avoid all the problems above + +namespace stlplus +{ + template const T& maximum(const T& l, const T& r) {return l > r ? l : r;} + template const T& minimum(const T& l, const T& r) {return l < r ? l : r;} +} + +//////////////////////////////////////////////////////////////////////////////// +// Problems with differences between namespaces +//////////////////////////////////////////////////////////////////////////////// + +// Note: not sure of the relevance of this - maybe deprecated? +// problem in gcc pre-v3 where the sub-namespaces in std aren't present +// this mean that the statement "using namespace std::rel_ops" created an error because the namespace didn't exist + +// I've done a fix here that creates an empty namespace for this case, but I +// do *not* try to move the contents of std::rel_ops into namespace std +// 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. ==) + +#ifdef __GNUC__ +namespace std +{ + namespace rel_ops + { + } +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// problems with missing functions +//////////////////////////////////////////////////////////////////////////////// + +#ifdef MSWINDOWS +unsigned sleep(unsigned seconds); +#else +#include +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Function for establishing endian-ness +//////////////////////////////////////////////////////////////////////////////// +// Different machine architectures store data using different byte orders. +// This is referred to as Big- and Little-Endian Byte Ordering. +// +// The issue is: where does a pointer to an integer type actually point? +// +// In both conventions, the address points to the left of the word but: +// Big-Endian - The most significant byte is on the left end of a word +// Little-Endian - The least significant byte is on the left end of a word +// +// Bytes are addressed left to right, so in big-endian order byte 0 is the +// msB, whereas in little-endian order byte 0 is the lsB. For example, +// Intel-based machines store data in little-endian byte order so byte 0 is +// the lsB. +// +// This function establishes byte order at run-time + +namespace stlplus +{ + bool little_endian(void); +} + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/portability/subprocesses.cpp b/src/stlplus/portability/subprocesses.cpp new file mode 100644 index 0000000..a8139c2 --- /dev/null +++ b/src/stlplus/portability/subprocesses.cpp @@ -0,0 +1,2077 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +// Bug fix by Alistair Low: kill on Windows now kills grandchild processes as +// well as the child process. This is done using jobs - which has to be +// enabled by stating that the version of Windows is at least 5.0 +#if defined(_WIN32) || defined(_WIN32_WCE) +#define _WIN32_WINNT 0x0500 +#endif + +#include "subprocesses.hpp" +#include "file_system.hpp" +#include "dprintf.hpp" +#include +#include +#include + +#ifdef MSWINDOWS +#else +#include +#include +#include +#include +#include +#endif + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // argument-vector related stuff + + static void skip_white (const std::string& command, unsigned& i) + { + while(i < command.size() && isspace(command[i])) + i++; + } + + // get_argument is the main function for breaking a string down into separate command arguments + // it mimics the way shells break down a command into an argv[] and unescapes the escaped characters on the way + + static std::string get_argument (const std::string& command, unsigned& i) + { + std::string result; +#ifdef MSWINDOWS + + // as far as I know, there is only double-quoting and no escape character in DOS + // so, how do you include a double-quote in an argument??? + + bool dquote = false; + for ( ; i < command.size(); i++) + { + char ch = command[i]; + if (!dquote && isspace(ch)) break; + if (dquote) + { + if (ch == '\"') + dquote = false; + else + result += ch; + } + else if (ch == '\"') + dquote = true; + else + result += ch; + } +#else + bool squote = false; + bool dquote = false; + bool escaped = false; + for ( ; i < command.size(); i++) + { + char ch = command[i]; + if (!squote && !dquote && !escaped && isspace(ch)) break; + if (escaped) + { + result += ch; + escaped = false; + } + else if (squote) + { + if (ch == '\'') + squote = false; + else + result += ch; + } + else if (ch == '\\') + escaped = true; + else if (dquote) + { + if (ch == '\"') + dquote = false; + else + result += ch; + } + else if (ch == '\'') + squote = true; + else if (ch == '\"') + dquote = true; + else + result += ch; + } +#endif + + return result; + } + + + // this function performs the reverse of the above on a single argument + // it escapes special characters and quotes the argument if necessary ready for shell interpretation + + static std::string make_argument (const std::string& arg) + { + std::string result; + bool needs_quotes = false; + + for (unsigned i = 0; i < arg.size(); i++) + { + switch (arg[i]) + { + // set of characters requiring escapes +#ifdef MSWINDOWS +#else + case '\\': case '\'': case '\"': case '`': case '(': case ')': + case '&': case '|': case '<': case '>': case '*': case '?': case '!': + result += '\\'; + result += arg[i]; + break; +#endif + // set of whitespace characters that force quoting + case ' ': + result += arg[i]; + needs_quotes = true; + break; + default: + result += arg[i]; + break; + } + } + + if (needs_quotes) + { + result.insert(result.begin(), '"'); + result += '"'; + } + return result; + } + + static char* copy_string (const char* str) + { + char* result = new char[strlen(str)+1]; + strcpy(result,str); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + + arg_vector::arg_vector (void) + { + m_argv = 0; + } + + arg_vector::arg_vector (const arg_vector& a) + { + m_argv = 0; + *this = a; + } + + arg_vector::arg_vector (char** a) + { + m_argv = 0; + *this = a; + } + + arg_vector::arg_vector (const std::string& command) + { + m_argv = 0; + *this = command; + } + + arg_vector::arg_vector (const char* command) + { + m_argv = 0; + *this = command; + } + + arg_vector::~arg_vector (void) + { + clear(); + } + + arg_vector& arg_vector::operator = (const arg_vector& a) + { + return *this = a.m_argv; + } + + arg_vector& arg_vector::operator = (char** argv) + { + clear(); + for (unsigned i = 0; argv[i]; i++) + operator += (argv[i]); + return *this; + } + + arg_vector& arg_vector::operator = (const std::string& command) + { + clear(); + for (unsigned i = 0; i < command.size(); ) + { + std::string argument = get_argument(command, i); + operator += (argument); + skip_white(command, i); + } + return *this; + } + + arg_vector& arg_vector::operator = (const char* command) + { + return operator = (std::string(command)); + } + + arg_vector& arg_vector::operator += (const std::string& str) + { + insert(size(), str); + return *this; + } + + arg_vector& arg_vector::operator -= (const std::string& str) + { + insert(0, str); + return *this; + } + + void arg_vector::insert (unsigned index, const std::string& str) throw(std::out_of_range) + { + if (index > size()) throw std::out_of_range("arg_vector::insert"); + // copy up to but not including index, then add the new argument, then copy the rest + char** new_argv = new char*[size()+2]; + unsigned i = 0; + for ( ; i < index; i++) + new_argv[i] = copy_string(m_argv[i]); + new_argv[index] = copy_string(str.c_str()); + for ( ; i < size(); i++) + new_argv[i+1] = copy_string(m_argv[i]); + new_argv[i+1] = 0; + clear(); + m_argv = new_argv; + } + + void arg_vector::clear (unsigned index) throw(std::out_of_range) + { + if (index >= size()) throw std::out_of_range("arg_vector::clear"); + // copy up to index, skip it, then copy the rest + char** new_argv = new char*[size()]; + unsigned i = 0; + for ( ; i < index; i++) + new_argv[i] = copy_string(m_argv[i]); + i++; + for ( ; i < size(); i++) + new_argv[i-1] = copy_string(m_argv[i]); + new_argv[i-1] = 0; + clear(); + m_argv = new_argv; + } + + void arg_vector::clear(void) + { + if (m_argv) + { + for (unsigned i = 0; m_argv[i]; i++) + delete[] m_argv[i]; + delete[] m_argv; + m_argv = 0; + } + } + + unsigned arg_vector::size (void) const + { + unsigned i = 0; + if (m_argv) + while (m_argv[i]) + i++; + return i; + } + + arg_vector::operator char** (void) const + { + return m_argv; + } + + char** arg_vector::argv (void) const + { + return m_argv; + } + + char* arg_vector::operator [] (unsigned index) const throw(std::out_of_range) + { + if (index >= size()) throw std::out_of_range("arg_vector::operator[]"); + return m_argv[index]; + } + + char* arg_vector::argv0 (void) const throw(std::out_of_range) + { + return operator [] (0); + } + + std::string arg_vector::image (void) const + { + std::string result; + for (unsigned i = 0; i < size(); i++) + { + if (i) result += ' '; + result += make_argument(m_argv[i]); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // environment-vector + + // Windoze environment is a single string containing null-terminated + // name=value strings and the whole terminated by a null + + // Unix environment is a null-terminated vector of pointers to null-terminated strings + + //////////////////////////////////////////////////////////////////////////////// + // platform specifics + +#ifdef MSWINDOWS + // Windows utilities + + // Windows environment variables are case-insensitive and I do comparisons by converting to lowercase + static std::string lowercase(const std::string& val) + { + std::string text = val; + for (unsigned i = 0; i < text.size(); i++) + text[i] = tolower(text[i]); + return text; + } + + static unsigned envp_size(const char* envp) + { + unsigned size = 0; + while (envp[size] || (size > 0 && envp[size-1])) size++; + size++; + return size; + } + + static void envp_extract(std::string& name, std::string& value, const char* envp, unsigned& envi) + { + name.erase(); + value.erase(); + if (!envp[envi]) return; + // some special variables start with '=' so ensure at least one character in the name + name += envp[envi++]; + while(envp[envi] != '=') + name += envp[envi++]; + envi++; + while(envp[envi]) + value += envp[envi++]; + envi++; + } + + static void envp_append(const std::string& name, const std::string& value, char* envp, unsigned& envi) + { + for (unsigned i = 0; i < name.size(); i++) + envp[envi++] = name[i]; + envp[envi++] = '='; + for (unsigned j = 0; j < value.size(); j++) + envp[envi++] = value[j]; + envp[envi++] = '\0'; + envp[envi] = '\0'; + } + + static char* envp_copy(const char* envp) + { + unsigned size = envp_size(envp); + char* result = new char[size]; + result[0] = '\0'; + unsigned i = 0; + unsigned j = 0; + while(envp[i]) + { + std::string name; + std::string value; + envp_extract(name, value, envp, i); + envp_append(name, value, result, j); + } + return result; + } + + static void envp_clear(char*& envp) + { + if (envp) + { + delete[] envp; + envp = 0; + } + } + + static bool envp_equal(const std::string& left, const std::string& right) + { + return lowercase(left) == lowercase(right); + } + + static bool envp_less(const std::string& left, const std::string& right) + { + return lowercase(left) < lowercase(right); + } + +#else + // Unix utilities + + extern char** environ; + + static unsigned envp_size(char* const* envp) + { + unsigned size = 0; + while(envp[size]) size++; + size++; + return size; + } + + static void envp_extract(std::string& name, std::string& value, char* const* envp, unsigned& envi) + { + name = ""; + value = ""; + if (!envp[envi]) return; + unsigned i = 0; + while(envp[envi][i] != '=') + name += envp[envi][i++]; + i++; + while(envp[envi][i]) + value += envp[envi][i++]; + envi++; + } + + static void envp_append(const std::string& name, const std::string& value, char** envp, unsigned& envi) + { + std::string entry = name + "=" + value; + envp[envi] = copy_string(entry.c_str()); + envi++; + envp[envi] = 0; + } + + static char** envp_copy(char* const* envp) + { + unsigned size = envp_size(envp); + char** result = new char*[size]; + unsigned i = 0; + unsigned j = 0; + while(envp[i]) + { + std::string name; + std::string value; + envp_extract(name, value, envp, i); + envp_append(name, value, result, j); + } + return result; + } + + static void envp_clear(char**& envp) + { + if (envp) + { + for (unsigned i = 0; envp[i]; i++) + delete[] envp[i]; + delete[] envp; + envp = 0; + } + } + + static bool envp_equal(const std::string& left, const std::string& right) + { + return left == right; + } + + static bool envp_less(const std::string& left, const std::string& right) + { + return left < right; + } + +#endif + //////////////////////////////////////////////////////////////////////////////// + + env_vector::env_vector(void) + { +#ifdef MSWINDOWS + char* env = (char*)GetEnvironmentStringsA(); + m_env = envp_copy(env); + FreeEnvironmentStringsA(env); +#else + m_env = envp_copy(::environ); +#endif + } + + env_vector::env_vector (const env_vector& a) + { + m_env = 0; + *this = a; + } + + env_vector::~env_vector (void) + { + clear(); + } + + env_vector& env_vector::operator = (const env_vector& a) + { + clear(); + m_env = envp_copy(a.m_env); + return *this; + } + + void env_vector::clear(void) + { + envp_clear(m_env); + } + + void env_vector::add(const std::string& name, const std::string& value) + { + // the trick is to add the value in alphabetic order + // this is done by copying the existing environment string to a new + // string, inserting the new value when a name greater than it is found + unsigned size = envp_size(m_env); +#ifdef MSWINDOWS + unsigned new_size = size + name.size() + value.size() + 2; + char* new_v = new char[new_size]; + new_v[0] = '\0'; +#else + unsigned new_size = size + 1; + char** new_v = new char*[new_size]; + new_v[0] = 0; +#endif + // now extract each name=value pair and check the ordering + bool added = false; + unsigned i = 0; + unsigned j = 0; + while(m_env[i]) + { + std::string current_name; + std::string current_value; + envp_extract(current_name, current_value, m_env, i); + if (envp_equal(name,current_name)) + { + // replace an existing value + envp_append(name, value, new_v, j); + } + else if (!added && envp_less(name,current_name)) + { + // add the new value first, then the existing one + envp_append(name, value, new_v, j); + envp_append(current_name, current_value, new_v, j); + added = true; + } + else + { + // just add the existing value + envp_append(current_name, current_value, new_v, j); + } + } + if (!added) + envp_append(name, value, new_v, j); + envp_clear(m_env); + m_env = new_v; + } + + + bool env_vector::remove (const std::string& name) + { + bool result = false; + // this is done by copying the existing environment string to a new string, but excluding the specified name + unsigned size = envp_size(m_env); +#ifdef MSWINDOWS + char* new_v = new char[size]; + new_v[0] = '\0'; +#else + char** new_v = new char*[size]; + new_v[0] = 0; +#endif + unsigned i = 0; + unsigned j = 0; + while(m_env[i]) + { + std::string current_name; + std::string current_value; + envp_extract(current_name, current_value, m_env, i); + if (envp_equal(name,current_name)) + result = true; + else + envp_append(current_name, current_value, new_v, j); + } + envp_clear(m_env); + m_env = new_v; + return result; + } + + std::string env_vector::operator [] (const std::string& name) const + { + return get(name); + } + + std::string env_vector::get (const std::string& name) const + { + unsigned i = 0; + while(m_env[i]) + { + std::string current_name; + std::string current_value; + envp_extract(current_name, current_value, m_env, i); + if (envp_equal(name,current_name)) + return current_value; + } + return std::string(); + } + + unsigned env_vector::size (void) const + { + unsigned i = 0; +#ifdef MSWINDOWS + unsigned offset = 0; + while(m_env[offset]) + { + std::string current_name; + std::string current_value; + envp_extract(current_name, current_value, m_env, offset); + i++; + } +#else + while(m_env[i]) + i++; +#endif + + return i; + } + + std::pair env_vector::operator [] (unsigned index) const throw(std::out_of_range) + { + return get(index); + } + + std::pair env_vector::get (unsigned index) const throw(std::out_of_range) + { + if (index >= size()) throw std::out_of_range("arg_vector::get"); + unsigned j = 0; + for (unsigned i = 0; i < index; i++) + { + std::string current_name; + std::string current_value; + envp_extract(current_name, current_value, m_env, j); + } + std::string name; + std::string value; + envp_extract(name, value, m_env, j); + return std::make_pair(name,value); + } + + ENVIRON_TYPE env_vector::envp (void) const + { + return m_env; + } + + //////////////////////////////////////////////////////////////////////////////// + // Synchronous subprocess + // Win32 implementation mostly cribbed from MSDN examples and then made (much) more readable + // Unix implementation mostly from man pages and bitter experience + //////////////////////////////////////////////////////////////////////////////// + +#ifdef MSWINDOWS + + subprocess::subprocess(void) + { + m_pid.hProcess = 0; + m_job = 0; + m_child_in = 0; + m_child_out = 0; + m_child_err = 0; + m_err = 0; + m_status = 0; + } + +#else + + subprocess::subprocess(void) + { + m_pid = -1; + m_child_in = -1; + m_child_out = -1; + m_child_err = -1; + m_err = 0; + m_status = 0; + } + +#endif + +#ifdef MSWINDOWS + + subprocess::~subprocess(void) + { + if (m_pid.hProcess != 0) + { + close_stdin(); + close_stdout(); + close_stderr(); + kill(); + WaitForSingleObject(m_pid.hProcess, INFINITE); + CloseHandle(m_pid.hThread); + CloseHandle(m_pid.hProcess); + CloseHandle(m_job); + } + } + +#else + + subprocess::~subprocess(void) + { + if (m_pid != -1) + { + close_stdin(); + close_stdout(); + close_stderr(); + kill(); + for (;;) + { + int wait_status = 0; + int wait_ret_val = waitpid(m_pid, &wait_status, 0); + if (wait_ret_val != -1 || errno != EINTR) break; + } + } + } + +#endif + + void subprocess::add_variable(const std::string& name, const std::string& value) + { + m_env.add(name, value); + } + + bool subprocess::remove_variable(const std::string& name) + { + return m_env.remove(name); + } + +#ifdef MSWINDOWS + + bool subprocess::spawn(const std::string& path, const arg_vector& argv, + bool connect_stdin, bool connect_stdout, bool connect_stderr) + { + bool result = true; + // first create the pipes to be used to connect to the child stdin/out/err + // If no pipes requested, then connect to the parent stdin/out/err + // for some reason you have to create a pipe handle, then duplicate it + // This is not well explained in MSDN but seems to work + PIPE_TYPE parent_stdin = 0; + if (!connect_stdin) + parent_stdin = GetStdHandle(STD_INPUT_HANDLE); + else + { + PIPE_TYPE tmp = 0; + SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; + CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0); + DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + } + + PIPE_TYPE parent_stdout = 0; + if (!connect_stdout) + parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + else + { + PIPE_TYPE tmp = 0; + SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; + CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0); + DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + } + + PIPE_TYPE parent_stderr = 0; + if (!connect_stderr) + parent_stderr = GetStdHandle(STD_ERROR_HANDLE); + else + { + PIPE_TYPE tmp = 0; + SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; + CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0); + DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + } + + // Now create the subprocess + // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work + // Note that the child will inherit a copy of the pipe handles + STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0, + STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0, + parent_stdin,parent_stdout,parent_stderr}; + bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0; + // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them + if (connect_stdin) CloseHandle(parent_stdin); + if (connect_stdout) CloseHandle(parent_stdout); + if (connect_stderr) CloseHandle(parent_stderr); + if (!created) + { + m_err = GetLastError(); + close_stdin(); + close_stdout(); + close_stderr(); + result = false; + } + else + { + m_job = CreateJobObject(NULL, NULL); + AssignProcessToJobObject(m_job, m_pid.hProcess); + ResumeThread(m_pid.hThread); + + // The child process is now running so call the user's callback + // The convention is that the callback can return false, in which case this will kill the child (if its still running) + if (!callback()) + { + result = false; + kill(); + } + close_stdin(); + close_stdout(); + close_stderr(); + // wait for the child to finish + // TODO - kill the child if a timeout happens + WaitForSingleObject(m_pid.hProcess, INFINITE); + DWORD exit_status = 0; + if (!GetExitCodeProcess(m_pid.hProcess, &exit_status)) + { + m_err = GetLastError(); + result = false; + } + else if (exit_status != 0) + result = false; + m_status = (int)exit_status; + CloseHandle(m_pid.hThread); + CloseHandle(m_pid.hProcess); + CloseHandle(m_job); + } + m_pid.hProcess = 0; + return result; + } + +#else + + bool subprocess::spawn(const std::string& path, const arg_vector& argv, + bool connect_stdin, bool connect_stdout, bool connect_stderr) + { + bool result = true; + // first create the pipes to be used to connect to the child stdin/out/err + + int stdin_pipe [2] = {-1, -1}; + if (connect_stdin) + pipe(stdin_pipe); + + int stdout_pipe [2] = {-1, -1}; + if (connect_stdout) + pipe(stdout_pipe); + + int stderr_pipe [2] = {-1, -1}; + if (connect_stderr) + pipe(stderr_pipe); + + // now create the subprocess + // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec + m_pid = ::fork(); + switch(m_pid) + { + case -1: // failed to fork + m_err = errno; + if (connect_stdin) + { + ::close(stdin_pipe[0]); + ::close(stdin_pipe[1]); + } + if (connect_stdout) + { + ::close(stdout_pipe[0]); + ::close(stdout_pipe[1]); + } + if (connect_stderr) + { + ::close(stderr_pipe[0]); + ::close(stderr_pipe[1]); + } + result = false; + break; + case 0: // in child; + { + // for each pipe, close the end of the duplicated pipe that is being used by the parent + // and connect the child's end of the pipe to the appropriate standard I/O device + if (connect_stdin) + { + ::close(stdin_pipe[1]); + dup2(stdin_pipe[0],STDIN_FILENO); + } + if (connect_stdout) + { + ::close(stdout_pipe[0]); + dup2(stdout_pipe[1],STDOUT_FILENO); + } + if (connect_stderr) + { + ::close(stderr_pipe[0]); + dup2(stderr_pipe[1],STDERR_FILENO); + } + execve(path.c_str(), argv.argv(), m_env.envp()); + // will only ever get here if the exec() failed completely - *must* now exit the child process + // by using errno, the parent has some chance of diagnosing the cause of the problem + exit(errno); + } + break; + default: // in parent + { + // for each pipe, close the end of the duplicated pipe that is being used by the child + // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback + if (connect_stdin) + { + ::close(stdin_pipe[0]); + m_child_in = stdin_pipe[1]; + } + if (connect_stdout) + { + ::close(stdout_pipe[1]); + m_child_out = stdout_pipe[0]; + } + if (connect_stderr) + { + ::close(stderr_pipe[1]); + m_child_err = stderr_pipe[0]; + } + // call the user's callback + if (!callback()) + { + result = false; + kill(); + } + // close the pipes and wait for the child to finish + // wait exits on a signal which may be the child signalling its exit or may be an interrupt + close_stdin(); + close_stdout(); + close_stderr(); + int wait_status = 0; + for (;;) + { + int wait_ret_val = waitpid(m_pid, &wait_status, 0); + if (wait_ret_val != -1 || errno != EINTR) break; + } + // establish whether an error occurred + if (WIFSIGNALED(wait_status)) + { + // set_error(errno); + m_status = WTERMSIG(wait_status); + result = false; + } + else if (WIFEXITED(wait_status)) + { + m_status = WEXITSTATUS(wait_status); + if (m_status != 0) + result = false; + } + m_pid = -1; + } + break; + } + return result; + } + +#endif + + bool subprocess::spawn(const std::string& command_line, + bool connect_stdin, bool connect_stdout, bool connect_stderr) + { + arg_vector arguments = command_line; + if (arguments.size() == 0) return false; + std::string path = path_lookup(arguments.argv0()); + if (path.empty()) return false; + return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr); + } + + bool subprocess::callback(void) + { + return true; + } + +#ifdef MSWINDOWS + + bool subprocess::kill (void) + { + if (!m_pid.hProcess) return false; + close_stdin(); + close_stdout(); + close_stderr(); + if (!TerminateJobObject(m_job, (UINT)-1)) + { + m_err = GetLastError(); + return false; + } + return true; + } + +#else + + bool subprocess::kill (void) + { + if (m_pid == -1) return false; + close_stdin(); + close_stdout(); + close_stderr(); + if (::kill(m_pid, SIGINT) == -1) + { + m_err = errno; + return false; + } + return true; + } + +#endif + +#ifdef MSWINDOWS + + int subprocess::write_stdin (std::string& buffer) + { + if (m_child_in == 0) return -1; + // do a blocking write of the whole buffer + DWORD bytes = 0; + if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0)) + { + m_err = GetLastError(); + close_stdin(); + return -1; + } + // now discard that part of the buffer that was written + if (bytes > 0) + buffer.erase(0, bytes); + return bytes; + } + +#else + + int subprocess::write_stdin (std::string& buffer) + { + if (m_child_in == -1) return -1; + // do a blocking write of the whole buffer + int bytes = write(m_child_in, buffer.c_str(), buffer.size()); + if (bytes == -1) + { + m_err = errno; + close_stdin(); + return -1; + } + // now discard that part of the buffer that was written + if (bytes > 0) + buffer.erase(0, bytes); + return bytes; + } + +#endif + +#ifdef MSWINDOWS + + int subprocess::read_stdout (std::string& buffer) + { + if (m_child_out == 0) return -1; + DWORD bytes = 0; + DWORD buffer_size = 256; + char* tmp = new char[buffer_size]; + if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0)) + { + if (GetLastError() != ERROR_BROKEN_PIPE) + m_err = GetLastError(); + close_stdout(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stdout(); + delete[] tmp; + return -1; + } + buffer.append(tmp, bytes); + delete[] tmp; + return (int)bytes; + } + +#else + + int subprocess::read_stdout (std::string& buffer) + { + if (m_child_out == -1) return -1; + int buffer_size = 256; + char* tmp = new char[buffer_size]; + int bytes = read(m_child_out, tmp, buffer_size); + if (bytes == -1) + { + m_err = errno; + close_stdout(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stdout(); + delete[] tmp; + return -1; + } + buffer.append(tmp, bytes); + delete[] tmp; + return bytes; + } + +#endif + +#ifdef MSWINDOWS + + int subprocess::read_stderr(std::string& buffer) + { + if (m_child_err == 0) return -1; + DWORD bytes = 0; + DWORD buffer_size = 256; + char* tmp = new char[buffer_size]; + if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0)) + { + if (GetLastError() != ERROR_BROKEN_PIPE) + m_err = GetLastError(); + close_stderr(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stderr(); + delete[] tmp; + return -1; + } + buffer.append(tmp, bytes); + delete[] tmp; + return (int)bytes; + } + +#else + + int subprocess::read_stderr (std::string& buffer) + { + if (m_child_err == -1) return -1; + int buffer_size = 256; + char* tmp = new char[buffer_size]; + int bytes = read(m_child_err, tmp, buffer_size); + if (bytes == -1) + { + m_err = errno; + close_stderr(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stderr(); + delete[] tmp; + return -1; + } + buffer.append(tmp, bytes); + delete[] tmp; + return bytes; + } + +#endif + +#ifdef MSWINDOWS + + void subprocess::close_stdin (void) + { + if (m_child_in) + { + CloseHandle(m_child_in); + m_child_in = 0; + } + } + +#else + + void subprocess::close_stdin (void) + { + if (m_child_in != -1) + { + ::close(m_child_in); + m_child_in = -1; + } + } + +#endif + +#ifdef MSWINDOWS + + void subprocess::close_stdout (void) + { + if (m_child_out) + { + CloseHandle(m_child_out); + m_child_out = 0; + } + } + +#else + + void subprocess::close_stdout (void) + { + if (m_child_out != -1) + { + ::close(m_child_out); + m_child_out = -1; + } + } + +#endif + +#ifdef MSWINDOWS + + void subprocess::close_stderr (void) + { + if (m_child_err) + { + CloseHandle(m_child_err); + m_child_err = 0; + } + } + +#else + + void subprocess::close_stderr (void) + { + if (m_child_err != -1) + { + ::close(m_child_err); + m_child_err = -1; + } + } + +#endif + + bool subprocess::error(void) const + { + return m_err != 0; + } + + int subprocess::error_number(void) const + { + return m_err; + } + +#ifdef MSWINDOWS + + std::string subprocess::error_text(void) const + { + if (m_err == 0) return std::string(); + char* message; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + 0, + m_err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&message, + 0,0); + std::string result = message; + LocalFree(message); + // the error message is for some perverse reason newline terminated - remove this + if (result[result.size()-1] == '\n') + result.erase(result.end()-1); + if (result[result.size()-1] == '\r') + result.erase(result.end()-1); + return result; + } + +#else + + std::string subprocess::error_text(void) const + { + if (m_err == 0) return std::string(); + char* text = strerror(m_err); + if (text) return std::string(text); + return "error number " + dformat("%d",m_err); + } + +#endif + + int subprocess::exit_status(void) const + { + return m_status; + } + + //////////////////////////////////////////////////////////////////////////////// + // backtick subprocess and operations + + backtick_subprocess::backtick_subprocess(void) : subprocess() + { + } + + bool backtick_subprocess::callback(void) + { + for (;;) + { + std::string buffer; + int read_size = read_stdout(buffer); + if (read_size < 0) break; + m_text += buffer; + } + return !error(); + } + + bool backtick_subprocess::spawn(const std::string& path, const arg_vector& argv) + { + return subprocess::spawn(path, argv, false, true, false); + } + + bool backtick_subprocess::spawn(const std::string& command_line) + { + return subprocess::spawn(command_line, false, true, false); + } + + std::vector backtick_subprocess::text(void) const + { + std::vector result; + // convert the raw text into a vector of strings, each corresponding to a line + // in the process, strip out platform-specific line-endings + for (unsigned i = 0; i < m_text.size(); i++) + { + // handle any kind of line-ending - Dos, Unix or MacOS + switch(m_text[i]) + { + case '\xd': // carriage-return - optionally followed by linefeed + { + // discard optional following linefeed + if ((i+1 < m_text.size()) && (m_text[i+1] == '\xa')) + i++; + // add a new line to the end of the vector + result.push_back(std::string()); + break; + } + case '\xa': // linefeed + { + // add a new line to the end of the vector + result.push_back(std::string()); + break; + } + default: + { + result.back() += m_text[i]; + break; + } + } + } + // tidy up - if the last line ended with a newline, the vector will end with an empty string - discard this + if ((result.size()) > 0 && result.back().empty()) + result.erase(result.end()-1); + return result; + } + + std::vector backtick(const std::string& path, const arg_vector& argv) + { + backtick_subprocess sub; + sub.spawn(path, argv); + return sub.text(); + } + + std::vector backtick(const std::string& command_line) + { + backtick_subprocess sub; + sub.spawn(command_line); + return sub.text(); + } + + //////////////////////////////////////////////////////////////////////////////// + // Asynchronous subprocess + +#ifdef MSWINDOWS + + async_subprocess::async_subprocess(void) + { + m_pid.hProcess = 0; + m_job = 0; + m_child_in = 0; + m_child_out = 0; + m_child_err = 0; + m_err = 0; + m_status = 0; + } + +#else + + async_subprocess::async_subprocess(void) + { + m_pid = -1; + m_child_in = -1; + m_child_out = -1; + m_child_err = -1; + m_err = 0; + m_status = 0; + } + +#endif + +#ifdef MSWINDOWS + + async_subprocess::~async_subprocess(void) + { + if (m_pid.hProcess != 0) + { + close_stdin(); + close_stdout(); + close_stderr(); + kill(); + WaitForSingleObject(m_pid.hProcess, INFINITE); + CloseHandle(m_pid.hThread); + CloseHandle(m_pid.hProcess); + CloseHandle(m_job); + } + } + +#else + + async_subprocess::~async_subprocess(void) + { + if (m_pid != -1) + { + close_stdin(); + close_stdout(); + close_stderr(); + kill(); + for (;;) + { + int wait_status = 0; + int wait_ret_val = waitpid(m_pid, &wait_status, 0); + if (wait_ret_val != -1 || errno != EINTR) break; + } + } + } + +#endif + + void async_subprocess::set_error(int e) + { + m_err = e; + } + + void async_subprocess::add_variable(const std::string& name, const std::string& value) + { + m_env.add(name, value); + } + + bool async_subprocess::remove_variable(const std::string& name) + { + return m_env.remove(name); + } + +#ifdef MSWINDOWS + + bool async_subprocess::spawn(const std::string& path, const arg_vector& argv, + bool connect_stdin, bool connect_stdout, bool connect_stderr) + { + bool result = true; + // first create the pipes to be used to connect to the child stdin/out/err + // If no pipes requested, then connect to the parent stdin/out/err + // for some reason you have to create a pipe handle, then duplicate it + // This is not well explained in MSDN but seems to work + PIPE_TYPE parent_stdin = 0; + if (!connect_stdin) + parent_stdin = GetStdHandle(STD_INPUT_HANDLE); + else + { + PIPE_TYPE tmp = 0; + SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; + CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0); + DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + } + + PIPE_TYPE parent_stdout = 0; + if (!connect_stdout) + parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + else + { + PIPE_TYPE tmp = 0; + SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; + CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0); + DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + } + + PIPE_TYPE parent_stderr = 0; + if (!connect_stderr) + parent_stderr = GetStdHandle(STD_ERROR_HANDLE); + else + { + PIPE_TYPE tmp = 0; + SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE}; + CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0); + DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + } + + // Now create the subprocess + // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work + // Note that the child will inherit a copy of the pipe handles + STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0, + STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0, + parent_stdin,parent_stdout,parent_stderr}; + bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0; + // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them + if (connect_stdin) CloseHandle(parent_stdin); + if (connect_stdout) CloseHandle(parent_stdout); + if (connect_stderr) CloseHandle(parent_stderr); + if (!created) + { + set_error(GetLastError()); + close_stdin(); + close_stdout(); + close_stderr(); + result = false; + } + else + { + m_job = CreateJobObject(NULL, NULL); + AssignProcessToJobObject(m_job, m_pid.hProcess); + ResumeThread(m_pid.hThread); + } + return result; + } + +#else + + bool async_subprocess::spawn(const std::string& path, const arg_vector& argv, + bool connect_stdin, bool connect_stdout, bool connect_stderr) + { + bool result = true; + // first create the pipes to be used to connect to the child stdin/out/err + + int stdin_pipe [2] = {-1, -1}; + if (connect_stdin) + pipe(stdin_pipe); + + int stdout_pipe [2] = {-1, -1}; + if (connect_stdout) + pipe(stdout_pipe); + + int stderr_pipe [2] = {-1, -1}; + if (connect_stderr) + pipe(stderr_pipe); + + // now create the subprocess + // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec + m_pid = ::fork(); + switch(m_pid) + { + case -1: // failed to fork + set_error(errno); + if (connect_stdin) + { + ::close(stdin_pipe[0]); + ::close(stdin_pipe[1]); + } + if (connect_stdout) + { + ::close(stdout_pipe[0]); + ::close(stdout_pipe[1]); + } + if (connect_stderr) + { + ::close(stderr_pipe[0]); + ::close(stderr_pipe[1]); + } + result = false; + break; + case 0: // in child; + { + // for each pipe, close the end of the duplicated pipe that is being used by the parent + // and connect the child's end of the pipe to the appropriate standard I/O device + if (connect_stdin) + { + ::close(stdin_pipe[1]); + dup2(stdin_pipe[0],STDIN_FILENO); + } + if (connect_stdout) + { + ::close(stdout_pipe[0]); + dup2(stdout_pipe[1],STDOUT_FILENO); + } + if (connect_stderr) + { + ::close(stderr_pipe[0]); + dup2(stderr_pipe[1],STDERR_FILENO); + } + execve(path.c_str(), argv.argv(), m_env.envp()); + // will only ever get here if the exec() failed completely - *must* now exit the child process + // by using errno, the parent has some chance of diagnosing the cause of the problem + exit(errno); + } + break; + default: // in parent + { + // for each pipe, close the end of the duplicated pipe that is being used by the child + // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback + if (connect_stdin) + { + ::close(stdin_pipe[0]); + m_child_in = stdin_pipe[1]; + if (fcntl(m_child_in, F_SETFL, O_NONBLOCK) == -1) + { + set_error(errno); + result = false; + } + } + if (connect_stdout) + { + ::close(stdout_pipe[1]); + m_child_out = stdout_pipe[0]; + if (fcntl(m_child_out, F_SETFL, O_NONBLOCK) == -1) + { + set_error(errno); + result = false; + } + } + if (connect_stderr) + { + ::close(stderr_pipe[1]); + m_child_err = stderr_pipe[0]; + if (fcntl(m_child_err, F_SETFL, O_NONBLOCK) == -1) + { + set_error(errno); + result = false; + } + } + } + break; + } + return result; + } + +#endif + + bool async_subprocess::spawn(const std::string& command_line, + bool connect_stdin, bool connect_stdout, bool connect_stderr) + { + arg_vector arguments = command_line; + if (arguments.size() == 0) return false; + std::string path = path_lookup(arguments.argv0()); + if (path.empty()) return false; + return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr); + } + + bool async_subprocess::callback(void) + { + return true; + } + +#ifdef MSWINDOWS + + bool async_subprocess::tick(void) + { + bool result = true; + if (!callback()) + kill(); + DWORD exit_status = 0; + if (!GetExitCodeProcess(m_pid.hProcess, &exit_status)) + { + set_error(GetLastError()); + result = false; + } + else if (exit_status != STILL_ACTIVE) + { + CloseHandle(m_pid.hThread); + CloseHandle(m_pid.hProcess); + CloseHandle(m_job); + m_pid.hProcess = 0; + result = false; + } + m_status = (int)exit_status; + return result; + } + +#else + + bool async_subprocess::tick(void) + { + bool result = true; + if (!callback()) + kill(); + int wait_status = 0; + int wait_ret_val = waitpid(m_pid, &wait_status, WNOHANG); + if (wait_ret_val == -1 && errno != EINTR) + { + set_error(errno); + result = false; + } + else if (wait_ret_val != 0) + { + // the only states that indicate a terminated child are WIFSIGNALLED and WIFEXITED + if (WIFSIGNALED(wait_status)) + { + // set_error(errno); + m_status = WTERMSIG(wait_status); + result = false; + } + else if (WIFEXITED(wait_status)) + { + // child has exited + m_status = WEXITSTATUS(wait_status); + result = false; + } + } + if (!result) + m_pid = -1; + return result; + } + +#endif + +#ifdef MSWINDOWS + + bool async_subprocess::kill(void) + { + if (!m_pid.hProcess) return false; + close_stdin(); + close_stdout(); + close_stderr(); + if (!TerminateJobObject(m_job, (UINT)-1)) + { + set_error(GetLastError()); + return false; + } + return true; + } + +#else + + bool async_subprocess::kill(void) + { + if (m_pid == -1) return false; + close_stdin(); + close_stdout(); + close_stderr(); + if (::kill(m_pid, SIGINT) == -1) + { + set_error(errno); + return false; + } + return true; + } + +#endif + +#ifdef MSWINDOWS + + int async_subprocess::write_stdin (std::string& buffer) + { + if (m_child_in == 0) return -1; + // there doesn't seem to be a way of doing non-blocking writes under Windoze + DWORD bytes = 0; + if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0)) + { + set_error(GetLastError()); + close_stdin(); + return -1; + } + // now discard that part of the buffer that was written + if (bytes > 0) + buffer.erase(0, bytes); + return (int)bytes; + } + +#else + + int async_subprocess::write_stdin (std::string& buffer) + { + if (m_child_in == -1) return -1; + // relies on the pipe being non-blocking + // This does block under Windoze + int bytes = write(m_child_in, buffer.c_str(), buffer.size()); + if (bytes == -1 && errno == EAGAIN) + { + // not ready + return 0; + } + if (bytes == -1) + { + // error on write - close the pipe and give up + set_error(errno); + close_stdin(); + return -1; + } + // successful write + // now discard that part of the buffer that was written + if (bytes > 0) + buffer.erase(0, bytes); + return bytes; + } + +#endif + +#ifdef MSWINDOWS + + int async_subprocess::read_stdout (std::string& buffer) + { + if (m_child_out == 0) return -1; + // peek at the buffer to see how much data there is in the first place + DWORD buffer_size = 0; + if (!PeekNamedPipe(m_child_out, 0, 0, 0, &buffer_size, 0)) + { + if (GetLastError() != ERROR_BROKEN_PIPE) + set_error(GetLastError()); + close_stdout(); + return -1; + } + if (buffer_size == 0) return 0; + DWORD bytes = 0; + char* tmp = new char[buffer_size]; + if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0)) + { + set_error(GetLastError()); + close_stdout(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stdout(); + delete[] tmp; + return -1; + } + buffer.append(tmp, bytes); + delete[] tmp; + return (int)bytes; + } + +#else + + int async_subprocess::read_stdout (std::string& buffer) + { + if (m_child_out == -1) return -1; + // rely on the pipe being non-blocking + int buffer_size = 256; + char* tmp = new char[buffer_size]; + int bytes = read(m_child_out, tmp, buffer_size); + if (bytes == -1 && errno == EAGAIN) + { + // not ready + delete[] tmp; + return 0; + } + if (bytes == -1) + { + // error + set_error(errno); + close_stdout(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stdout(); + delete[] tmp; + return -1; + } + // successful read + buffer.append(tmp, bytes); + delete[] tmp; + return bytes; + } + +#endif + +#ifdef MSWINDOWS + + int async_subprocess::read_stderr (std::string& buffer) + { + if (m_child_err == 0) return -1; + // peek at the buffer to see how much data there is in the first place + DWORD buffer_size = 0; + if (!PeekNamedPipe(m_child_err, 0, 0, 0, &buffer_size, 0)) + { + if (GetLastError() != ERROR_BROKEN_PIPE) + set_error(GetLastError()); + close_stderr(); + return -1; + } + if (buffer_size == 0) return 0; + DWORD bytes = 0; + char* tmp = new char[buffer_size]; + if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0)) + { + set_error(GetLastError()); + close_stderr(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stderr(); + delete[] tmp; + return -1; + } + buffer.append(tmp, bytes); + delete[] tmp; + return (int)bytes; + } + +#else + + int async_subprocess::read_stderr (std::string& buffer) + { + if (m_child_err == -1) return -1; + // rely on the pipe being non-blocking + int buffer_size = 256; + char* tmp = new char[buffer_size]; + int bytes = read(m_child_err, tmp, buffer_size); + if (bytes == -1 && errno == EAGAIN) + { + // not ready + delete[] tmp; + return 0; + } + if (bytes == -1) + { + // error + set_error(errno); + close_stderr(); + delete[] tmp; + return -1; + } + if (bytes == 0) + { + // EOF + close_stderr(); + delete[] tmp; + return -1; + } + // successful read + buffer.append(tmp, bytes); + delete[] tmp; + return bytes; + } + +#endif + +#ifdef MSWINDOWS + + void async_subprocess::close_stdin (void) + { + if (m_child_in) + { + CloseHandle(m_child_in); + m_child_in = 0; + } + } + +#else + + void async_subprocess::close_stdin (void) + { + if (m_child_in != -1) + { + ::close(m_child_in); + m_child_in = -1; + } + } + +#endif + +#ifdef MSWINDOWS + + void async_subprocess::close_stdout (void) + { + if (m_child_out) + { + CloseHandle(m_child_out); + m_child_out = 0; + } + } + +#else + + void async_subprocess::close_stdout (void) + { + if (m_child_out != -1) + { + ::close(m_child_out); + m_child_out = -1; + } + } + +#endif + +#ifdef MSWINDOWS + + void async_subprocess::close_stderr (void) + { + if (m_child_err) + { + CloseHandle(m_child_err); + m_child_err = 0; + } + } + +#else + + void async_subprocess::close_stderr (void) + { + if (m_child_err != -1) + { + ::close(m_child_err); + m_child_err = -1; + } + } + +#endif + + bool async_subprocess::error(void) const + { + return m_err != 0; + } + + int async_subprocess::error_number(void) const + { + return m_err; + } + +#ifdef MSWINDOWS + + std::string async_subprocess::error_text(void) const + { + if (m_err == 0) return std::string(); + char* message; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + 0, + m_err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&message, + 0,0); + std::string result = message; + LocalFree(message); + // the error message is for some perverse reason newline terminated - remove this + if (result[result.size()-1] == '\n') + result.erase(result.end()-1); + if (result[result.size()-1] == '\r') + result.erase(result.end()-1); + return result; + } + +#else + + std::string async_subprocess::error_text(void) const + { + if (m_err == 0) return std::string(); + char* text = strerror(m_err); + if (text) return std::string(text); + return "error number " + dformat("%d",m_err); + } + +#endif + + int async_subprocess::exit_status(void) const + { + return m_status; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/subprocesses.hpp b/src/stlplus/portability/subprocesses.hpp new file mode 100644 index 0000000..0369daa --- /dev/null +++ b/src/stlplus/portability/subprocesses.hpp @@ -0,0 +1,282 @@ +#ifndef STLPLUS_SUBPROCESSES +#define STLPLUS_SUBPROCESSES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Platform-independent wrapper around the very platform-specific handling of +// subprocesses. Uses the C++ convention that all resources must be contained in +// an object so that when a subprocess object goes out of scope the subprocess +// itself gets closed down. + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#ifdef MSWINDOWS +#include +#endif +#include +#include +#include +#include // for std::pair - why is this not defined separately? + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Argument vector class + // allows manipulation of argv-like vectors + // includes splitting of command lines into argvectors as per the shell + // (removing quotes) and the reverse conversion (adding quotes where necessary) + + class arg_vector + { + private: + char** m_argv; + + public: + // create an empty vector + arg_vector (void); + + // copy constructor (yes it copies) + arg_vector (const arg_vector&); + + // construct from an argv + arg_vector (char**); + + // construct from a command-line string + // includes de-quoting of values + arg_vector (const std::string&); + arg_vector (const char*); + + ~arg_vector (void); + + // assignment operators are compatible with the constructors + arg_vector& operator = (const arg_vector&); + arg_vector& operator = (char**); + arg_vector& operator = (const std::string&); + arg_vector& operator = (const char*); + + // add an argument to the vector + arg_vector& operator += (const std::string&); + arg_vector& operator -= (const std::string&); + + // insert/clear an argument at a certain index + // adding is like the other array classes - it moves the current item at index + // up one (and all subsequent values) to make room + void insert (unsigned index, const std::string&) throw(std::out_of_range); + void clear (unsigned index) throw(std::out_of_range); + void clear (void); + + // number of values in the vector (including argv[0], the command itself + unsigned size (void) const; + + // type conversion to the argv type + operator char** (void) const; + // function-based version of the above for people who don't like type conversions + char** argv (void) const; + + // access individual values in the vector + char* operator [] (unsigned index) const throw(std::out_of_range); + + // special-case access of the command name (e.g. to do path lookup on the command) + char* argv0 (void) const throw(std::out_of_range); + + // get the command-line string represented by this vector + // includes escaping of special characters and quoting + std::string image (void) const; + }; + + //////////////////////////////////////////////////////////////////////////////// + // Environment class + // Allows manipulation of an environment vector + // This is typically used to create an environment to be used by a subprocess + // It does NOT modify the environment of the current process + +#ifdef MSWINDOWS +#define ENVIRON_TYPE char* +#else +#define ENVIRON_TYPE char** +#endif + + class env_vector + { + private: + ENVIRON_TYPE m_env; + + public: + // create an env_vector vector from the current process + env_vector (void); + env_vector (const env_vector&); + ~env_vector (void); + + env_vector& operator = (const env_vector&); + + void clear (void); + + // manipulate the env_vector by adding or removing variables + // adding a name that already exists replaces its value + void add (const std::string& name, const std::string& value); + bool remove (const std::string& name); + + // get the value associated with a name + // the first uses an indexed notation (e.g. env["PATH"] ) + // the second is a function based form (e.g. env.get("PATH")) + std::string operator [] (const std::string& name) const; + std::string get (const std::string& name) const; + + // number of name=value pairs in the env_vector + unsigned size (void) const; + + // get the name=value pairs by index (in the range 0 to size()-1) + std::pair operator [] (unsigned index) const throw(std::out_of_range); + std::pair get (unsigned index) const throw(std::out_of_range); + + // access the env_vector as an envp type - used for passing to subprocesses + ENVIRON_TYPE envp (void) const; + }; + + //////////////////////////////////////////////////////////////////////////////// + +#ifdef MSWINDOWS +#define PID_TYPE PROCESS_INFORMATION +#define PIPE_TYPE HANDLE +#else +#define PID_TYPE int +#define PIPE_TYPE int +#endif + + //////////////////////////////////////////////////////////////////////////////// + // Synchronous subprocess + + class subprocess + { + private: + + PID_TYPE m_pid; +#ifdef MSWINDOWS + HANDLE m_job; +#endif + PIPE_TYPE m_child_in; + PIPE_TYPE m_child_out; + PIPE_TYPE m_child_err; + env_vector m_env; + int m_err; + int m_status; + + public: + subprocess(void); + virtual ~subprocess(void); + + void add_variable(const std::string& name, const std::string& value); + bool remove_variable(const std::string& name); + + bool spawn(const std::string& path, const arg_vector& argv, + bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false); + bool spawn(const std::string& command_line, + bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false); + + virtual bool callback(void); + bool kill(void); + + int write_stdin(std::string& buffer); + int read_stdout(std::string& buffer); + int read_stderr(std::string& buffer); + + void close_stdin(void); + void close_stdout(void); + void close_stderr(void); + + bool error(void) const; + int error_number(void) const; + std::string error_text(void) const; + + int exit_status(void) const; + + private: + // disallow copying + subprocess(const subprocess&); + subprocess& operator=(const subprocess&); + }; + + //////////////////////////////////////////////////////////////////////////////// + // Preconfigured subprocess which executes a command and captures its output + + class backtick_subprocess : public subprocess + { + private: + std::string m_text; + public: + backtick_subprocess(void); + virtual bool callback(void); + bool spawn(const std::string& path, const arg_vector& argv); + bool spawn(const std::string& command_line); + std::vector text(void) const; + }; + + std::vector backtick(const std::string& path, const arg_vector& argv); + std::vector backtick(const std::string& command_line); + + //////////////////////////////////////////////////////////////////////////////// + // Asynchronous subprocess + + class async_subprocess + { + private: + PID_TYPE m_pid; +#ifdef MSWINDOWS + HANDLE m_job; +#endif + PIPE_TYPE m_child_in; + PIPE_TYPE m_child_out; + PIPE_TYPE m_child_err; + env_vector m_env; + int m_err; + int m_status; + void set_error(int); + + public: + async_subprocess(void); + virtual ~async_subprocess(void); + + void add_variable(const std::string& name, const std::string& value); + bool remove_variable(const std::string& name); + + bool spawn(const std::string& path, const arg_vector& argv, + bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false); + bool spawn(const std::string& command_line, + bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false); + + virtual bool callback(void); + bool tick(void); + bool kill(void); + + int write_stdin(std::string& buffer); + int read_stdout(std::string& buffer); + int read_stderr(std::string& buffer); + + void close_stdin(void); + void close_stdout(void); + void close_stderr(void); + + bool error(void) const; + int error_number(void) const; + std::string error_text(void) const; + + int exit_status(void) const; + + private: + // disallow copying + async_subprocess(const async_subprocess&); + async_subprocess& operator=(const async_subprocess&); + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/tcp.hpp b/src/stlplus/portability/tcp.hpp new file mode 100644 index 0000000..3af0d3f --- /dev/null +++ b/src/stlplus/portability/tcp.hpp @@ -0,0 +1,17 @@ +#ifndef STLPLUS_TCP +#define STLPLUS_TCP +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A deprecated legacy header - please use tcp_socket.hpp + +//////////////////////////////////////////////////////////////////////////////// + +#include "portability_fixes.hpp" +#include "tcp_sockets.hpp" + +#endif diff --git a/src/stlplus/portability/tcp_sockets.cpp b/src/stlplus/portability/tcp_sockets.cpp new file mode 100644 index 0000000..84152f4 --- /dev/null +++ b/src/stlplus/portability/tcp_sockets.cpp @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +#include "tcp_sockets.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + ////////////////////////////////////////////////////////////////////////////// + // TCP Connection + + + TCP_connection::TCP_connection(const IP_socket& socket) : IP_socket(socket) + { + } + + TCP_connection::TCP_connection(void) : IP_socket(TCP) + { + } + + unsigned short TCP_connection::port(void) const + { + return remote_port(); + } + + //////////////////////////////////////////////////////////////////////////////// + // Server + + TCP_server::TCP_server(void) : IP_socket(TCP) + { + } + + TCP_server::TCP_server(unsigned short port, unsigned short queue) : IP_socket(TCP) + { + initialise(port,queue); + } + + bool TCP_server::initialise(unsigned short port, unsigned short queue) + { + if (!IP_socket::bind_any(port)) return false; + return IP_socket::listen(queue); + } + + TCP_connection TCP_server::accept(void) + { + return TCP_connection(IP_socket::accept()); + } + + bool TCP_server::connection_ready(unsigned timeout) + { + return accept_ready(timeout); + } + + TCP_connection TCP_server::connection(void) + { + return accept(); + } + + ////////////////////////////////////////////////////////////////////////////// + // Client + + TCP_client::TCP_client(void) : IP_socket(TCP) + { + } + + TCP_client::TCP_client(const std::string& address, unsigned short port, unsigned int timeout) : IP_socket(TCP) + { + initialise(address,port,timeout); + } + + TCP_client::TCP_client(unsigned long address, unsigned short port, unsigned int timeout) : IP_socket(TCP) + { + initialise(address,port,timeout); + } + + bool TCP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned int timeout) + { + if (!IP_socket::connect(remote_address, remote_port)) + { + close(); + return false; + } + if (timeout && !IP_socket::connected(timeout)) + { + close(); + return false; + } + return true; + } + + bool TCP_client::initialise(const std::string& address, unsigned short remote_port, unsigned int timeout) + { + // lookup the address and convert it into an IP number + unsigned long remote_address = IP_socket::ip_lookup(address); + if (!remote_address) return false; + return initialise(remote_address, remote_port, timeout); + } + + unsigned short TCP_client::port(void) const + { + return remote_port(); + } + + unsigned long TCP_client::address(void) const + { + return remote_address(); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/tcp_sockets.hpp b/src/stlplus/portability/tcp_sockets.hpp new file mode 100644 index 0000000..94b98c5 --- /dev/null +++ b/src/stlplus/portability/tcp_sockets.hpp @@ -0,0 +1,385 @@ +#ifndef STLPLUS_TCP_SOCKET +#define STLPLUS_TCP_SOCKET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A platform-independent (Unix and Windows anyway) interface to TCP sockets + +//////////////////////////////////////////////////////////////////////////////// + +#include "portability_fixes.hpp" +#include "ip_sockets.hpp" +#include + +namespace stlplus +{ + + ////////////////////////////////////////////////////////////////////////////// + // Server Classes: A server creates a listening port which waits for incoming + // connections. This is placed on the port number appropriate for the service + // - for example, a Telnet server would typically use port 23. For a new + // service you should of course use any port not allocated to a standard + // service. I believe that RFC 1700 defines the standard service port numbers. + // When an incoming connection is made, the server accepts it and in the + // process creates a new connection on a different port. This leaves the + // standard port listening for further connections. In effect, the server + // farms out the handling of the connections themselves and only takes + // responsibility for accepting the connection. This is reflected in the class + // structure. A TCP_server performs the listening and accepting roles, but + // creates a TCP_connection to handle the accepted connection. + ////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////// + // connection class created by TCP_server when a connection is accepted + // this is then used to perform any communications with the remote client + + class TCP_connection : protected IP_socket + { + private: + // constructor to actually initialise the class - can only be constructed by TCP_server + friend class TCP_server; + TCP_connection(const IP_socket& socket); + + public: + + //////////////////////////////////////////////////////////////////////////// + // constructors/destructors + + // create an uninitialised connection + TCP_connection(void); + + //////////////////////////////////////////////////////////////////////////// + // initialisation, connection control + // Note: TCP connections can only be initialised by a TCP server + + // test whether this is an initialised socket + // - returns whether this is initialised + // bool initialised(void) const; + using IP_socket::initialised; + + // close, i.e. disconnect the socket + // - returns a success flag + // bool close(void); + using IP_socket::close; + + //////////////////////////////////////////////////////////////////////////// + // sending/receiving + + // test whether a socket is connected and ready to send data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool send_ready(unsigned timeout = 0); + using IP_socket::send_ready; + + // send data through the socket - if the data is long only part of it may + // be sent. The sent part is removed from the data, so the same string can + // be sent again and again until it is empty. + // - data: string containing data to be sent - any data successfully sent is removed + // - returns success flag + // bool send (std::string& data); + using IP_socket::send; + + // test whether a socket is connected and ready to receive data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool receive_ready(unsigned timeout = 0); + using IP_socket::receive_ready; + + // receive data through the socket - if the data is long only part of it + // may be received. The received data is appended to the string, building + // it up in stages, so the same string can be received again and again + // until all information has been received. + // - data: string receiving data from socket - any data successfully received is appended + // - returns success flag + // bool receive (std::string& data); + using IP_socket::receive; + + //////////////////////////////////////////////////////////////////////////// + // informational + + // the local port number of the connection + // - returns the port number, 0 if not bound to a port + // unsigned short local_port(void) const; + using IP_socket::local_port; + + // the remote address of the connection + // - returns the address, 0 if not connected + // unsigned long remote_address(void) const; + using IP_socket::remote_address; + + // the remote port number of the connection + // - returns the port number, 0 if not connected to a port + // unsigned short remote_port(void) const; + using IP_socket::remote_port; + + //////////////////////////////////////////////////////////////////////////// + // error handling + // errors are set internally + // an error code of 0 is the test for no error, don't rely on the message being an empty string + // an error code != 0 means an error, then there will be a message explaining the error + + // if an error is set it stays set - so you must clear it before further operations + // void clear_error(void) const; + using IP_socket::clear_error; + + // get the error code of the last error + // int error(void) const; + using IP_socket::error; + + // get the explanatory message of the last error + // std::string message(void) const; + using IP_socket::message; + + //////////////////////////////////////////////////////////////////////////// + + // deprecated version of remote_port + unsigned short port(void) const; + + //////////////////////////////////////////////////////////////////////////// + }; + + ////////////////////////////////////////////////////////////////////////////// + // server class that does the listening on the designated port + // incoming connections can be queued up to a maximum queue length specified + // in the constructor/initialise + + class TCP_server : protected IP_socket + { + public: + + // create an uninitialised server + TCP_server(void); + + // initialise a socket and set it up to be a listening port + // - port: port to listen on + // - queue: length of backlog queue to manage - may be zero + // - returns success status + TCP_server(unsigned short port, unsigned short queue = 0); + + //////////////////////////////////////////////////////////////////////////// + // initialisation + + // initialise a socket and set it up to be a listening port + // - port: port to listen on + // - queue: length of backlog queue to manage - may be zero + // - returns success status + bool initialise(unsigned short port, unsigned short queue = 0); + + // test whether this is an initialised socket + // - returns whether this is initialised + // bool initialised(void) const; + using IP_socket::initialised; + + // close, i.e. disconnect the socket + // - returns a success flag + // bool close(void); + using IP_socket::close; + + ////////////////////////////////////////////////////////////////////////////// + // server operation - accepting a connection + + // test for a connection on the object's socket - only applicable if it has been set up as a listening port + // - timeout: how long to wait in microseconds if not connected yet + // - returns true if a connection is ready to be accepted + // bool accept_ready(unsigned timeout = 0); + using IP_socket::accept_ready; + + // accept a connection on the object's socket - only applicable if it has been set up as a listening port + // - returns the connection as a new socket + TCP_connection accept(void); + + //////////////////////////////////////////////////////////////////////////// + // error handling + // errors are set internally + // an error code of 0 is the test for no error, don't rely on the message being an empty string + // an error code != 0 means an error, then there will be a message explaining the error + + // if an error is set it stays set - so you must clear it before further operations + // void clear_error (void) const; + using IP_socket::clear_error; + + // get the error code of the last error + // int error(void) const; + using IP_socket::error; + + // get the explanatory message of the last error + // std::string message(void) const; + using IP_socket::message; + + ////////////////////////////////////////////////////////////////////////////// + + // deprecated versions of accept_ready and accept + bool connection_ready(unsigned timeout = 0); + TCP_connection connection(void); + + ////////////////////////////////////////////////////////////////////////////// + }; + + //////////////////////////////////////////////////////////////////////////////// + // Client Class: a client is simpler in that there is no listening port - you + // just create a connection and get on with it. Thus the client class does the + // whole job - create the connection and handle communications to/from it. + // + // Blocking mode: To use the client in blocking mode, use non-zero timeout for + // the initialisation method. In this mode, the connection operation must + // complete before the call will return or an error is indicated if the + // timeout is reached without completion. This usage was designed for + // applications which either just to TCP and nothing else or which do TCP + // operations in a separate thread. + // + // Non-blocking mode: To use the client in non-blocking mode, use a zero + // timeout for the initialisation method. Instead, you can ask it if it has + // connected once you've initialised it. It is not an error for it to be + // initialised but not connected. This usage was designed so that you can poll + // the connection periodically to implement a timeout for as long as you like for + // the connection to occur without blocking the thread that uses the client. + // + // In both modes, the send_ready/receive_ready methods can be called with any + // timeout including zero. + + class TCP_client : protected IP_socket + { + public: + + // create an uninitialised client + TCP_client(void); + + // client connect to a server + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - remote_port: port number of remote host + // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed + TCP_client(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0); + + // client connect to a server + // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup + // - remote_port: port number of remote host + // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed + TCP_client(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0); + + //////////////////////////////////////////////////////////////////////////// + // initialisation, connection + + // function for performing IP lookup (i.e. gethostbyname) + // could be standalone but making it a member means that it can use the socket's error handler + // i.e. if this fails, the sockets error code will be set - clear it to use the socket again + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - returns the IP address as a long integer - zero if there's an error + // unsigned long ip_lookup(const std::string& remote_address); + using IP_socket::ip_lookup; + + // client connect to a server + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - remote_port: port number of remote host + // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed + // - returns a success flag + bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0); + + // client connect to a server + // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup + // - remote_port: port number of remote host + // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed + // - returns a success flag + bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0); + + // test whether this is an initialised socket + // - returns whether this is initialised + // bool initialised(void) const; + using IP_socket::initialised; + + // test whether a socket is connected and ready to communicate, returns on successful connect or timeout + // - timeout: how long to wait in microseconds if not connected yet + // - returns success flag + // bool connected(unsigned timeout = 0); + using IP_socket::connected; + + // close, i.e. disconnect the socket + // - returns a success flag + // bool close(void); + using IP_socket::close; + + //////////////////////////////////////////////////////////////////////////// + // sending/receiving + + // test whether a socket is connected and ready to send data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool send_ready(unsigned timeout = 0); + using IP_socket::send_ready; + + // send data through the socket - if the data is long only part of it may + // be sent. The sent part is removed from the data, so the same string can + // be sent again and again until it is empty. + // - data: string containing data to be sent - any data successfully sent is removed + // - returns success flag + // bool send (std::string& data); + using IP_socket::send; + + // test whether a socket is connected and ready to receive data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool receive_ready(unsigned timeout = 0); + using IP_socket::receive_ready; + + // receive data through the socket - if the data is long only part of it + // may be received. The received data is appended to the string, building + // it up in stages, so the same string can be received again and again + // until all information has been received. + // - data: string receiving data from socket - any data successfully received is appended + // - returns success flag + // bool receive (std::string& data); + using IP_socket::receive; + + //////////////////////////////////////////////////////////////////////////// + // informational + + // the local port number of the connection + // - returns the port number, 0 if not bound to a port + // unsigned short local_port(void) const; + using IP_socket::local_port; + + // the remote address of the connection + // - returns the address, 0 if not connected + // unsigned long remote_address(void) const; + using IP_socket::remote_address; + + // the remote port number of the connection + // - returns the port number, 0 if not connected to a port + // unsigned short remote_port(void) const; + using IP_socket::remote_port; + + //////////////////////////////////////////////////////////////////////////// + // error handling + // errors are set internally + // an error code of 0 is the test for no error, don't rely on the message being an empty string + // an error code != 0 means an error, then there will be a message explaining the error + + // if an error is set it stays set - so you must clear it before further operations + // void clear_error (void) const; + using IP_socket::clear_error; + + // get the error code of the last error + // int error(void) const; + using IP_socket::error; + + // get the explanatory message of the last error + // std::string message(void) const; + using IP_socket::message; + + ////////////////////////////////////////////////////////////////////////////// + + // deprecated versions + unsigned long address(void) const; + unsigned short port(void) const; + + ////////////////////////////////////////////////////////////////////////////// + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/time.cpp b/src/stlplus/portability/time.cpp new file mode 100644 index 0000000..250a825 --- /dev/null +++ b/src/stlplus/portability/time.cpp @@ -0,0 +1,129 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "time.hpp" +#include "dprintf.hpp" +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + time_t time_now(void) + { + return time(0); + } + + time_t localtime_create(int year, int month, int day, int hour, int minute, int second) + { + tm tm_time; + tm_time.tm_year = year-1900; // years are represented as an offset from 1900, for reasons unknown + tm_time.tm_mon = month-1; // internal format represents month as 0-11, but it is friendlier to take an input 1-12 + tm_time.tm_mday = day; + tm_time.tm_hour = hour; + tm_time.tm_min = minute; + tm_time.tm_sec = second; + tm_time.tm_isdst = -1; // specify that the function should work out daylight savings + time_t result = mktime(&tm_time); + return result; + } + + int localtime_year(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_year + 1900; + } + + int localtime_month(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_mon + 1; + } + + int localtime_day(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_mday; + } + + int localtime_hour(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_hour; + } + + int localtime_minute(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_min; + } + + int localtime_second(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_sec; + } + + int localtime_weekday(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_wday; + } + + int localtime_yearday(time_t t) + { + tm* tm_time = localtime(&t); + return tm_time->tm_yday; + } + + std::string localtime_string(time_t t) + { + tm* local = localtime(&t); + std::string result = local ? asctime(local) : "*time not available*"; + // ctime appends a newline for no apparent reason - clean up + while (!result.empty() && isspace(result[result.size()-1])) + result.erase(result.size()-1,1); + return result; + } + + std::string delaytime_string(time_t seconds) + { + unsigned minutes = (unsigned)seconds / 60; + seconds %= 60; + unsigned hours = minutes / 60; + minutes %= 60; + unsigned days = hours / 24; + hours %= 24; + unsigned weeks = days / 7; + days %= 7; + std::string result; + if (weeks > 0) + result += dformat("%dw ",weeks); + if (!result.empty() || days > 0) + result += dformat("%dd ", days); + if (!result.empty() || hours > 0) + result += dformat("%d:", hours); + if (!result.empty() || minutes > 0) + { + if (!result.empty()) + result += dformat("%02d:", minutes); + else + result += dformat("%d:", minutes); + } + if (!result.empty()) + result += dformat("%02d:", seconds); + else + result += dformat("%ds", seconds); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/time.hpp b/src/stlplus/portability/time.hpp new file mode 100644 index 0000000..f8abe6c --- /dev/null +++ b/src/stlplus/portability/time.hpp @@ -0,0 +1,55 @@ +#ifndef STLPLUS_TIME +#define STLPLUS_TIME +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Simplified access to representations of time and conversions between them. +// The motivation for this package is that the low-level system calls for +// accessing time are ugly and therefore potentially error-prone. I hope that +// this interface is much simpler and therefore easier to use and more likely +// to yield first-time right programs. + +// time is represented as the built-in integer type time_t - this is the +// standard representation of system time in computerland and represents the +// number of seconds since midnight 1 Jan 1970, believe it or not. + +// Functions are provided here for converting to and from more +// human-comprehendable forms. + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include +#include + +namespace stlplus +{ + + // get the integer representing the time now + time_t time_now(void); + + // get the integer representing the requested time - the local time is expressed in the local timezone + time_t localtime_create(int year, int month, int day, int hour, int minute, int second); + + // extract human-centric form of the machine representation time_t + int localtime_year(time_t); // the year e.g. 1962 + int localtime_month(time_t); // the month, numbered 1-12 e.g. August = 8 + int localtime_day(time_t); // the day of the month numbered 1-31 e.g. 29 + int localtime_hour(time_t); // the hour of day numbered 0-23 + int localtime_minute(time_t); // minute past the hour numbered 0-59 + int localtime_second(time_t); // second past the minute numbered 0-59 + int localtime_weekday(time_t); // the day of the week numbered 0-6 with 0=Sunday + int localtime_yearday(time_t); // the number of days into the year + + // convert the integer representation of time to a human-readable form + std::string localtime_string(time_t); + + // convert a time delay in seconds to human-readable form + std::string delaytime_string(time_t); + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/udp_sockets.cpp b/src/stlplus/portability/udp_sockets.cpp new file mode 100644 index 0000000..137be3d --- /dev/null +++ b/src/stlplus/portability/udp_sockets.cpp @@ -0,0 +1,167 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Daniel Milton adapted by Andy Rushton +// Copyright: (c) Daniel Milton, Andy Rushton 2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +#include "udp_sockets.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // UDP client + //////////////////////////////////////////////////////////////////////////////// + + // create an uninitialised socket + UDP_client::UDP_client(void) : IP_socket(UDP) + { + } + + // Send/Receive datagram packets to/from the given address/remote port on the local port. + // Enables default send to remote address/port + // - remote_address: IP name or number of remote host + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + UDP_client::UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port) : + IP_socket(UDP) + { + initialise(remote_address, remote_port, local_port); + } + + // Send/Receive datagram packets to/from the given address/remote port on the given local port + // Enables default send to remote address/port + // - remote_address: IP address of remote host - pre-looked-up using ip_lookup + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + UDP_client::UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port) : + IP_socket(UDP) + { + initialise(remote_address, remote_port, local_port); + } + + // Send/Receive datagram packets to/from the given address/remote port on the local port. + // Enables default send to remote address/port + // - remote_address: IP name or number of remote host + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + // - returns a success flag + bool UDP_client::initialise(const std::string& address, unsigned short remote_port, unsigned short local_port) + { + // lookup the address and convert it into an IP number + unsigned long remote_address = IP_socket::ip_lookup(address); + if (!remote_address) return false; + return initialise(remote_address, remote_port, local_port); + } + + // Send/Receive datagram packets to/from the given address/remote port on the given local port + // Enables default send to remote address/port + // - remote_address: IP address of remote host - pre-looked-up using ip_lookup + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + // - returns a success flag + bool UDP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port) + { + if (!IP_socket::bind(remote_address, local_port)) return false; + return IP_socket::connect(remote_address, remote_port); + } + + // send to the remote address/port setup in initialise, from the local port also setup in initialise. + // send data through the socket as a single datagram + // - packet: string containing data to be sent - if data is successfully sent it is removed + // - returns success flag + bool UDP_client::send(std::string& packet) + { + return IP_socket::send_packet(packet); + } + + // datagram receive + // - packet: string to receive data from datagram - if data is successfully sent it is appended + // - returns success flag - i.e. packet successfully received + bool UDP_client::receive(std::string& packet) + { + return IP_socket::receive_packet(packet); + } + + //////////////////////////////////////////////////////////////////////////////// + // UDP Server + //////////////////////////////////////////////////////////////////////////////// + + // create an uninitialised socket + UDP_server::UDP_server(void) : IP_socket(UDP) + { + } + + // Initialise socket. + // Receive datagram packets from any address on provided local receiving port. + // No default send possible. + // - local_port: port number to receive on - 0 to get an ephemeral port. + UDP_server::UDP_server(unsigned short local_port) : IP_socket(UDP) + { + initialise(local_port); + } + + // Initialise socket. + // Receive datagram packets from any address on provided local receiving port. + // No default send possible. + // - local_port: port number to receive on - 0 to get an ephemeral port. + // - returns a success flag + bool UDP_server::initialise(unsigned short local_port) + { + return IP_socket::bind_any(local_port); + } + + // send to the address/port given here, from the local port setup in initialise. + // send data through the socket as a single datagram + // - packet: string containing data to be sent - if data is successfully sent it is removed + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - remote_port: port number of remote host + // - returns success flag + bool UDP_server::send(std::string& packet, const std::string& remote_address, unsigned short remote_port) + { + unsigned long ip_address = ip_lookup(remote_address); + if (ip_address == 0) return false; + return send(packet, ip_address, remote_port); + } + + // send to the address/port given here, from the local port setup in initialise. + // send data through the socket as a single datagram + // - packet: string containing data to be sent - if data is successfully sent it is removed + // - remote_address: pre-looked-up IP address of remote host + // - remote_port: port number of remote host + // - returns success flag + bool UDP_server::send(std::string& packet, unsigned long remote_address, unsigned short remote_port) + { + return IP_socket::send_packet(packet, remote_address, remote_port); + } + + // datagram receive + // - packet: string to receive data from datagram - if data is successfully sent it is appended + // - remote_address: the address of the client that sent the packet, can then be used to reply + // - remote_port: the port of the client that sent the packet, can then be used to reply + // - returns success flag - i.e. packet successfully received + bool UDP_server::receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port) + { + return IP_socket::receive_packet(packet, remote_address, remote_port); + } + + ///////////////////////////////////////////////////////////////////////////// + // fire and forget UDP client packet send function + //////////////////////////////////////////////////////////////////////////////// + + bool UDP_send(const std::string& packet, + const std::string& remote_address, unsigned short remote_port, unsigned short local_port) + { + UDP_client client(remote_address, remote_port, local_port); + if (!client.initialised()) return false; + std::string packet_copy = packet; + return client.send(packet_copy); + } + + ///////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/portability/udp_sockets.hpp b/src/stlplus/portability/udp_sockets.hpp new file mode 100644 index 0000000..5097d44 --- /dev/null +++ b/src/stlplus/portability/udp_sockets.hpp @@ -0,0 +1,268 @@ +#ifndef STLPLUS_UDP_SOCKET +#define STLPLUS_UDP_SOCKET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A platform-independent (Unix and Windows anyway) interface to UDP sockets + +//////////////////////////////////////////////////////////////////////////////// + +#include "portability_fixes.hpp" +#include "ip_sockets.hpp" +#include + +namespace stlplus +{ + + ////////////////////////////////////////////////////////////////////////////// + // UDP client - creates a connectioned socket + + class UDP_client : protected IP_socket + { + public: + + // create an uninitialised socket + UDP_client(void); + + // Send/Receive datagram packets to/from the given address/remote port on the local port. + // - remote_address: IP name or number of remote host + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0); + + // Send/Receive datagram packets to/from the given address/remote port on the given local port + // Enables default send to remote address/port + // - remote_address: IP address of remote host - pre-looked-up using ip_lookup + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0); + + //////////////////////////////////////////////////////////////////////////// + // initialisation, connection + + // function for performing IP lookup (i.e. gethostbyname) + // could be standalone but making it a member means that it can use the socket's error handler + // i.e. if this fails, the sockets error code will be set - clear it to use the socket again + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - returns the IP address as a long integer - zero if there's an error + // unsigned long ip_lookup(const std::string& remote_address); + using IP_socket::ip_lookup; + + // Send/Receive datagram packets to/from the given address/remote port on the local port. + // Enables default send to remote address/port + // - remote_address: IP name or number of remote host + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + // - returns a success flag + bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0); + + // Send/Receive datagram packets to/from the given address/remote port on the given local port + // Enables default send to remote address/port + // - remote_address: IP address of remote host - pre-looked-up using ip_lookup + // - remote_port: port number of remote host + // - local_port: port number to receive on - 0 to get an ephemeral port. + // - returns a success flag + bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0); + + // test whether this is an initialised socket + // - returns whether this is initialised + // bool initialised(void) const; + using IP_socket::initialised; + + // close, i.e. disconnect the socket + // - returns a success flag + // bool close(void); + using IP_socket::close; + + //////////////////////////////////////////////////////////////////////////// + // sending/receiving + + // test whether a socket is connected and ready to send data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool send_ready(unsigned timeout = 0); + using IP_socket::send_ready; + + // send to the remote address/port setup in initialise, from the local port also setup in initialise. + // send data through the socket as a single datagram + // - packet: string containing data to be sent - if data is successfully sent it is removed + // - returns success flag + bool send(std::string& packet); + + // test whether a socket is connected and ready to receive data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool receive_ready(unsigned timeout = 0); + using IP_socket::receive_ready; + + // datagram receive + // - packet: string to receive data from datagram - if data is successfully sent it is appended + // - returns success flag - i.e. packet successfully received + bool receive(std::string& packet); + + //////////////////////////////////////////////////////////////////////////// + // informational + + // the local port number of the connection + // returns the port number, 0 if not bound to a port + // unsigned short local_port(void) const; + using IP_socket::local_port; + + // the remote address of the connection + // returns the address, 0 if ANY address + // unsigned long remote_address(void) const; + using IP_socket::remote_address; + + // the remote port number of the connection + // returns the port number, 0 if not bound to a port + // unsigned short remote_port(void) const; + using IP_socket::remote_port; + + //////////////////////////////////////////////////////////////////////////// + // error handling + // errors are set internally + // an error code of 0 is the test for no error, don't rely on the message being an empty string + // an error code != 0 means an error, then there will be a message explaining the error + + // if an error is set it stays set - so you must clear it before further operations + // void clear_error (void); + using IP_socket::clear_error ; + + // get the error code of the last error + // int error(void) const; + using IP_socket::error; + + // get the explanatory message of the last error + // std::string message(void) const; + using IP_socket::message; + + //////////////////////////////////////////////////////////////////////////// + + private: + IP_socket m_socket; + }; + + //////////////////////////////////////////////////////////////////////////////// + // UDP server - creates a connectionless (multicast) listener socket + + class UDP_server : protected IP_socket + { + public: + + // create an uninitialised socket + UDP_server(void); + + // Initialise socket. + // Receive datagram packets from any address on provided local receiving port. + // - local_port: port number to receive on - 0 to get an ephemeral port. + UDP_server(unsigned short local_port); + + //////////////////////////////////////////////////////////////////////////// + // initialisation, connection + + // function for performing IP lookup (i.e. gethostbyname) + // could be standalone but making it a member means that it can use the socket's error handler + // i.e. if this fails, the sockets error code will be set - clear it to use the socket again + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - returns the IP address as a long integer - zero if there's an error + // unsigned long ip_lookup(const std::string& remote_address); + using IP_socket::ip_lookup; + + // Initialise socket. + // Receive datagram packets from any address on provided local receiving port. + // - local_port: port number to receive on - 0 to get an ephemeral port. + // - returns a success flag + bool initialise(unsigned short local_port); + + // test whether this is an initialised socket + // - returns whether this is initialised + // bool initialised(void) const; + using IP_socket::initialised; + + // close, i.e. disconnect the socket + // - returns a success flag + // bool close(void); + using IP_socket::close; + + //////////////////////////////////////////////////////////////////////////// + // sending/receiving + + // test whether a socket is connected and ready to send data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool send_ready(unsigned timeout = 0); + using IP_socket::send_ready; + + // send to the address/port given here, from the local port setup in initialise. + // send data through the socket as a single datagram + // - packet: string containing data to be sent - if data is successfully sent it is removed + // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96) + // - remote_port: port number of remote host + // - returns success flag + bool send(std::string& packet, const std::string& remote_address, unsigned short remote_port); + + // send to the address/port given here, from the local port setup in initialise. + // send data through the socket as a single datagram + // - packet: string containing data to be sent - if data is successfully sent it is removed + // - remote_address: pre-looked-up IP address of remote host + // - remote_port: port number of remote host + // - returns success flag + bool send(std::string& packet, unsigned long remote_address, unsigned short remote_port); + + // test whether a socket is connected and ready to receive data, returns if ready or on timeout + // - timeout: how long to wait in microseconds if not connected yet (blocking) + // - returns status + // bool receive_ready(unsigned timeout = 0); + using IP_socket::receive_ready; + + // datagram receive + // - packet: string to receive data from datagram - if data is successfully sent it is appended + // - remote_address: the address of the client that sent the packet, can then be used to reply + // - remote_port: the port of the client that sent the packet, can then be used to reply + // - returns success flag - i.e. packet successfully received + bool receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port); + + //////////////////////////////////////////////////////////////////////////// + // informational + + // the local port number of the connection + // returns the port number, 0 if not bound to a port + // unsigned short local_port(void) const; + using IP_socket::local_port; + + //////////////////////////////////////////////////////////////////////////// + // error handling + // errors are set internally + // an error code of 0 is the test for no error, don't rely on the message being an empty string + // an error code != 0 means an error, then there will be a message explaining the error + + // if an error is set it stays set - so you must clear it before further operations + // void clear_error(void); + using IP_socket::clear_error; + + // get the error code of the last error + // int error(void) const; + using IP_socket::error; + + // get the explanatory message of the last error + // std::string message(void) const; + using IP_socket::message; + + //////////////////////////////////////////////////////////////////////////// + }; + + ///////////////////////////////////////////////////////////////////////////// + // fire and forget UDP client packet send function + + bool UDP_send(const std::string& packet, + const std::string& remote_address, unsigned short remote_port, unsigned short local_port = 0); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/portability/version.cpp b/src/stlplus/portability/version.cpp new file mode 100644 index 0000000..8a34c6e --- /dev/null +++ b/src/stlplus/portability/version.cpp @@ -0,0 +1,20 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "version.hpp" +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + std::string version(void) + { + return STLPLUS_VERSION; + } + +} // end namespace stlplus diff --git a/src/stlplus/portability/version.hpp b/src/stlplus/portability/version.hpp new file mode 100644 index 0000000..027e131 --- /dev/null +++ b/src/stlplus/portability/version.hpp @@ -0,0 +1,24 @@ +#ifndef STLPLUS_VERSION +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Contains just the STLplus version number + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include + +#define STLPLUS_VERSION "3.5" + +namespace stlplus +{ + + std::string version(void); + +} +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/portability/wildcard.cpp b/src/stlplus/portability/wildcard.cpp new file mode 100644 index 0000000..c15252e --- /dev/null +++ b/src/stlplus/portability/wildcard.cpp @@ -0,0 +1,164 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Simple wildcard matching function. + +// WARNING: wheel re-invention follows +// Given that all shells perform wildcard matching, why don't the library writers put it in the C run-time???????? + +//////////////////////////////////////////////////////////////////////////////// +#include "wildcard.hpp" + +namespace stlplus +{ + + // function for testing whether a character matches a set + // I can't remember the exact rules and I have no definitive references but: + // a set contains characters, escaped characters (I think) and ranges in the form a-z + // The character '-' can only appear at the start of the set where it is not interpreted as a range + // This is a horrible mess - blame the Unix folks for making a hash of wildcards + // first expand any ranges and remove escape characters to make life more palatable + + static bool match_set (const std::string& set, char match) + { + std::string simple_set; + for (std::string::const_iterator i = set.begin(); i != set.end(); ++i) + { + switch(*i) + { + case '-': + { + if (i == set.begin()) + { + simple_set += *i; + } + else if (i+1 == set.end()) + { + return false; + } + else + { + // found a set. The first character is already in the result, so first remove it (the set might be empty) + simple_set.erase(simple_set.end()-1); + char last = *++i; + for (char ch = *(i-2); ch <= last; ch++) + { + simple_set += ch; + } + } + break; + } + case '\\': + if (i+1 == set.end()) {return false;} + simple_set += *++i; + break; + default: + simple_set += *i; + break; + } + } + std::string::size_type result = simple_set.find(match); + return result != std::string::npos; + } + + // the recursive bit - basically whenever a * is found you recursively call this for each candidate substring match + // until either it succeeds or you run out of string to match + // for each * in the wildcard another level of recursion is created + + static bool match_remainder (const std::string& wild, std::string::const_iterator wildi, const std::string& match, std::string::const_iterator matchi) + { + //cerr << "match_remainder called at " << *matchi << " with wildcard " << *wildi << endl; + while (wildi != wild.end() && matchi != match.end()) + { + //cerr << "trying to match " << *matchi << " with wildcard " << *wildi << endl; + switch(*wildi) + { + case '*': + { + ++wildi; + ++matchi; + for (std::string::const_iterator i = matchi; i != match.end(); ++i) + { + // deal with * at the end of the wildcard - there is no remainder then + if (wildi == wild.end()) + { + if (i == match.end()-1) + return true; + } + else if (match_remainder(wild, wildi, match, i)) + { + return true; + } + } + return false; + } + case '[': + { + // scan for the end of the set using a similar method for avoiding escaped characters + bool found = false; + std::string::const_iterator end = wildi + 1; + for (; !found && end != wild.end(); ++end) + { + switch(*end) + { + case ']': + { + // found the set, now match with its contents excluding the brackets + if (!match_set(wild.substr(wildi - wild.begin() + 1, end - wildi - 1), *matchi)) + return false; + found = true; + break; + } + case '\\': + if (end == wild.end()-1) + return false; + ++end; + break; + default: + break; + } + } + if (!found) + return false; + ++matchi; + wildi = end; + break; + } + case '?': + ++wildi; + ++matchi; + break; + case '\\': + if (wildi == wild.end()-1) + return false; + ++wildi; + if (*wildi != *matchi) + return false; + ++wildi; + ++matchi; + break; + default: + if (*wildi != *matchi) + return false; + ++wildi; + ++matchi; + break; + } + } + bool result = wildi == wild.end() && matchi == match.end(); + return result; + } + + // like all recursions the exported function has a simpler interface than the + // recursive function and is just a 'seed' to the recursion itself + + bool wildcard(const std::string& wild, const std::string& match) + { + return match_remainder(wild, wild.begin(), match, match.begin()); + } + +} // end namespace stlplus diff --git a/src/stlplus/portability/wildcard.hpp b/src/stlplus/portability/wildcard.hpp new file mode 100644 index 0000000..398c3dd --- /dev/null +++ b/src/stlplus/portability/wildcard.hpp @@ -0,0 +1,35 @@ +#ifndef STLPLUS_WILDCARD +#define STLPLUS_WILDCARD +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// This is a portable interface to wildcard matching. + +// The problem: +// * matches any number of characters - this is achieved by matching 1 and seeing if the remainder matches +// if not, try 2 characters and see if the remainder matches etc. +// this must be recursive, not iterative, so that multiple *s can appear in the same wildcard expression +// ? matches exactly one character so doesn't need the what-if approach +// \ escapes special characters such as *, ? and [ +// [] matches exactly one character in the set - the difficulty is the set can contain ranges, e.g [a-zA-Z0-9] +// a set cannot be empty and the ] character can be included by making it the first character + +//////////////////////////////////////////////////////////////////////////////// +#include "portability_fixes.hpp" +#include + +namespace stlplus +{ + + // wild = the wildcard expression + // match = the string to test against that expression + // e.g. wildcard("[a-f]*", "fred") returns true + bool wildcard(const std::string& wild, const std::string& match); + +} + +#endif diff --git a/src/stlplus/rules.mk b/src/stlplus/rules.mk new file mode 100644 index 0000000..74f1c37 --- /dev/null +++ b/src/stlplus/rules.mk @@ -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 index 0000000..5df7711 --- /dev/null +++ b/src/stlplus/strings/format_types.hpp @@ -0,0 +1,60 @@ +#ifndef STLPLUS_FORMAT_TYPES +#define STLPLUS_FORMAT_TYPES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A Set of enumerations controlling the string formatting of numbers. + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Integer radix display representations: +// There are three ways in which the radix is represented: +// - none - the number is just printed as a number (e.g. 12345). Can be confusing for non-decimal radix +// - C style - for binary, octal and hex, the C-style prefices 0b, 0 and 0x are used +// note that this is an unsigned representation +// - Hash style - in the form radix#value - the value may be signed, e.g. 10#-9 + +enum radix_display_t +{ + radix_none, // just print the number with no radix indicated + radix_hash_style, // none for decimal, hash style for all others + radix_hash_style_all, // hash style for all radices including decimal + radix_c_style, // C style for hex and octal, none for others + radix_c_style_or_hash // C style for hex and octal, none for decimal, hash style for others +}; + +//////////////////////////////////////////////////////////////////////////////// +// Floating-point display representations: +// There are three formats for the printout: +// - fixed - formatted as a fixed-point number, so no mantissa is printed (equivalent to %f in printf) +// - floating - formatted as a normalised floating-point number (equivalent to %e in printf) +// - mixed - formatted as fixed-point if appropriate, otherwise the floating format (equivalent to %g in printf) + +enum real_display_t +{ + display_fixed, // %f + display_floating, // %e + display_mixed // %g +}; + +//////////////////////////////////////////////////////////////////////////////// +// Alignment: +// There are three field alignments: +// - left aligned - the value is to the left of the field which is padded to the right with spaces +// - right aligned - the value is to the right of the field which is padded to the left with spaces +// - centred - the value is in the centre of the field and spaces added to both left and right + +enum alignment_t +{ + align_left, + align_right, + align_centre +}; + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/strings/print_address.cpp b/src/stlplus/strings/print_address.cpp new file mode 100644 index 0000000..3962557 --- /dev/null +++ b/src/stlplus/strings/print_address.cpp @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned long representation for pointers + +//////////////////////////////////////////////////////////////////////////////// +#include "print_address.hpp" +#include "print_int.hpp" +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_address(std::ostream& device, const void* i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + print_unsigned_long(device, (unsigned long)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_address.hpp b/src/stlplus/strings/print_address.hpp new file mode 100644 index 0000000..f89bdae --- /dev/null +++ b/src/stlplus/strings/print_address.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_PRINT_ADDRESS +#define STLPLUS_PRINT_ADDRESS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_address(std::ostream& device, + const void*, + unsigned radix = 16, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_basic.hpp b/src/stlplus/strings/print_basic.hpp new file mode 100644 index 0000000..9d67862 --- /dev/null +++ b/src/stlplus/strings/print_basic.hpp @@ -0,0 +1,21 @@ +#ifndef STLPLUS_PRINT_BASIC +#define STLPLUS_PRINT_BASIC +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Utilities for converting printing basic C types + +//////////////////////////////////////////////////////////////////////////////// + +#include "print_address.hpp" +#include "print_bool.hpp" +#include "print_cstring.hpp" +#include "print_float.hpp" +#include "print_int.hpp" +#include "print_pointer.hpp" + +#endif diff --git a/src/stlplus/strings/print_bitset.hpp b/src/stlplus/strings/print_bitset.hpp new file mode 100644 index 0000000..f7151dd --- /dev/null +++ b/src/stlplus/strings/print_bitset.hpp @@ -0,0 +1,27 @@ +#ifndef STLPLUS_PRINT_BITSET +#define STLPLUS_PRINT_BITSET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a bitset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_bitset(std::ostream& device, const std::bitset& data); + +} // end namespace stlplus + +#include "print_bitset.tpp" +#endif diff --git a/src/stlplus/strings/print_bitset.tpp b/src/stlplus/strings/print_bitset.tpp new file mode 100644 index 0000000..833103a --- /dev/null +++ b/src/stlplus/strings/print_bitset.tpp @@ -0,0 +1,20 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_bitset.hpp" + +namespace stlplus +{ + + template + void print_bitset(std::ostream& device, const std::bitset& data) + { + device << bitset_to_string(data); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_bool.cpp b/src/stlplus/strings/print_bool.cpp new file mode 100644 index 0000000..adcebc1 --- /dev/null +++ b/src/stlplus/strings/print_bool.cpp @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned short representation for bool + +//////////////////////////////////////////////////////////////////////////////// +#include "print_bool.hpp" +#include "print_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_bool(std::ostream& device, bool i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + print_unsigned_short(device, (unsigned short)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_bool.hpp b/src/stlplus/strings/print_bool.hpp new file mode 100644 index 0000000..5e808d1 --- /dev/null +++ b/src/stlplus/strings/print_bool.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PRINT_BOOL +#define STLPLUS_PRINT_BOOL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Conversion of string to/from bool + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_bool(std::ostream& device, bool i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_cstring.cpp b/src/stlplus/strings/print_cstring.cpp new file mode 100644 index 0000000..423c741 --- /dev/null +++ b/src/stlplus/strings/print_cstring.cpp @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_cstring.hpp" + +namespace stlplus +{ + + void print_cstring(std::ostream& device, const char* value) + { + device << value; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_cstring.hpp b/src/stlplus/strings/print_cstring.hpp new file mode 100644 index 0000000..57cdbcd --- /dev/null +++ b/src/stlplus/strings/print_cstring.hpp @@ -0,0 +1,27 @@ +#ifndef STLPLUS_PRINT_CSTRING +#define STLPLUS_PRINT_CSTRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C/STL strings to string + +// This is necessary for completeness + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + void print_cstring(std::ostream& device, const char* value); + +} + +#endif diff --git a/src/stlplus/strings/print_digraph.hpp b/src/stlplus/strings/print_digraph.hpp new file mode 100644 index 0000000..e5c2ff8 --- /dev/null +++ b/src/stlplus/strings/print_digraph.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_PRINT_DIGRAPH +#define STLPLUS_PRINT_DIGRAPH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a digraph + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "digraph.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_digraph(std::ostream& device, + const digraph& values, + NS node_print_fn, + AS arc_print_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_digraph.tpp" +#endif diff --git a/src/stlplus/strings/print_digraph.tpp b/src/stlplus/strings/print_digraph.tpp new file mode 100644 index 0000000..69e8018 --- /dev/null +++ b/src/stlplus/strings/print_digraph.tpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void print_digraph(std::ostream& device, const digraph& values, + NS node_print_fn, + AS arc_print_fn, + const std::string& separator) + { + device << "nodes:"; + device << separator; + print_sequence(device, values.begin(), values.end(), node_print_fn, separator); + device << "arcs:"; + device << separator; + print_sequence(device, values.arc_begin(), values.arc_end(), arc_print_fn, separator); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_float.cpp b/src/stlplus/strings/print_float.cpp new file mode 100644 index 0000000..a36c2c1 --- /dev/null +++ b/src/stlplus/strings/print_float.cpp @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_float.hpp" +#include "string_float.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // floating-point types + + void print_float(std::ostream& device, float f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + device << float_to_string(f, display, width, precision); + } + + void print_double(std::ostream& device, double f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + device << double_to_string(f, display, width, precision); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_float.hpp b/src/stlplus/strings/print_float.hpp new file mode 100644 index 0000000..2de8b8f --- /dev/null +++ b/src/stlplus/strings/print_float.hpp @@ -0,0 +1,47 @@ +#ifndef STLPLUS_PRINT_FLOAT +#define STLPLUS_PRINT_FLOAT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Convert a float/double to/from string + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // convert a real type to string + //////////////////////////////////////////////////////////////////////////////// + + // Only decimal radix is supported + + // The way in which the number is displayed is defined in radix_types.hpp + // Using any other value for the display type causes std::invalid_argument to be thrown + + void print_float(std::ostream& device, float f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + void print_double(std::ostream& device, double f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_foursome.hpp b/src/stlplus/strings/print_foursome.hpp new file mode 100644 index 0000000..3ee3696 --- /dev/null +++ b/src/stlplus/strings/print_foursome.hpp @@ -0,0 +1,35 @@ +#ifndef STLPLUS_PRINT_FOURSOME +#define STLPLUS_PRINT_FOURSOME +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a foursome + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "foursome.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_foursome(std::ostream& device, + const foursome& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + S4 print_fn4, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "print_foursome.tpp" +#endif diff --git a/src/stlplus/strings/print_foursome.tpp b/src/stlplus/strings/print_foursome.tpp new file mode 100644 index 0000000..8b9f319 --- /dev/null +++ b/src/stlplus/strings/print_foursome.tpp @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_foursome(std::ostream& device, + const foursome& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + S4 print_fn4, + const std::string& separator) + { + print_fn1(device, values.first); + device << separator; + print_fn2(device, values.second); + device << separator; + print_fn3(device, values.third); + device << separator; + print_fn4(device, values.fourth); + } + + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_hash.hpp b/src/stlplus/strings/print_hash.hpp new file mode 100644 index 0000000..43a76f4 --- /dev/null +++ b/src/stlplus/strings/print_hash.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PRINT_HASH +#define STLPLUS_PRINT_HASH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a hash + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "hash.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_hash(std::ostream& device, + const hash& values, + KS key_print_fn, + TS value_print_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_hash.tpp" +#endif diff --git a/src/stlplus/strings/print_hash.tpp b/src/stlplus/strings/print_hash.tpp new file mode 100644 index 0000000..5b3bd96 --- /dev/null +++ b/src/stlplus/strings/print_hash.tpp @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + template + void print_hash(std::ostream& device, + const hash& values, + KS key_print_fn, + TS value_print_fn, + const std::string& pair_separator, + const std::string& separator) + { + print_pair_sequence(device, + values.begin(), values.end(), + key_print_fn, value_print_fn, + pair_separator, separator); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_inf.cpp b/src/stlplus/strings/print_inf.cpp new file mode 100644 index 0000000..8a12f40 --- /dev/null +++ b/src/stlplus/strings/print_inf.cpp @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded from the build to break the dependency on the portability library +#ifndef NO_STLPLUS_INF + +#include "print_inf.hpp" +#include "string_inf.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + void print_inf(std::ostream& device, + const stlplus::inf& data, + unsigned radix, + radix_display_t display, + unsigned width) + throw(std::invalid_argument) + { + device << inf_to_string(data, radix, display, width); + } + + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_inf.hpp b/src/stlplus/strings/print_inf.hpp new file mode 100644 index 0000000..51b0377 --- /dev/null +++ b/src/stlplus/strings/print_inf.hpp @@ -0,0 +1,40 @@ +#ifndef STLPLUS_PRINT_INF +#define STLPLUS_PRINT_INF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +// The conversion supports all the formatting modes defined on format_types + +//////////////////////////////////////////////////////////////////////////////// + +#include "strings_fixes.hpp" +#include "inf.hpp" +#include "format_types.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_inf(std::ostream& device, + const inf&, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + +//////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus + + +#endif diff --git a/src/stlplus/strings/print_int.cpp b/src/stlplus/strings/print_int.cpp new file mode 100644 index 0000000..e2b63be --- /dev/null +++ b/src/stlplus/strings/print_int.cpp @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_int.hpp" +#include "string_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_short(std::ostream& device, short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << short_to_string(i, radix, display, width); + } + + void print_unsigned_short(std::ostream& device, unsigned short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << unsigned_short_to_string(i, radix, display, width); + } + + void print_int(std::ostream& device, int i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << int_to_string(i, radix, display, width); + } + + void print_unsigned(std::ostream& device, unsigned i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << unsigned_to_string(i, radix, display, width); + } + + void print_long(std::ostream& device, long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << long_to_string(i, radix, display, width); + } + + void print_unsigned_long(std::ostream& device, unsigned long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << unsigned_long_to_string(i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_int.hpp b/src/stlplus/strings/print_int.hpp new file mode 100644 index 0000000..4f97293 --- /dev/null +++ b/src/stlplus/strings/print_int.hpp @@ -0,0 +1,81 @@ +#ifndef STLPLUS_PRINT_INT +#define STLPLUS_PRINT_INT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Print integer types + +// This extends the formatting available from iostream + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Conversions of Integer types to string + //////////////////////////////////////////////////////////////////////////////// + + // The radix (i.e. base) for these conversions can be any value from base 2 to base 36 + // specifying any other radix causes std::invalid_argument to be thrown + + // The way in which the radix is displayed is defined in radix_types.hpp + // If any other value is used, std::invalid_argument is thrown + + // The width argument specifies the number of numerical digits to use in the result + // This is a minimum - if the value requires more digits then it will be wider than the width argument + // However, if it is smaller, then it will be extended to the specified width + // Then, the radix display prefix is added to this width + + // For example, using the hash representation of 0 in hex with width=4 gives: + // 16#0000 - so there's 4 digits in the number part + + void print_short(std::ostream& device, short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_unsigned_short(std::ostream& device, unsigned short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_int(std::ostream& device, int i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_unsigned(std::ostream& device, unsigned i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_long(std::ostream& device, long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_unsigned_long(std::ostream& device, unsigned long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_list.hpp b/src/stlplus/strings/print_list.hpp new file mode 100644 index 0000000..d1767b2 --- /dev/null +++ b/src/stlplus/strings/print_list.hpp @@ -0,0 +1,30 @@ +#ifndef STLPLUS_PRINT_LIST +#define STLPLUS_PRINT_LIST +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a list + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_list(std::ostream& device, + const std::list& values, + S print_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_list.tpp" +#endif diff --git a/src/stlplus/strings/print_list.tpp b/src/stlplus/strings/print_list.tpp new file mode 100644 index 0000000..e60c39a --- /dev/null +++ b/src/stlplus/strings/print_list.tpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + template + void print_list(std::ostream& device, + const std::list& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_map.hpp b/src/stlplus/strings/print_map.hpp new file mode 100644 index 0000000..6935037 --- /dev/null +++ b/src/stlplus/strings/print_map.hpp @@ -0,0 +1,38 @@ +#ifndef STLPLUS_PRINT_MAP +#define STLPLUS_PRINT_MAP +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a map/multimap + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_map(std::ostream& device, const std::map& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + + template + void print_multimap(std::ostream& device, const std::multimap& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_map.tpp" +#endif diff --git a/src/stlplus/strings/print_map.tpp b/src/stlplus/strings/print_map.tpp new file mode 100644 index 0000000..b3a727a --- /dev/null +++ b/src/stlplus/strings/print_map.tpp @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // map + + template + void print_map(std::ostream& device, const std::map& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator, + const std::string& separator) + { + print_pair_sequence(values.begin(), values.end(), + key_print_fn, value_print_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multimap + + template + void print_multimap(std::ostream& device, const std::multimap& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator, + const std::string& separator) + { + print_pair_sequence(device, + values.begin(), values.end(), + key_print_fn, value_print_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/print_matrix.hpp b/src/stlplus/strings/print_matrix.hpp new file mode 100644 index 0000000..f3fb8d5 --- /dev/null +++ b/src/stlplus/strings/print_matrix.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_PRINT_MATRIX +#define STLPLUS_PRINT_MATRIX +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a matrix + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "matrix.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_matrix(std::ostream& device, + const matrix& values, + S print_fn, + const std::string& column_separator = "|", + const std::string& row_separator = ","); + +} // end namespace stlplus + +#include "print_matrix.tpp" +#endif diff --git a/src/stlplus/strings/print_matrix.tpp b/src/stlplus/strings/print_matrix.tpp new file mode 100644 index 0000000..406c69f --- /dev/null +++ b/src/stlplus/strings/print_matrix.tpp @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void print_matrix(std::ostream& device, const matrix& values, + S print_fn, + const std::string& column_separator, + const std::string& row_separator) + { + for (unsigned r = 0; r < values.rows(); r++) + { + if (r != 0) device << row_separator; + for (unsigned c = 0; c < values.columns(); c++) + { + if (c != 0) device << column_separator; + print_fn(device, values(r,c)); + } + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_ntree.hpp b/src/stlplus/strings/print_ntree.hpp new file mode 100644 index 0000000..47c1a46 --- /dev/null +++ b/src/stlplus/strings/print_ntree.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_PRINT_NTREE +#define STLPLUS_PRINT_NTREE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an ntree + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "ntree.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_ntree(std::ostream& device, + const ntree& values, + S print_fn, + const std::string& separator = "|", + const std::string& indent_string = " "); + +} // end namespace stlplus + +#include "print_ntree.tpp" +#endif diff --git a/src/stlplus/strings/print_ntree.tpp b/src/stlplus/strings/print_ntree.tpp new file mode 100644 index 0000000..ab85611 --- /dev/null +++ b/src/stlplus/strings/print_ntree.tpp @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_ntree(std::ostream& device, + const ntree& values, + S print_fn, + const std::string& separator, + const std::string& indent_string) + { + for (TYPENAME ntree::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++) + { + if (i != values.prefix_begin()) device << separator; + for (unsigned indent = values.depth(i.simplify()); --indent; ) + device << indent_string; + print_fn(device, *i); + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_pair.hpp b/src/stlplus/strings/print_pair.hpp new file mode 100644 index 0000000..144461f --- /dev/null +++ b/src/stlplus/strings/print_pair.hpp @@ -0,0 +1,31 @@ +#ifndef STLPLUS_PRINT_PAIR +#define STLPLUS_PRINT_PAIR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a pair + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_pair(std::ostream& device, + const std::pair& values, + S1 print_fn1, + S2 print_fn2, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "print_pair.tpp" +#endif diff --git a/src/stlplus/strings/print_pair.tpp b/src/stlplus/strings/print_pair.tpp new file mode 100644 index 0000000..a93c0c0 --- /dev/null +++ b/src/stlplus/strings/print_pair.tpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_pair(std::ostream& device, + const std::pair& values, + S1 print_fn1, + S2 print_fn2, + const std::string& separator) + { + print_fn1(device, values.first); + device << separator; + print__fn2(device, values.second); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_pointer.hpp b/src/stlplus/strings/print_pointer.hpp new file mode 100644 index 0000000..cb738cc --- /dev/null +++ b/src/stlplus/strings/print_pointer.hpp @@ -0,0 +1,32 @@ +#ifndef STLPLUS_PRINT_POINTER +#define STLPLUS_PRINT_POINTER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an object pointed to + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + void print_pointer(std::ostream& device, + const T* value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} + +#include "print_pointer.tpp" +#endif diff --git a/src/stlplus/strings/print_pointer.tpp b/src/stlplus/strings/print_pointer.tpp new file mode 100644 index 0000000..be96d91 --- /dev/null +++ b/src/stlplus/strings/print_pointer.tpp @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include + +namespace stlplus +{ + + template + void print_pointer(std::ostream& device, + const T* value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_sequence.hpp b/src/stlplus/strings/print_sequence.hpp new file mode 100644 index 0000000..411ddd5 --- /dev/null +++ b/src/stlplus/strings/print_sequence.hpp @@ -0,0 +1,46 @@ +#ifndef STLPLUS_PRINT_SEQUENCE +#define STLPLUS_PRINT_SEQUENCE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate string representations of sequences represented by forward iterators + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + void print_sequence(std::ostream& device, + I begin, I end, + S print_fn, + const std::string& separator); + + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence of pairs + + template + void print_pair_sequence(std::ostream& device, + I begin, I end, + S1 print_fn1, + S2 print_fn2, + const std::string& pair_separator, + const std::string& separator); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "print_sequence.tpp" +#endif diff --git a/src/stlplus/strings/print_sequence.tpp b/src/stlplus/strings/print_sequence.tpp new file mode 100644 index 0000000..cea8056 --- /dev/null +++ b/src/stlplus/strings/print_sequence.tpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_pair.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + void print_sequence(std::ostream& device, + I begin, I end, + S print_fn, + const std::string& separator) + { + for (I i = begin; i != end; i++) + { + if (i != begin) device << separator; + print_fn(device, *i); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // any sequence where the value is a pair + + template + void print_pair_sequence(std::ostream& device, + I begin, I end, + S1 print_fn1, + S2 print_fn2, + const std::string& pair_separator, + const std::string& separator) + { + for (I i = begin; i != end; i++) + { + if (i != begin) device << separator; + print_pair(device, *i, print_fn1, print_fn2, pair_separator); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_set.hpp b/src/stlplus/strings/print_set.hpp new file mode 100644 index 0000000..33c3835 --- /dev/null +++ b/src/stlplus/strings/print_set.hpp @@ -0,0 +1,36 @@ +#ifndef STLPLUS_PRINT_SET +#define STLPLUS_PRINT_SET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a set/multiset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_set(std::ostream& device, + const std::set& values, + S print_fn, + const std::string& separator = ","); + + template + void print_multiset(std::ostream& device, + const std::multiset& values, + S print_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_set.tpp" +#endif diff --git a/src/stlplus/strings/print_set.tpp b/src/stlplus/strings/print_set.tpp new file mode 100644 index 0000000..25c88ef --- /dev/null +++ b/src/stlplus/strings/print_set.tpp @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // set + + template + void print_set(std::ostream& device, + const std::set& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multiset + + template + void print_multiset(std::ostream& device, + const std::multiset& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/print_simple_ptr.hpp b/src/stlplus/strings/print_simple_ptr.hpp new file mode 100644 index 0000000..e34a8ec --- /dev/null +++ b/src/stlplus/strings/print_simple_ptr.hpp @@ -0,0 +1,51 @@ +#ifndef STLPLUS_PRINT_SIMPLE_PTR +#define STLPLUS_PRINT_SIMPLE_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "simple_ptr.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_simple_ptr(std::ostream& device, + const simple_ptr& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_simple_ptr_clone(std::ostream& device, + const simple_ptr_clone& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_simple_ptr_nocopy(std::ostream& device, + const simple_ptr_nocopy& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "print_simple_ptr.tpp" +#endif diff --git a/src/stlplus/strings/print_simple_ptr.tpp b/src/stlplus/strings/print_simple_ptr.tpp new file mode 100644 index 0000000..30af07e --- /dev/null +++ b/src/stlplus/strings/print_simple_ptr.tpp @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_simple_ptr(std::ostream& device, + const simple_ptr& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_simple_ptr_clone(std::ostream& device, + const simple_ptr_clone& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_simple_ptr_nocopy(std::ostream& device, + const simple_ptr_nocopy& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_smart_ptr.hpp b/src/stlplus/strings/print_smart_ptr.hpp new file mode 100644 index 0000000..031c814 --- /dev/null +++ b/src/stlplus/strings/print_smart_ptr.hpp @@ -0,0 +1,51 @@ +#ifndef STLPLUS_PRINT_SMART_PTR +#define STLPLUS_PRINT_SMART_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "smart_ptr.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_smart_ptr(std::ostream& device, + const smart_ptr& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_smart_ptr_clone(std::ostream& device, + const smart_ptr_clone& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_smart_ptr_nocopy(std::ostream& device, + const smart_ptr_nocopy& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "print_smart_ptr.tpp" +#endif diff --git a/src/stlplus/strings/print_smart_ptr.tpp b/src/stlplus/strings/print_smart_ptr.tpp new file mode 100644 index 0000000..51877c7 --- /dev/null +++ b/src/stlplus/strings/print_smart_ptr.tpp @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_smart_ptr(std::ostream& device, + const smart_ptr& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_smart_ptr_clone(std::ostream& device, + const smart_ptr_clone& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_smart_ptr_nocopy(std::ostream& device, + const smart_ptr_nocopy& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_stl.hpp b/src/stlplus/strings/print_stl.hpp new file mode 100644 index 0000000..fb2b553 --- /dev/null +++ b/src/stlplus/strings/print_stl.hpp @@ -0,0 +1,23 @@ +#ifndef STLPLUS_PRINT_STL +#define STLPLUS_PRINT_STL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for pointers and STL containers + +//////////////////////////////////////////////////////////////////////////////// + +#include "print_bitset.hpp" +#include "print_list.hpp" +#include "print_map.hpp" +#include "print_pair.hpp" +#include "print_sequence.hpp" +#include "print_set.hpp" +#include "print_string.hpp" +#include "print_vector.hpp" + +#endif diff --git a/src/stlplus/strings/print_stlplus.hpp b/src/stlplus/strings/print_stlplus.hpp new file mode 100644 index 0000000..3f2cd46 --- /dev/null +++ b/src/stlplus/strings/print_stlplus.hpp @@ -0,0 +1,26 @@ +#ifndef STLPLUS_PRINT_STLPLUS +#define STLPLUS_PRINT_STLPLUS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for the STLplus containers + +//////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_STLPLUS_CONTAINERS +#include "print_digraph.hpp" +#include "print_foursome.hpp" +#include "print_hash.hpp" +#include "print_matrix.hpp" +#include "print_ntree.hpp" +#include "print_smart_ptr.hpp" +#include "print_triple.hpp" +#endif + +#include "print_inf.hpp" + +#endif diff --git a/src/stlplus/strings/print_string.cpp b/src/stlplus/strings/print_string.cpp new file mode 100644 index 0000000..8cf3b5b --- /dev/null +++ b/src/stlplus/strings/print_string.cpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_string.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // strings + + void print_string(std::ostream& device, const std::string& value) + { + device << value; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_string.hpp b/src/stlplus/strings/print_string.hpp new file mode 100644 index 0000000..dd23f56 --- /dev/null +++ b/src/stlplus/strings/print_string.hpp @@ -0,0 +1,26 @@ +#ifndef STLPLUS_PRINT_STRING +#define STLPLUS_PRINT_STRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C/STL strings to string + +// This is necessary for completeness, e.g. for use in print_vector for vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + void print_string(std::ostream& device, const std::string& value); +} + +#endif diff --git a/src/stlplus/strings/print_triple.hpp b/src/stlplus/strings/print_triple.hpp new file mode 100644 index 0000000..37b7af9 --- /dev/null +++ b/src/stlplus/strings/print_triple.hpp @@ -0,0 +1,34 @@ +#ifndef STLPLUS_PRINT_TRIPLE +#define STLPLUS_PRINT_TRIPLE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a triple + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "triple.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_triple(std::ostream& device, + const triple& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "print_triple.tpp" +#endif diff --git a/src/stlplus/strings/print_triple.tpp b/src/stlplus/strings/print_triple.tpp new file mode 100644 index 0000000..079c592 --- /dev/null +++ b/src/stlplus/strings/print_triple.tpp @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_triple(std::ostream& device, + const triple& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + const std::string& separator) + { + + print_fn1(device, values.first); + device << separator; + print_fn2(device, values.second); + device << separator; + print_fn3(device, values.third); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_vector.cpp b/src/stlplus/strings/print_vector.cpp new file mode 100644 index 0000000..41ea42e --- /dev/null +++ b/src/stlplus/strings/print_vector.cpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_vector.hpp" +#include "string_vector.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // special case of vector + + void print_bool_vector(std::ostream& device, const std::vector& values) + { + device << bool_vector_to_string(values); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_vector.hpp b/src/stlplus/strings/print_vector.hpp new file mode 100644 index 0000000..5916532 --- /dev/null +++ b/src/stlplus/strings/print_vector.hpp @@ -0,0 +1,38 @@ +#ifndef STLPLUS_PRINT_VECTOR +#define STLPLUS_PRINT_VECTOR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // vector + + template + void print_vector(std::ostream& device, + const std::vector& values, + S print_fn, + const std::string& separator); + + // specialisation for vector which has a different implementation + void print_bool_vector(std::ostream& device, const std::vector& values); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "print_vector.tpp" +#endif diff --git a/src/stlplus/strings/print_vector.tpp b/src/stlplus/strings/print_vector.tpp new file mode 100644 index 0000000..102cfd2 --- /dev/null +++ b/src/stlplus/strings/print_vector.tpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + template + void print_vector(std::ostream& device, const std::vector& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_address.cpp b/src/stlplus/strings/string_address.cpp new file mode 100644 index 0000000..74315e0 --- /dev/null +++ b/src/stlplus/strings/string_address.cpp @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned long representation for pointers + +//////////////////////////////////////////////////////////////////////////////// +#include "string_address.hpp" +#include "string_int.hpp" +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string address_to_string(const void* i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return unsigned_long_to_string((unsigned long)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + + void* string_to_address(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return (void*)string_to_unsigned_long(str, radix); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_address.hpp b/src/stlplus/strings/string_address.hpp new file mode 100644 index 0000000..f73d581 --- /dev/null +++ b/src/stlplus/strings/string_address.hpp @@ -0,0 +1,39 @@ +#ifndef STLPLUS_STRING_ADDRESS +#define STLPLUS_STRING_ADDRESS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting addresses to/from strings + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string address_to_string(const void*, + unsigned radix = 16, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + + void* string_to_address(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_basic.hpp b/src/stlplus/strings/string_basic.hpp new file mode 100644 index 0000000..f88dbcd --- /dev/null +++ b/src/stlplus/strings/string_basic.hpp @@ -0,0 +1,21 @@ +#ifndef STLPLUS_STRING_BASIC +#define STLPLUS_STRING_BASIC +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Utilities for converting basic C types to/from std::strings + +//////////////////////////////////////////////////////////////////////////////// + +#include "string_address.hpp" +#include "string_bool.hpp" +#include "string_cstring.hpp" +#include "string_float.hpp" +#include "string_int.hpp" +#include "string_pointer.hpp" + +#endif diff --git a/src/stlplus/strings/string_bitset.hpp b/src/stlplus/strings/string_bitset.hpp new file mode 100644 index 0000000..8c96ef3 --- /dev/null +++ b/src/stlplus/strings/string_bitset.hpp @@ -0,0 +1,26 @@ +#ifndef STLPLUS_STRING_BITSET +#define STLPLUS_STRING_BITSET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a bitset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string bitset_to_string(const std::bitset& data); + +} // end namespace stlplus + +#include "string_bitset.tpp" +#endif diff --git a/src/stlplus/strings/string_bitset.tpp b/src/stlplus/strings/string_bitset.tpp new file mode 100644 index 0000000..13b78a3 --- /dev/null +++ b/src/stlplus/strings/string_bitset.tpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string bitset_to_string(const std::bitset& data) + { + std::string result; + for (unsigned i = data.size(); i--; ) + result += data.test(i) ? '1' : '0'; + return result; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_bool.cpp b/src/stlplus/strings/string_bool.cpp new file mode 100644 index 0000000..ee25231 --- /dev/null +++ b/src/stlplus/strings/string_bool.cpp @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned short representation for bool + +//////////////////////////////////////////////////////////////////////////////// +#include "string_bool.hpp" +#include "string_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string bool_to_string(bool i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return unsigned_short_to_string((unsigned short)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool string_to_bool(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return string_to_unsigned_short(str, radix) != 0; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_bool.hpp b/src/stlplus/strings/string_bool.hpp new file mode 100644 index 0000000..adadcc6 --- /dev/null +++ b/src/stlplus/strings/string_bool.hpp @@ -0,0 +1,39 @@ +#ifndef STLPLUS_STRING_BOOL +#define STLPLUS_STRING_BOOL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Conversion of string to/from bool + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string bool_to_string(bool i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + + bool string_to_bool(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_cstring.cpp b/src/stlplus/strings/string_cstring.cpp new file mode 100644 index 0000000..0745049 --- /dev/null +++ b/src/stlplus/strings/string_cstring.cpp @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_cstring.hpp" + +namespace stlplus +{ + + std::string cstring_to_string(const char* value) + { + return std::string(value); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_cstring.hpp b/src/stlplus/strings/string_cstring.hpp new file mode 100644 index 0000000..554fbe3 --- /dev/null +++ b/src/stlplus/strings/string_cstring.hpp @@ -0,0 +1,26 @@ +#ifndef STLPLUS_STRING_CSTRING +#define STLPLUS_STRING_CSTRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C strings to string + +// This is necessary for completeness + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + std::string cstring_to_string(const char* value); + +} + +#endif diff --git a/src/stlplus/strings/string_digraph.hpp b/src/stlplus/strings/string_digraph.hpp new file mode 100644 index 0000000..ea70cc7 --- /dev/null +++ b/src/stlplus/strings/string_digraph.hpp @@ -0,0 +1,31 @@ +#ifndef STLPLUS_STRING_DIGRAPH +#define STLPLUS_STRING_DIGRAPH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a digraph + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "digraph.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string digraph_to_string(const digraph& values, + NS node_to_string_fn, + AS arc_to_string_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_digraph.tpp" +#endif diff --git a/src/stlplus/strings/string_digraph.tpp b/src/stlplus/strings/string_digraph.tpp new file mode 100644 index 0000000..9fd63d5 --- /dev/null +++ b/src/stlplus/strings/string_digraph.tpp @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + std::string digraph_to_string(const digraph& values, + NS node_to_string_fn, + AS arc_to_string_fn, + const std::string& separator) + { + std::string result; + result += "nodes:"; + result += separator; + result += sequence_to_string(values.begin(), values.end(), node_to_string_fn, separator); + result += "arcs:"; + result += separator; + result += sequence_to_string(values.arc_begin(), values.arc_end(), arc_to_string_fn, separator); + return result; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_float.cpp b/src/stlplus/strings/string_float.cpp new file mode 100644 index 0000000..0de056f --- /dev/null +++ b/src/stlplus/strings/string_float.cpp @@ -0,0 +1,96 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_float.hpp" +#include +#include +#include +#include + +namespace stlplus +{ + + // added as a local copy to break the dependency on the portability library + static std::string local_dformat(const char* format, ...) throw(std::invalid_argument) + { + std::string formatted; + va_list args; + va_start(args, format); +#ifdef MSWINDOWS + int length = 0; + char* buffer = 0; + for(int buffer_length = 256; ; buffer_length*=2) + { + buffer = (char*)malloc(buffer_length); + if (!buffer) throw std::invalid_argument("string_float"); + length = _vsnprintf(buffer, buffer_length-1, format, args); + if (length >= 0) + { + buffer[length] = 0; + formatted += std::string(buffer); + free(buffer); + break; + } + free(buffer); + } +#else + char* buffer = 0; + int length = vasprintf(&buffer, format, args); + if (!buffer) throw std::invalid_argument("string_float"); + if (length >= 0) + formatted += std::string(buffer); + free(buffer); +#endif + va_end(args); + if (length < 0) throw std::invalid_argument("string_float"); + return formatted; + } + + //////////////////////////////////////////////////////////////////////////////// + // floating-point types + + std::string float_to_string(float f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + return double_to_string((double)f, display, width, precision); + } + + std::string double_to_string(double f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + switch(display) + { + case display_fixed: + return local_dformat("%*.*f", width, precision, f); + case display_floating: + return local_dformat("%*.*e", width, precision, f); + case display_mixed: + return local_dformat("%*.*g", width, precision, f); + default: + throw std::invalid_argument("invalid radix display value"); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + float string_to_float(const std::string& value) + throw(std::invalid_argument) + { + return (float)string_to_double(value); + } + + double string_to_double(const std::string& value) + throw(std::invalid_argument) + { + // TODO - error checking + return strtod(value.c_str(), 0); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_float.hpp b/src/stlplus/strings/string_float.hpp new file mode 100644 index 0000000..797d4a4 --- /dev/null +++ b/src/stlplus/strings/string_float.hpp @@ -0,0 +1,55 @@ +#ifndef STLPLUS_STRING_FLOAT +#define STLPLUS_STRING_FLOAT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Convert a float/double to/from string + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // convert a real type to string + //////////////////////////////////////////////////////////////////////////////// + + // Only decimal radix is supported + + // The way in which the number is displayed is defined in radix_types.hpp + // Using any other value for the display type causes std::invalid_argument to be thrown + + std::string float_to_string(float f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + std::string double_to_string(double f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + // Convert a string to a floating-point type + + float string_to_float(const std::string& value) + throw(std::invalid_argument); + + double string_to_double(const std::string& value) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_foursome.hpp b/src/stlplus/strings/string_foursome.hpp new file mode 100644 index 0000000..1cf3db7 --- /dev/null +++ b/src/stlplus/strings/string_foursome.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_STRING_FOURSOME +#define STLPLUS_STRING_FOURSOME +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a foursome + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "foursome.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string foursome_to_string(const foursome& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + S4 to_string_fn4, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "string_foursome.tpp" +#endif diff --git a/src/stlplus/strings/string_foursome.tpp b/src/stlplus/strings/string_foursome.tpp new file mode 100644 index 0000000..a3e1536 --- /dev/null +++ b/src/stlplus/strings/string_foursome.tpp @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string foursome_to_string(const foursome& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + S4 to_string_fn4, + const std::string& separator) + { + return + to_string_fn1(values.first) + + separator + + to_string_fn2(values.second) + + separator + + to_string_fn3(values.third) + + separator + + to_string_fn4(values.fourth); + } + + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_hash.hpp b/src/stlplus/strings/string_hash.hpp new file mode 100644 index 0000000..a01f87b --- /dev/null +++ b/src/stlplus/strings/string_hash.hpp @@ -0,0 +1,32 @@ +#ifndef STLPLUS_STRING_HASH +#define STLPLUS_STRING_HASH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a hash + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "hash.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string hash_to_string(const hash& values, + KS key_to_string_fn, + TS value_to_string_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_hash.tpp" +#endif diff --git a/src/stlplus/strings/string_hash.tpp b/src/stlplus/strings/string_hash.tpp new file mode 100644 index 0000000..b3b2a41 --- /dev/null +++ b/src/stlplus/strings/string_hash.tpp @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + template + std::string hash_to_string(const hash& values, + KS key_to_string_fn, + TS value_to_string_fn, + const std::string& pair_separator, + const std::string& separator) + { + return pair_sequence_to_string(values.begin(), values.end(), + key_to_string_fn, value_to_string_fn, + pair_separator, separator); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_inf.cpp b/src/stlplus/strings/string_inf.cpp new file mode 100644 index 0000000..1734335 --- /dev/null +++ b/src/stlplus/strings/string_inf.cpp @@ -0,0 +1,536 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded from the build to break the dependency on the portability library +#ifndef NO_STLPLUS_INF + +#include "string_inf.hpp" +#include "string_basic.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static int from_char [] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + //////////////////////////////////////////////////////////////////////////////// + + std::string inf_to_string(const stlplus::inf& data, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + std::string result; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value"); + inf local_i = data; + // untangle all the options + bool hashed = false; + bool binary = false; + bool octal = false; + bool hex = false; + switch(display) + { + case radix_none: + break; + case radix_hash_style: + hashed = radix != 10; + break; + case radix_hash_style_all: + hashed = true; + break; + case radix_c_style: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + break; + case radix_c_style_or_hash: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + else if (radix != 10) + hashed = true; + break; + default: + throw std::invalid_argument("invalid radix display value"); + } + // create constants of the same type as the template parameter to avoid type mismatches + const inf t_zero(0); + const inf t_radix(radix); + // the C representations for binary, octal and hex use 2's-complement representation + // all other represenations use sign-magnitude + if (hex || octal || binary) + { + // bit-pattern representation + // this is the binary representation optionally shown in octal or hex + // first generate the binary by masking the bits + for (unsigned j = local_i.bits(); j--; ) + result += (local_i.bit(j) ? '1' : '0'); + // the result is now the full width of the type - e.g. int will give a 32-bit result + // now interpret this as either binary, octal or hex and add the prefix + if (binary) + { + // the result is already binary - but the width may be wrong + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + while (result.size() < width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > width) + { + // do not trim to less than 1 bit (sign only) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // add the prefix + result.insert((std::string::size_type)0, "0b"); + } + else if (octal) + { + // the result is currently binary - but before converting get the width right + // the width is expressed in octal digits so make the binary 3 times this + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() < 3*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 3*width) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + while (result.size() % 3 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to octal + std::string octal_result; + for (unsigned i = 0; i < result.size()/3; i++) + { + // yuck - ugly or what? + if (result[i*3] == '0') + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '0'; + else + octal_result += '1'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '2'; + else + octal_result += '3'; + } + } + else + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '4'; + else + octal_result += '5'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '6'; + else + octal_result += '7'; + } + } + } + result = octal_result; + // add the prefix + result.insert((std::string::size_type)0, "0"); + } + else + { + // similar to octal + while (result.size() < 4*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 4*width) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + while (result.size() % 4 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to hex + std::string hex_result; + for (unsigned i = 0; i < result.size()/4; i++) + { + // yuck - ugly or what? + if (result[i*4] == '0') + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '0'; + else + hex_result += '1'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '2'; + else + hex_result += '3'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '4'; + else + hex_result += '5'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '6'; + else + hex_result += '7'; + } + } + } + else + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '8'; + else + hex_result += '9'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'a'; + else + hex_result += 'b'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += 'c'; + else + hex_result += 'd'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'e'; + else + hex_result += 'f'; + } + } + } + } + result = hex_result; + // add the prefix + result.insert((std::string::size_type)0, "0x"); + } + } + else + { + // convert to sign-magnitude + // the representation is: + // [radix#][sign]magnitude + bool negative = local_i.negative(); + local_i.abs(); + // create a representation of the magnitude by successive division + do + { + std::pair divided = local_i.divide(t_radix); + unsigned remainder = divided.second.to_unsigned(); + char digit = to_char[remainder]; + result.insert((std::string::size_type)0, 1, digit); + local_i = divided.first; + } + while(!local_i.zero() || result.size() < width); + // add the prefixes + // add a sign only for negative values + if (negative) + result.insert((std::string::size_type)0, 1, '-'); + // then prefix everything with the radix if the hashed representation was requested + if (hashed) + result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions FROM string + + inf string_to_inf(const std::string& str, unsigned radix) throw(std::invalid_argument) + { + inf result; + if (radix != 0 && (radix < 2 || radix > 36)) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + unsigned i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by either the C prefix or the hash prefix + // Note: a leading zero is the C-style prefix for octal - I only make this + // override the default when the default radix is not specified + // first check for a C-style prefix + bool c_style = false; + if (i < str.size() && str[i] == '0') + { + // binary or hex + if (i+1 < str.size() && tolower(str[i+1]) == 'x') + { + c_style = true; + radix = 16; + i += 2; + } + else if (i+1 < str.size() && tolower(str[i+1]) == 'b') + { + c_style = true; + radix = 2; + i += 2; + } + else if (radix == 0) + { + c_style = true; + radix = 8; + i += 1; + } + } + // now check for a hash-style prefix if a C-style prefix was not found + if (i == 0) + { + // scan for the sequence {digits}# + bool hash_found = false; + unsigned j = i; + for (; j < str.size(); j++) + { + if (!isdigit(str[j])) + { + if (str[j] == '#') + hash_found = true; + break; + } + } + if (hash_found) + { + // use the hash prefix to define the radix + // i points to the start of the radix and j points to the # character + std::string slice = str.substr(i, j-i); + radix = string_to_unsigned(slice); + i = j+1; + } + } + if (radix == 0) + radix = 10; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value"); + if (c_style) + { + // the C style formats are bit patterns not integer values - these need + // to be sign-extended to get the right value + std::string binary; + if (radix == 2) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += '0'; + break; + case '1': + binary += '1'; + break; + default: + throw std::invalid_argument("invalid binary character in string " + str); + } + } + } + else if (radix == 8) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += "000"; + break; + case '1': + binary += "001"; + break; + case '2': + binary += "010"; + break; + case '3': + binary += "011"; + break; + case '4': + binary += "100"; + break; + case '5': + binary += "101"; + break; + case '6': + binary += "110"; + break; + case '7': + binary += "111"; + break; + default: + throw std::invalid_argument("invalid octal character in string " + str); + } + } + } + else + { + for (unsigned j = i; j < str.size(); j++) + { + switch(tolower(str[j])) + { + case '0': + binary += "0000"; + break; + case '1': + binary += "0001"; + break; + case '2': + binary += "0010"; + break; + case '3': + binary += "0011"; + break; + case '4': + binary += "0100"; + break; + case '5': + binary += "0101"; + break; + case '6': + binary += "0110"; + break; + case '7': + binary += "0111"; + break; + case '8': + binary += "1000"; + break; + case '9': + binary += "1001"; + break; + case 'a': + binary += "1010"; + break; + case 'b': + binary += "1011"; + break; + case 'c': + binary += "1100"; + break; + case 'd': + binary += "1101"; + break; + case 'e': + binary += "1110"; + break; + case 'f': + binary += "1111"; + break; + default: + throw std::invalid_argument("invalid hex character in string " + str); + } + } + } + // now convert the value + result.resize(binary.size()); + for (unsigned j = 0; j < binary.size(); j++) + result.preset(binary.size() - j - 1, binary[j] == '1'); + } + else + { + // now scan for a sign and find whether this is a negative number + bool negative = false; + if (i < str.size()) + { + switch (str[i]) + { + case '-': + negative = true; + i++; + break; + case '+': + i++; + break; + } + } + for (; i < str.size(); i++) + { + result *= inf(radix); + int ch = from_char[(unsigned char)str[i]] ; + if (ch == -1) + throw std::invalid_argument("invalid character in string " + str + " for radix " + unsigned_to_string(radix)); + result += inf(ch); + } + if (negative) + result.negate(); + } + return result; + } + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_inf.hpp b/src/stlplus/strings/string_inf.hpp new file mode 100644 index 0000000..1543263 --- /dev/null +++ b/src/stlplus/strings/string_inf.hpp @@ -0,0 +1,40 @@ +#ifndef STLPLUS_STRING_INF +#define STLPLUS_STRING_INF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +// The conversion supports all the formatting modes defined on format_types + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "inf.hpp" +#include "format_types.hpp" +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + // conversion TO string + std::string inf_to_string(const inf&, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + // conversion FROM string + inf string_to_inf(const std::string&, + unsigned radix = 0) + throw(std::invalid_argument); + +//////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus +#endif diff --git a/src/stlplus/strings/string_int.cpp b/src/stlplus/strings/string_int.cpp new file mode 100644 index 0000000..178cf4c --- /dev/null +++ b/src/stlplus/strings/string_int.cpp @@ -0,0 +1,1111 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_int.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // character mappings + + static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static int from_char [] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + //////////////////////////////////////////////////////////////////////////////// + // Conversions to string + // Local generic routines + + // signed version of the generic image generation function for all integer types + template + static std::string simage (T i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + // untangle all the options + bool hashed = false; + bool binary = false; + bool octal = false; + bool hex = false; + switch(display) + { + case radix_none: + break; + case radix_hash_style: + hashed = radix != 10; + break; + case radix_hash_style_all: + hashed = true; + break; + case radix_c_style: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + break; + case radix_c_style_or_hash: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + else if (radix != 10) + hashed = true; + break; + default: + throw std::invalid_argument("invalid radix display value"); + } + // create constants of the same type as the template parameter to avoid type mismatches + const T t_zero(0); + const T t_radix(radix); + // the C representations for binary, octal and hex use 2's-complement representation + // all other represenations use sign-magnitude + std::string result; + if (hex || octal || binary) + { + // bit-pattern representation + // this is the binary representation optionally shown in octal or hex + // first generate the binary by masking the bits + // ensure that it has at least one bit! + for (T mask(1); ; mask <<= 1) + { + result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0'); + if (mask == t_zero) break; + } + // the result is now the full width of the type - e.g. int will give a 32-bit result + // now interpret this as either binary, octal or hex and add the prefix + if (binary) + { + // the result is already binary - but the width may be wrong + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + while (result.size() < width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > width) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // add the prefix + result.insert((std::string::size_type)0, "0b"); + } + else if (octal) + { + // the result is currently binary - but before converting get the width right + // the width is expressed in octal digits so make the binary 3 times this + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() < 3*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 3*width) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + while (result.size() % 3 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to octal + std::string octal_result; + for (unsigned i = 0; i < result.size()/3; i++) + { + // yuck - ugly or what? + if (result[i*3] == '0') + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '0'; + else + octal_result += '1'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '2'; + else + octal_result += '3'; + } + } + else + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '4'; + else + octal_result += '5'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '6'; + else + octal_result += '7'; + } + } + } + result = octal_result; + // add the prefix + result.insert((std::string::size_type)0, "0"); + } + else + { + // hex - similar to octal + while (result.size() < 4*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 4*width) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + while (result.size() % 4 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to hex + std::string hex_result; + for (unsigned i = 0; i < result.size()/4; i++) + { + // yuck - ugly or what? + if (result[i*4] == '0') + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '0'; + else + hex_result += '1'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '2'; + else + hex_result += '3'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '4'; + else + hex_result += '5'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '6'; + else + hex_result += '7'; + } + } + } + else + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '8'; + else + hex_result += '9'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'a'; + else + hex_result += 'b'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += 'c'; + else + hex_result += 'd'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'e'; + else + hex_result += 'f'; + } + } + } + } + result = hex_result; + // add the prefix + result.insert((std::string::size_type)0, "0x"); + } + } + else + { + // convert to sign-magnitude + // the representation is: + // [radix#][sign]magnitude + bool negative = i < t_zero; + // create a representation of the magnitude by successive division + do + { + T ch = abs(i % t_radix); + i /= t_radix; + result.insert((std::string::size_type)0, 1, to_char[ch]); + } + while(i != t_zero || result.size() < width); + // add the prefixes + // add a sign only for negative values + if (negative) + result.insert((std::string::size_type)0, 1, '-'); + // then prefix everything with the radix if the hashed representation was requested + if (hashed) + result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); + } + return result; + } + + // unsigned version + template + static std::string uimage (T i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + // untangle all the options + bool hashed = false; + bool binary = false; + bool octal = false; + bool hex = false; + switch(display) + { + case radix_none: + break; + case radix_hash_style: + hashed = radix != 10; + break; + case radix_hash_style_all: + hashed = true; + break; + case radix_c_style: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + break; + case radix_c_style_or_hash: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + else if (radix != 10) + hashed = true; + break; + default: + throw std::invalid_argument("invalid radix display value"); + } + // create constants of the same type as the template parameter to avoid type mismatches + const T t_zero(0); + const T t_radix(radix); + // the C representations for binary, octal and hex use 2's-complement representation + // all other represenations use sign-magnitude + std::string result; + if (hex || octal || binary) + { + // bit-pattern representation + // this is the binary representation optionally shown in octal or hex + // first generate the binary by masking the bits + // ensure at least one bit + for (T mask(1); ; mask <<= 1) + { + result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0'); + if (mask == t_zero) break; + } + // the result is now the full width of the type - e.g. int will give a 32-bit result + // now interpret this as either binary, octal or hex and add the prefix + if (binary) + { + // the result is already binary - but the width may be wrong + // if this is still smaller than the width field, zero extend + // otherwise trim down to either the width or the smallest string that preserves the value + while (result.size() < width) + result.insert((std::string::size_type)0, 1, '0'); + while (result.size() > width) + { + // do not trim to less than 1 bit (1-bit magnitude) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != '0') break; + result.erase(0,1); + } + // add the prefix + result.insert((std::string::size_type)0, "0b"); + } + else if (octal) + { + // the result is currently binary - but before converting get the width right + // the width is expressed in octal digits so make the binary 3 times this + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() < 3*width) + result.insert((std::string::size_type)0, 1, '0'); + while (result.size() > 3*width) + { + // do not trim to less than 1 bit (1-bit magnitude) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != '0') break; + result.erase(0,1); + } + while (result.size() % 3 != 0) + result.insert((std::string::size_type)0, 1, '0'); + // now convert to octal + std::string octal_result; + for (unsigned i = 0; i < result.size()/3; i++) + { + // yuck - ugly or what? + if (result[i*3] == '0') + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '0'; + else + octal_result += '1'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '2'; + else + octal_result += '3'; + } + } + else + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '4'; + else + octal_result += '5'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '6'; + else + octal_result += '7'; + } + } + } + result = octal_result; + // add the prefix if the leading digit is not already 0 + if (result.empty() || result[0] != '0') result.insert((std::string::size_type)0, "0"); + } + else + { + // similar to octal + while (result.size() < 4*width) + result.insert((std::string::size_type)0, 1, '0'); + while (result.size() > 4*width) + { + // do not trim to less than 1 bit (1-bit magnitude) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != '0') break; + result.erase(0,1); + } + while (result.size() % 4 != 0) + result.insert((std::string::size_type)0, 1, '0'); + // now convert to hex + std::string hex_result; + for (unsigned i = 0; i < result.size()/4; i++) + { + // yuck - ugly or what? + if (result[i*4] == '0') + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '0'; + else + hex_result += '1'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '2'; + else + hex_result += '3'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '4'; + else + hex_result += '5'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '6'; + else + hex_result += '7'; + } + } + } + else + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '8'; + else + hex_result += '9'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'a'; + else + hex_result += 'b'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += 'c'; + else + hex_result += 'd'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'e'; + else + hex_result += 'f'; + } + } + } + } + result = hex_result; + // add the prefix + result.insert((std::string::size_type)0, "0x"); + } + } + else + { + // convert to sign-magnitude + // the representation is: + // [radix#]magnitude + // create a representation of the magnitude by successive division + do + { + T ch = i % t_radix; + i /= t_radix; + result.insert((std::string::size_type)0, 1, to_char[(int)ch]); + } + while(i != t_zero || result.size() < width); + // prefix everything with the radix if the hashed representation was requested + if (hashed) + result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // exported conversions to string + + std::string short_to_string(short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return simage(i, radix, display, width); + } + + std::string unsigned_short_to_string(unsigned short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return uimage(i, radix, display, width); + } + + std::string int_to_string(int i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return simage(i, radix, display, width); + } + + std::string unsigned_to_string(unsigned i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return uimage(i, radix, display, width); + } + + std::string long_to_string(long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return simage(i, radix, display, width); + } + + std::string unsigned_long_to_string(unsigned long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return uimage(i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions FROM string + // local template function + // Note: this has been copied and modified for the inf class - so any changes here must be made there too + + // signed version + template + static T svalue(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + if (radix != 0 && (radix < 2 || radix > 36)) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + std::string::size_type i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by either the C prefix or the hash prefix. Note: a leading zero + // is the C-style prefix for octal - I only make this override the default + // when the default prefix is not specified + // First check for a C-style prefix + bool c_style = false; + if (i < str.size() && str[i] == '0') + { + // octal, binary or hex + if (i+1 < str.size() && tolower(str[i+1]) == 'x') + { + radix = 16; + i += 2; + c_style = true; + } + else if (i+1 < str.size() && tolower(str[i+1]) == 'b') + { + radix = 2; + i += 2; + c_style = true; + } + else if (radix == 0) + { + radix = 8; + i += 1; + c_style = true; + } + } + // now check for a hash-style prefix if a C-style prefix was not found + if (i == 0) + { + // scan for the sequence {digits}# + bool hash_found = false; + std::string::size_type j = i; + for (; j < str.size(); j++) + { + if (!isdigit(str[j])) + { + if (str[j] == '#') + hash_found = true; + break; + } + } + if (hash_found) + { + // use the hash prefix to define the radix + // i points to the start of the radix and j points to the # character + std::string slice = str.substr(i, j-i); + radix = string_to_unsigned(slice); + i = j+1; + } + } + if (radix == 0) + radix = 10; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + T val(0); + if (c_style) + { + // the C style formats are bit patterns not integer values - these need + // to be sign-extended to get the right value + std::string binary; + if (radix == 2) + { + for (std::string::size_type j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += '0'; + break; + case '1': + binary += '1'; + break; + default: + throw std::invalid_argument("invalid binary character in string " + str); + break; + } + } + } + else if (radix == 8) + { + for (std::string::size_type j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += "000"; + break; + case '1': + binary += "001"; + break; + case '2': + binary += "010"; + break; + case '3': + binary += "011"; + break; + case '4': + binary += "100"; + break; + case '5': + binary += "101"; + break; + case '6': + binary += "110"; + break; + case '7': + binary += "111"; + break; + default: + throw std::invalid_argument("invalid octal character in string " + str); + break; + } + } + } + else + { + for (std::string::size_type j = i; j < str.size(); j++) + { + switch(tolower(str[j])) + { + case '0': + binary += "0000"; + break; + case '1': + binary += "0001"; + break; + case '2': + binary += "0010"; + break; + case '3': + binary += "0011"; + break; + case '4': + binary += "0100"; + break; + case '5': + binary += "0101"; + break; + case '6': + binary += "0110"; + break; + case '7': + binary += "0111"; + break; + case '8': + binary += "1000"; + break; + case '9': + binary += "1001"; + break; + case 'a': + binary += "1010"; + break; + case 'b': + binary += "1011"; + break; + case 'c': + binary += "1100"; + break; + case 'd': + binary += "1101"; + break; + case 'e': + binary += "1110"; + break; + case 'f': + binary += "1111"; + break; + default: + throw std::invalid_argument("invalid hex character in string " + str); + break; + } + } + } + // now sign-extend to the right number of bits for the type + while (binary.size() < sizeof(T)*8) + binary.insert((std::string::size_type)0, 1, binary.empty() ? '0' : binary[0]); + // now convert the value + for (std::string::size_type j = 0; j < binary.size(); j++) + { + val *= 2; + int ch = from_char[(unsigned char)binary[j]] ; + val += T(ch); + } + } + else + { + // now scan for a sign and find whether this is a negative number + bool negative = false; + if (i < str.size()) + { + switch (str[i]) + { + case '-': + negative = true; + i++; + break; + case '+': + i++; + break; + } + } + for (; i < str.size(); i++) + { + val *= T(radix); + int ch = from_char[(unsigned char)str[i]] ; + if (ch == -1 || (unsigned)ch >= radix) + throw std::invalid_argument("invalid character in string " + str); + val += T(ch); + } + if (negative) + val = -val; + } + return val; + } + + // unsigned version + template + static T uvalue(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + if (radix != 0 && (radix < 2 || radix > 36)) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + unsigned i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by either the C prefix or the hash prefix. Note: a leading + // zero is the C-style prefix for octal - I only make this override the + // default when the default prefix is not specified + // First check for a C-style prefix + bool c_style = false; + if (i < str.size() && str[i] == '0') + { + // binary or hex + if (i+1 < str.size() && tolower(str[i+1]) == 'x') + { + radix = 16; + i += 2; + c_style = true; + } + else if (i+1 < str.size() && tolower(str[i+1]) == 'b') + { + radix = 2; + i += 2; + c_style = true; + } + else if (radix == 0) + { + radix = 8; + i += 1; + c_style = true; + } + } + // now check for a hash-style prefix if a C-style prefix was not found + if (i == 0) + { + // scan for the sequence {digits}# + bool hash_found = false; + unsigned j = i; + for (; j < str.size(); j++) + { + if (!isdigit(str[j])) + { + if (str[j] == '#') + hash_found = true; + break; + } + } + if (hash_found) + { + // use the hash prefix to define the radix + // i points to the start of the radix and j points to the # character + std::string slice = str.substr(i, j-i); + radix = string_to_unsigned(slice); + i = j+1; + } + } + if (radix == 0) + radix = 10; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + T val(0); + if (c_style) + { + // the C style formats are bit patterns not integer values - these need + // to be sign-extended to get the right value + std::string binary; + if (radix == 2) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += '0'; + break; + case '1': + binary += '1'; + break; + default: + throw std::invalid_argument("invalid hex character in string " + str); + break; + } + } + } + else if (radix == 8) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += "000"; + break; + case '1': + binary += "001"; + break; + case '2': + binary += "010"; + break; + case '3': + binary += "011"; + break; + case '4': + binary += "100"; + break; + case '5': + binary += "101"; + break; + case '6': + binary += "110"; + break; + case '7': + binary += "111"; + break; + default: + throw std::invalid_argument("invalid octal character in string " + str); + break; + } + } + } + else + { + for (unsigned j = i; j < str.size(); j++) + { + switch(tolower(str[j])) + { + case '0': + binary += "0000"; + break; + case '1': + binary += "0001"; + break; + case '2': + binary += "0010"; + break; + case '3': + binary += "0011"; + break; + case '4': + binary += "0100"; + break; + case '5': + binary += "0101"; + break; + case '6': + binary += "0110"; + break; + case '7': + binary += "0111"; + break; + case '8': + binary += "1000"; + break; + case '9': + binary += "1001"; + break; + case 'a': + binary += "1010"; + break; + case 'b': + binary += "1011"; + break; + case 'c': + binary += "1100"; + break; + case 'd': + binary += "1101"; + break; + case 'e': + binary += "1110"; + break; + case 'f': + binary += "1111"; + break; + default: + throw std::invalid_argument("invalid hex character in string " + str); + break; + } + } + } + // now zero-extend to the right number of bits for the type + while (binary.size() < sizeof(T)*8) + binary.insert((std::string::size_type)0, 1, '0'); + // now convert the value + for (unsigned j = 0; j < binary.size(); j++) + { + val *= 2; + int ch = from_char[(unsigned char)binary[j]] ; + val += T(ch); + } + } + else + { + // now scan for a sign and find whether this is a negative number + if (i < str.size()) + { + switch (str[i]) + { + case '-': + throw std::invalid_argument("invalid sign character in string " + str + " for unsigned value"); + i++; + break; + case '+': + i++; + break; + } + } + for (; i < str.size(); i++) + { + val *= T(radix); + int ch = from_char[(unsigned char)str[i]] ; + if (ch == -1 || (unsigned)ch >= radix) + { + throw std::invalid_argument("invalid character in string " + str); + } + val += T(ch); + } + } + return val; + } + + //////////////////////////////////////////////////////////////////////////////// + // exported functions + + short string_to_short(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return svalue(str, radix); + } + + unsigned short string_to_unsigned_short(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return uvalue(str, radix); + } + + int string_to_int(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return svalue(str, radix); + } + + unsigned string_to_unsigned(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return uvalue(str, radix); + } + + long string_to_long(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return svalue(str, radix); + } + + unsigned long string_to_unsigned_long(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return uvalue(str, radix); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_int.hpp b/src/stlplus/strings/string_int.hpp new file mode 100644 index 0000000..a45b982 --- /dev/null +++ b/src/stlplus/strings/string_int.hpp @@ -0,0 +1,118 @@ +#ifndef STLPLUS_STRING_INT +#define STLPLUS_STRING_INT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Convert integer types to/from string + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Conversions of Integer types to string + //////////////////////////////////////////////////////////////////////////////// + + // The radix (i.e. base) for these conversions can be any value from base 2 to base 36 + // specifying any other radix causes std::invalid_argument to be thrown + + // The way in which the radix is displayed is defined in radix_types.hpp + // If any other value is used, std::invalid_argument is thrown + + // The width argument specifies the number of numerical digits to use in the result + // This is a minimum - if the value requires more digits then it will be wider than the width argument + // However, if it is smaller, then it will be extended to the specified width + // Then, the radix display prefix is added to this width + + // For example, using the hash representation of 0 in hex with width=4 gives: + // 16#0000 - so there's 4 digits in the number part + + std::string short_to_string(short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string unsigned_short_to_string(unsigned short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string int_to_string(int i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string unsigned_to_string(unsigned i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string long_to_string(long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string unsigned_long_to_string(unsigned long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + // Convert a string to an integer type + //////////////////////////////////////////////////////////////////////////////// + // supports all the formats described above for the reverse conversion + // If the radix is set to zero, the conversions deduce the radix from the string representation + // So, + // 0b prefix is binary, + // 0 prefix is octal, + // 0x is hex + // # prefix is my hash format + // The radix must be either zero as explained above, or in the range 2 to 16 + // A non-zero radix should be used when the string value has no radix information and is non-decimal + // e.g. the hex value FEDCBA has no indication that it is hex, so specify radix 16 + // Any other value of radix will cause std::invalid_argument to be thrown + + short string_to_short(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + unsigned short string_to_unsigned_short(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + int string_to_int(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + unsigned string_to_unsigned(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + long string_to_long(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + unsigned long string_to_unsigned_long(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_list.hpp b/src/stlplus/strings/string_list.hpp new file mode 100644 index 0000000..6728690 --- /dev/null +++ b/src/stlplus/strings/string_list.hpp @@ -0,0 +1,28 @@ +#ifndef STLPLUS_STRING_LIST +#define STLPLUS_STRING_LIST +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a list + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string list_to_string(const std::list& values, + S to_string_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_list.tpp" +#endif diff --git a/src/stlplus/strings/string_list.tpp b/src/stlplus/strings/string_list.tpp new file mode 100644 index 0000000..061f427 --- /dev/null +++ b/src/stlplus/strings/string_list.tpp @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + template + std::string list_to_string(const std::list& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_map.hpp b/src/stlplus/strings/string_map.hpp new file mode 100644 index 0000000..8bf7e8b --- /dev/null +++ b/src/stlplus/strings/string_map.hpp @@ -0,0 +1,37 @@ +#ifndef STLPLUS_STRING_MAP +#define STLPLUS_STRING_MAP +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a map/multimap + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string map_to_string(const std::map& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + + template + std::string multimap_to_string(const std::multimap& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_map.tpp" +#endif diff --git a/src/stlplus/strings/string_map.tpp b/src/stlplus/strings/string_map.tpp new file mode 100644 index 0000000..49cb51f --- /dev/null +++ b/src/stlplus/strings/string_map.tpp @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // map + + template + std::string map_to_string(const std::map& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator, + const std::string& separator) + { + return pair_sequence_to_string(values.begin(), values.end(), + key_to_string_fn, value_to_string_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multimap + + template + std::string multimap_to_string(const std::multimap& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator, + const std::string& separator) + { + return pair_sequence_to_string(values.begin(), values.end(), + key_to_string_fn, value_to_string_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/string_matrix.hpp b/src/stlplus/strings/string_matrix.hpp new file mode 100644 index 0000000..dd1fa2b --- /dev/null +++ b/src/stlplus/strings/string_matrix.hpp @@ -0,0 +1,31 @@ +#ifndef STLPLUS_STRING_MATRIX +#define STLPLUS_STRING_MATRIX +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a matrix + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "matrix.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string matrix_to_string(const matrix& values, + S to_string_fn, + const std::string& column_separator = "|", + const std::string& row_separator = ","); + +} // end namespace stlplus + +#include "string_matrix.tpp" +#endif diff --git a/src/stlplus/strings/string_matrix.tpp b/src/stlplus/strings/string_matrix.tpp new file mode 100644 index 0000000..345d15c --- /dev/null +++ b/src/stlplus/strings/string_matrix.tpp @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + std::string matrix_to_string(const matrix& values, + S to_string_fn, + const std::string& column_separator, + const std::string& row_separator) + { + std::string result; + for (unsigned r = 0; r < values.rows(); r++) + { + if (r != 0) result += row_separator; + for (unsigned c = 0; c < values.columns(); c++) + { + if (c != 0) result += column_separator; + result += to_string_fn(values(r,c)); + } + } + return result; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_ntree.hpp b/src/stlplus/strings/string_ntree.hpp new file mode 100644 index 0000000..177b53d --- /dev/null +++ b/src/stlplus/strings/string_ntree.hpp @@ -0,0 +1,31 @@ +#ifndef STLPLUS_STRING_NTREE +#define STLPLUS_STRING_NTREE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an ntree + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "ntree.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string ntree_to_string(const ntree& values, + S to_string_fn, + const std::string& separator = "|", + const std::string& indent_string = " "); + +} // end namespace stlplus + +#include "string_ntree.tpp" +#endif diff --git a/src/stlplus/strings/string_ntree.tpp b/src/stlplus/strings/string_ntree.tpp new file mode 100644 index 0000000..040dad3 --- /dev/null +++ b/src/stlplus/strings/string_ntree.tpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string ntree_to_string(const ntree& values, + S to_string_fn, + const std::string& separator, + const std::string& indent_string) + { + std::string result; + for (TYPENAME ntree::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++) + { + if (i != values.prefix_begin()) result += separator; + for (unsigned indent = values.depth(i.simplify()); --indent; ) + result += indent_string; + result += to_string_fn(*i); + } + return result; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_pair.hpp b/src/stlplus/strings/string_pair.hpp new file mode 100644 index 0000000..483a0b1 --- /dev/null +++ b/src/stlplus/strings/string_pair.hpp @@ -0,0 +1,29 @@ +#ifndef STLPLUS_STRING_PAIR +#define STLPLUS_STRING_PAIR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a pair + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string pair_to_string(const std::pair& values, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "string_pair.tpp" +#endif diff --git a/src/stlplus/strings/string_pair.tpp b/src/stlplus/strings/string_pair.tpp new file mode 100644 index 0000000..f458836 --- /dev/null +++ b/src/stlplus/strings/string_pair.tpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string pair_to_string(const std::pair& values, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& separator) + { + return to_string_fn1(values.first) + separator + to_string_fn2(values.second); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_pointer.hpp b/src/stlplus/strings/string_pointer.hpp new file mode 100644 index 0000000..2170000 --- /dev/null +++ b/src/stlplus/strings/string_pointer.hpp @@ -0,0 +1,30 @@ +#ifndef STLPLUS_STRING_POINTER +#define STLPLUS_STRING_POINTER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an object pointed to + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include + +namespace stlplus +{ + + template + std::string pointer_to_string(const T* value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} + +#include "string_pointer.tpp" +#endif diff --git a/src/stlplus/strings/string_pointer.tpp b/src/stlplus/strings/string_pointer.tpp new file mode 100644 index 0000000..d77a902 --- /dev/null +++ b/src/stlplus/strings/string_pointer.tpp @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include + +namespace stlplus +{ + + template + std::string pointer_to_string(const T* value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_sequence.hpp b/src/stlplus/strings/string_sequence.hpp new file mode 100644 index 0000000..f029031 --- /dev/null +++ b/src/stlplus/strings/string_sequence.hpp @@ -0,0 +1,45 @@ +#ifndef STLPLUS_STRING_SEQUENCE +#define STLPLUS_STRING_SEQUENCE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate string representations of sequences represented by forward iterators + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + std::string sequence_to_string(I begin, + I end, + S to_string, + const std::string& separator); + + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence of pairs + + template + std::string pair_sequence_to_string(I begin, + I end, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& pair_separator, + const std::string& separator); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "string_sequence.tpp" +#endif diff --git a/src/stlplus/strings/string_sequence.tpp b/src/stlplus/strings/string_sequence.tpp new file mode 100644 index 0000000..0bc522e --- /dev/null +++ b/src/stlplus/strings/string_sequence.tpp @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_pair.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + std::string sequence_to_string(I begin, + I end, + S to_string, + const std::string& separator) + { + std::string result; + for (I i = begin; i != end; i++) + { + if (i != begin) result += separator; + result += to_string(*i); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // any sequence where the value is a pair + + template + std::string pair_sequence_to_string(I begin, + I end, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& pair_separator, + const std::string& separator) + { + std::string result; + for (I i = begin; i != end; i++) + { + if (i != begin) result += separator; + result += pair_to_string(*i, to_string_fn1, to_string_fn2, pair_separator); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_set.hpp b/src/stlplus/strings/string_set.hpp new file mode 100644 index 0000000..ce06768 --- /dev/null +++ b/src/stlplus/strings/string_set.hpp @@ -0,0 +1,33 @@ +#ifndef STLPLUS_STRING_SET +#define STLPLUS_STRING_SET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a set/multiset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string set_to_string(const std::set& values, + S to_string_fn, + const std::string& separator = ","); + + template + std::string multiset_to_string(const std::multiset& values, + S to_string_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_set.tpp" +#endif diff --git a/src/stlplus/strings/string_set.tpp b/src/stlplus/strings/string_set.tpp new file mode 100644 index 0000000..d7c7e80 --- /dev/null +++ b/src/stlplus/strings/string_set.tpp @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // set + + template + std::string set_to_string(const std::set& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multiset + + template + std::string multiset_to_string(const std::multiset& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/string_simple_ptr.hpp b/src/stlplus/strings/string_simple_ptr.hpp new file mode 100644 index 0000000..813432c --- /dev/null +++ b/src/stlplus/strings/string_simple_ptr.hpp @@ -0,0 +1,47 @@ +#ifndef STLPLUS_STRING_SIMPLE_PTR +#define STLPLUS_STRING_SIMPLE_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "simple_ptr.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string simple_ptr_to_string(const simple_ptr& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string simple_ptr_clone_to_string(const simple_ptr_clone& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string simple_ptr__nocopy_to_string(const simple_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "string_simple_ptr.tpp" +#endif diff --git a/src/stlplus/strings/string_simple_ptr.tpp b/src/stlplus/strings/string_simple_ptr.tpp new file mode 100644 index 0000000..7da84af --- /dev/null +++ b/src/stlplus/strings/string_simple_ptr.tpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string simple_ptr_to_string(const simple_ptr& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string simple_ptr_clone_to_string(const simple_ptr_clone& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string simple_ptr_nocopy_to_string(const simple_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_smart_ptr.hpp b/src/stlplus/strings/string_smart_ptr.hpp new file mode 100644 index 0000000..3a5e253 --- /dev/null +++ b/src/stlplus/strings/string_smart_ptr.hpp @@ -0,0 +1,47 @@ +#ifndef STLPLUS_STRING_SMART_PTR +#define STLPLUS_STRING_SMART_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "smart_ptr.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string smart_ptr_to_string(const smart_ptr& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string smart_ptr_clone_to_string(const smart_ptr_clone& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string smart_ptr__nocopy_to_string(const smart_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "string_smart_ptr.tpp" +#endif diff --git a/src/stlplus/strings/string_smart_ptr.tpp b/src/stlplus/strings/string_smart_ptr.tpp new file mode 100644 index 0000000..7953fbf --- /dev/null +++ b/src/stlplus/strings/string_smart_ptr.tpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string smart_ptr_to_string(const smart_ptr& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string smart_ptr_clone_to_string(const smart_ptr_clone& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string smart_ptr_nocopy_to_string(const smart_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_stl.hpp b/src/stlplus/strings/string_stl.hpp new file mode 100644 index 0000000..11e05fb --- /dev/null +++ b/src/stlplus/strings/string_stl.hpp @@ -0,0 +1,23 @@ +#ifndef STLPLUS_STRING_STL +#define STLPLUS_STRING_STL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for pointers and STL containers + +//////////////////////////////////////////////////////////////////////////////// + +#include "string_bitset.hpp" +#include "string_list.hpp" +#include "string_map.hpp" +#include "string_pair.hpp" +#include "string_sequence.hpp" +#include "string_set.hpp" +#include "string_string.hpp" +#include "string_vector.hpp" + +#endif diff --git a/src/stlplus/strings/string_stlplus.hpp b/src/stlplus/strings/string_stlplus.hpp new file mode 100644 index 0000000..bdcad7b --- /dev/null +++ b/src/stlplus/strings/string_stlplus.hpp @@ -0,0 +1,30 @@ +#ifndef STLPLUS_STRING_STLPLUS +#define STLPLUS_STRING_STLPLUS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for the STLplus containers + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded to break the dependency on the containers library +#ifndef NO_STLPLUS_CONTAINERS +#include "string_digraph.hpp" +#include "string_foursome.hpp" +#include "string_hash.hpp" +#include "string_matrix.hpp" +#include "string_ntree.hpp" +#include "string_smart_ptr.hpp" +#include "string_triple.hpp" +#endif + +// can be excluded to break the dependency on the portability library +#ifndef NO_STLPLUS_INF +#include "string_inf.hpp" +#endif + +#endif diff --git a/src/stlplus/strings/string_string.cpp b/src/stlplus/strings/string_string.cpp new file mode 100644 index 0000000..53e8189 --- /dev/null +++ b/src/stlplus/strings/string_string.cpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_string.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // strings + + std::string string_to_string(const std::string& value) + { + return value; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_string.hpp b/src/stlplus/strings/string_string.hpp new file mode 100644 index 0000000..5f5b044 --- /dev/null +++ b/src/stlplus/strings/string_string.hpp @@ -0,0 +1,26 @@ +#ifndef STLPLUS_STRING_STRING +#define STLPLUS_STRING_STRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C/STL strings to string + +// This is necessary for completeness, e.g. for use in vector_to_string for vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + std::string string_to_string(const std::string& value); + +} + +#endif diff --git a/src/stlplus/strings/string_triple.hpp b/src/stlplus/strings/string_triple.hpp new file mode 100644 index 0000000..c812789 --- /dev/null +++ b/src/stlplus/strings/string_triple.hpp @@ -0,0 +1,32 @@ +#ifndef STLPLUS_STRING_TRIPLE +#define STLPLUS_STRING_TRIPLE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a triple + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "triple.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string triple_to_string(const triple& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "string_triple.tpp" +#endif diff --git a/src/stlplus/strings/string_triple.tpp b/src/stlplus/strings/string_triple.tpp new file mode 100644 index 0000000..ddd574a --- /dev/null +++ b/src/stlplus/strings/string_triple.tpp @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string triple_to_string(const triple& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + const std::string& separator) + { + return + to_string_fn1(values.first) + + separator + + to_string_fn2(values.second) + + separator + + to_string_fn3(values.third); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_utilities.cpp b/src/stlplus/strings/string_utilities.cpp new file mode 100644 index 0000000..9ff5345 --- /dev/null +++ b/src/stlplus/strings/string_utilities.cpp @@ -0,0 +1,442 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_utilities.hpp" +#include "string_basic.hpp" +#include +#include +#include +#include + +namespace stlplus +{ + + // added as a local copy to break the dependency on the portability library + static std::string local_dformat(const char* format, ...) throw(std::invalid_argument) + { + std::string formatted; + va_list args; + va_start(args, format); +#ifdef MSWINDOWS + int length = 0; + char* buffer = 0; + for(int buffer_length = 256; ; buffer_length*=2) + { + buffer = (char*)malloc(buffer_length); + if (!buffer) throw std::invalid_argument("string_utilities"); + length = _vsnprintf(buffer, buffer_length-1, format, args); + if (length >= 0) + { + buffer[length] = 0; + formatted += std::string(buffer); + free(buffer); + break; + } + free(buffer); + } +#else + char* buffer = 0; + int length = vasprintf(&buffer, format, args); + if (!buffer) throw std::invalid_argument("string_utilities"); + if (length >= 0) + formatted += std::string(buffer); + free(buffer); +#endif + va_end(args); + if (length < 0) throw std::invalid_argument("string_utilities"); + return formatted; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string pad(const std::string& str, alignment_t alignment, unsigned width, char padch) + throw(std::invalid_argument) + { + std::string result = str; + switch(alignment) + { + case align_left: + { + unsigned padding = width>str.size() ? width - str.size() : 0; + unsigned i = 0; + while (i++ < padding) + result.insert(result.end(), padch); + break; + } + case align_right: + { + unsigned padding = width>str.size() ? width - str.size() : 0; + unsigned i = 0; + while (i++ < padding) + result.insert(result.begin(), padch); + break; + } + case align_centre: + { + unsigned padding = width>str.size() ? width - str.size() : 0; + unsigned i = 0; + while (i++ < padding/2) + result.insert(result.end(), padch); + i--; + while (i++ < padding) + result.insert(result.begin(), padch); + break; + } + default: + throw std::invalid_argument("invalid alignment value"); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string trim_left(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[0])) + result.erase(result.begin()); + return result; + } + + std::string trim_right(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[result.size()-1])) + result.erase(result.end()-1); + return result; + } + + std::string trim(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[0])) + result.erase(result.begin()); + while (!result.empty() && isspace(result[result.size()-1])) + result.erase(result.end()-1); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string lowercase(const std::string& val) + { + std::string text = val; + for (unsigned i = 0; i < text.size(); i++) + text[i] = tolower(text[i]); + return text; + } + + std::string uppercase(const std::string& val) + { + std::string text = val; + for (unsigned i = 0; i < text.size(); i++) + text[i] = toupper(text[i]); + return text; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string translate(const std::string& input, const std::string& from_set, const std::string& to_set) + { + std::string result; + for (unsigned i = 0; i < input.size(); i++) + { + char ch = input[i]; + // check to see if the character is in the from set + std::string::size_type found = from_set.find(ch); + if (found == std::string::npos) + { + // not found so just copy across + result += ch; + } + else if (found < to_set.size()) + { + // found and in range so translate + result += to_set[found]; + } + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // WARNING: wheel re-invention follows + // Given that all shells perform wildcard matching, why don't the library writers put it in the C run-time???????? + // The problem: + // * matches any number of characters - this is achieved by matching 1 and seeing if the remainder matches + // if not, try 2 characters and see if the remainder matches etc. + // this must be recursive, not iterative, so that multiple *s can appear in the same wildcard expression + // ? matches exactly one character so doesn't need the what-if approach + // \ escapes special characters such as *, ? and [ + // [] matches exactly one character in the set - the difficulty is the set can contain ranges, e.g [a-zA-Z0-9] + // a set cannot be empty and the ] character can be included by making it the first character + + // function for testing whether a character matches a set + // I can't remember the exact rules and I have no definitive references but: + // a set contains characters, escaped characters (I think) and ranges in the form a-z + // The character '-' can only appear at the start of the set where it is not interpreted as a range + // This is a horrible mess - blame the Unix folks for making a hash of wildcards + + static bool match_set (const std::string& set, char match) + { + // first expand any ranges and remove escape characters to make life more palatable + std::string simple_set; + for (std::string::const_iterator i = set.begin(); i != set.end(); ++i) + { + switch(*i) + { + case '-': + { + if (i == set.begin()) + { + simple_set += *i; + } + else if (i+1 == set.end()) + { + return false; + } + else + { + // found a set. The first character is already in the result, so first remove it (the set might be empty) + simple_set.erase(simple_set.end()-1); + char last = *++i; + for (char ch = *(i-2); ch <= last; ch++) + { + simple_set += ch; + } + } + break; + } + case '\\': + if (i+1 == set.end()) {return false;} + simple_set += *++i; + break; + default: + simple_set += *i; + break; + } + } + std::string::size_type result = simple_set.find(match); + return result != std::string::npos; + } + + // the recursive bit - basically whenever a * is found you recursively call this for each candidate substring match + // until either it succeeds or you run out of string to match + // for each * in the wildcard another level of recursion is created + + static bool match_remainder (const std::string& wild, std::string::const_iterator wildi, + const std::string& match, std::string::const_iterator matchi) + { + //cerr << "match_remainder called at " << *matchi << " with wildcard " << *wildi << endl; + while (wildi != wild.end() && matchi != match.end()) + { + //cerr << "trying to match " << *matchi << " with wildcard " << *wildi << endl; + switch(*wildi) + { + case '*': + { + ++wildi; + ++matchi; + for (std::string::const_iterator i = matchi; i != match.end(); ++i) + { + // deal with * at the end of the wildcard - there is no remainder then + if (wildi == wild.end()) + { + if (i == match.end()-1) + return true; + } + else if (match_remainder(wild, wildi, match, i)) + { + return true; + } + } + return false; + } + case '[': + { + // scan for the end of the set using a similar method for avoiding escaped characters + bool found = false; + std::string::const_iterator end = wildi + 1; + for (; !found && end != wild.end(); ++end) + { + switch(*end) + { + case ']': + { + // found the set, now match with its contents excluding the brackets + if (!match_set(wild.substr(wildi - wild.begin() + 1, end - wildi - 1), *matchi)) + return false; + found = true; + break; + } + case '\\': + if (end == wild.end()-1) + return false; + ++end; + break; + default: + break; + } + } + if (!found) + return false; + ++matchi; + wildi = end; + break; + } + case '?': + ++wildi; + ++matchi; + break; + case '\\': + if (wildi == wild.end()-1) + return false; + ++wildi; + if (*wildi != *matchi) + return false; + ++wildi; + ++matchi; + break; + default: + if (*wildi != *matchi) + return false; + ++wildi; + ++matchi; + break; + } + } + bool result = wildi == wild.end() && matchi == match.end(); + return result; + } + + // like all recursions the exported function has a simpler interface than the + // recursive function and is just a 'seed' to the recursion itself + + bool match_wildcard(const std::string& wild, const std::string& match) + { + return match_remainder(wild, wild.begin(), match, match.begin()); + } + + //////////////////////////////////////////////////////////////////////////////// + + std::vector split(const std::string& str, const std::string& splitter) + { + std::vector result; + if (!str.empty()) + { + for(std::string::size_type offset = 0;;) + { + std::string::size_type found = str.find(splitter, offset); + if (found != std::string::npos) + { + result.push_back(str.substr(offset, found-offset)); + offset = found + splitter.size(); + } + else + { + result.push_back(str.substr(offset, str.size()-offset)); + break; + } + } + } + return result; + } + + std::string join (const std::vector& str, + const std::string& joiner, + const std::string& prefix, + const std::string& suffix) + { + std::string result = prefix; + for (unsigned i = 0; i < str.size(); i++) + { + if (i) result += joiner; + result += str[i]; + } + result += suffix; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string display_bytes(long bytes) + { + std::string result; + if (bytes < 0) + { + result += '-'; + bytes = -bytes; + } + static const long kB = 1024l; + static const long MB = kB * kB; + static const long GB = MB * kB; + if (bytes < kB) + result += local_dformat("%i", bytes); + else if (bytes < (10l * kB)) + result += local_dformat("%.2fk", ((float)bytes / (float)kB)); + else if (bytes < (100l * kB)) + result += local_dformat("%.1fk", ((float)bytes / (float)kB)); + else if (bytes < MB) + result += local_dformat("%.0fk", ((float)bytes / (float)kB)); + else if (bytes < (10l * MB)) + result += local_dformat("%.2fM", ((float)bytes / (float)MB)); + else if (bytes < (100l * MB)) + result += local_dformat("%.1fM", ((float)bytes / (float)MB)); + else if (bytes < GB) + result += local_dformat("%.0fM", ((float)bytes / (float)MB)); + else + result += local_dformat("%.2fG", ((float)bytes / (float)GB)); + return result; + } + + std::string display_time(time_t seconds) + { + unsigned minutes = (unsigned)seconds / 60; + seconds %= 60; + unsigned hours = minutes / 60; + minutes %= 60; + unsigned days = hours / 24; + hours %= 24; + unsigned weeks = days / 7; + days %= 7; + std::string result; + if (weeks > 0) + { + result += unsigned_to_string(weeks, 10, radix_none, 1); + result += "w "; + } + if (!result.empty() || days > 0) + { + result += unsigned_to_string(days, 10, radix_none, 1); + result += "d "; + } + if (!result.empty() || hours > 0) + { + result += unsigned_to_string(hours, 10, radix_none, 1); + result += ":"; + } + if (!result.empty() || minutes > 0) + { + if (!result.empty()) + result += unsigned_to_string(minutes, 10, radix_none, 2); + else + result += unsigned_to_string(minutes, 10, radix_none, 1); + result += ":"; + } + if (!result.empty()) + result += unsigned_to_string((unsigned)seconds, 10, radix_none, 2); + else + { + result += unsigned_to_string((unsigned)seconds, 10, radix_none, 1); + result += "s"; + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_utilities.hpp b/src/stlplus/strings/string_utilities.hpp new file mode 100644 index 0000000..d8d39c4 --- /dev/null +++ b/src/stlplus/strings/string_utilities.hpp @@ -0,0 +1,120 @@ +#ifndef STLPLUS_STRING_UTILITIES +#define STLPLUS_STRING_UTILITIES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Utilities for manipulating std::strings + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Padding function allows a string to be printed in a fixed-width field + //////////////////////////////////////////////////////////////////////////////// + + // The definitions for the alignment are declared in format_types.hpp + // Any other value will cause std::invalid_argument to be thrown + + std::string pad(const std::string& str, + alignment_t alignment, + unsigned width, + char padch = ' ') + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + // whitespace trimming + //////////////////////////////////////////////////////////////////////////////// + + std::string trim_left(const std::string& val); + std::string trim_right(const std::string& val); + std::string trim(const std::string& val); + + //////////////////////////////////////////////////////////////////////////////// + // case conversion for std::strings + //////////////////////////////////////////////////////////////////////////////// + + std::string lowercase(const std::string& val); + std::string uppercase(const std::string& val); + + //////////////////////////////////////////////////////////////////////////////// + // character translation - inspired by Unix 'tr' command + //////////////////////////////////////////////////////////////////////////////// + + // convert characters represented in from_set to the characters in the same position in to_set + // for example: + // filename = translate(filename,"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + // converts the filename to uppercase and returns the result (Note that the + // uppercase function does this more easily). If the from_set is longer than + // the to_set, then the overlap represents characters to delete (i.e. they map + // to nothing) + + std::string translate(const std::string& input, + const std::string& from_set, + const std::string& to_set = std::string()); + + //////////////////////////////////////////////////////////////////////////////// + // wildcard matching + //////////////////////////////////////////////////////////////////////////////// + + // this function does wildcard matching of the wildcard expression against the candidate std::string + // wildcards are NOT regular expressions + // the wildcard characters are * and ? where * matches 1 or more characters and ? matches only one + // there are also character sets [a-z] [qwertyuiop] etc. which match 1 character + // TODO: character sets like [:alpha:] + // TODO eventually: regular expression matching and substitution (3rd party library?) + + bool match_wildcard(const std::string& wild, + const std::string& match); + + //////////////////////////////////////////////////////////////////////////////// + // Perl-inspired split/join functions + //////////////////////////////////////////////////////////////////////////////// + + // splits the string at every occurance of splitter and adds it as a separate string to the return value + // the splitter is removed + // a string with no splitter in it will give a single-value vector + // an empty string gives an empty vector + + std::vector split (const std::string& str, + const std::string& splitter = "\n"); + + // the reverse of the above + // joins the string vector to create a single string with the joiner inserted between the joins + // Note: the joiner will not be added at the beginning or the end + // However, there are optional fields to add such prefix and suffix strings + + std::string join (const std::vector&, + const std::string& joiner = "\n", + const std::string& prefix = "", + const std::string& suffix = ""); + + //////////////////////////////////////////////////////////////////////////////// + // special displays + //////////////////////////////////////////////////////////////////////////////// + + // display the parameter as a number in bytes, kbytes, Mbytes, Gbytes depending on range + + std::string display_bytes(long bytes); + + // display the parameter in seconds as a string representation in weeks, days, hours, minutes, seconds + // e.g. "1d 1:01:01" means 1 day, 1 hour, 1 minute and 1 second + + std::string display_time(time_t seconds); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_vector.cpp b/src/stlplus/strings/string_vector.cpp new file mode 100644 index 0000000..bee9b88 --- /dev/null +++ b/src/stlplus/strings/string_vector.cpp @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_vector.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // special case of vector + + std::string bool_vector_to_string(const std::vector& values) + { + std::string result; + for (size_t i = 0; i < values.size(); i++) + result.append(1, values[i] ? '1' : '0'); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_vector.hpp b/src/stlplus/strings/string_vector.hpp new file mode 100644 index 0000000..6842a98 --- /dev/null +++ b/src/stlplus/strings/string_vector.hpp @@ -0,0 +1,36 @@ +#ifndef STLPLUS_STRING_VECTOR +#define STLPLUS_STRING_VECTOR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // vector + + template + std::string vector_to_string(const std::vector& values, + S to_string_fn, + const std::string& separator = ","); + + // specialisation for vector which has a different implementation + std::string bool_vector_to_string(const std::vector& values); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "string_vector.tpp" +#endif diff --git a/src/stlplus/strings/string_vector.tpp b/src/stlplus/strings/string_vector.tpp new file mode 100644 index 0000000..de535ee --- /dev/null +++ b/src/stlplus/strings/string_vector.tpp @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + template + std::string vector_to_string(const std::vector& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/strings.hpp b/src/stlplus/strings/strings.hpp new file mode 100644 index 0000000..06a6e92 --- /dev/null +++ b/src/stlplus/strings/strings.hpp @@ -0,0 +1,30 @@ +#ifndef STLPLUS_STRINGS +#define STLPLUS_STRINGS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Header for including the whole strings library in one go + +// The strings library prints C and C++ types into a std::string + +// - it extends the numeric presentation of integers to include any radix from 2 - 36 +// - it allows the printing of data structures - useful for diagnostic dumps + +//////////////////////////////////////////////////////////////////////////////// + +#include "string_utilities.hpp" + +#include "string_basic.hpp" +#include "string_stl.hpp" +#include "string_stlplus.hpp" + +#include "print_basic.hpp" +#include "print_stl.hpp" +#include "print_stlplus.hpp" + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/strings/strings_fixes.hpp b/src/stlplus/strings/strings_fixes.hpp new file mode 100644 index 0000000..2ed1dc5 --- /dev/null +++ b/src/stlplus/strings/strings_fixes.hpp @@ -0,0 +1,56 @@ +#ifndef STLPLUS_STRINGS_FIXES +#define STLPLUS_STRINGS_FIXES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Contains work arounds for OS or Compiler specific problems + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Problem with MicroSoft defining two different macros to identify Windows +//////////////////////////////////////////////////////////////////////////////// + +#if defined(_WIN32) || defined(_WIN32_WCE) +#define MSWINDOWS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Unnecessary compiler warnings +//////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +// Microsoft Visual Studio +// shut up the following irritating warnings +// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger) +// 4305 - VC6, identifier type was converted to a smaller type +// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger) +// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it +// 4290 - VC6, C++ exception specification ignored +// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning) +// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program +// 4996 - VC8, 'xxxx' was declared deprecated +#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996) +#endif + +#ifdef __BORLANDC__ +// Borland +// Shut up the following irritating warnings +// 8026 - Functions with exception specifications are not expanded inline +// 8027 - Functions with xxx are not expanded inline +// 8066 - Unreachable code. +// A break, continue, goto, or return statement was not followed by a +// label or the end of a loop or function. The compiler checks while, +// do, and for loops with a constant test condition, and attempts to +// recognize loops that can't fall through. +#pragma warn -8026 +#pragma warn -8027 +#pragma warn -8066 +#endif + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/subsystems/cli_parser.cpp b/src/stlplus/subsystems/cli_parser.cpp new file mode 100644 index 0000000..1088f38 --- /dev/null +++ b/src/stlplus/subsystems/cli_parser.cpp @@ -0,0 +1,637 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "cli_parser.hpp" +#include "file_system.hpp" +#include "dprintf.hpp" +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // cli_definition internals + + const std::string& stlplus::cli_definition::name(void) const + { + return m_name; + } + + stlplus::cli_kind_t stlplus::cli_definition::kind(void) const + { + return m_kind; + } + + stlplus::cli_mode_t stlplus::cli_definition::mode(void) const + { + return m_mode; + } + + const std::string& stlplus::cli_definition::message(void) const + { + return m_message; + } + + const std::string& stlplus::cli_definition::default_value(void) const + { + return m_default; + } + + //////////////////////////////////////////////////////////////////////////////// + // internal data structures + + class cli_value + { + public: + unsigned m_definition; + std::string m_value; + unsigned m_level; + std::string m_source; + + cli_value(unsigned definition, const std::string& value, unsigned level, const std::string& source) : + m_definition(definition), m_value(value), m_level(level), m_source(source) + { + } + }; + + //////////////////////////////////////////////////////////////////////////////// + + class cli_parser_data + { + public: + message_handler& m_messages; + std::string m_executable; + cli_parser::definitions m_definitions; + std::vector m_values; + unsigned m_level; + bool m_valid; + std::vector m_ini_files; + + public: + + cli_parser_data(message_handler& messages) : + m_messages(messages), m_level(1), m_valid(true) + { + // ensure that the CLI's messages are in the message handler - these + // can be overridden from a file later - see message_handler + if (!m_messages.message_present("CLI_VALUE_MISSING")) + m_messages.add_message("CLI_VALUE_MISSING", "option @0 requires a value - end of line was reached instead"); + if (!m_messages.message_present("CLI_UNRECOGNISED_OPTION")) + m_messages.add_message("CLI_UNRECOGNISED_OPTION", "@0 is not a recognised option for this command"); + if (!m_messages.message_present("CLI_NO_VALUES")) + m_messages.add_message("CLI_NO_VALUES", "argument @0 is invalid - this program doesn't take command-line arguments"); + if (!m_messages.message_present("CLI_USAGE_PROGRAM")) + m_messages.add_message("CLI_USAGE_PROGRAM", "usage:\n\t@0 { arguments }"); + if (!m_messages.message_present("CLI_USAGE_DEFINITIONS")) + m_messages.add_message("CLI_USAGE_DEFINITIONS", "arguments:"); + if (!m_messages.message_present("CLI_USAGE_VALUES")) + m_messages.add_message("CLI_USAGE_VALUES", "values:"); + if (!m_messages.message_present("CLI_USAGE_VALUE_RESULT")) + m_messages.add_message("CLI_USAGE_VALUE_RESULT", "\t@0 : from @1"); + if (!m_messages.message_present("CLI_USAGE_SWITCH_RESULT")) + m_messages.add_message("CLI_USAGE_SWITCH_RESULT", "\t-@0 : from @1"); + if (!m_messages.message_present("CLI_USAGE_OPTION_RESULT")) + m_messages.add_message("CLI_USAGE_OPTION_RESULT", "\t-@0 @1 : from @2"); + if (!m_messages.message_present("CLI_INI_HEADER")) + m_messages.add_message("CLI_INI_HEADER", "configuration files:"); + if (!m_messages.message_present("CLI_INI_FILE_PRESENT")) + m_messages.add_message("CLI_INI_FILE_PRESENT", "\t@0"); + if (!m_messages.message_present("CLI_INI_FILE_ABSENT")) + m_messages.add_message("CLI_INI_FILE_ABSENT", "\t@0 (not found)"); + if (!m_messages.message_present("CLI_INI_VARIABLE")) + m_messages.add_message("CLI_INI_VARIABLE", "unknown variable \"@0\" found in Ini file"); + } + + unsigned add_definition(const cli_parser::definition& definition) + { + m_definitions.push_back(definition); + return m_definitions.size()-1; + } + + std::string name(unsigned i) const throw(cli_index_error) + { + if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); + return m_definitions[m_values[i].m_definition].name(); + } + + unsigned id(unsigned i) const throw(cli_index_error) + { + if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); + return m_values[i].m_definition; + } + + cli_parser::kind_t kind(unsigned i) const throw(cli_index_error) + { + if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); + return m_definitions[m_values[i].m_definition].kind(); + } + + cli_parser::mode_t mode(unsigned i) const throw(cli_index_error) + { + if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); + return m_definitions[m_values[i].m_definition].mode(); + } + +// unsigned add_definition(const std::string& name, +// cli_parser::kind_t kind, +// cli_parser::mode_t mode, +// const std::string& message) +// { +// return add_definition(cli_parser::definition(name, kind, mode, message)); +// } + + unsigned find_definition(const std::string& name) + { + // this does substring matching on the definitions and returns the first + // match - however, it requires at least one character in the substring so + // that the "" convention for command line arguments doesn't match with + // anything. It returns size() if it fails + for (unsigned i = 0; i < m_definitions.size(); i++) + { + std::string candidate = m_definitions[i].name(); + if ((candidate.empty() && name.empty()) || + (!name.empty() && candidate.size() >= name.size() && candidate.substr(0,name.size()) == name)) + return i; + } + return m_definitions.size(); + } + + void clear_definitions(void) + { + m_definitions.clear(); + m_values.clear(); + reset_level(); + set_valid(); + } + + void reset_level(void) + { + // the starting level is 1 so that later functions can call clear_level with + // a value of m_level-1 without causing underflow + m_level = 1; + } + + void increase_level(void) + { + m_level++; + } + + void clear_level(unsigned definition, unsigned level) + { + // clears all values with this definition at the specified level or below + for (std::vector::iterator i = m_values.begin(); i != m_values.end(); ) + { + if (i->m_definition == definition && i->m_level <= level) + i = m_values.erase(i); + else + i++; + } + } + + void set_valid(void) + { + m_valid = true; + } + + void set_invalid(void) + { + m_valid = false; + } + + bool valid(void) const + { + return m_valid; + } + + unsigned add_value(unsigned definition, const std::string& value, const std::string& source) + { + // behaviour depends on mode: + // - single: erase all previous values + // - multiple: erase values at a lower level than current + // - cumulative: erase no previous values + switch (m_definitions[definition].mode()) + { + case cli_single_mode: + clear_level(definition, m_level); + break; + case cli_multiple_mode: + clear_level(definition, m_level-1); + break; + case cli_cumulative_mode: + break; + } + m_values.push_back(cli_value(definition,value,m_level,source)); + return m_values.size()-1; + } + + unsigned add_switch(unsigned definition, bool value, const std::string& source) + { + return add_value(definition, value ? "on" : "off", source); + } + + void erase_value(unsigned definition) + { + // this simply erases all previous values + clear_level(definition, m_level); + } + + void add_ini_file(const std::string& file) + { + m_ini_files.push_back(file); + } + + unsigned ini_file_size(void) const + { + return m_ini_files.size(); + } + + const std::string& ini_file(unsigned i) const + { + return m_ini_files[i]; + } + + unsigned add_checked_definition(const cli_parser::definition& definition) throw(cli_mode_error) + { + // check for stupid combinations + // at this stage the only really stupid one is to declare command line arguments to be switch mode + if (definition.name().empty() && definition.kind() == cli_switch_kind) + { + set_invalid(); + throw cli_mode_error("CLI arguments cannot be switch kind"); + } + // add the definition to the set of all definitions + unsigned i = add_definition(definition); + // also add it to the list of values, but only if it has a default value + if (!definition.default_value().empty()) + add_value(i, definition.default_value(), "builtin default"); + return i; + } + + bool switch_value(unsigned i) const throw(cli_mode_error,cli_index_error) + { + if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); + if (kind(i) != cli_switch_kind) throw cli_mode_error(name(i) + " is not a switch kind"); + std::string value = m_values[i].m_value; + return value == "on" || value == "true" || value == "1"; + } + + std::string string_value(unsigned i) const throw(cli_mode_error,cli_index_error) + { + if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); + if (kind(i) != cli_value_kind) throw cli_mode_error(name(i) + " is not a value kind"); + return m_values[i].m_value; + } + + void set_defaults(const ini_manager& defaults, const std::string& ini_section) throw() + { + // import default values from the Ini Manager + increase_level(); + // get the set of all names from the Ini manager so that illegal names generate meaningful error messages + std::vector names = defaults.variable_names(ini_section); + for (unsigned i = 0; i < names.size(); i++) + { + std::string name = names[i]; + unsigned definition = find_definition(name); + if (definition == (unsigned)-1) + { + // not found - give an error report + message_position position(defaults.variable_filename(ini_section,name), + defaults.variable_linenumber(ini_section,name), + 0); + m_messages.error(position,"CLI_INI_VARIABLE", name); + } + else + { + // found - so add the value + // multi-valued variables are entered as a comma-separated list and this is then turned into a vector + // the vector is empty if the value was empty + std::vector values = defaults.variable_values(ini_section, name); + // an empty string is used to negate the value + if (values.empty()) + erase_value(definition); + else + { + std::string source = filespec_to_relative_path(defaults.variable_filename(ini_section, name)); + for (unsigned j = 0; j < values.size(); j++) + add_value(definition, values[j], source); + } + } + } + // add the set of ini files to the list for usage reports + for (unsigned j = 0; j < defaults.size(); j++) + add_ini_file(defaults.filename(j)); + } + + bool parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error) + { + bool result = true; + if (!argv) throw cli_argument_error("Argument vector cannot be null"); + increase_level(); + if (argv[0]) + m_executable = argv[0]; + for (unsigned i = 1; argv[i]; i++) + { + std::string name = argv[i]; + if (!name.empty() && name[0] == '-') + { + // we have a command line option + unsigned found = find_definition(name.substr(1, name.size()-1)); + if (found < m_definitions.size()) + { + // found it in its positive form + switch (m_definitions[found].kind()) + { + case cli_switch_kind: + add_switch(found, true, "command line"); + break; + case cli_value_kind: + // get the next argument in argv as the value of this option + // first check that there is a next value + if (!argv[i+1]) + result &= m_messages.error("CLI_VALUE_MISSING", name); + else + add_value(found, argv[++i], "command line"); + break; + } + } + else if (name.size() > 3 && name.substr(1,2) == "no") + { + found = find_definition(name.substr(3, name.size()-3)); + if (found < m_definitions.size()) + { + // found it in its negated form + switch (m_definitions[found].kind()) + { + case cli_switch_kind: + add_switch(found, false, "command line"); + break; + case cli_value_kind: + erase_value(found); + break; + } + } + else + { + // could not find this option in either its true or negated form + result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name); + } + } + else + { + // could not find this option and it could not be negated + result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name); + } + } + else + { + // we have a command-line value which is represented internally as an option with an empty string as its name + // some very obscure commands do not have values - only options, so allow for that case too + unsigned found = find_definition(""); + if (found < m_definitions.size()) + add_value(found, name, "command line"); + else + result &= m_messages.error("CLI_NO_VALUES", name); + } + } + if (!result) set_invalid(); + return result; + } + + void usage(void) const throw(std::runtime_error) + { + m_messages.information("CLI_USAGE_PROGRAM", m_executable); + m_messages.information("CLI_USAGE_DEFINITIONS"); + for (unsigned d = 0; d < m_definitions.size(); d++) + m_messages.information(m_definitions[d].message()); + if (m_values.size() != 0) + { + m_messages.information("CLI_USAGE_VALUES"); + for (unsigned v = 0; v < m_values.size(); v++) + { + std::string source = m_values[v].m_source; + std::string key = name(v); + if (key.empty()) + { + // command-line values + m_messages.information("CLI_USAGE_VALUE_RESULT", string_value(v), source); + } + else if (kind(v) == cli_switch_kind) + { + // a switch + m_messages.information("CLI_USAGE_SWITCH_RESULT", (switch_value(v) ? name(v) : "no" + name(v)), source); + } + else + { + // other values + std::vector args; + args.push_back(name(v)); + args.push_back(string_value(v)); + args.push_back(source); + m_messages.information("CLI_USAGE_OPTION_RESULT", args); + } + } + } + if (ini_file_size() > 0) + { + m_messages.information("CLI_INI_HEADER"); + for (unsigned i = 0; i < ini_file_size(); i++) + { + if (file_exists(ini_file(i))) + m_messages.information("CLI_INI_FILE_PRESENT", filespec_to_relative_path(ini_file(i))); + else + m_messages.information("CLI_INI_FILE_ABSENT", filespec_to_relative_path(ini_file(i))); + } + } + } + + private: + // make this class uncopyable + cli_parser_data(const cli_parser_data&); + cli_parser_data& operator = (const cli_parser_data&); + }; + + //////////////////////////////////////////////////////////////////////////////// + + cli_parser::cli_parser(message_handler& messages) throw() : + m_data(new cli_parser_data(messages)) + { + } + + cli_parser::cli_parser(cli_parser::definitions_t definitions, message_handler& messages) throw(cli_mode_error) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + } + + 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) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + set_defaults(defaults, ini_section); + } + + 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) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + parse(argv); + } + + 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) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + set_defaults(defaults, ini_section); + parse(argv); + } + + cli_parser::cli_parser(cli_parser::definitions definitions, message_handler& messages) throw(cli_mode_error) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + } + + cli_parser::cli_parser(cli_parser::definitions definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + set_defaults(defaults, ini_section); + } + + 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) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + parse(argv); + } + + 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) : + m_data(new cli_parser_data(messages)) + { + add_definitions(definitions); + set_defaults(defaults, ini_section); + parse(argv); + } + + cli_parser::~cli_parser(void) throw() + { + } + + void cli_parser::add_definitions(cli_parser::definitions_t definitions) throw(cli_mode_error) + { + m_data->clear_definitions(); + // the definitions array is terminated by a definition with a null name pointer + for (unsigned i = 0; definitions[i].m_name; i++) + add_definition(definitions[i]); + } + + unsigned cli_parser::add_definition(const cli_parser::definition_t& definition) throw(cli_mode_error,cli_argument_error) + { + std::string name = definition.m_name ? definition.m_name : ""; + std::string message = definition.m_message ? definition.m_message : ""; + std::string value = definition.m_default ? definition.m_default : ""; + return add_definition(cli_parser::definition(name, definition.m_kind, definition.m_mode, message, value)); + } + + void cli_parser::add_definitions(cli_parser::definitions definitions) throw(cli_mode_error) + { + m_data->clear_definitions(); + for (unsigned i = 0; i < definitions.size(); i++) + add_definition(definitions[i]); + } + + unsigned cli_parser::add_definition(const cli_parser::definition& definition) throw(cli_mode_error) + { + return m_data->add_checked_definition(definition); + } + + void cli_parser::set_defaults(const ini_manager& defaults, const std::string& ini_section) throw() + { + m_data->set_defaults(defaults, ini_section); + } + + bool cli_parser::parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error) + { + return m_data->parse(argv); + } + + bool cli_parser::valid(void) throw() + { + return m_data->valid(); + } + + unsigned cli_parser::size(void) const throw() + { + return m_data->m_values.size(); + } + + std::string cli_parser::name(unsigned i) const throw(cli_index_error) + { + return m_data->name(i); + } + + unsigned cli_parser::id(unsigned i) const throw(cli_index_error) + { + return m_data->id(i); + } + + cli_parser::kind_t cli_parser::kind(unsigned i) const throw(cli_index_error) + { + return m_data->kind(i); + } + + bool cli_parser::switch_kind(unsigned i) const throw(cli_index_error) + { + return kind(i) == cli_switch_kind; + } + + bool cli_parser::value_kind(unsigned i) const throw(cli_index_error) + { + return kind(i) == cli_value_kind; + } + + cli_parser::mode_t cli_parser::mode(unsigned i) const throw(cli_index_error) + { + return m_data->mode(i); + } + + bool cli_parser::single_mode(unsigned i) const throw(cli_index_error) + { + return mode(i) == cli_single_mode; + } + + bool cli_parser::multiple_mode(unsigned i) const throw(cli_index_error) + { + return mode(i) == cli_multiple_mode; + } + + bool cli_parser::cumulative_mode(unsigned i) const throw(cli_index_error) + { + return mode(i) == cli_cumulative_mode; + } + + bool cli_parser::switch_value(unsigned i) const throw(cli_mode_error,cli_index_error) + { + return m_data->switch_value(i); + } + + std::string cli_parser::string_value(unsigned i) const throw(cli_mode_error,cli_index_error) + { + return m_data->string_value(i); + } + + //////////////////////////////////////////////////////////////////////////////// + + void cli_parser::usage(void) const throw(std::runtime_error) + { + m_data->usage(); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/subsystems/cli_parser.hpp b/src/stlplus/subsystems/cli_parser.hpp new file mode 100644 index 0000000..dc9740d --- /dev/null +++ b/src/stlplus/subsystems/cli_parser.hpp @@ -0,0 +1,309 @@ +#ifndef STLPLUS_CLI_PARSER +#define STLPLUS_CLI_PARSER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A subsystem for managing command-line parsing, including using INI files to +// control the default options. + +//////////////////////////////////////////////////////////////////////////////// +#include "subsystems_fixes.hpp" +#include "message_handler.hpp" +#include "ini_manager.hpp" +#include "smart_ptr.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Internals + + class cli_parser_data; + + //////////////////////////////////////////////////////////////////////////////// + // declarations + + // enum to define the basic behaviour of an argument + // - a switch is an option with no value but which can be switched on or off e.g. -help and -nohelp + // - a value is an option followed by a value e.g. -output results.txt + // (a default value can be removed by using the option as a negated switch e.g. -nooutput) + // - command-line values (i.e. any strings not preceded by '-') are treated + // internally as an option with no name and must be values + enum cli_kind_t {cli_switch_kind, cli_value_kind}; + + // the mode controls the behaviour if an option appears more than once in either the command-line or the ini files + // - a single mode option overrides all previous values so will only be found once in the parsed result + // - a multiple mode option can be repeated to define multiple values, but overrides values from ini files + // - a cumulative mode option is a multiple mode option which keeps ini file values as well + enum cli_mode_t {cli_single_mode, cli_multiple_mode, cli_cumulative_mode}; + + // There are two structures used for defining command-line parameters + // (1) a C struct which is used in a C array - this is used for declaring + // command-line parameters in a static declaration + // (2) a C++ class which is used in an STL vector - this is used for building + // command-line parameters within code + + // The C struct for definitions + struct cli_definition_t + { + // the name of the option, e.g. "help" + const char* m_name; + + // the kind of the option, e.g. cli_switch_kind + cli_kind_t m_kind; + + // the mode e.g. cli_single_mode + cli_mode_t m_mode; + + // the mnemonic for the message giving usage information for this option + const char* m_message; + + // built-in default value - null if not present + const char* m_default; + }; + + // The C array of the C struct. The array must be terminated by END_CLI_DEFINITIONS. + typedef cli_definition_t cli_definitions_t []; +#define END_CLI_DEFINITIONS {0,stlplus::cli_switch_kind,stlplus::cli_single_mode,"",0} + + // The C++ class for definitions + class cli_definition + { + public: + // constructor that allows a definition to be created in one line + cli_definition(const std::string& name, cli_kind_t kind, cli_mode_t mode, + const std::string& message, const std::string& default_value = std::string()) : + m_name(name), m_kind(kind), m_mode(mode), m_message(message), m_default(default_value) {} + + // the name of the option, e.g. "help" + const std::string& name(void) const; + + // the kind of the option, e.g. switch_kind + cli_kind_t kind(void) const; + + // the mode e.g. single_mode + cli_mode_t mode(void) const; + + // the mnemonic for the message giving usage + const std::string& message(void) const; + + // built-in default value - empty string if not present + const std::string& default_value(void) const; + + private: + std::string m_name; + cli_kind_t m_kind; + cli_mode_t m_mode; + std::string m_message; + std::string m_default; + }; + + // The C++ vector of the C++ class + typedef std::vector cli_definitions; + + ////////////////////////////////////////////////////////////////////////////// + // exceptions that can be thrown by the CLI parser + // they are all derivatives of std::logic_error because all errors are predictable by code inspection + // a correct program will never throw an exception + + // thrown if a command-line argument is accessed with the wrong mode - i.e. attempt to get the value of a switch + class cli_mode_error : public std::invalid_argument + { + public: + cli_mode_error(const std::string& arg) : std::invalid_argument(arg) {} + ~cli_mode_error(void) throw() {} + }; + + // similar to std::out_of_range thrown for using an index out of range + class cli_index_error : public std::out_of_range + { + public: + cli_index_error(const std::string& arg) : std::out_of_range(arg) {} + ~cli_index_error(void) throw() {} + }; + + // similar to std::invalid_argument - thrown for passing an illegal argument to a method + class cli_argument_error : public std::invalid_argument + { + public: + cli_argument_error(const std::string& arg) : std::invalid_argument(arg) {} + ~cli_argument_error(void) throw() {} + }; + + //////////////////////////////////////////////////////////////////////////////// + + class cli_parser + { + public: + // Type definitions map the global type names onto convenient scoped names + + typedef cli_kind_t kind_t; + typedef cli_mode_t mode_t; + typedef cli_definition_t definition_t; + typedef cli_definitions_t definitions_t; + typedef cli_definition definition; + typedef cli_definitions definitions; + + //////////////////////////////////////////////////////////////////////////////// + // Methods + + // various constructors + + // you have a choice of either creating an uninitialised CLI parser and then + // calling separate functions to set it up or of calling one of the + // composite constructors. However, you must set up the error handler in the + // constructor. + + // set up the parser with its error handler + // defer everything else + cli_parser(message_handler& errors) + throw(); + + // constructors using the C definitions_t structure + + // set up the parser with the error handler and define all the command-line options + // defer default values and parameter parsing + cli_parser(cli_definitions_t, message_handler& errors) + throw(cli_mode_error); + // set up the parser with the error handler and define all the command-line + // options and their default from the ini files + // defer parameter parsing + cli_parser(cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) + throw(cli_mode_error); + // set up the parser with the error handler and define all the command-line + // options no ini files used for default values, so only built-in defaults + // supported then parse the command line + cli_parser(char* argv[], cli_definitions_t, message_handler& errors) + throw(cli_mode_error,message_handler_id_error,message_handler_format_error); + // set up the parser with the error handler and define all the command-line + // options and their default from the ini files then parse the command line + cli_parser(char* argv[], cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) + throw(cli_mode_error,message_handler_id_error,message_handler_format_error); + + // constructors using the C++ definitions structure + + // set up the parser with the error handler and define all the command-line + // options from a C array of structs + // defer default values and parameter parsing + cli_parser(cli_definitions, message_handler& errors) + throw(cli_mode_error); + // set up the parser with the error handler and define all the command-line + // options and their default from the ini files + // defer parameter parsing + cli_parser(cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) + throw(cli_mode_error); + // set up the parser with the error handler and define all the command-line + // options no ini files used for default values, so only built-in defaults + // supported then parse the command line + cli_parser(char* argv[], cli_definitions, message_handler& errors) + throw(cli_mode_error,message_handler_id_error,message_handler_format_error); + // set up the parser with the error handler and define all the command-line + // options and their default from the ini files then parse the command line + cli_parser(char* argv[], cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) + throw(cli_mode_error,message_handler_id_error,message_handler_format_error); + + ~cli_parser(void) + throw(); + + // the separate functions for initialising the parser in steps. These are + // declared in the order of use. Firts, add definitions of command-line + // arguments. Then optionally load default values from ini files, then + // finally parse the command line. + + // add a set of C definitions. The definitions will be given ID codes from 0 + // to the number of elements - 1 in the array + void add_definitions(cli_definitions_t) + throw(cli_mode_error); + // add a single C definition, returning the ID code for it + unsigned add_definition(const definition_t&) + throw(cli_mode_error,cli_argument_error); + // add a set of C++ definitions. The definitions will be given ID codes from + // 0 to the number of elements - 1 in the array + void add_definitions(cli_definitions) + throw(cli_mode_error); + // add a single C++ definition, returning the ID code for it + unsigned add_definition(const definition&) + throw(cli_mode_error); + + // All definitions have an optional built-in default value which is stored + // in the definition types above. However, these can optionally be + // overridden by a value from an ini file. If you want this functionality, + // call this function. If you don't want ini file handling, simply don't + // call it. The values will be searched for only in the named section of the + // ini file (sections are labelled by e.g. [vassemble]), so in this case you + // would specify the section name as "vassemble" (exclude the brackets). + void set_defaults(const ini_manager& defaults, const std::string& ini_section) + throw(); + + // the final stage of initialisation is to read the command-line and extract + // the values from it. If parse errors are found, this will report the + // errors using the error handler and return false. + bool parse(char* argv[]) + throw(cli_argument_error,message_handler_id_error,message_handler_format_error); + + // test for whether the CLI parser is still valid (no errors have happened) + // after the initialisation phase + bool valid(void) + throw(); + + // iteration functions avoiding the use of iterators. Just loop through the + // arguments from 0 to size()-1 and use the index of the loop to interrogate + // the command-line for the value at that position. + + // the number of values to read, indexed 0 to size()-1 + unsigned size(void) const + throw(); + + // the argument name + std::string name(unsigned i) const + throw(cli_index_error); + // the argument ID, that is, the offset into the original definitions + unsigned id(unsigned i) const + throw(cli_index_error); + + // the kind (switch or value) and short-cut tests for the different kinds + cli_kind_t kind(unsigned i) const + throw(cli_index_error); + bool switch_kind(unsigned i) const + throw(cli_index_error); + bool value_kind(unsigned i) const + throw(cli_index_error); + + // the mode (single, multiple, cumulative) and short-cut tests for the + // different modes - you rarely need to know this since it mainly controls + // the parsing + cli_mode_t mode(unsigned i) const + throw(cli_index_error); + bool single_mode(unsigned i) const + throw(cli_index_error); + bool multiple_mode(unsigned i) const + throw(cli_index_error); + bool cumulative_mode(unsigned i) const + throw(cli_index_error); + + // get the switch's value, but only if the value is of switch kind + bool switch_value(unsigned i) const + throw(cli_mode_error,cli_index_error); + + // get the option's value, but only if it is of value kind + std::string string_value(unsigned i) const + throw(cli_mode_error,cli_index_error); + + // print the usage report - typically in response to the -help switch being on + void usage(void) const + throw(std::runtime_error); + + private: + friend class cli_parser_data; + smart_ptr_nocopy m_data; + }; + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/subsystems/ini_manager.cpp b/src/stlplus/subsystems/ini_manager.cpp new file mode 100644 index 0000000..41f5b69 --- /dev/null +++ b/src/stlplus/subsystems/ini_manager.cpp @@ -0,0 +1,1243 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "ini_manager.hpp" +#include "file_system.hpp" +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // local utilities + + static std::string trim(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[0])) + result.erase(result.begin()); + while (!result.empty() && isspace(result[result.size()-1])) + result.erase(result.end()-1); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // internal data structure for storing a single entry (i.e. line) from an ini file + // lines are categorised as blanks, comments or variables + // TODO - do I need an error category? + //////////////////////////////////////////////////////////////////////////////// + + class ini_entry + { + public: + enum kind_t {BLANK, COMMENT, VARIABLE}; + friend std::string to_string(kind_t kind) + { + switch(kind) + { + case BLANK: return "BLANK"; + case COMMENT: return "COMMENT"; + case VARIABLE: return "VARIABLE"; + } + return "<*unknown kind*>"; + } + + private: + unsigned m_line; + kind_t m_kind; + std::string m_text; + std::string m_name; + std::string m_value; + + public: + ini_entry(unsigned line) : m_line(line), m_kind(BLANK) {} + ini_entry(unsigned line, const std::string& comment) : m_line(line), m_kind(COMMENT), m_text("; " + comment) {} + 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) {} + ~ini_entry(void) {} + + unsigned line(void) const {return m_line;} + kind_t kind(void) const {return m_kind;} + bool blank(void) const {return m_kind == BLANK;} + bool comment(void) const {return m_kind == COMMENT;} + bool variable(void) const {return m_kind == VARIABLE;} + + const std::string& text(void) const {return m_text;} + const std::string& variable_name(void) const {return m_name;} + const std::string& variable_value(void) const {return m_value;} + + bool print(std::ostream& str) const + { + str << " " << m_line << ":" << to_string(m_kind) << ": " << m_text << std::endl; + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // internal data structure representing an ini file section containing all the + // entries for that section from a single ini file + //////////////////////////////////////////////////////////////////////////////// + + class ini_section + { + private: + friend class ini_file; + std::string m_title; + std::list m_entries; + + public: + ini_section(const std::string& title) : + m_title(title) + { + } + + ~ini_section(void) + { + } + + const std::string& title(void) const + { + return m_title; + } + + bool empty(void) const + { + // a section is empty if it contains no variables + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable()) + return false; + } + return true; + } + + void clear(void) + { + m_entries.clear(); + } + + bool variable_exists(const std::string variable) const + { + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + return true; + } + return false; + } + + unsigned variables_size(void) const + { + unsigned result = 0; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + if (i->variable()) + result++; + return result; + } + + std::string variable_name(unsigned offset) const + { + unsigned j = 0; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable()) + { + if (j == offset) + return i->variable_name(); + j++; + } + } + return std::string(); + } + + std::vector variable_names(void) const + { + std::vector result; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + if (i->variable()) + result.push_back(i->variable_name()); + return result; + } + + std::string variable_value(unsigned offset) const + { + unsigned j = 0; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable()) + { + if (j == offset) + return i->variable_value(); + j++; + } + } + return std::string(); + } + + std::string variable_value(const std::string variable) const + { + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + return i->variable_value(); + } + return std::string(); + } + + unsigned variable_line(const std::string variable) const + { + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + return i->line(); + } + return 0; + } + + bool add_variable(unsigned line, const std::string& variable, const std::string& value) + { + erase_variable(variable); + m_entries.push_back(ini_entry(line ? line : m_entries.size(), variable, value)); + return true; + } + + bool add_comment(unsigned line, const std::string& comment) + { + m_entries.push_back(ini_entry(line ? line : m_entries.size(), comment)); + return true; + } + + bool add_blank(unsigned line) + { + m_entries.push_back(ini_entry(line ? line : m_entries.size())); + return true; + } + + bool erase_variable(const std::string& variable) + { + for (std::list::iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + { + m_entries.erase(i); + return true; + } + } + return false; + } + + bool print(std::ostream& str) const + { + str << " [" << m_title << "]" << std::endl; + for (std::list::const_iterator entry = m_entries.begin(); entry != m_entries.end(); entry++) + entry->print(str); + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // internal data structure representing a single ini file + //////////////////////////////////////////////////////////////////////////////// + + class ini_file + { + private: + friend class ini_section; + std::string m_filename; + bool m_changed; + bool m_writable; + std::list m_sections; + + std::list::const_iterator find_section(const std::string& section) const + { + for (std::list::const_iterator i = m_sections.begin(); i != m_sections.end(); i++) + { + if (i->title() == section) + return i; + } + return m_sections.end(); + } + + std::list::iterator find_section(const std::string& section) + { + for (std::list::iterator i = m_sections.begin(); i != m_sections.end(); i++) + { + if (i->title() == section) + return i; + } + return m_sections.end(); + } + + public: + + ini_file(void) : m_changed(false), m_writable(false) + { + } + + ini_file(const std::string& filename) : m_changed(false), m_writable(false) + { + read_file(filename); + } + + ~ini_file(void) + { + if (writable()) + save_file(); + } + + bool initialised(void) const + { + return !m_filename.empty(); + } + + bool writable(void) const + { + return m_writable; + } + + bool read_file(const std::string& filename) + { + m_filename = filename; + m_changed = false; + // file may not yet exist - possible to create an Ini file using this class + // so it is writable if the file exists and is writable or if the folder is writable + m_writable = file_writable(m_filename); + if (!file_exists(m_filename)) + return true; + // create a dummy top section to hold file comments + std::list::iterator section = m_sections.insert(m_sections.end(), ini_section("")); + std::ifstream source(m_filename.c_str()); + unsigned line_number = 0; + std::string line; + while(std::getline(source,line)) + { + line_number++; + unsigned i = 0; + while(i < line.size() && isspace(line[i])) + i++; + if (i < line.size() && line[i] == '[') + { + // found a section title + // skip the [ + i++; + // get the text up to the end ] or the end of the line + std::string title; + while (i < line.size() && line[i] != ']') + title += line[i++]; + // create a new section and make it the current section + section = m_sections.insert(m_sections.end(), ini_section(title)); + } + else if (i < line.size() && line[i] == ';') + { + // found a comment + // skip the ; because that is not part of the comment + i++; + // add the rest of the line as a comment to the current section + section->add_comment(line_number, line.substr(i, line.size()-1)); + } + else if (i == line.size()) + { + // found a blank line + section->add_blank(line_number); + } + else + { + // found a *possible* variable - now scan forwards for the = operator + std::string name; + while (i < line.size() && line[i] != '=') + name += line[i++]; + // skip the = sign + // TODO - detect a missing = sign here and convert to an error + if (i < line.size()) + i++; + // trim trailing whitespace off the name + name = trim(name); + // now get the value, minus any leading whitespace + std::string value; + while(i < line.size() && isspace(line[i])) + i++; + while (i < line.size()) + value += line[i++]; + // trim trailing whitespace off the value + value = trim(value); + // and finally add the name/value pair to the data structure + section->add_variable(line_number, name, value); + } + } + return true; + } + + bool save_file(void) + { + if (!initialised()) return false; + if (!m_changed) return true; + if (!m_writable) return false; + std::ofstream output(m_filename.c_str()); + for (std::list::iterator section = m_sections.begin(); section != m_sections.end(); section++) + { + if (!(section->title().empty())) + output << "[" << section->title() << "]" << std::endl; + for (std::list::iterator entry = section->m_entries.begin(); entry != section->m_entries.end(); entry++) + output << entry->text() << std::endl; + } + m_changed = false; + return true; + } + + std::string filename(void) const + { + return m_filename; + } + + bool empty(void) const + { + // file is empty if it has either no sections or an empty header section + if (m_sections.empty()) return true; + if ((m_sections.begin() == --m_sections.end()) && m_sections.begin()->empty()) return true; + return false; + } + + bool section_exists(const std::string& title) const + { + return find_section(title) != m_sections.end(); + } + + bool add_section(const std::string& section) + { + if (!m_writable) return false; + m_sections.push_back(ini_section(section)); + m_changed = true; + return true; + } + + bool empty_section(const std::string& section) + { + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + return found->empty(); + } + + bool erase_section(const std::string& section) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + m_sections.erase(found); + m_changed = true; + return true; + } + + bool clear_section(const std::string& section) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + found->clear(); + m_changed = true; + return true; + } + + unsigned sections_size(void) const + { + return m_sections.size(); + } + + const std::string& section_name(unsigned i) const + { + std::list::const_iterator result = m_sections.begin(); + for (unsigned j = 1; j <= i; j++) + result++; + return result->title(); + } + + std::vector section_names(void) const + { + std::vector result; + for (unsigned j = 0; j < sections_size(); j++) + result.push_back(section_name(j)); + return result; + } + + bool variable_exists(const std::string& section, const std::string variable) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return false; + return found->variable_exists(variable); + } + + std::vector variable_names(const std::string& section) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::vector(); + return found->variable_names(); + } + + unsigned variables_size(const std::string& section) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return 0; + return found->variables_size(); + } + + unsigned variable_line(const std::string& section, const std::string variable) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return 0; + return found->variable_line(variable); + } + + std::string variable_name(const std::string& section, unsigned i) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::string(); + return found->variable_name(i); + } + + std::string variable_value(const std::string& section, unsigned i) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::string(); + return found->variable_value(i); + } + + std::string variable_value(const std::string& section, const std::string variable) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::string(); + return found->variable_value(variable); + } + + bool add_variable(const std::string& section, const std::string& variable, const std::string& value) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); + if (found->add_variable(0,variable,value)) + m_changed = true; + return true; + } + + bool add_comment(const std::string& section, const std::string& comment) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); + if (found->add_comment(0,comment)) + m_changed = true; + return true; + } + + bool add_blank(const std::string& section) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); + if (found->add_blank(0)) + m_changed = true; + return true; + } + + bool erase_variable(const std::string& section, const std::string& variable) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + if (found->erase_variable(variable)) + { + m_changed = true; + return true; + } + return false; + } + + bool print(std::ostream& str) const + { + str << "file: " << m_filename << std::endl; + for (std::list::const_iterator section = m_sections.begin(); section != m_sections.end(); section++) + section->print(str); + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // body data structure contains all the data and is pointed-to by exported data structure + + class ini_manager_body + { + private: + std::vector m_files; + unsigned m_count; + + ini_manager_body(const ini_manager_body&); + ini_manager_body& operator= (const ini_manager_body&); + + public: + + ini_manager_body(void) : m_count(1) + { + } + + ~ini_manager_body(void) + { + save(); + } + + void increment(void) + { + ++m_count; + } + + bool decrement(void) + { + --m_count; + return m_count == 0; + } + + ////////////////////////////////////////////////////////////////////////////// + // file management + + // add files starting with the most local file (e.g. the current project) which has depth 0 + // and working back to the most global (e.g. the installation settings) which has a depth of size()-1 + // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice. + // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did + // not exist, only read errors cause a failure) + bool add_file(const std::string& filename) + { + // if this file has been loaded, don't load it again + // this is not an error + for (unsigned i = 0; i < m_files.size(); i++) + { + if (filespec_to_path(filename) == filespec_to_path(m_files[i].filename())) + return true; + } + // add a new file to the back of the list and then load it + // I do it in two steps rather than passing the filename to the constructor in order to return the load status + m_files.push_back(ini_file()); + return m_files.back().read_file(filename); + } + + // as above, returns false if *none* of the files were added + // filenames[0] is the local file, and so on + bool add_files(const std::vector& filenames) + { + bool result = true; + for (unsigned i = 0; i < filenames.size(); i++) + result &= add_file(filenames[i]); + return result; + } + + // saves modified ini files - returns true if all modified files were written successfully + bool save(void) + { + bool result = true; + for (unsigned i = 0; i < m_files.size(); i++) + result &= m_files[i].save_file(); + return result; + } + + // get the number of files being managed + unsigned size(void) const + { + return m_files.size(); + } + + // get the ini filename associated with a depth + std::string filename(unsigned depth) const + { + return m_files[depth].filename(); + } + + // test whether a file in the ini manager is writable + bool writable(unsigned depth) const + { + return m_files[depth].writable(); + } + + // test whether a file is empty + // An ini file is considered empty if it has no named sections and the header is empty or missing + bool empty(unsigned depth) const + { + return m_files[depth].empty(); + } + + // erase the ini file from the ini manager and from the disk + bool erase(unsigned depth) + { + std::string file = filename(depth); + remove(depth); + return file_delete(file); + } + + // remove the file from the ini manager but do not erase it from the disk + bool remove(unsigned depth) + { + if (m_files[depth].writable()) + m_files[depth].save_file(); + m_files.erase(m_files.begin() + depth); + return true; + } + + ////////////////////////////////////////////////////////////////////////////// + // section management + + // returns the union of all section names in all of the ini files + std::vector section_names(void) const + { + std::vector result; + for (unsigned i = 0; i < m_files.size(); i++) + { + std::vector file_result = section_names(i); + for (unsigned j = 0; j < file_result.size(); j++) + { + if (std::find(result.begin(), result.end(), file_result[j]) == result.end()) + result.push_back(file_result[j]); + } + } + return result; + } + + // returns the section names in one of the ini files + std::vector section_names(unsigned depth) const + { + return m_files[depth].section_names(); + } + + // tests whether a section is found in any of the ini files + bool section_exists(const std::string& title) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (m_files[i].section_exists(title)) + return true; + } + return false; + } + + // tests whether the section is found in the specific ini file + bool section_exists(const std::string& title, unsigned depth) const + { + return m_files[depth].section_exists(title); + } + + // adds a section to the specified ini file - does nothing if it is already present + bool add_section(const std::string& section, unsigned depth) + { + return m_files[depth].add_section(section); + } + + // test whether a section is empty + bool empty_section(const std::string& section, unsigned depth) + { + return m_files[depth].empty_section(section); + } + + // removes a section from the specified ini file if it exists there but cannot remove it from any other file + bool erase_section(const std::string& section, unsigned depth) + { + return m_files[depth].erase_section(section); + } + + // removes all the contents of a section from the specified ini file but keeps the empty section + bool clear_section(const std::string& section, unsigned depth) + { + return m_files[depth].clear_section(section); + } + + ////////////////////////////////////////////////////////////////////////////// + // variable management + + // test whether a variable exists in any of the ini files + bool variable_exists(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return true; + } + return false; + } + + // test whether a variable exists in specified ini file + bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const + { + return m_files[depth].variable_exists(section, variable); + } + + // get the union of all variables declared in all ini files + std::vector variable_names(const std::string& section) const + { + std::vector result; + for (unsigned i = 0; i < m_files.size(); i++) + { + std::vector file_result = variable_names(section, i); + for (unsigned j = 0; j < file_result.size(); j++) + { + if (std::find(result.begin(), result.end(), file_result[j]) == result.end()) + result.push_back(file_result[j]); + } + } + return result; + } + + // get the set of all varaibale names from one file + std::vector variable_names(const std::string& section, unsigned depth) const + { + return m_files[depth].variable_names(section); + } + + // get the depth of the first ini file to define a variable + // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist + unsigned variable_depth(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return i; + } + return (unsigned)-1; + } + + // get the filename that first defines the variable + std::string variable_filename(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return filename(i); + } + return std::string(); + } + + unsigned variable_linenumber(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return m_files[i].variable_line(section,variable); + } + return 0; + } + + // get the value of a variable as a single unprocessed string + // if the variable does not exist the string will be empty, but beware that + // you also get an empty string if a variable exists but has no value + // you can differentiate between the two cases by using variable_exists_all above + std::string variable_value(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return variable_value(section, variable, i); + } + return std::string(); + } + + // get the value from the specified file + std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const + { + return m_files[depth].variable_value(section, variable); + } + + // get the value of a variable as a processed string + // processing splits the value at commas and furthermore supports quoted + // strings (so that values can contain commas for example) + // quoted strings are dequoted before they are added to the result + // the result is a vector of dequoted strings, one per value in the comma-separated list + std::vector variable_values(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return variable_values(section, variable, i); + } + return std::vector(); + } + + // get the processed variable from the specified file + std::vector variable_values(const std::string& section, const std::string variable, unsigned depth) const + { + // get the unprocessed value and then do the processing into processed separate values + std::string value = variable_value(section, variable, depth); + std::vector result; + if (!value.empty()) + { + result.push_back(std::string()); + unsigned i = 0; + // loop which is repeated for each element in the comma-separated list + while(i < value.size()) + { + // skip leading whitespace + while (i < value.size() && isspace(value[i])) i++; + // get the value - this means all text up to the next comma or end of line + // also allow escaped characters + while (i < value.size()) + { + if (value[i] == ',') break; + if (value[i] == '\\') + { + // found an escaped character - de-escape it by only getting the next character + // beware of an escape character at the end of the line which just gets ignored + i++; + if (i < value.size()) result.back() += value[i++]; + } + else if (value[i] == '"') + { + // get a quoted substring + // first skip the opening quote + i++; + // keep getting characters until a close-quote, but allow the quote character to be escaped by itself + while (i < value.size()) + { + if (value[i] == '"') + { + // found a quote skip it + i++; + // now establish whether its an escaped quote + // if it is, keep it, but de-escape it by only getting the next character + // it it isn't, break out and continue processing the value as a non-quoted string + if (i < value.size() && value[i] != '"') break; + if (i < value.size()) result.back() += value[i++]; + } + else + { + // non-quote and non-escape so just get the character + result.back() += value[i++]; + } + } + } + else + { + // non-escape so just get the character + result.back() += value[i++]; + } + } + // trim trailing whitespace off the value + while (!result.back().empty() && isspace(result.back()[result.back().size()-1])) result.back().erase(result.back().size()-1,1); + // now check for whether there is a comma or end of line + if (i <= value.size() && value[i] == ',') + { + // skip the comma and add a new string to the result ready for the next iteration + i++; + result.push_back(std::string()); + } + else + { + // end of line found so break out + break; + } + } + } + return result; + } + + // add a variable to the specified file + bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth) + { + return m_files[depth].add_variable(section, variable, value); + } + + // add a variable as a processed string + // processing means that the values in the string vector are converted into a comma-separated list + // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself + bool add_variable(const std::string& section, const std::string& variable, const std::vector& values, unsigned depth) + { + // convert the values vector into a comma-separated string with each value escaped so that special characters do not confuse the reader + // the characters escaped are: '\', ',', '"' + std::string value; + for (unsigned v = 0; v < values.size(); v++) + { + const std::string& element = values[v]; + // add the comma between values === add a comma before all but the first value + if (v > 0) value += ','; + for (unsigned i = 0; i < element.size(); i++) + { + // add a character at a time, escaping special characters + if (element[i] == '"' || element[i] == ',' || element[i] == '\\') + { + // escape the character + value += '\\'; + } + value += element[i]; + } + } + return add_variable(section, variable, value, depth); + } + + // erase a variable from the specified file + // this does not remove the variable from other ini files, so the variable may still exist + // to mask a global variable, set the variable to an empty string instead + bool erase_variable(const std::string& section, const std::string& variable, unsigned depth) + { + return m_files[depth].erase_variable(section, variable); + } + + ////////////////////////////////////////////////////////////////////////////// + // sundry line-entry management + + // add a comment to the specified ini file + bool add_comment(const std::string& section, const std::string& comment, unsigned depth) + { + return m_files[depth].add_comment(section, comment); + } + + // add a blank line to the specified ini file + bool add_blank(const std::string& section, unsigned depth) + { + return m_files[depth].add_blank(section); + } + + bool print(std::ostream& str) const + { + str << "----------------------------------------" << std::endl; + for (unsigned depth = 0; depth < m_files.size(); depth++) + { + m_files[depth].print(str); + str << "----------------------------------------" << std::endl; + } + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // exported data structure representing the set of all ini files and providing + // the access functions exported by the class + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + // constructors/destructors + + ini_manager::ini_manager(void) : m_body(new ini_manager_body) + { + } + + ini_manager::ini_manager(const std::vector& filenames) : m_body(new ini_manager_body) + { + add_files(filenames); + } + + ini_manager::ini_manager(const ini_manager& manager) : m_body(0) + { + m_body = manager.m_body; + m_body->increment(); + } + + ini_manager& ini_manager::operator= (const ini_manager& manager) + { + if (m_body == manager.m_body) return *this; + if (m_body->decrement()) + delete m_body; + m_body = manager.m_body; + m_body->increment(); + return *this; + } + + ini_manager::~ini_manager(void) + { + if (m_body->decrement()) + delete m_body; + } + + //////////////////////////////////////////////////////////////////////////////// + // file management + + bool ini_manager::add_file(const std::string& filename) + { + return m_body->add_file(filename); + } + + bool ini_manager::add_files(const std::vector& filenames) + { + return m_body->add_files(filenames); + } + + bool ini_manager::save(void) + { + return m_body->save(); + } + + unsigned ini_manager::size(void) const + { + return m_body->size(); + } + + std::string ini_manager::filename(unsigned depth) const + { + return m_body->filename(depth); + } + + bool ini_manager::writable(unsigned depth) const + { + return m_body->writable(depth); + } + + bool ini_manager::empty(unsigned depth) const + { + return m_body->empty(depth); + } + + bool ini_manager::erase(unsigned depth) + { + return m_body->erase(depth); + } + + bool ini_manager::remove(unsigned depth) + { + return m_body->remove(depth); + } + + //////////////////////////////////////////////////////////////////////////////// + // section management + + std::vector ini_manager::section_names(void) const + { + return m_body->section_names(); + } + + std::vector ini_manager::section_names(unsigned depth) const + { + return m_body->section_names(depth); + } + + bool ini_manager::section_exists(const std::string& section) const + { + return m_body->section_exists(section); + } + + bool ini_manager::section_exists(const std::string& section, unsigned depth) const + { + return m_body->section_exists(section, depth); + } + + bool ini_manager::add_section(const std::string& section, unsigned depth) + { + return m_body->add_section(section, depth); + } + + bool ini_manager::empty_section(const std::string& section, unsigned depth) + { + return m_body->empty_section(section, depth); + } + + bool ini_manager::erase_section(const std::string& section, unsigned depth) + { + return m_body->erase_section(section, depth); + } + + bool ini_manager::clear_section(const std::string& section, unsigned depth) + { + return m_body->clear_section(section, depth); + } + + //////////////////////////////////////////////////////////////////////////////// + // variable management + + bool ini_manager::variable_exists(const std::string& section, const std::string variable) const + { + return m_body->variable_exists(section, variable); + } + + bool ini_manager::variable_exists(const std::string& section, const std::string variable, unsigned depth) const + { + return m_body->variable_exists(section, variable, depth); + } + + std::vector ini_manager::variable_names(const std::string& section) const + { + return m_body->variable_names(section); + } + + std::vector ini_manager::variable_names(const std::string& section, unsigned depth) const + { + return m_body->variable_names(section, depth); + } + + unsigned ini_manager::variable_depth(const std::string& section, const std::string variable) const + { + return m_body->variable_depth(section, variable); + } + + std::string ini_manager::variable_filename(const std::string& section, const std::string variable) const + { + return m_body->variable_filename(section, variable); + } + + unsigned ini_manager::variable_linenumber(const std::string& section, const std::string variable) const + { + return m_body->variable_linenumber(section, variable); + } + + std::string ini_manager::variable_value(const std::string& section, const std::string variable) const + { + return m_body->variable_value(section, variable); + } + + std::string ini_manager::variable_value(const std::string& section, const std::string variable, unsigned depth) const + { + return m_body->variable_value(section, variable, depth); + } + + std::vector ini_manager::variable_values(const std::string& section, const std::string variable) const + { + return m_body->variable_values(section, variable); + } + + std::vector ini_manager::variable_values(const std::string& section, const std::string variable, unsigned depth) const + { + return m_body->variable_values(section, variable, depth); + } + + bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth) + { + return m_body->add_variable(section, variable, value, depth); + } + + bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::vector& values, unsigned depth) + { + return m_body->add_variable(section, variable, values, depth); + } + + bool ini_manager::erase_variable(const std::string& section, const std::string& variable, unsigned depth) + { + return m_body->erase_variable(section, variable, depth); + } + + + //////////////////////////////////////////////////////////////////////////////// + // sundry entries + + bool ini_manager::add_comment(const std::string& section, const std::string& comment, unsigned depth) + { + return m_body->add_comment(section, comment, depth); + } + + bool ini_manager::add_blank(const std::string& section, unsigned depth) + { + return m_body->add_blank(section, depth); + } + + bool ini_manager::print(std::ostream& str) const + { + return m_body->print(str); + } + + //////////////////////////////////////////////////////////////////////////////// + // diagnostic print + + std::ostream& operator << (std::ostream& str, const ini_manager& manager) + { + manager.print(str); + return str; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/subsystems/ini_manager.hpp b/src/stlplus/subsystems/ini_manager.hpp new file mode 100644 index 0000000..f1d7a66 --- /dev/null +++ b/src/stlplus/subsystems/ini_manager.hpp @@ -0,0 +1,201 @@ +#ifndef STLPLUS_INI_MANAGER +#define STLPLUS_INI_MANAGER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A subsystem for managing INI (i.e. .ini) files +// An INI file has the following format + +// file ::= header { section }* +// header ::= { comment | blank }* +// section ::= section_header { declaration | comment | blank }* +// section_header ::= '[' title ']' '\n' +// declaration ::= variable '=' value '\n' +// comment ::= ';' text '\n' +// blank ::= '\n' +// title ::= [~']']* +// variable ::= [~'=']* +// value ::= .* +// text ::= .* + +// Whitespace is trimmed from the leading and trailing ends of title, variable and value +// Note: a header is represented internally as a Clint section (i.e. a section with no name) + +//////////////////////////////////////////////////////////////////////////////// +#include "subsystems_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Internals + + class ini_manager_body; + + //////////////////////////////////////////////////////////////////////////////// + // Ini-file manager class + + class ini_manager + { + public: + + ini_manager(void); + + explicit ini_manager(const std::vector& filenames); + + ini_manager(const ini_manager&); + ini_manager& operator= (const ini_manager&); + + ~ini_manager(void); + + ////////////////////////////////////////////////////////////////////////////// + // file management + + // add files starting with the most local file (e.g. the current project) which has depth 0 + // and working back to the most global (e.g. the installation settings) which has a depth of size()-1 + // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice. + // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did + // not exist, only read errors cause a failure) + bool add_file(const std::string& filename); + + // as above, returns false if *none* of the files were added + // filenames[0] is the local file, and so on + bool add_files(const std::vector& filenames); + + // saves modified ini files - returns true if all modified files were written successfully + bool save(void); + + // get the number of files being managed + unsigned size(void) const; + + // get the ini filename associated with a depth + std::string filename(unsigned depth = 0) const; + + // test whether a file in the ini manager is writable + bool writable(unsigned depth = 0) const; + + // test whether a file is empty + // An ini file is considered empty if it has no named sections and the header is empty or missing + bool empty(unsigned depth = 0) const; + + // erase the ini file from the ini manager and from the disk + bool erase(unsigned depth = 0); + + // remove the file from the ini manager but do not erase it from the disk + bool remove(unsigned depth = 0); + + ////////////////////////////////////////////////////////////////////////////// + // section management + + // returns the union of all section names in all of the ini files + std::vector section_names(void) const; + + // returns the section names in one of the ini files + std::vector section_names(unsigned depth) const; + + // tests whether a section is found in any of the ini files + bool section_exists(const std::string& title) const; + + // tests whether the section is found in the specific ini file + bool section_exists(const std::string& title, unsigned depth) const; + + // adds a section to the specified ini file - does nothing if it is already present + bool add_section(const std::string& section, unsigned depth = 0); + + // test whether a section is empty + bool empty_section(const std::string& section, unsigned depth = 0); + + // removes a section from the specified ini file if it exists there but cannot remove it from any other file + bool erase_section(const std::string& section, unsigned depth = 0); + + // removes all the contents of a section from the specified ini file but keeps the empty section + bool clear_section(const std::string& section, unsigned depth = 0); + + ////////////////////////////////////////////////////////////////////////////// + // variable management + + // test whether a variable exists in any of the ini files + bool variable_exists(const std::string& section, const std::string variable) const; + + // test whether a variable exists in specified ini file + bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const; + + // get the union of all variables declared in all ini files + std::vector variable_names(const std::string& section) const; + + // get the set of all varaibale names from one file + std::vector variable_names(const std::string& section, unsigned depth) const; + + // get the depth of the first ini file to define a variable + // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist + unsigned variable_depth(const std::string& section, const std::string variable) const; + + // get the filename that first defines the variable + std::string variable_filename(const std::string& section, const std::string variable) const; + // ditto for its linenumber within that file + unsigned variable_linenumber(const std::string& section, const std::string variable) const; + + // get the value of a variable as a single unprocessed string + // if the variable does not exist the string will be empty, but beware that + // you also get an empty string if a variable exists but has no value + // you can differentiate between the two cases by using variable_exists_all above + std::string variable_value(const std::string& section, const std::string variable) const; + + // get the value from the specified file + std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const; + + // get the value of a variable as a processed string + // processing splits the value at commas and furthermore supports quoted strings (so that values can contain commas for example) + // quoted strings are dequoted before they are added to the result + // the result is a vector of dequoted strings, one per value in the comma-separated list + std::vector variable_values(const std::string& section, const std::string variable) const; + + // get the processed variable from the specified file + std::vector variable_values(const std::string& section, const std::string variable, unsigned depth) const; + + // add a variable to the specified file + bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth = 0); + + // add a variable as a processed string + // processing means that the values in the string vector are converted into a comma-separated list + // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself + bool add_variable(const std::string& section, const std::string& variable, const std::vector& values, unsigned depth = 0); + + // erase a variable from the specified file + // this does not remove the variable from other ini files, so the variable may still exist + // to mask a global variable, set the variable to an empty string instead + bool erase_variable(const std::string& section, const std::string& variable, unsigned depth = 0); + + ////////////////////////////////////////////////////////////////////////////// + // sundry line-entry management + + // add a comment to the specified ini file + bool add_comment(const std::string& section, const std::string& comment, unsigned depth = 0); + + // add a blank line to the specified ini file + bool add_blank(const std::string& section, unsigned depth = 0); + + bool print(std::ostream&) const; + + private: + friend class ini_manager_body; + ini_manager_body* m_body; + }; + + //////////////////////////////////////////////////////////////////////////////// + // diagnostic print routine + + std::ostream& operator << (std::ostream&, const ini_manager&); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/subsystems/library_manager.cpp b/src/stlplus/subsystems/library_manager.cpp new file mode 100644 index 0000000..0daf1be --- /dev/null +++ b/src/stlplus/subsystems/library_manager.cpp @@ -0,0 +1,2332 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "library_manager.hpp" +#include "file_system.hpp" +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +static const char* LibraryNameExtension = "lmn"; +static const char* HeaderExtension = "lmh"; + +//////////////////////////////////////////////////////////////////////////////// +// local operations +//////////////////////////////////////////////////////////////////////////////// + +typedef std::map lm_callback_map; + +static std::string lowercase(const std::string& val) +{ + std::string text = val; + for (unsigned i = 0; i < text.size(); i++) + text[i] = tolower(text[i]); + return text; +} + +// Context file and library operations +// These must be readable/writeable without a library data structure present +// so that the name of the library is known before creation + +static bool read_context(const std::string& path, const std::string& owner, std::string& name, bool& writable) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + name = ""; + writable = false; + if (!stlplus::file_exists(spec)) return false; + std::ifstream input(spec.c_str()); + input >> name >> writable; + return !input.fail(); +} + +static bool write_context(const std::string& path, const std::string& owner, const std::string& name, bool writable) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + std::ofstream output(spec.c_str()); + output << name << " " << writable << std::endl; + return !output.fail(); +} + +static bool create_library(const std::string& path, const std::string& owner, const std::string& name, bool writable) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + if (stlplus::is_present(path) && !stlplus::is_folder(path)) return false; + if (!stlplus::folder_exists(path) && !stlplus::folder_create(path)) return false; + return write_context(path, owner, name, writable); +} + +static bool erase_library(const std::string& path, const std::string& owner) +{ + // check that it is a library before deleting it! + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + if (!stlplus::file_exists(spec)) return false; + return stlplus::folder_delete(path, true); +} + +// dependency checking + +typedef std::map,stlplus::lm_dependencies> visited_map; + +static stlplus::lm_dependencies& out_of_date_check (visited_map& visited, + const stlplus::library_manager* manager, + const std::string& library, + const stlplus::lm_unit_name& name) +{ + // the visited field contains an entry if a unit has been visited - it also contains the reason for out-of-date-ness + // this is initially set empty (up to date) since the unit has to be added + // before it is checked just in case there is a recursive loop + // the dependencies field is filled in if the unit is subsequently found to be out of date + std::pair full_name = std::make_pair(library,name); + // first check whether this unit has already been visited - this should in + // principle never happen because the same test is performed before trying + // to recurse, so consider this to be paranoid-mode programming + visited_map::iterator found = visited.find(full_name); + if (found != visited.end()) + return found->second; + // now add this unit to prevent recursion loops later. This entry is added + // to when out-of-date dependencies are found and is also the return value + // of the function + visited[full_name] = stlplus::lm_dependencies(); + // now find the unit + const stlplus::lm_unit_ptr unit = manager->find(library,name); + if (!unit) + { + // if a unit is missing it fails with a dependency on itself - this is a + // bit of a work-around, but this again is paranoid-mode programming since + // the calling function (this function calling itself recursively) should + // check the unit's existence before recursing + visited[full_name].unit_add(stlplus::lm_unit_dependency(library,name)); + } + else + { + // we're onto the real checks now - first get the datestamp of this unit: + // all dependencies must be older than this + time_t unit_modified = unit->modified(); + // check dependency on source file if there is one + if (unit->source_file_present()) + { + std::string source_file = unit->source_file().path_full(unit->library_path()); + time_t source_modified = stlplus::file_modified(source_file); + if (source_modified == 0 || source_modified > unit_modified) + { + visited[full_name].set_source_file(unit->source_file()); + } + } + // now check the other file dependencies + for (unsigned i = 0; i < unit->file_size(); i++) + { + // a file dependency is a dependency on a file outside of the library system + const stlplus::lm_file_dependency& dependency = unit->file_dependency(i); + std::string file_full = dependency.path_full(unit->library_path()); + time_t source_modified = stlplus::file_modified(file_full); + if (source_modified == 0 || source_modified > unit_modified) + { + visited[full_name].file_add(dependency); + } + } + // now check and recurse on unit dependencies + for (unsigned j = 0; j < unit->unit_size(); j++) + { + const stlplus::lm_unit_dependency& dependency = unit->unit_dependency(j); + std::pair other_name = std::make_pair(dependency.library(), dependency.unit_name()); + // perform the datestamp checking at this level and only recurse if this does not detect an error + const stlplus::lm_unit_ptr other_unit = manager->find(other_name.first, other_name.second); + if (!other_unit) + { + visited[full_name].unit_add(dependency); + } + else + { + time_t other_modified = other_unit->modified(); + if (other_modified == 0 || other_modified > unit_modified) + { + visited[full_name].unit_add(dependency); + } + else + { + // prevent recursion before doing it + visited_map::iterator other_found = visited.find(other_name); + if (other_found != visited.end()) + { + // if the unit was found to be out of date on the previous visit, add it to the failed dependencies + if (!other_found->second.empty()) + { + visited[full_name].unit_add(dependency); + } + } + else + { + // the unit hasn't been visited before, so recurse on it now + stlplus::lm_dependencies other_dependencies = + out_of_date_check(visited, manager, other_name.first, other_name.second); + if (!other_dependencies.empty()) + { + visited[full_name].unit_add(dependency); + } + } + } + } + } + } + return visited[full_name]; +} + +//////////////////////////////////////////////////////////////////////////////// +// class lm_unit_name + +stlplus::lm_unit_name::lm_unit_name(const std::string& name, const std::string& type) : + m_name(name), m_type(type) +{ +} + +stlplus::lm_unit_name::~lm_unit_name(void) +{ +} + +const std::string& stlplus::lm_unit_name::name(void) const +{ + return m_name; +} + +void stlplus::lm_unit_name::set_name(const std::string& name) +{ + m_name = name; +} + +void stlplus::lm_unit_name::lowercase(void) +{ + m_name = ::lowercase(m_name); +} + +const std::string& stlplus::lm_unit_name::type(void) const +{ + return m_type; +} + +void stlplus::lm_unit_name::set_type(const std::string& type) +{ + m_type = type; +} + +bool stlplus::lm_unit_name::write(std::ostream& context) const +{ + context << m_name << " " << m_type; + return !context.fail(); +} + +bool stlplus::lm_unit_name::read(std::istream& context) +{ + context >> m_name >> m_type; + return !context.fail(); +} + +std::string stlplus::lm_unit_name::to_string(void) const +{ + return m_name + ":" + m_type; +} + +bool stlplus::lm_unit_name::print(std::ostream& str) const +{ + str << to_string(); + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_name& name) +{ + name.print(str); + return str; +} + +bool stlplus::operator == (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r) +{ + return l.name() == r.name() && l.type() == r.type(); +} + +bool stlplus::operator < (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r) +{ + // sort by name then type + return (l.name() != r.name()) ? l.name() < r.name() : l.type() < r.type(); +} + +//////////////////////////////////////////////////////////////////////////////// +// dependencies + +// file dependencies + +stlplus::lm_file_dependency::lm_file_dependency(void) : m_line(0), m_column(0) +{ +} + +stlplus::lm_file_dependency::lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line, unsigned column) +{ + set_path(library_path, path); + set_line(line); + set_column(column); +} + +stlplus::lm_file_dependency::~lm_file_dependency(void) +{ +} + +const std::string& stlplus::lm_file_dependency::path(void) const +{ + return m_path; +} + +std::string stlplus::lm_file_dependency::path_full(const std::string& library_path) const +{ + return filespec_to_path(library_path, m_path); +} + +void stlplus::lm_file_dependency::set_path(const std::string& library_path, const std::string& path) +{ + m_path = filespec_to_relative_path(library_path, path); +} + +unsigned stlplus::lm_file_dependency::line(void) const +{ + return m_line; +} + +void stlplus::lm_file_dependency::set_line(unsigned line) +{ + m_line = line; +} + +unsigned stlplus::lm_file_dependency::column(void) const +{ + return m_column; +} + +void stlplus::lm_file_dependency::set_column(unsigned column) +{ + m_column = column; +} + +bool stlplus::lm_file_dependency::write(std::ostream& context) const +{ + context << m_path << " " << m_line << " " << m_column; + return !context.fail(); +} + +bool stlplus::lm_file_dependency::read(std::istream& context) +{ + context >> m_path >> m_line >> m_column; + return !context.fail(); +} + +bool stlplus::lm_file_dependency::print(std::ostream& str) const +{ + str << "file: " << m_path; + if (m_line != 0) + str << ":" << m_line << ":" << m_column; + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_file_dependency& dependency) +{ + dependency.print(str); + return str; +} + +// unit dependency + +stlplus::lm_unit_dependency::lm_unit_dependency(void) +{ +} + +stlplus::lm_unit_dependency::lm_unit_dependency(const std::string& library, const lm_unit_name& name) : + m_library(library), m_name(name) +{ +} + +stlplus::lm_unit_dependency::~lm_unit_dependency(void) +{ +} + +const std::string& stlplus::lm_unit_dependency::library(void) const +{ + return m_library; +} + +void stlplus::lm_unit_dependency::set_library(const std::string& library) +{ + m_library = library; +} + +const stlplus::lm_unit_name& stlplus::lm_unit_dependency::unit_name(void) const +{ + return m_name; +} + +void stlplus::lm_unit_dependency::set_unit_name(const lm_unit_name& unit_name) +{ + m_name = unit_name; +} + +const std::string& stlplus::lm_unit_dependency::name(void) const +{ + return m_name.name(); +} + +void stlplus::lm_unit_dependency::set_name(const std::string& name) +{ + m_name.set_name(name); +} + +const std::string& stlplus::lm_unit_dependency::type(void) const +{ + return m_name.type(); +} + +void stlplus::lm_unit_dependency::set_type(const std::string& type) +{ + m_name.set_type(type); +} + +bool stlplus::lm_unit_dependency::write(std::ostream& context) const +{ + context << m_library; + m_name.write(context); + return !context.fail(); +} + +bool stlplus::lm_unit_dependency::read(std::istream& context) +{ + context >> m_library; + m_name.read(context);; + return !context.fail(); +} + +bool stlplus::lm_unit_dependency::print(std::ostream& str) const +{ + str << "unit: " << m_library << "." << m_name; + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_dependency& dependency) +{ + dependency.print(str); + return str; +} + +// dependencies + +stlplus::lm_dependencies::lm_dependencies(void) : m_source(0) +{ +} + +stlplus::lm_dependencies::lm_dependencies(const lm_dependencies& r) : m_source(0) +{ + *this = r; +} + +stlplus::lm_dependencies& stlplus::lm_dependencies::operator=(const stlplus::lm_dependencies& r) +{ + if (m_source) + delete m_source; + m_source = 0; + if (r.m_source) + m_source = new lm_file_dependency(*r.m_source); + m_files = r.m_files; + m_units = r.m_units; + return *this; +} + +stlplus::lm_dependencies::~lm_dependencies(void) +{ + if (m_source) + delete m_source; +} + +void stlplus::lm_dependencies::set_source_file(const lm_file_dependency& source) +{ + if (m_source) + delete m_source; + m_source = new lm_file_dependency(source); +} + +bool stlplus::lm_dependencies::source_file_present(void) const +{ + return (m_source != 0); +} + +const stlplus::lm_file_dependency& stlplus::lm_dependencies::source_file(void) const +{ + return *m_source; +} + +unsigned stlplus::lm_dependencies::file_add(const lm_file_dependency& dependency) +{ + m_files.push_back(dependency); + return m_files.size()-1; +} + +unsigned stlplus::lm_dependencies::file_size(void) const +{ + return m_files.size(); +} + +const stlplus::lm_file_dependency& stlplus::lm_dependencies::file_dependency(unsigned i) const +{ + return m_files[i]; +} + +void stlplus::lm_dependencies::file_erase(unsigned i) +{ + m_files.erase(m_files.begin()+i); +} + +unsigned stlplus::lm_dependencies::unit_add(const lm_unit_dependency& dependency) +{ + m_units.push_back(dependency); + return m_units.size()-1; +} + +unsigned stlplus::lm_dependencies::unit_size(void) const +{ + return m_units.size(); +} + +const stlplus::lm_unit_dependency& stlplus::lm_dependencies::unit_dependency(unsigned i) const +{ + return m_units[i]; +} + +void stlplus::lm_dependencies::unit_erase(unsigned i) +{ + m_units.erase(m_units.begin()+i); +} + +void stlplus::lm_dependencies::clear(void) +{ + if (m_source) + delete m_source; + m_source = 0; + m_files.clear(); + m_units.clear(); +} + +bool stlplus::lm_dependencies::empty(void) const +{ + return (m_source == 0) && m_files.empty() && m_units.empty(); +} + +bool stlplus::lm_dependencies::write(std::ostream& context) const +{ + context << (m_source ? true : false) << " "; + if (m_source) m_source->write(context); + context << std::endl; + context << m_files.size() << std::endl; + for (unsigned i = 0; i < m_files.size(); i++) + { + m_files[i].write(context); + context << std::endl; + } + context << m_units.size() << std::endl; + for (unsigned j = 0; j < m_units.size(); j++) + { + m_units[j].write(context); + context << std::endl; + } + return !context.fail(); +} + +bool stlplus::lm_dependencies::read(std::istream& context) +{ + clear(); + bool source_present = false; + context >> source_present; + if (source_present) + { + m_source = new lm_file_dependency(); + m_source->read(context); + } + unsigned files_size = 0; + context >> files_size; + for (unsigned i = 0; i < files_size; i++) + { + m_files.push_back(lm_file_dependency()); + m_files.back().read(context); + } + unsigned units_size = 0; + context >> units_size; + for (unsigned j = 0; j < units_size; j++) + { + m_units.push_back(lm_unit_dependency()); + m_units.back().read(context); + } + return !context.fail(); +} + +bool stlplus::lm_dependencies::print(std::ostream& str) const +{ + if (m_source) + str << " source file: " << *m_source << std::endl; + for (unsigned i = 0; i < m_files.size(); i++) + str << " " << m_files[i] << std::endl; + for (unsigned j = 0; j < m_units.size(); j++) + str << " " << m_units[j] << std::endl; + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_dependencies& dependencies) +{ + dependencies.print(str); + return str; +} + +//////////////////////////////////////////////////////////////////////////////// +// lm_unit +//////////////////////////////////////////////////////////////////////////////// + +stlplus::lm_unit::lm_unit(const lm_unit_name& name, lm_library* library) : + m_name(name), m_header_modified(false), m_loaded(false), m_marked(false), m_library(library), m_error(false) +{ + read_header(); +} + +stlplus::lm_unit::~lm_unit(void) +{ + write_header(); +} + +//////////////////////////////////////// +// Header data + +const stlplus::lm_unit_name& stlplus::lm_unit::unit_name(void) const +{ + return m_name; +} + +const std::string& stlplus::lm_unit::name(void) const +{ + return m_name.name(); +} + +const std::string& stlplus::lm_unit::type(void) const +{ + return m_name.type(); +} + +// dependencies + +// source file dependency + +void stlplus::lm_unit::set_source_file(const lm_file_dependency& dependency) +{ + m_header_modified = true; + m_dependencies.set_source_file(dependency); +} + +bool stlplus::lm_unit::source_file_present(void) const +{ + return m_dependencies.source_file_present(); +} + +const stlplus::lm_file_dependency& stlplus::lm_unit::source_file(void) const +{ + return m_dependencies.source_file(); +} + +// other file dependencies + +unsigned stlplus::lm_unit::file_add(const lm_file_dependency& dependency) +{ + m_header_modified = true; + return m_dependencies.file_add(dependency); +} + +unsigned stlplus::lm_unit::file_size(void) const +{ + return m_dependencies.file_size(); +} + +const stlplus::lm_file_dependency& stlplus::lm_unit::file_dependency(unsigned i) const +{ + return m_dependencies.file_dependency(i); +} + +void stlplus::lm_unit::file_erase(unsigned i) +{ + m_header_modified = true; + m_dependencies.file_erase(i); +} + +// unit dependencies + +unsigned stlplus::lm_unit::unit_add(const lm_unit_dependency& dependency) +{ + m_header_modified = true; + return m_dependencies.unit_add(dependency); +} + +unsigned stlplus::lm_unit::unit_size(void) const +{ + return m_dependencies.unit_size(); +} + +const stlplus::lm_unit_dependency& stlplus::lm_unit::unit_dependency(unsigned i) const +{ + return m_dependencies.unit_dependency(i); +} + +void stlplus::lm_unit::unit_erase(unsigned i) +{ + m_header_modified = true; + m_dependencies.unit_erase(i); +} + +const stlplus::lm_dependencies& stlplus::lm_unit::dependencies(void) const +{ + return m_dependencies; +} + +void stlplus::lm_unit::set_dependencies(const lm_dependencies& dependencies) +{ + m_header_modified = true; + m_dependencies = dependencies; +} + +void stlplus::lm_unit::clear_dependencies(void) +{ + m_header_modified = true; + m_dependencies.clear(); +} + +bool stlplus::lm_unit::empty_dependencies(void) const +{ + return m_dependencies.empty(); +} + +// dependency checking + +bool stlplus::lm_unit::out_of_date(void) const +{ + return m_library->out_of_date(m_name); +} + +bool stlplus::lm_unit::up_to_date(void) const +{ + return m_library->up_to_date(m_name); +} + +stlplus::lm_dependencies stlplus::lm_unit::out_of_date_reason(void) const +{ + return m_library->out_of_date_reason(m_name); +} + +// supplementary data + +const std::string& stlplus::lm_unit::supplementary_data(void) const +{ + return m_supplement; +} + +void stlplus::lm_unit::set_supplementary_data(const std::string& data) +{ + m_supplement = data; + m_header_modified = true; +} + +//////////////////////////////////////// +// unit data management + +bool stlplus::lm_unit::load(void) +{ + if (out_of_date()) return false; + if (m_loaded) return true; + // get the user data for this type + lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type()); + if (callback == m_library->m_manager->m_callbacks.end()) return false; + void* data = callback->second.m_type_data; + bool result = read(filename(), data); + m_loaded = true; + return result; +} + +bool stlplus::lm_unit::save(void) +{ + if (!m_marked) return true; + if (!m_loaded) return false; + // get the user data for this type + lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type()); + if (callback == m_library->m_manager->m_callbacks.end()) return false; + void* data = callback->second.m_type_data; + bool result = write(filename(), data); + if (result) m_marked = false; + return result; +} + +bool stlplus::lm_unit::loaded(void) const +{ + return m_loaded; +} + +void stlplus::lm_unit::mark(void) +{ + m_marked = true; +} + +time_t stlplus::lm_unit::modified(void) const +{ + return file_modified(filename()); +} + +//////////////////////////////////////// +// containing library manager details + +const stlplus::lm_library* stlplus::lm_unit::library(void) const +{ + return m_library; +} + +stlplus::lm_library* stlplus::lm_unit::library(void) +{ + return m_library; +} + +const std::string& stlplus::lm_unit::library_name(void) const +{ + return m_library->name(); +} + +const std::string& stlplus::lm_unit::library_path(void) const +{ + return m_library->path(); +} + +// error handling - these apply to the last read/write operation + +bool stlplus::lm_unit::error(void) const +{ + return m_error; +} + +// functions that customise subclasses of this superclass + +bool stlplus::lm_unit::read(const std::string& filespec, void* type_data) +{ + std::ifstream input(filespec.c_str()); + bool result = read(input, type_data); + if (input.fail()) + { + result = false; + m_error = true; + } + return result; +} + +bool stlplus::lm_unit::read(std::istream&, void*) +{ + return false; +} + +bool stlplus::lm_unit::write(const std::string& filespec, void* type_data) +{ + std::ofstream output(filespec.c_str()); + bool result = write(output, type_data); + if (output.fail()) + { + result = false; + m_error = true; + } + return result; +} + +bool stlplus::lm_unit::write(std::ostream&, void*) +{ + return false; +} + +bool stlplus::lm_unit::purge(void) +{ + return true; +} + +stlplus::lm_unit* stlplus::lm_unit::clone(void) const +{ + return new lm_unit(*this); +} + +bool stlplus::lm_unit::print(std::ostream& str) const +{ + str << m_name << " " << (m_loaded ? "loaded" : "") << " " << (m_marked ? "needs saving" : ""); + return !str.fail(); +} + +bool stlplus::lm_unit::print_long(std::ostream& str) const +{ + str << "header:" << std::endl; + str << " name: " << m_name.name() << std::endl; + str << " type: " << library()->manager()->description(m_name.type()) << std::endl; + str << " dependencies:" << std::endl; + // Note: I've inlined this rather than call the above-defined print for dependencies + // This is so that I can use the library manager to look up the descriptions of unit types + // I can also convert paths so that they are relative to the current directory rather than the library + // print the source file dependency if present + if (m_dependencies.source_file_present()) + { + str << " source file: "; + str << filespec_to_relative_path(m_dependencies.source_file().path_full(library()->path())); + if (m_dependencies.source_file().line() != 0) + str << ":" << m_dependencies.source_file().line() << ":" << m_dependencies.source_file().column(); + str << std::endl; + } + // now print other file dependencies + // convert these to relative paths too + for (unsigned f = 0; f < m_dependencies.file_size(); f++) + { + str << " file: "; + str << filespec_to_relative_path(m_dependencies.file_dependency(f).path_full(library()->path())); + if (m_dependencies.file_dependency(f).line() != 0) + str << ":" << m_dependencies.file_dependency(f).line() << ":" << m_dependencies.file_dependency(f).column(); + str << std::endl; + } + // now print unit dependencies + // convert unit types to their descriptive strings + for (unsigned u = 0; u < m_dependencies.unit_size(); u++) + { + str << " " << library()->manager()->description(m_dependencies.unit_dependency(u).type()) << ": "; + str << m_dependencies.unit_dependency(u).library() << "." << m_dependencies.unit_dependency(u).name(); + str << std::endl; + } + if (!m_supplement.empty()) + { + str << " supplementary data: " << m_supplement << std::endl; + } + return !str.fail(); +} + +// header file management + +std::string stlplus::lm_unit::filename(void) const +{ + return stlplus::create_filespec(library_path(), m_name.name(), m_name.type()); +} + +std::string stlplus::lm_unit::header_filename(void) const +{ + return stlplus::create_filespec(library_path(), m_name.name(), m_name.type() + std::string(HeaderExtension)); +} + +bool stlplus::lm_unit::read_header(void) +{ + if (file_exists(header_filename())) + { + std::ifstream input(header_filename().c_str()); + m_dependencies.read(input); + input.get(); + std::getline(input, m_supplement); + } + m_header_modified = false; + return true; +} + +bool stlplus::lm_unit::write_header(void) +{ + if (!m_header_modified) return true; + std::ofstream output(header_filename().c_str()); + m_dependencies.write(output); + output << m_supplement << std::endl; + m_header_modified = false; + return true; +} + +// print diagnostics + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit& u) +{ + u.print(str); + return str; +} + +//////////////////////////////////////////////////////////////////////////////// +// lm_library +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// constructors/destructors +// copy operations only legal on unopened libraries + +stlplus::lm_library::lm_library(library_manager* manager) : m_writable(false), m_manager(manager) +{ +} + +stlplus::lm_library::lm_library(const lm_library& library) : m_writable(library.m_writable), m_manager(library.m_manager) +{ +} + +stlplus::lm_library& stlplus::lm_library::operator = (const stlplus::lm_library& library) +{ + m_writable = library.m_writable; + m_manager = library.m_manager; + return *this; +} + +stlplus::lm_library::~lm_library(void) +{ + close(); +} + +const stlplus::library_manager* stlplus::lm_library::manager(void) const +{ + return m_manager; +} + +stlplus::library_manager* stlplus::lm_library::manager(void) +{ + return m_manager; +} + +////////////////////////////////////////////////////////////////////////////// +// initialisers + +bool stlplus::lm_library::create(const std::string& name, const std::string& path, bool writable) +{ + close(); + if (!create_library(path, m_manager->m_owner, name, writable)) return false; + return open(path); +} + +bool stlplus::lm_library::open(const std::string& path) +{ + close(); + if (!read_context(path, m_manager->m_owner, m_name, m_writable)) return false; + // convert path to full path on load + m_path = folder_to_path(path); + return load_types(); +} + +////////////////////////////////////////////////////////////////////////////// +// management of types + +bool stlplus::lm_library::load_type(const std::string& type) +{ + lm_callback_map::iterator callback = m_manager->m_callbacks.find(type); + if (callback == m_manager->m_callbacks.end()) return false; + // a null callback means create a dummy unit from the baseclass only + lm_create_callback fn = callback->second.m_callback; + void* data = callback->second.m_type_data; + // for each file in the library folder that matches the type of the create callback, create an unloaded unit + std::vector files = folder_wildcard(m_path, stlplus::create_filename("*", type), false, true); + for (unsigned i = 0; i < files.size(); i++) + { + // key by unit name - lowercase name if case-insensitive + lm_unit_name uname(basename_part(files[i]),type); + if (!m_manager->m_unit_case) uname.lowercase(); + lm_unit_ptr unit; + // if there's a callback, use it to create the subclass, else create the + // superclass which only contains header information + if (fn) + unit.set(fn(uname,this,data)); + else + unit.set(new lm_unit(uname,this)); + m_units[uname] = unit; + } + return true; +} + +bool stlplus::lm_library::load_types(void) +{ + bool result = true; + for (lm_callback_map::const_iterator i = m_manager->m_callbacks.begin(); i != m_manager->m_callbacks.end(); i++) + result &= load_type(i->first); + return result; +} + +bool stlplus::lm_library::remove_type(const std::string& type) +{ + bool result = true; + std::vector units = names(type); + for (std::vector::iterator i = units.begin(); i != units.end(); i++) + { + lm_unit_name name(*i, type); + std::map::iterator ni = local_find(name); + if (ni != m_units.end()) + m_units.erase(ni); + } + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// whole library operations + +bool stlplus::lm_library::load(void) +{ + bool result = true; + for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) + result &= i->second->load(); + return result; +} + +bool stlplus::lm_library::save(void) +{ + bool result = true; + for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) + result &= i->second->save(); + return result; +} + +bool stlplus::lm_library::purge(void) +{ + bool result = true; + for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) + result &= i->second->purge(); + return result; +} + +bool stlplus::lm_library::close(void) +{ + bool result = save(); + m_units.clear(); + m_path = ""; + m_writable = false; + return result; +} + +bool stlplus::lm_library::erase(void) +{ + // preserve the path because close destroys it + std::string path = m_path; + return close() && erase_library(path, m_manager->m_owner); +} + +const std::string& stlplus::lm_library::name(void) const +{ + return m_name; +} + +const std::string& stlplus::lm_library::path(void) const +{ + return m_path; +} + +////////////////////////////////////////////////////////////////////////////// +// managing read/write status + +bool stlplus::lm_library::set_read_write(bool writable) +{ + if (m_writable == writable) return true; + if (os_read_only()) return false; + m_writable = writable; + if (!write_context(m_path, m_manager->m_owner, m_name, m_writable)) + read_context(m_path, m_manager->m_owner, m_name, m_writable); + return m_writable == writable; +} + +bool stlplus::lm_library::set_writable(void) +{ + return set_read_write(true); +} + +bool stlplus::lm_library::set_read_only(void) +{ + return set_read_write(false); +} + +bool stlplus::lm_library::writable(void) const +{ + return os_writable() && lm_writable(); +} + +bool stlplus::lm_library::read_only(void) const +{ + return os_read_only() || lm_read_only(); +} + +bool stlplus::lm_library::os_writable(void) const +{ + return folder_writable(path()); +} + +bool stlplus::lm_library::os_read_only(void) const +{ + return !folder_writable(path()); +} + +bool stlplus::lm_library::lm_writable(void) const +{ + return m_writable; +} + +bool stlplus::lm_library::lm_read_only(void) const +{ + return !m_writable; +} + +////////////////////////////////////////////////////////////////////////////// +// unit management + +std::map::iterator stlplus::lm_library::local_find(const lm_unit_name& name) +{ + // implement the case-sensitivity + lm_unit_name local = name; + if (!m_manager->m_unit_case) local.lowercase(); + return m_units.find(local); +} + +std::map::const_iterator stlplus::lm_library::local_find(const lm_unit_name& name) const +{ + // implement the case-sensitivity + lm_unit_name local = name; + if (!m_manager->m_unit_case) local.set_name(lowercase(local.name())); + return m_units.find(local); +} + +bool stlplus::lm_library::exists(const lm_unit_name& name) const +{ + return find(name).present(); +} + +stlplus::lm_unit_ptr stlplus::lm_library::create(const stlplus::lm_unit_name& name) +{ + if (read_only()) return lm_unit_ptr(); + // preserve the unit's name, but use a lowercase key in case-insensitive mode + lm_unit_name uname = name; + if (!m_manager->m_unit_case) uname.lowercase(); + // remove any existing unit with the same name + erase(uname); + // use the callbacks to create a new unit + lm_callback_map::iterator callback = m_manager->m_callbacks.find(name.type()); + if (callback == m_manager->m_callbacks.end()) return lm_unit_ptr(); + lm_unit_ptr new_unit; + new_unit.set(callback->second.m_callback(name,this,callback->second.m_type_data)); + new_unit->m_loaded = true; + // add it to the library manager + m_units[uname] = new_unit; + return m_units[uname]; +} + +bool stlplus::lm_library::loaded(const lm_unit_name& name) const +{ + lm_unit_ptr unit = find(name); + return unit && unit->loaded(); +} + +bool stlplus::lm_library::load(const lm_unit_name& name) +{ + lm_unit_ptr unit = find(name); + if (!unit) return false; + return unit->load(); +} + +bool stlplus::lm_library::purge(const lm_unit_name& name) +{ + lm_unit_ptr unit = find(name); + if (!unit) return false; + bool result = save(name); + result &= unit->purge(); + unit->m_loaded = false; + return result; +} + +bool stlplus::lm_library::save(const lm_unit_name& name) +{ + if (read_only()) return false; + lm_unit_ptr unit = find(name); + if (!unit) return false; + return unit->save(); +} + +bool stlplus::lm_library::erase(const lm_unit_name& name) +{ + if (read_only()) return false; + std::map::iterator i = local_find(name); + if (i == m_units.end()) return false; + std::string spec = i->second->filename(); + std::string header_spec = i->second->header_filename(); + m_units.erase(i); + file_delete(spec); + file_delete(header_spec); + return true; +} + +bool stlplus::lm_library::mark(const lm_unit_name& name) +{ + if (read_only()) return false; + lm_unit_ptr unit = find(name); + if (!unit) return false; + unit->mark(); + return true; +} + +time_t stlplus::lm_library::modified(const lm_unit_name& name) const +{ + lm_unit_ptr unit = find(name); + return unit ? unit->modified() : 0; +} + +bool stlplus::lm_library::erase_by_source(const std::string& source_file) +{ + if (read_only()) return false; + if (source_file.empty()) return false; + bool result = false; + std::string source_file_full = filespec_to_path(source_file); + // erase by unit name so that I don't have to deal with an iterator to a changing map + std::vector units = names(); + for (std::vector::iterator i = units.begin(); i != units.end(); i++) + { + lm_unit_ptr unit = find(*i); + if (unit && unit->source_file_present()) + { + std::string file_full = unit->source_file().path_full(unit->library_path()); + if (file_full == source_file_full) + { + erase(*i); + result = true; + } + } + } + return result; +} + +const stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name) const +{ + std::map::const_iterator i = local_find(name); + if (i == m_units.end()) return lm_unit_ptr(); + return i->second; +} + +stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name) +{ + std::map::iterator i = local_find(name); + if (i == m_units.end()) return lm_unit_ptr(); + return i->second; +} + +std::vector stlplus::lm_library::names(void) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + result.push_back(i->second->unit_name()); + return result; +} + +std::vector stlplus::lm_library::names(const std::string& type) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + if (i->first.type() == type) + result.push_back(i->second->name()); + return result; +} + +std::vector stlplus::lm_library::handles(void) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + result.push_back(i->second); + return result; +} + +std::vector stlplus::lm_library::handles(const std::string& type) const +{ + std::vector result; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + if (i->first.type() == type) + result.push_back(i->second); + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// dependency checking + +bool stlplus::lm_library::out_of_date(const stlplus::lm_unit_name& name) const +{ + return m_manager->out_of_date(m_name, name); +} + +bool stlplus::lm_library::up_to_date(const stlplus::lm_unit_name& name) const +{ + return m_manager->up_to_date(m_name, name); +} + +stlplus::lm_dependencies stlplus::lm_library::out_of_date_reason(const stlplus::lm_unit_name& unit) const +{ + return m_manager->out_of_date_reason(m_name, unit); +} + +std::pair stlplus::lm_library::tidy(void) +{ + std::pair result = std::make_pair(true,0); + // erase every unit that is out of date + // this will potentially make other units out of date, so keep erasing until + // everything is up to date or an error occurs + for (;;) + { + std::vector units = names(); + unsigned initial_count = result.second; + for (std::vector::iterator i = units.begin(); i != units.end(); i++) + { + if (out_of_date(*i)) + { + if (!erase(*i)) + result.first = false; + else + result.second++; + } + } + if (!result.first) break; + if (result.second == initial_count) break; + } + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// do-everything print routine! + +bool stlplus::lm_library::pretty_print(std::ostream& str, + bool print_units, + const std::string& type) const +{ + // print the library information + if (this == m_manager->work()) + str << "->> "; + else + str << " "; + str << name() << " in directory " << folder_to_relative_path(path()); + if (read_only()) str << " (locked)"; + str << std::endl; + // select the units + if (print_units) + { + // separate into a block per unit kind + for (lm_callback_map::const_iterator j = m_manager->m_callbacks.begin(); j != m_manager->m_callbacks.end(); j++) + { + // select the requested unit kind + if (type.empty() || type == j->first) + { + // get all the units of this kind + std::vector unit_names = names(j->first); + if (!unit_names.empty()) + { + str << " " << j->second.m_description << std::endl; + for (unsigned k = 0; k < unit_names.size(); k++) + { + lm_dependencies reason = out_of_date_reason(stlplus::lm_unit_name(unit_names[k],j->first)); + str << " - " << unit_names[k]; + if (!reason.empty()) str << " (out of date)"; + str << std::endl; + if (!reason.empty()) + { + if (reason.source_file_present()) + { + const lm_file_dependency& file = reason.source_file(); + str << " * source file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl; + } + for (unsigned i1 = 0; i1 < reason.file_size(); i1++) + { + const lm_file_dependency& file = reason.file_dependency(i1); + str << " * file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl; + } + for (unsigned i2 = 0; i2 < reason.unit_size(); i2++) + { + const lm_unit_dependency& file = reason.unit_dependency(i2); + lm_callback_map::const_iterator entry = m_manager->m_callbacks.find(file.type()); + str << " * " << entry->second.m_description << " " << file.library() << "." << file.name() << " has changed" << std::endl; + } + } + } + } + } + } + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// diagnostic print routines + +bool stlplus::lm_library::print(std::ostream& str) const +{ + str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + i->second->print(str); + return !str.fail(); +} + +bool stlplus::lm_library::print_long(std::ostream& str) const +{ + str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl; + for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) + i->second->print_long(str); + return !str.fail(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_library& lib) +{ + lib.print(str); + return str; +} + +//////////////////////////////////////////////////////////////////////////////// +// library manager +//////////////////////////////////////////////////////////////////////////////// + +// static functions allow you to test whether a directory is a library before opening it +// you can also find the library's name without opening it + +bool stlplus::library_manager::is_library(const std::string& path, const std::string& owner) +{ + std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); + return file_exists(spec); +} + +std::string stlplus::library_manager::library_name(const std::string& path, const std::string& owner) +{ + std::string name; + bool writable = false; + if (!read_context(path, owner, name, writable)) + return std::string(); + return name; +} + +bool stlplus::library_manager::is_library(const std::string& path) +{ + return is_library(path, m_owner); +} + +std::string stlplus::library_manager::library_name(const std::string& path) +{ + return library_name(path, m_owner); +} + +////////////////////////////////////////////////////////////////////////////// +// tructors + +stlplus::library_manager::library_manager(const std::string& owner, bool library_case, bool unit_case) : + m_owner(owner), m_ini_files(0), m_library_case(library_case), m_unit_case(unit_case) +{ +} + +stlplus::library_manager::~library_manager(void) +{ + close(); +} + +////////////////////////////////////////////////////////////////////////////// +// case sensitivity + +bool stlplus::library_manager::library_case(void) const +{ + return m_library_case; +} + +void stlplus::library_manager::set_library_case(bool library_case) +{ + m_library_case = library_case; +} + +bool stlplus::library_manager::unit_case(void) const +{ + return m_unit_case; +} + +void stlplus::library_manager::set_unit_case(bool unit_case) +{ + m_unit_case = unit_case; +} + +//////////////////////////////////////////////////////////////////////////////// +// type handling + +bool stlplus::library_manager::add_type(const std::string& type, + const std::string& description, + lm_create_callback fn, + void* type_data) +{ + bool result = true; + m_callbacks[type] = lm_callback_entry(fn, description, type_data); + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->load_type(type); + return result; +} + +bool stlplus::library_manager::remove_type(const std::string& type) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->remove_type(type); + m_callbacks.erase(type); + return result; +} + +std::vector stlplus::library_manager::types(void) const +{ + std::vector result; + for (lm_callback_map::const_iterator i = m_callbacks.begin(); i != m_callbacks.end(); i++) + result.push_back(i->first); + return result; +} + +std::string stlplus::library_manager::description(const std::string& type) const +{ + lm_callback_map::const_iterator found = m_callbacks.find(type); + if (found == m_callbacks.end()) return std::string(); + return found->second.m_description; +} + +stlplus::lm_create_callback stlplus::library_manager::callback(const std::string& type) const +{ + lm_callback_map::const_iterator found = m_callbacks.find(type); + if (found == m_callbacks.end()) return 0; + return found->second.m_callback; +} + +void* stlplus::library_manager::type_data(const std::string& type) const +{ + lm_callback_map::const_iterator found = m_callbacks.find(type); + if (found == m_callbacks.end()) return 0; + return found->second.m_type_data; +} + +////////////////////////////////////////////////////////////////////////////// +// mapping file handling + +void stlplus::library_manager::set_mapping_file(const std::string& mapping_file) +{ + m_mapping_file = mapping_file; +} + +bool stlplus::library_manager::load_mappings(const std::string& mapping_file) +{ + m_mapping_file = mapping_file; + if (!file_exists(mapping_file)) + { + return false; + } + std::ifstream input(mapping_file.c_str()); + if (input.fail()) + return false; + // each line of the map file is a path to a library + // the first line is the work library - may be empty + // mappings are saved as paths relative to the mapping file and converted to full paths on load + bool result = true; + unsigned line = 1; + for (std::string path = ""; std::getline(input,path); line++) + { + if (path.empty()) continue; + std::string full_path = folder_to_path(folder_part(m_mapping_file), path); + if (!is_library(full_path)) + { + result = false; + } + else + { + lm_library* lib = open(full_path); + if (!lib) + result = false; + else if (line == 1) + setwork(lib->name()); + } + } + return result; +} + +std::string stlplus::library_manager::mapping_file() +{ + return m_mapping_file; +} + +bool stlplus::library_manager::set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_name) +{ + if (!ini_files) return false; + bool result = true; + m_ini_files = ini_files; + m_ini_section = library_section; + m_ini_work = work_name; + // now load the existing library mappings if present - in any case create the sections + // 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 + if (m_ini_files->section_exists(m_ini_section)) + { + std::vector library_names = m_ini_files->variable_names(m_ini_section); + std::string work_name; + for (unsigned i = 0; i < library_names.size(); i++) + { + std::string library_name = library_names[i]; + // if the variable name is the work name, then this is a mapping to an existing library name + // if it is null, then it is masking a global library definition so ignore it + // otherwise it is a mapping to a directory containing the library + if (library_name.empty()) + { + } + else if (library_name == m_ini_work) + { + work_name = m_ini_files->variable_value(m_ini_section, library_name); + } + else + { + std::string value = m_ini_files->variable_value(m_ini_section, library_name); + std::string filename = m_ini_files->variable_filename(m_ini_section, library_name); + // get the path to the ini file defining this library, strip off the ini filename to get the folder + // then combine this with the library path from that ini file to the library to get a full path to the library + // whew! + std::string full_path = folder_to_path(folder_part(filename),value); + if (!is_library(full_path)) + result = false; + else + { + lm_library* lib = open(full_path); + if (!lib) + result = false; + } + } + } + // work must be set after all the libraries have been opened because it is + // illegal to set work to a library that doesn't already exist in the + // library manager + if (work_name.empty()) + unsetwork(); + else + result &= setwork(work_name); + } + return result; +} + +stlplus::ini_manager* stlplus::library_manager::get_ini_manager(void) const +{ + return m_ini_files; +} + +bool stlplus::library_manager::save_mappings (void) +{ + bool result = true; + // save to mapping file or ini manager or both + if (!m_mapping_file.empty()) + { + if (m_libraries.size() == 0) + { + // if the file would be empty, delete it + if (!file_delete(m_mapping_file)) + result = false; + } + else + { + std::ofstream output(m_mapping_file.c_str()); + if (output.fail()) + { + result = false; + } + else + { + // each line of the map file is a path to a library + // the first line is the work library + // mappings are saved as relative paths to the mapping file and converted to full paths on load + if (!work_name().empty()) + output << folder_to_relative_path(folder_part(m_mapping_file), path(work_name())); + output << std::endl; + std::vector libraries = names(); + for (unsigned i = 0; i < libraries.size(); ++i) + { + if (libraries[i] != work_name()) + output << folder_to_relative_path(folder_part(m_mapping_file), path(libraries[i])) << std::endl; + } + if (output.fail()) + result = false; + } + } + } + if (m_ini_files) + { + // this is somewhat tricky! + // first remove all local mappings + // then need to compare the surviving library mappings with the contents of the library manager + // if there's a library in the ini files not in the library manager, mask it with an empty local declaration + // if there's a library in the ini files with the same mapping as the library manager, do nothing + // if there's a library in the ini files with a different mapping write that library mapping + // if there's a mapping missing from the ini files, write it + // finally write the work mapping if there is one + // clear all local mappings + // TODO - rework this so that the ini files only change if the mappings change + m_ini_files->clear_section(m_ini_section); + m_ini_files->add_comment(m_ini_section, "generated automatically by the library manager"); + // look for globally defined library mappings that need to be overridden in the local ini file + std::vector ini_names = m_ini_files->variable_names(m_ini_section); + for (unsigned i = 0; i < ini_names.size(); i++) + { + std::string ini_name = ini_names[i]; + // check for a global library that needs to be locally masked + if (!exists(ini_name)) + m_ini_files->add_variable(m_ini_section, ini_name, ""); + else + { + // check for a library that is locally remapped + std::string value = m_ini_files->variable_value(m_ini_section, ini_name); + std::string filename = m_ini_files->variable_filename(m_ini_section, ini_name); + std::string full_ini_path = folder_to_path(folder_part(filename), value); + std::string full_lib_path = folder_to_path(path(ini_name)); + if (full_ini_path != full_lib_path) + { + // write the path relative to the ini file + std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path); + m_ini_files->add_variable(m_ini_section, ini_name, relative_path); + } + } + } + // now scan the library for mappings that aren't yet in the ini file + std::vector lib_names = names(); + for (unsigned j = 0; j < lib_names.size(); j++) + { + std::string lib_name = lib_names[j]; + if (std::find(ini_names.begin(), ini_names.end(), lib_name) == ini_names.end()) + { + // write the path relative to the ini file + std::string full_lib_path = folder_to_path(path(lib_name)); + std::string filename = m_ini_files->variable_filename(m_ini_section, lib_name); + std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path); + m_ini_files->add_variable(m_ini_section, lib_name, relative_path); + } + } + // write the work library - also write a blank value if work is not set but is defined in another library + if (!work_name().empty()) + m_ini_files->add_variable(m_ini_section, m_ini_work, work_name()); + else if (m_ini_files->variable_exists(m_ini_section, m_ini_work)) + m_ini_files->add_variable(m_ini_section, m_ini_work, ""); + m_ini_files->add_blank(m_ini_section); + // remove the section from the ini file manager if there's nothing in it + if (m_ini_files->empty_section(m_ini_section)) + m_ini_files->erase_section(m_ini_section); + if (!m_ini_files->save()) + result = false; + } + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// library management + +bool stlplus::library_manager::exists(const std::string& name) const +{ + return find(name) != 0; +} + +stlplus::lm_library* stlplus::library_manager::create(const std::string& name, const std::string& path, bool writable) +{ + if (!create_library(path, m_owner, name, writable)) return 0; + return open(path); +} + +stlplus::lm_library* stlplus::library_manager::open(const std::string& path) +{ + std::string name; + bool writable = false; + if (!read_context(path, m_owner, name, writable)) return 0; + // remove any pre-existing library with the same name + close(name); + // add the library to the manager and open it + m_libraries.push_back(lm_library(this)); + if (!m_libraries.back().open(folder_to_path(path))) + { + // remove the library in the event of an error + m_libraries.erase(--m_libraries.end()); + return 0; + } + return &m_libraries.back(); +} + +bool stlplus::library_manager::load(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->load(); +} + +bool stlplus::library_manager::save(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->save(); +} + +bool stlplus::library_manager::purge(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->purge(); +} + +bool stlplus::library_manager::close(const std::string& name) +{ + bool result= true; + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + result &= found->close(); + m_libraries.erase(found); + if (name == m_work) m_work = ""; + return result; +} + +bool stlplus::library_manager::erase(const std::string& name) +{ + bool result= true; + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + result &= found->erase(); + m_libraries.erase(found); + if (name == m_work) m_work = ""; + return result; +} + +// operations on all libraries - as above but applied to all the libraries in the manager + +bool stlplus::library_manager::load(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->load(); + return result; +} + +bool stlplus::library_manager::save(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->save(); + return result; +} + +bool stlplus::library_manager::purge(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result &= i->purge(); + return result; +} + +bool stlplus::library_manager::close(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); ) + { + std::list::iterator next = i; + next++; + result &= i->close(); + m_libraries.erase(i); + i = next; + } + return result; +} + +bool stlplus::library_manager::erase(void) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); ) + { + std::list::iterator next = i; + next++; + result &= i->erase(); + m_libraries.erase(i); + i = next; + } + return result; +} + +// get name and path of a library - name can differ in case if the library manager is case-insensitive + +std::string stlplus::library_manager::name(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::string(); + return found->name(); +} + +std::string stlplus::library_manager::path(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::string(); + return found->path(); +} + +// control and test read/write status + +bool stlplus::library_manager::set_writable(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->set_writable(); +} + +bool stlplus::library_manager::set_read_only(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->set_read_only(); +} + +bool stlplus::library_manager::writable(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->writable(); +} + +bool stlplus::library_manager::read_only(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->read_only(); +} + +bool stlplus::library_manager::os_writable(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->os_writable(); +} + +bool stlplus::library_manager::os_read_only(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->os_read_only(); +} + +bool stlplus::library_manager::lm_writable(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->lm_writable(); +} + +bool stlplus::library_manager::lm_read_only(const std::string& library) const +{ + std::list::const_iterator found = local_find(library); + if (found == m_libraries.end()) return false; + return found->lm_read_only(); +} + +// find a library in the manager - returns null if not found + +stlplus::lm_library* stlplus::library_manager::find(const std::string& name) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return 0; + return &(*found); +} + +const stlplus::lm_library* stlplus::library_manager::find(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return 0; + return &(*found); +} + +// get the set of all library names + +std::vector stlplus::library_manager::names(void) const +{ + std::vector result; + for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result.push_back(i->name()); + return result; +} + +// get the set of all libraries + +std::vector stlplus::library_manager::handles(void) const +{ + std::vector result; + for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result.push_back(&(*i)); + return result; +} + +std::vector stlplus::library_manager::handles(void) +{ + std::vector result; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + result.push_back(&(*i)); + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// current library management + +bool stlplus::library_manager::setwork(const std::string& name) +{ + unsetwork(); + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + m_work = found->name(); + return true; +} + +bool stlplus::library_manager::unsetwork(void) +{ + m_work = ""; + return true; +} + +const stlplus::lm_library* stlplus::library_manager::work(void) const +{ + if (m_work.empty()) return 0; + return find(m_work); +} + +stlplus::lm_library* stlplus::library_manager::work(void) +{ + if (m_work.empty()) return 0; + return find(m_work); +} + +std::string stlplus::library_manager::work_name(void) const +{ + return m_work; +} + +////////////////////////////////////////////////////////////////////////////// +// unit management within a library + +bool stlplus::library_manager::exists(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->exists(unit); +} + +stlplus::lm_unit_ptr stlplus::library_manager::create(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->create(unit); +} + +bool stlplus::library_manager::loaded(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->loaded(unit); +} + +bool stlplus::library_manager::load(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->load(unit); +} + +bool stlplus::library_manager::purge(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->purge(unit); +} + +bool stlplus::library_manager::save(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->save(unit); +} + +bool stlplus::library_manager::erase(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->erase(unit); +} + +bool stlplus::library_manager::mark(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return false; + return found->mark(unit); +} + +time_t stlplus::library_manager::modified(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return 0; + return found->modified(unit); +} + +bool stlplus::library_manager::erase_by_source(const std::string& source_file) +{ + bool result = true; + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + if (i->writable()) + result &= i->erase_by_source(source_file); + return result; +} + +const stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) + return stlplus::lm_unit_ptr(); + return found->find(unit); +} + +stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit) +{ + std::list::iterator found = local_find(name); + if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); + return found->find(unit); +} + +std::vector stlplus::library_manager::names(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->names(); +} + +std::vector stlplus::library_manager::names(const std::string& name, const std::string& type) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->names(type); +} + +std::vector stlplus::library_manager::handles(const std::string& name) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->handles(); +} + +std::vector stlplus::library_manager::handles(const std::string& name, const std::string& type) const +{ + std::list::const_iterator found = local_find(name); + if (found == m_libraries.end()) return std::vector(); + return found->handles(type); +} + +////////////////////////////////////////////////////////////////////////////// +// dependency checking +// done at the top level because a global view of the libraries is required + +bool stlplus::library_manager::out_of_date(const std::string& library, const stlplus::lm_unit_name& name) const +{ + return !up_to_date(library, name); +} + +bool stlplus::library_manager::up_to_date(const std::string& library, const stlplus::lm_unit_name& name) const +{ + lm_dependencies reason = out_of_date_reason(library, name); + return reason.empty(); +} + +stlplus::lm_dependencies stlplus::library_manager::out_of_date_reason(const std::string& library, const stlplus::lm_unit_name& name) const +{ + std::map,lm_dependencies> visited; + return out_of_date_check(visited, this, library, name); +} + +std::pair stlplus::library_manager::tidy(const std::string& library) +{ + std::list::iterator found = local_find(library); + if (found == m_libraries.end()) return std::make_pair(false,0); + return found->tidy(); +} + +std::pair stlplus::library_manager::tidy(void) +{ + std::pair result = std::make_pair(true,0); + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + { + if (i->writable()) + { + std::pair library_result = i->tidy(); + result.second += library_result.second; + result.first &= library_result.first; + } + } + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// do-everything print routine! + +bool stlplus::library_manager::pretty_print(std::ostream& str, + bool print_units, + const std::string& lib, + const std::string& type) const +{ + bool library_found = false; + for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) + { + // select the library + if (lib.empty() || lib == l->name()) + { + l->pretty_print(str, print_units, type); + library_found = true; + } + } + if (!library_found) + { + if (lib.empty()) + str << "there are no libraries in the library list" << std::endl; + else + str << "there is no library called " << lib << " in the library list" << std::endl; + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// diagnostic print routines + +bool stlplus::library_manager::print(std::ostream& str) const +{ + for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) + l->print(str); + return str; +} + +bool stlplus::library_manager::print_long(std::ostream& str) const +{ + for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) + l->print_long(str); + return str; +} + +// find a library by name + +std::list::iterator stlplus::library_manager::local_find(const std::string& name) +{ + for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + { + if (m_library_case) + { + if (i->name() == name) return i; + } + else + { + if (lowercase(i->name()) == lowercase(name)) return i; + } + } + return m_libraries.end(); +} + +std::list::const_iterator stlplus::library_manager::local_find(const std::string& name) const +{ + for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) + { + if (m_library_case) + { + if (i->name() == name) return i; + } + else + { + if (lowercase(i->name()) == lowercase(name)) return i; + } + } + return m_libraries.end(); +} + +std::ostream& stlplus::operator << (std::ostream& str, const stlplus::library_manager& manager) +{ + manager.print(str); + return str; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/subsystems/library_manager.hpp b/src/stlplus/subsystems/library_manager.hpp new file mode 100644 index 0000000..96e1afd --- /dev/null +++ b/src/stlplus/subsystems/library_manager.hpp @@ -0,0 +1,708 @@ +#ifndef STLPLUS_LIBRARY_MANAGER +#define STLPLUS_LIBRARY_MANAGER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generalised library manager. + +// Manages library units in a set of library directories. A unit is both a file +// on-disk and a data-structure in memory. To use the library manager, you need +// to: + +// - design a type based on lm_unit with serialising functions read/write +// - decide on a file extension for the type +// - decide on a description of the type +// - write a create callback for this type +// - register the file extension, description and callback with the library manager + +//////////////////////////////////////////////////////////////////////////////// +#include "subsystems_fixes.hpp" +#include "ini_manager.hpp" +#include "smart_ptr.hpp" +#include +#include +#include +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Internals + //////////////////////////////////////////////////////////////////////////////// + + class lm_library; + class library_manager; + + //////////////////////////////////////////////////////////////////////////////// + // unit names + //////////////////////////////////////////////////////////////////////////////// + + class lm_unit_name + { + public: + lm_unit_name(const std::string& name = std::string(), const std::string& type = std::string()); + ~lm_unit_name(void); + + const std::string& name(void) const; + void set_name(const std::string& name); + void lowercase(void); + + const std::string& type(void) const; + void set_type(const std::string& type); + + bool write(std::ostream& context) const; + bool read(std::istream& context); + + std::string to_string(void) const; + bool print(std::ostream&) const; + + private: + std::string m_name; + std::string m_type; + }; + + std::ostream& operator << (std::ostream&, const lm_unit_name&); + bool operator == (const lm_unit_name& l, const lm_unit_name& r); + bool operator < (const lm_unit_name& l, const lm_unit_name& r); + + //////////////////////////////////////////////////////////////////////////////// + // dependencies + //////////////////////////////////////////////////////////////////////////////// + + // dependencies on external files + + class lm_file_dependency + { + public: + lm_file_dependency(void); + lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line = 0, unsigned column = 0); + ~lm_file_dependency(void); + + // a path can be retrieved as either a relative path to the library or as a + // full path by providing the library path as an argument + const std::string& path(void) const; + std::string path_full(const std::string& library_path) const; + void set_path(const std::string& library_path, const std::string& path); + + unsigned line(void) const; + void set_line(unsigned line = 0); + + unsigned column(void) const; + void set_column(unsigned column = 0); + + bool write(std::ostream& context) const; + bool read(std::istream& context); + + bool print(std::ostream&) const; + + private: + std::string m_path; // file dependencies are stored as paths relative to the containing library + unsigned m_line; // line - starts at 1, 0 means no line/column information + unsigned m_column; // column - starts at 0 + }; + + std::ostream& operator <<(std::ostream&, const lm_file_dependency&); + + // dependencies on other units + + class lm_unit_dependency + { + public: + lm_unit_dependency(void); + lm_unit_dependency(const std::string& library, const lm_unit_name& name); + ~lm_unit_dependency(void); + + const std::string& library(void) const; + void set_library(const std::string& library); + + const lm_unit_name& unit_name(void) const; + void set_unit_name(const lm_unit_name& unit_name); + + const std::string& name(void) const; + void set_name(const std::string& name); + + const std::string& type(void) const; + void set_type(const std::string& type); + + bool write(std::ostream& context) const; + bool read(std::istream& context); + + bool print(std::ostream&) const; + + private: + std::string m_library; + lm_unit_name m_name; + }; + + std::ostream& operator<<(std::ostream&, const lm_unit_dependency&); + + // the set of all dependencies + + class lm_dependencies + { + public: + lm_dependencies(void); + lm_dependencies(const lm_dependencies&); + lm_dependencies& operator=(const lm_dependencies&); + ~lm_dependencies(void); + + // source file dependency + void set_source_file(const lm_file_dependency&); + bool source_file_present(void) const; + const lm_file_dependency& source_file(void) const; + + // other file dependencies + unsigned file_add(const lm_file_dependency& dependency); + unsigned file_size(void) const; + const lm_file_dependency& file_dependency(unsigned) const; + void file_erase(unsigned); + + // unit dependencies + unsigned unit_add(const lm_unit_dependency& dependency); + unsigned unit_size(void) const; + const lm_unit_dependency& unit_dependency(unsigned) const; + void unit_erase(unsigned); + + void clear(void); + bool empty(void) const; + + bool write(std::ostream& context) const; + bool read(std::istream& context); + + bool print(std::ostream&) const; + + private: + lm_file_dependency* m_source; // source file dependency (optional) + std::vector m_files; // other file dependencies + std::vector m_units; // unit dependencies + }; + + std::ostream& operator << (std::ostream&, const lm_dependencies&); + + //////////////////////////////////////////////////////////////////////////////// + // library unit superclass + // user's units must be derivatives of lm_unit and overload all the virtuals + + class lm_unit + { + friend class lm_library; + public: + //////////////////////////////////////// + // constructor/destructor + + lm_unit(const lm_unit_name& name, lm_library* library); + virtual ~lm_unit(void); + + //////////////////////////////////////// + // Header data + + // unit name + const lm_unit_name& unit_name(void) const; + const std::string& name(void) const; + const std::string& type(void) const; + + // dependencies + // all file dependencies are converted for internal use to a path relative to the library + // they can be retrieved either in this form or as a full path + + // source file dependency + void set_source_file(const lm_file_dependency&); + bool source_file_present(void) const; + const lm_file_dependency& source_file(void) const; + + // other file dependencies + unsigned file_add(const lm_file_dependency& dependency); + unsigned file_size(void) const; + const lm_file_dependency& file_dependency(unsigned) const; + void file_erase(unsigned); + + // unit dependencies + unsigned unit_add(const lm_unit_dependency& dependency); + unsigned unit_size(void) const; + const lm_unit_dependency& unit_dependency(unsigned) const; + void unit_erase(unsigned); + + const lm_dependencies& dependencies(void) const; + void set_dependencies(const lm_dependencies&); + void clear_dependencies(void); + bool empty_dependencies(void) const; + + // dependency checking + + bool out_of_date(void) const; + bool up_to_date(void) const; + lm_dependencies out_of_date_reason(void) const; + + // supplementary data + + const std::string& supplementary_data(void) const; + void set_supplementary_data(const std::string& data); + + //////////////////////////////////////// + // unit data management + + bool load(void); + bool save(void); + bool loaded(void) const; + void mark(void); + + // file modified time - only changes after a save + + time_t modified(void) const; + + //////////////////////////////////////// + // containing library manager details + + // get the owning library + const lm_library* library(void) const; + lm_library* library(void); + + // owning library name and path + const std::string& library_name(void) const; + const std::string& library_path(void) const; + + //////////////////////////////////////// + // error handling - these apply to the last read/write operation + + bool error(void) const; + + //////////////////////////////////////// + // functions that customise subclasses of this superclass + // You MUST provide at least: + // - read - either read operation can be overloaded + // - write - either write operation can be overloaded + // - clone + + // read(filename) is the one actually called to read your data + // the default read(filename) simply calls read(istream) to actually read the file + // the default read(istream) does nothing but fail by returning false so you must overload one or other + + // you can just overload read(istream) if you want to use IOstream, or you + // can overload read(filename) to use any I/O system + + virtual bool read(const std::string& filename, void* type_data); + virtual bool read(std::istream& file, void* type_data); + + // as above, but for writing the data type + + // write(filename) is the one actually called to write your data + // the default write(filename) simply calls write(ostream) to actually write the file + // the default write(ostream) does nothing but fail by returning false so you must overload one or other + + // you can just overload write(ostream) if you want to use IOstream, or you + // can overload write(filename) to use any I/O system + + virtual bool write(const std::string& filename, void* type_data); + virtual bool write(std::ostream& file, void* type_data); + + // purge clears any memory associated with the unit - makes the unit unloaded + // the default does nothing + virtual bool purge(void); + + // the clone function creates a new-ed copy of the subclass + virtual lm_unit* clone(void) const; + + // the default print routines print header-only information + // you can overload these to provide a debug printout of the data structure + virtual bool print(std::ostream&) const; + virtual bool print_long(std::ostream&) const; + + protected: + // header file management + std::string filename(void) const; + std::string header_filename(void) const; + bool write_header(void); + bool read_header(void); + + private: + // header fields + lm_unit_name m_name; // name + lm_dependencies m_dependencies; // file and unit dependencies + std::string m_supplement; // supplementary data + bool m_header_modified; // header modified + + // internal fields + bool m_loaded; // loaded flag + bool m_marked; // mark - determines whether the unit needs saving + + // library manager fields + lm_library* m_library; // parent library + + // error handling fields + bool m_error; // error flag if load or save fails - from IOstream + }; + + // Iostream print calls the short print method + std::ostream& operator << (std::ostream& str, const lm_unit& u); + + //////////////////////////////////////////////////////////////////////////////// + // other types used in the library manager + //////////////////////////////////////////////////////////////////////////////// + + // user types + + typedef smart_ptr_nocopy lm_unit_ptr; + typedef lm_unit* (*lm_create_callback)(const lm_unit_name& unit_name, lm_library* parent_library, void* type_data); + + // internal types used in the library manager but made global because they are shared + + struct lm_callback_entry + { + lm_create_callback m_callback; + std::string m_description; + void* m_type_data; + + lm_callback_entry(lm_create_callback callback = 0, const std::string& description = std::string(), void* type_data = 0) : + m_callback(callback), m_description(description), m_type_data(type_data) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + // Library + // Must be contained in a library_manager + // Manages objects of class lm_unit and its subclasses + //////////////////////////////////////////////////////////////////////////////// + + class lm_library + { + public: + friend class library_manager; + friend class lm_unit; + + ////////////////////////////////////////////////////////////////////////////// + // constructors/destructor - lm_library should only ever be constructed by library_manager + + lm_library(library_manager* manager); + lm_library(const lm_library&); + lm_library& operator = (const lm_library&); + ~lm_library(void); + + public: + + const library_manager* manager(void) const; + library_manager* manager(void); + + ////////////////////////////////////////////////////////////////////////////// + // initialisers + + bool create(const std::string& name, const std::string& path, bool writable); + bool open(const std::string& path); + + ////////////////////////////////////////////////////////////////////////////// + // management of types + + bool load_type(const std::string& type); + bool load_types(void); + bool remove_type(const std::string& type); + + ////////////////////////////////////////////////////////////////////////////// + // whole library operations + + bool load(void); + bool save(void); + bool purge(void); + bool close(void); + bool erase(void); + + const std::string& name(void) const; + const std::string& path(void) const; + + ////////////////////////////////////////////////////////////////////////////// + // managing read/write status + + bool set_read_write(bool writable); + bool set_writable(void); + bool set_read_only(void); + bool writable(void) const; + bool read_only(void) const; + bool os_writable(void) const; + bool os_read_only(void) const; + bool lm_writable(void) const; + bool lm_read_only(void) const; + + ////////////////////////////////////////////////////////////////////////////// + // unit management + + bool exists(const lm_unit_name& name) const; + lm_unit_ptr create(const lm_unit_name&); + bool loaded(const lm_unit_name& name) const; + bool load(const lm_unit_name& unit); + bool purge(const lm_unit_name& unit); + bool save(const lm_unit_name& unit); + bool erase(const lm_unit_name& name); + bool mark(const lm_unit_name& name); + time_t modified(const lm_unit_name& name) const; + + bool erase_by_source(const std::string& source_file); + + const lm_unit_ptr find(const lm_unit_name& name) const; + lm_unit_ptr find(const lm_unit_name& name); + + std::vector names(void) const; + std::vector names(const std::string& type) const; + std::vector handles(void) const; + std::vector handles(const std::string& type) const; + + ////////////////////////////////////////////////////////////////////////////// + // dependency checking + + bool out_of_date(const lm_unit_name& name) const; + bool up_to_date(const lm_unit_name& name) const; + lm_dependencies out_of_date_reason(const lm_unit_name& name) const; + + std::pair tidy(void); + + ////////////////////////////////////////////////////////////////////////////// + // do-everything print function + + bool pretty_print(std::ostream& str, + bool print_units = false, // print the unit names not just the library names + const std::string& type = std::string()) const; // print just this type ("" means all) + + //////////////////////////////////////////////////////////////////////////////// + // diagnostic print routines + + bool print(std::ostream& str) const; + bool print_long(std::ostream& str) const; + + private: + + std::map::iterator local_find(const lm_unit_name& name); + std::map::const_iterator local_find(const lm_unit_name& name) const; + + std::string m_name; // name + std::string m_path; // path + bool m_writable; // writable + std::map m_units; // units + library_manager* m_manager; // parent library manager + }; + + std::ostream& operator << (std::ostream& str, const lm_library& lib); + + //////////////////////////////////////////////////////////////////////////////// + // Library Manager + //////////////////////////////////////////////////////////////////////////////// + + class library_manager + { + public: + friend class lm_library; + friend class lm_unit; + + //////////////////////////////////////////////////////////////////////////////// + // static functions allow you to test whether a directory is a library before opening it + // you can also find the library's name without opening it + + static bool is_library(const std::string& path, const std::string& owner); + static std::string library_name(const std::string& path, const std::string& owner); + + // non-static forms test for libraries with the same owner as the library manager + + bool is_library(const std::string& path); + std::string library_name(const std::string& path); + + ////////////////////////////////////////////////////////////////////////////// + // tructors + + explicit library_manager(const std::string& owner, bool library_case = false, bool unit_case = false); + ~library_manager(void); + + ////////////////////////////////////////////////////////////////////////////// + // case sensitivity + + bool library_case(void) const; + void set_library_case(bool library_case); + + bool unit_case(void) const; + void set_unit_case(bool library_case); + + ////////////////////////////////////////////////////////////////////////////// + // type handling + // only units of types added in this way will be recognised + + bool add_type(const std::string& type, + const std::string& description, + lm_create_callback fn = 0, + void* type_data = 0); + bool remove_type(const std::string& type); + std::vector types(void) const; + + std::string description(const std::string& type) const; + lm_create_callback callback(const std::string& type) const; + void* type_data(const std::string& type) const; + + ////////////////////////////////////////////////////////////////////////////// + // Library mappings + // The library manager implements two different styles of library mappings + // - mapping file + // - ini file + // mapping file handling uses a simple text file to store the mappings in an internally-defined format + // ini file handling stores library mappings using the ini_manager component + // These modes are switched on by simply specifying a mapping file or an ini file to hold the mappings + + // mapping file methods + + // set but do not load - use this when you want to create a new mapping file + void set_mapping_file(const std::string& mapping_file); + // set and load - use this with an existing mapping file + bool load_mappings (const std::string& mapping_file); + // return the mapping file string + std::string mapping_file(); + + // ini file methods - the ini manager must be pre-loaded with the list of ini files to manage + + // set and load - this will create the relevant sections in the local ini file if not present already + bool set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_section); + ini_manager* get_ini_manager(void) const; + + // save to the library mapping handler, whichever kind it is + bool save_mappings (void); + + ////////////////////////////////////////////////////////////////////////////// + // library management + + // operations on a single library + // test whether a named library exists + bool exists(const std::string& name) const; + // create a new libarry in the specified directory + lm_library* create(const std::string& name, const std::string& path, bool writable = true); + // open an existing library + lm_library* open(const std::string& path); + // load all units in the library + bool load(const std::string& name); + // save all marked units in the library + bool save(const std::string& name); + // purge all loaded units in the library + bool purge(const std::string& name); + // close the library - remove it from the manager but leave on disk + bool close(const std::string& name); + // erase the library - delete the directory and remove the library from the manager + bool erase(const std::string& name); + + // operations on all libraries - as above but applied to all the libraries in the manager + bool load(void); + bool save(void); + bool purge(void); + bool close(void); + bool erase(void); + + // get name and path of a library - name can differ in case if the library manager is case-insensitive + std::string name(const std::string& library) const; + std::string path(const std::string& library) const; + + // control and test read/write status + bool set_writable(const std::string& library); + bool set_read_only(const std::string& library); + bool writable(const std::string& library) const; + bool read_only(const std::string& library) const; + bool os_writable(const std::string& library) const; + bool os_read_only(const std::string& library) const; + bool lm_writable(const std::string& library) const; + bool lm_read_only(const std::string& library) const; + + // find a library in the manager - returns null if not found + lm_library* find(const std::string& name); + const lm_library* find(const std::string& name) const; + + // get the set of all library names + std::vector names(void) const; + // get the set of all libraries + std::vector handles(void) const; + std::vector handles(void); + + ////////////////////////////////////////////////////////////////////////////// + // current library management + + bool setwork(const std::string& library); + bool unsetwork(void); + const lm_library* work(void) const; + lm_library* work(void); + std::string work_name(void) const; + + ////////////////////////////////////////////////////////////////////////////// + // unit management within a library + // Note: you can also manipulate the library class through a handle returned by find() or handles() + + bool exists(const std::string& library, const lm_unit_name& name) const; + lm_unit_ptr create(const std::string& library, const lm_unit_name& name); + bool loaded(const std::string& library, const lm_unit_name& name) const; + bool load(const std::string& library, const lm_unit_name& name); + bool purge(const std::string& library, const lm_unit_name& name); + bool save(const std::string& library, const lm_unit_name& name); + bool erase(const std::string& library, const lm_unit_name& name); + bool mark(const std::string& library, const lm_unit_name& name); + time_t modified(const std::string& library, const lm_unit_name& name) const; + + bool erase_by_source(const std::string& source_file); + + const lm_unit_ptr find(const std::string& library, const lm_unit_name& name) const; + lm_unit_ptr find(const std::string& library, const lm_unit_name& name); + + std::vector names(const std::string& library) const; + std::vector names(const std::string& library, const std::string& type) const; + std::vector handles(const std::string& library) const; + std::vector handles(const std::string& library, const std::string& type) const; + + ////////////////////////////////////////////////////////////////////////////// + // dependency checking + + bool out_of_date(const std::string& library, const lm_unit_name& name) const; + bool up_to_date(const std::string& library, const lm_unit_name& name) const; + lm_dependencies out_of_date_reason(const std::string& library, const lm_unit_name& name) const; + + // delete out of date units from a library or all libraries + // return the number of units tidied and a flag to say whether all units were successfully tidied + std::pair tidy(const std::string& library); + std::pair tidy(void); + + ////////////////////////////////////////////////////////////////////////////// + // do-everything print routine! + + bool pretty_print(std::ostream& str, + bool print_units = false, // print the unit names not just the library names + const std::string& library = std::string(), // print just the specified library ("" means all) + const std::string& type = std::string()) const; // print just this type ("" means all) + + //////////////////////////////////////////////////////////////////////////////// + // diagnostic print routines + + bool print(std::ostream& str) const; + bool print_long(std::ostream& str) const; + + ////////////////////////////////////////////////////////////////////////////// + // internals + + private: + // NOT a copyable object + library_manager(const library_manager&); + library_manager& operator = (const library_manager&); + + protected: + std::list::iterator local_find(const std::string& name); + std::list::const_iterator local_find(const std::string& name) const; + + std::string m_owner; // owner application name + std::string m_mapping_file; // mapping file method of library management + ini_manager* m_ini_files; // ini manager method of library management + std::string m_ini_section; // ini manager method of library management + std::string m_ini_work; // ini manager method of library management + std::list m_libraries; // libraries + std::string m_work; // work library + std::map m_callbacks; // callbacks + bool m_library_case; // case sensitivity for library names + bool m_unit_case; // case sensitivity for unit names + }; + + std::ostream& operator << (std::ostream& str, const library_manager& libraries); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/subsystems/message_handler.cpp b/src/stlplus/subsystems/message_handler.cpp new file mode 100644 index 0000000..fd1d2e8 --- /dev/null +++ b/src/stlplus/subsystems/message_handler.cpp @@ -0,0 +1,2014 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "message_handler.hpp" +#include "dprintf.hpp" +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Utilities + + static std::ostream& operator<< (std::ostream& device, const std::vector& data) + { + for (unsigned i = 0; i < data.size(); i++) + device << data[i] << std::endl; + device.flush(); + return device; + } + + //////////////////////////////////////////////////////////////////////////////// + + static const char* information_id = "INFORMATION"; + static const char* context_id = "CONTEXT"; + static const char* supplement_id = "SUPPLEMENT"; + static const char* warning_id = "WARNING"; + static const char* error_id = "ERROR"; + static const char* fatal_id = "FATAL"; + static const char* position_id = "POSITION"; + + static const char* default_information_format = "@0"; + static const char* default_context_format = "context: @0"; + static const char* default_supplement_format = "supplement: @0"; + static const char* default_warning_format = "warning: @0"; + static const char* default_error_format = "error: @0"; + static const char* default_fatal_format = "FATAL: @0"; + static const char* default_position_format = "\"@1\" (@2,@3) : @0"; + + //////////////////////////////////////////////////////////////////////////////// + // position class + + message_position::message_position(void) : + m_filename(std::string()), m_line(0), m_column(0) + { + } + + message_position::message_position(const std::string& filename, unsigned line, unsigned column) : + m_filename(filename), m_line(line), m_column(column) + { + } + + message_position::~message_position(void) + { + } + + const std::string& message_position::filename(void) const + { + return m_filename; + } + + unsigned message_position::line(void) const + { + return m_line; + } + + unsigned message_position::column(void) const + { + return m_column; + } + + message_position message_position::operator + (unsigned offset) const + { + message_position result(*this); + result += offset; + return result; + } + + message_position& message_position::operator += (unsigned offset) + { + m_column += offset; + return *this; + } + + bool message_position::empty(void) const + { + return m_filename.empty(); + } + + bool message_position::valid(void) const + { + return !empty(); + } + + std::vector message_position::show(void) const + { + std::vector result; + if (valid()) + { + // skip row-1 lines of the file and print out the resultant line + std::ifstream source(filename().c_str()); + std::string current_line; + for (unsigned i = 0; i < line(); i++) + { + if (!source.good()) + return result; + std::getline(source,current_line); + } + result.push_back(current_line); + // now put an up-arrow at the appropriate column + // preserve any tabs in the original line + result.push_back(std::string()); + for (unsigned j = 0; j < column(); j++) + { + if (j < current_line.size() && current_line[j] == '\t') + result.back() += '\t'; + else + result.back() += ' '; + } + result.back() += '^'; + } + return result; + } + + std::string to_string(const message_position& where) + { + return dformat("{%s:%u:%u}", where.filename().c_str(), where.line(), where.column()); + } + + //////////////////////////////////////////////////////////////////////////////// + // context subclass + + class message_context_body + { + private: + unsigned m_depth; + message_handler_base* m_base; + + public: + message_context_body(message_handler_base& handler) : + m_depth(0), m_base(0) + { + set(handler); + } + + ~message_context_body(void) + { + pop(); + } + + void set(message_handler_base& handler) + { + m_base = &handler; + m_depth = m_base->context_depth(); + } + + void pop(void) + { + if (m_base) + m_base->pop_context(m_depth); + } + private: + message_context_body(const message_context_body&); + message_context_body& operator=(const message_context_body&); + }; + + // exported context class + + message_context::message_context(message_handler_base& handler) : m_body(new message_context_body(handler)) + { + } + + void message_context::set(message_handler_base& handler) + { + m_body->set(handler); + } + + void message_context::pop(void) + { + m_body->pop(); + } + + //////////////////////////////////////////////////////////////////////////////// + // exceptions + + // read_error + + message_handler_read_error::message_handler_read_error(const message_position& position, const std::string& reason) : + std::runtime_error(to_string(position) + std::string(": Message handler read error - ") + reason), + m_position(position) + { + } + + message_handler_read_error::~message_handler_read_error(void) throw() + { + } + + const message_position& message_handler_read_error::where(void) const + { + return m_position; + } + + // format error + + message_handler_format_error::message_handler_format_error(const std::string& format, unsigned offset) : + std::runtime_error(std::string("Message handler formatting error in \"") + format + std::string("\"")), + m_position("",0,0), m_format(format), m_offset(offset) + { + } + + message_handler_format_error::message_handler_format_error(const message_position& pos, const std::string& format, unsigned offset) : + std::runtime_error(to_string(pos) + std::string(": Message handler formatting error in \"") + format + std::string("\"")), + m_position(pos), m_format(format), m_offset(offset) + { + } + + message_handler_format_error::~message_handler_format_error(void) throw() + { + } + + const message_position& message_handler_format_error::where(void) const + { + return m_position; + } + + const std::string& message_handler_format_error::format(void) const + { + return m_format; + } + + unsigned message_handler_format_error::offset(void) const + { + return m_offset; + } + + // id_error + + message_handler_id_error::message_handler_id_error(const std::string& id) : + std::runtime_error(std::string("Message handler message ID not found: ") + id), m_id(id) + { + } + + message_handler_id_error::~message_handler_id_error(void) throw() + { + } + + const std::string& message_handler_id_error::id(void) const + { + return m_id; + } + + // limit_error + + message_handler_limit_error::message_handler_limit_error(unsigned limit) : + std::runtime_error(std::string("Message handler limit error: ") + dformat("%u",limit) + std::string(" reached")), + m_limit(limit) + { + } + + message_handler_limit_error::~message_handler_limit_error(void) throw() + { + } + + unsigned message_handler_limit_error::limit(void) const + { + return m_limit; + } + + // fatal_error + + message_handler_fatal_error::message_handler_fatal_error(const std::string& id) : + std::runtime_error(std::string("Message Handler Fatal error: ") + id), + m_id(id) + { + } + + message_handler_fatal_error::~message_handler_fatal_error(void) throw() + { + } + + const std::string& message_handler_fatal_error::id(void) const + { + return m_id; + } + + //////////////////////////////////////////////////////////////////////////////// + + class message + { + private: + unsigned m_index; // index into message files + unsigned m_line; // line + unsigned m_column; // column + std::string m_text; // text + + public: + message(unsigned index = (unsigned)-1, + unsigned line = 0, + unsigned column = 0, + const std::string& text = "") : + m_index(index),m_line(line),m_column(column),m_text(text) + { + } + message(const std::string& text) : + m_index((unsigned)-1),m_line(0),m_column(0),m_text(text) + { + } + + ~message(void) + { + } + + unsigned index(void) const + { + return m_index; + } + + unsigned line(void) const + { + return m_line; + } + + unsigned column(void) const + { + return m_column; + } + + const std::string& text(void) const + { + return m_text; + } + }; + + //////////////////////////////////////////////////////////////////////////////// + + class pending_message + { + private: + message_position m_position; + std::string m_id; + std::vector m_args; + public: + pending_message(const message_position& position, const std::string& id, const std::vector& args) : + m_position(position), m_id(id), m_args(args) {} + pending_message(const std::string& id, const std::vector& args) : + m_position(message_position()), m_id(id), m_args(args) {} + ~pending_message(void) {} + + const message_position& position(void) const {return m_position;} + const std::string& id(void) const {return m_id;} + const std::vector& args(void) const {return m_args;} + }; + + //////////////////////////////////////////////////////////////////////////////// + + class message_handler_base_body + { + public: + std::vector m_files; // message files in the order they were added + std::map m_messages; // messages stored as id:message pairs + bool m_show; // show source + std::list m_context; // context message stack + std::list m_supplement; // supplementary message stack + + public: + message_handler_base_body(void) : + m_show(false) + { + // preload with default formats + add_message(information_id,default_information_format); + add_message(warning_id,default_warning_format); + add_message(error_id,default_error_format); + add_message(fatal_id,default_fatal_format); + add_message(position_id,default_position_format); + add_message(context_id,default_context_format); + add_message(supplement_id,default_supplement_format); + } + + ~message_handler_base_body(void) + { + } + + void set_show(bool show) + { + m_show = show; + } + + void add_message_file(const std::string& file) + throw(message_handler_read_error) + { + m_files.push_back(file); + std::ifstream input(file.c_str()); + if (!input.good()) + throw message_handler_read_error(message_position(file,0,0), std::string("file not found: ") + file); + std::string line; + unsigned l = 0; + while(input.good()) + { + std::getline(input,line); + l++; + if (line.size() > 0 && isalpha(line[0])) + { + // Get the id field which starts with an alphabetic and contains alphanumerics and underscore + std::string id; + unsigned i = 0; + for (; i < line.size(); i++) + { + if (isalnum(line[i]) || line[i] == '_') + id += line[i]; + else + break; + } + // check this ID is unique within the files - however it is legal to override a hard-coded message + std::map::iterator found = m_messages.find(id); + if (found != m_messages.end() && found->second.index() != (unsigned)-1) + throw message_handler_read_error(message_position(file,l,i), std::string("repeated ID ") + id); + // skip whitespace + for (; i < line.size(); i++) + { + if (!isspace(line[i])) + break; + } + // now get the text part and add the message to the message map + std::string text = line.substr(i, line.size()-i); + m_messages[id] = message(m_files.size()-1, l, i, text); + } + } + } + + void add_message(const std::string& id, const std::string& text) + throw() + { + m_messages[id] = message((unsigned)-1, 0, 0, text); + } + + bool message_present(const std::string& id) const + throw() + { + return m_messages.find(id) != m_messages.end(); + } + + void push_supplement(const message_position& position, + const std::string& message_id, + const std::vector& args) + { + m_supplement.push_back(pending_message(position,message_id,args)); + } + + void push_context(const message_position& position, + const std::string& message_id, + const std::vector& args) + { + m_context.push_back(pending_message(position,message_id,args)); + } + + void pop_context(unsigned depth) + { + while (depth < m_context.size()) + m_context.pop_back(); + } + + unsigned context_depth(void) const + { + return m_context.size(); + } + + std::vector format_report(const message_position& position, + const std::string& message_id, + const std::string& status_id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + // gathers everything together into a full multi-line report + std::vector result; + // the first part of the report is the status message (info/warning/error/fatal) + result.push_back(format_id(position, message_id, status_id, args)); + // now append any supplemental messages that have been stacked + // these are printed in FIFO order i.e. the order that they were added to the handler + for (std::list::iterator j = m_supplement.begin(); j != m_supplement.end(); j++) + result.push_back(format_id(j->position(),j->id(),supplement_id,j->args())); + // now discard any supplementary messages because they only persist until they are printed once + m_supplement.clear(); + // now append any context messages that have been stacked + // these are printed in LIFO order i.e. closest context first + for (std::list::reverse_iterator i = m_context.rbegin(); i != m_context.rend(); i++) + result.push_back(format_id(i->position(),i->id(),context_id,i->args())); + return result; + } + + std::string format_id(const message_position& position, + const std::string& message_id, + const std::string& status_id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + // This function creates a fully-formatted single-line message from a + // combination of the position format and the status format plus the message + // ID and its arguments. There are up to three levels of substitution + // required to do this. + + // get the status format from the status_id + std::map::iterator status_found = m_messages.find(status_id); + if (status_found == m_messages.end()) throw message_handler_id_error(status_id); + + // similarly get the message format + std::map::iterator message_found = m_messages.find(message_id); + if (message_found == m_messages.end()) throw message_handler_id_error(message_id); + + // format the message contents + std::string message_text = format_message(message_found->second, args); + + // now format the message in the status string (informational, warning etc). + std::vector status_args; + status_args.push_back(message_text); + std::string result = format_message(status_found->second, status_args); + + // finally, if the message is positional, format the status message in the positional string + if (position.valid()) + { + // get the position format from the message set + std::map::iterator position_found = m_messages.find(position_id); + if (position_found == m_messages.end()) throw message_handler_id_error(position_id); + + // now format the message + std::vector position_args; + position_args.push_back(result); + position_args.push_back(position.filename()); + position_args.push_back(dformat("%u",position.line())); + position_args.push_back(dformat("%u",position.column())); + result = format_message(position_found->second, position_args); + // add the source file text and position if that option is on + + if (m_show) + { + std::vector show = position.show(); + for (unsigned i = 0; i < show.size(); i++) + { + result += std::string("\n"); + result += show[i]; + } + } + } + return result; + } + + std::string format_message(const message& mess, + const std::vector& args) + throw(message_handler_format_error) + { + // this function creates a formatted string from the stored message text and + // the arguments. Most of the work is done in format_string. However, if a + // formatting error is found, this function uses extra information stored in + // the message data structure to improve the reporting of the error + try + { + // This is the basic string formatter which performs parameter substitution + // into a message. Parameter placeholders are in the form @0, @1 etc, where + // the number is the index of the argument in the args vector. At present, + // no field codes are supported as in printf formats Algorithm: scan the + // input string and transfer it into the result. When an @nnn appears, + // substitute the relevant argument from the args vector. Throw an exception + // if its out of range. Also convert C-style escaped characters of the form + // \n. + std::string format = mess.text(); + std::string result; + for (unsigned i = 0; i < format.size(); ) + { + if (format[i] == '@') + { + // an argument substitution has been found - now find the argument number + if (i+1 == format.size()) throw message_handler_format_error(format, i); + i++; + // check for @@ which is an escaped form of '@' + if (format[i] == '@') + { + result += '@'; + i++; + } + else + { + // there must be at least one digit in the substitution number + if (!isdigit(format[i])) throw message_handler_format_error(format,i); + unsigned a = 0; + for (; i < format.size() && isdigit(format[i]); i++) + { + a *= 10; + a += (format[i] - '0'); + } + // check for an argument request out of the range of the set of arguments + if (a >= args.size()) throw message_handler_format_error(format,i-1); + result += args[a]; + } + } + else if (format[i] == '\\') + { + // an escaped character has been found + if (i+1 == format.size()) throw message_handler_format_error(format, i); + i++; + // do the special ones first, then all the others just strip off the \ and leave the following characters + switch(format[i]) + { + case '\\': + result += '\\'; + break; + case 't': + result += '\t'; + break; + case 'n': + result += '\n'; + break; + case 'r': + result += '\r'; + break; + case 'a': + result += '\a'; + break; + default: + result += format[i]; + break; + } + i++; + } + else + { + // plain text found - just append to the result + result += format[i++]; + } + } + return result; + } + catch(message_handler_format_error& exception) + { + // if the message came from a message file, improve the error reporting by adding file positional information + // Also adjust the position from the start of the text (stored in the message field) to the column of the error + if (mess.index() != (unsigned)-1) + throw message_handler_format_error( + message_position(m_files[mess.index()], mess.line(), mess.column()+exception.offset()), + exception.format(), + exception.offset()); + else + throw exception; + } + } + + private: + message_handler_base_body(message_handler_base_body&); + message_handler_base_body& operator=(message_handler_base_body&); + }; + + //////////////////////////////////////////////////////////////////////////////// + + message_handler_base::message_handler_base(bool show) + throw() : + m_base_body(new message_handler_base_body) + { + m_base_body->set_show(show); + } + + message_handler_base::message_handler_base(const std::string& file, bool show) + throw(message_handler_read_error) : + m_base_body(new message_handler_base_body) + { + m_base_body->set_show(show); + add_message_file(file); + } + + message_handler_base::message_handler_base(const std::vector& files, bool show) + throw(message_handler_read_error) : + m_base_body(new message_handler_base_body) + { + m_base_body->set_show(show); + add_message_files(files); + } + + message_handler_base::~message_handler_base(void) + throw() + { + } + + //////////////////////////////////////////////////////////////////////////////// + + void message_handler_base::add_message_file(const std::string& file) + throw(message_handler_read_error) + { + m_base_body->add_message_file(file); + } + + void message_handler_base::add_message_files(const std::vector& files) + throw(message_handler_read_error) + { + for (unsigned i = 0; i < files.size(); i++) + add_message_file(files[i]); + } + + void message_handler_base::add_message(const std::string& id, const std::string& text) + throw() + { + m_base_body->add_message(id,text); + } + + bool message_handler_base::message_present(const std::string& id) const + throw() + { + return m_base_body->message_present(id); + } + + //////////////////////////////////////////////////////////////////////////////// + + void message_handler_base::set_information_format(const std::string& format) throw() + { + add_message(information_id, format); + } + + void message_handler_base::set_warning_format(const std::string& format) throw() + { + add_message(warning_id, format); + } + + void message_handler_base::set_error_format(const std::string& format) throw() + { + add_message(error_id, format); + } + + void message_handler_base::set_fatal_format(const std::string& format) throw() + { + add_message(fatal_id, format); + } + + void message_handler_base::set_position_format(const std::string& format) throw() + { + add_message(position_id, format); + } + + void message_handler_base::set_context_format(const std::string& format) throw() + { + add_message(context_id, format); + } + + void message_handler_base::set_supplement_format(const std::string& format) throw() + { + add_message(supplement_id, format); + } + + //////////////////////////////////////////////////////////////////////////////// + + void message_handler_base::show_position(void) + throw() + { + m_base_body->set_show(true); + } + + void message_handler_base::hide_position(void) + throw() + { + m_base_body->set_show(false); + } + + //////////////////////////////////////////////////////////////////////////////// + // information messages + + std::vector message_handler_base::information_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return information_message(message_position(), id, args); + } + + std::vector message_handler_base::information_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return information_message(id, args); + } + + std::vector message_handler_base::information_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return information_message(id, args); + } + + std::vector message_handler_base::information_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return information_message(id, args); + } + + std::vector message_handler_base::information_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return information_message(id, args); + } + + std::vector message_handler_base::information_message(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return m_base_body->format_report(position, id, information_id, args); + } + + std::vector message_handler_base::information_message(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return information_message(position, id, args); + } + + std::vector message_handler_base::information_message(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return information_message(position, id, args); + } + + std::vector message_handler_base::information_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return information_message(position, id, args); + } + + std::vector message_handler_base::information_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return information_message(position, id, args); + } + + //////////////////////////////////////////////////////////////////////////////// + // warning messages + + std::vector message_handler_base::warning_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return warning_message(message_position(), id, args); + } + + std::vector message_handler_base::warning_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return warning_message(id, args); + } + + std::vector message_handler_base::warning_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return warning_message(id, args); + } + + std::vector message_handler_base::warning_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return warning_message(id, args); + } + + std::vector message_handler_base::warning_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return warning_message(id, args); + } + + std::vector message_handler_base::warning_message(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return m_base_body->format_report(position, id, warning_id, args); + } + + std::vector message_handler_base::warning_message(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return warning_message(position, id, args); + } + + std::vector message_handler_base::warning_message(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return warning_message(position, id, args); + } + + std::vector message_handler_base::warning_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return warning_message(position, id, args); + } + + std::vector message_handler_base::warning_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return warning_message(position, id, args); + } + + //////////////////////////////////////////////////////////////////////////////// + // error messages + + std::vector message_handler_base::error_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return error_message(message_position(), id, args); + } + + std::vector message_handler_base::error_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return error_message(id, args); + } + + std::vector message_handler_base::error_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return error_message(id, args); + } + + std::vector message_handler_base::error_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return error_message(id, args); + } + + std::vector message_handler_base::error_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return error_message(id, args); + } + + std::vector message_handler_base::error_message(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return m_base_body->format_report(position, id, error_id, args); + } + + std::vector message_handler_base::error_message(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return error_message(position, id, args); + } + + std::vector message_handler_base::error_message(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return error_message(position, id, args); + } + + std::vector message_handler_base::error_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return error_message(position, id, args); + } + + std::vector message_handler_base::error_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return error_message(position, id, args); + } + + //////////////////////////////////////////////////////////////////////////////// + // fatal messages + + std::vector message_handler_base::fatal_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return fatal_message(message_position(), id, args); + } + + std::vector message_handler_base::fatal_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return fatal_message(id, args); + } + + std::vector message_handler_base::fatal_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return fatal_message(id, args); + } + + std::vector message_handler_base::fatal_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return fatal_message(id, args); + } + + std::vector message_handler_base::fatal_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return fatal_message(id, args); + } + + std::vector message_handler_base::fatal_message(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return m_base_body->format_report(position, id, fatal_id, args); + } + + std::vector message_handler_base::fatal_message(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return fatal_message(position, id, args); + } + + std::vector message_handler_base::fatal_message(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return fatal_message(position, id, args); + } + + std::vector message_handler_base::fatal_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return fatal_message(position, id, args); + } + + std::vector message_handler_base::fatal_message(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return fatal_message(position, id, args); + } + + //////////////////////////////////////////////////////////////////////////////// + // supplemental messages + + void message_handler_base::push_supplement(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + push_supplement(message_position(), id, args); + } + + void message_handler_base::push_supplement(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + push_supplement(id, args); + } + + void message_handler_base::push_supplement(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + push_supplement(id, args); + } + + void message_handler_base::push_supplement(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + push_supplement(id, args); + } + + void message_handler_base::push_supplement(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + push_supplement(id, args); + } + + void message_handler_base::push_supplement(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + m_base_body->push_supplement(position, id, args); + } + + void message_handler_base::push_supplement(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + push_supplement(position, id, args); + } + + void message_handler_base::push_supplement(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + push_supplement(position, id, args); + } + + void message_handler_base::push_supplement(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + push_supplement(position, id, args); + } + + void message_handler_base::push_supplement(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + push_supplement(position, id, args); + } + + //////////////////////////////////////////////////////////////////////////////// + // context + + void message_handler_base::push_context (const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + push_context(message_position(), id, args); + } + + void message_handler_base::push_context (const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + push_context(id, args); + } + + void message_handler_base::push_context (const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + push_context(id, args); + } + + void message_handler_base::push_context (const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + push_context(id, args); + } + + void message_handler_base::push_context (const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + push_context(id, args); + } + + void message_handler_base::push_context (const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + m_base_body->push_context(position, id, args); + } + + void message_handler_base::push_context (const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + push_context(position, id, args); + } + + void message_handler_base::push_context (const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + push_context(position, id, args); + } + + void message_handler_base::push_context (const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + push_context(position, id, args); + } + + void message_handler_base::push_context (const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + push_context(position, id, args); + } + + void message_handler_base::pop_context(void) throw() + { + m_base_body->pop_context(m_base_body->context_depth()-1); + } + + void message_handler_base::pop_context(unsigned depth) throw() + { + m_base_body->pop_context(depth); + } + + unsigned message_handler_base::context_depth(void) const + throw() + { + return m_base_body->context_depth(); + } + + message_context message_handler_base::auto_push_context(const std::string& id, const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + return auto_push_context(message_position(), id, args); + } + + message_context message_handler_base::auto_push_context(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return auto_push_context(id, args); + } + + message_context message_handler_base::auto_push_context(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return auto_push_context(id, args); + } + + message_context message_handler_base::auto_push_context (const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return auto_push_context(id, args); + } + + message_context message_handler_base::auto_push_context(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return auto_push_context(id, args); + } + + message_context message_handler_base::auto_push_context(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + message_context result(*this); + m_base_body->push_context(position, id, args); + return result; + } + + message_context message_handler_base::auto_push_context(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + return auto_push_context(position, id, args); + } + + message_context message_handler_base::auto_push_context(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + return auto_push_context(position, id, args); + } + + message_context message_handler_base::auto_push_context(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + return auto_push_context(position, id, args); + } + + message_context message_handler_base::auto_push_context(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + std::vector args; + args.push_back(arg1); + args.push_back(arg2); + args.push_back(arg3); + return auto_push_context(position, id, args); + } + + //////////////////////////////////////////////////////////////////////////////// + // iostream-based derivative uses the above base class to generate messages then uses iostream to print them + //////////////////////////////////////////////////////////////////////////////// + + class message_handler_body + { + private: + std::ostream* m_device; // TextIO output device + unsigned m_limit; // error limit + unsigned m_errors; // error count + + public: + message_handler_body(std::ostream& device, unsigned limit) : + m_device(&device), m_limit(limit), m_errors(0) + { + } + + ~message_handler_body(void) + { + device().flush(); + } + + std::ostream& device(void) + { + return *m_device; + } + + unsigned limit(void) const + { + return m_limit; + } + + void set_limit(unsigned limit) + { + m_limit = limit; + } + + unsigned count(void) const + { + return m_errors; + } + + void set_count(unsigned count) + { + m_errors = count; + } + + void error_increment(void) + { + ++m_errors; + } + + bool limit_reached(void) const + { + return m_limit > 0 && m_errors >= m_limit; + } + + private: + message_handler_body(const message_handler_body&); + message_handler_body& operator=(const message_handler_body&); + }; + + //////////////////////////////////////////////////////////////////////////////// + + message_handler::message_handler(std::ostream& device, unsigned limit, bool show) + throw() : + message_handler_base(show), m_body(new message_handler_body(device, limit)) + { + } + + message_handler::message_handler(std::ostream& device, + const std::string& message_file, unsigned limit, bool show) + throw(message_handler_read_error) : + message_handler_base(message_file,show), m_body(new message_handler_body(device, limit)) + { + } + + message_handler::message_handler(std::ostream& device, const std::vector& message_files, unsigned limit, bool show) + throw(message_handler_read_error) : + message_handler_base(message_files,show), m_body(new message_handler_body(device, limit)) + { + } + + message_handler::~message_handler(void) + throw() + { + } + + //////////////////////////////////////////////////////////////////////////////// + // error count + + void message_handler::set_error_limit(unsigned error_limit) + throw() + { + m_body->set_limit(error_limit); + } + + unsigned message_handler::error_limit(void) const + throw() + { + return m_body->limit(); + } + + void message_handler::reset_error_count(void) + throw() + { + m_body->set_count(0); + } + + unsigned message_handler::error_count(void) const + throw() + { + return m_body->count(); + } + + std::ostream& message_handler::device(void) + { + return m_body->device(); + } + + //////////////////////////////////////////////////////////////////////////////// + // information messages + + bool message_handler::information(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(id, args); + return true; + } + + bool message_handler::information(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(id); + return true; + } + + bool message_handler::information(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(id, arg1); + return true; + } + + bool message_handler::information(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(id, arg1, arg2); + return true; + } + + bool message_handler::information(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(id, arg1, arg2, arg3); + return true; + } + + bool message_handler::information(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(position, id, args); + return true; + } + + bool message_handler::information(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(position, id); + return true; + } + + bool message_handler::information(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(position, id, arg1); + return true; + } + + bool message_handler::information(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(position, id, arg1, arg2); + return true; + } + + bool message_handler::information(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + device() << information_message(position, id, arg1, arg2, arg3); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // warning messages + + bool message_handler::warning(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(id, args); + return true; + } + + bool message_handler::warning(const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(id); + return true; + } + + bool message_handler::warning(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(id, arg1); + return true; + } + + bool message_handler::warning(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(id, arg1, arg2); + return true; + } + + bool message_handler::warning(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(id, arg1, arg2, arg3); + return true; + } + + bool message_handler::warning(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(position, id, args); + return true; + } + + bool message_handler::warning(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(position, id); + return true; + } + + bool message_handler::warning(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(position, id, arg1); + return true; + } + + bool message_handler::warning(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(position, id, arg1, arg2); + return true; + } + + bool message_handler::warning(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error) + { + device() << warning_message(position, id, arg1, arg2, arg3); + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + // error messages + + bool message_handler::error(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(id, args); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(id); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(id, arg1); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(id, arg1, arg2); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(id, arg1, arg2, arg3); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(position, id, args); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(position, id); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(position, id, arg1); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(position, id, arg1, arg2); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + bool message_handler::error(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) + { + device() << error_message(position, id, arg1, arg2, arg3); + m_body->error_increment(); + if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // fatal messages + + bool message_handler::fatal(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(id, args); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(id); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(id, arg1); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(id, arg1, arg2); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(id, arg1, arg2, arg3); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const message_position& position, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(position, id, args); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const message_position& position, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(position, id); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const message_position& position, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(position, id, arg1); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(position, id, arg1, arg2); + throw message_handler_fatal_error(id); + } + + bool message_handler::fatal(const message_position& position, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) + { + device() << fatal_message(position, id, arg1, arg2, arg3); + throw message_handler_fatal_error(id); + } + + /////////////////////////////////////////////////////////////////////////////// + // plain text + + bool message_handler::plaintext(const std::string& text) + { + device() << text << std::endl; + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/subsystems/message_handler.hpp b/src/stlplus/subsystems/message_handler.hpp new file mode 100644 index 0000000..26b8ed6 --- /dev/null +++ b/src/stlplus/subsystems/message_handler.hpp @@ -0,0 +1,1015 @@ +#ifndef STLPLUS_MESSAGE_HANDLER +#define STLPLUS_MESSAGE_HANDLER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A general-purpose message handler using a message file as the source of all text + +//////////////////////////////////////////////////////////////////////////////// +#include "subsystems_fixes.hpp" +#include "smart_ptr.hpp" +#include +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Internals + + class message_handler_base; + class message_handler_base_body; + + class message_handler; + class message_handler_body; + + class message_context_body; + + //////////////////////////////////////////////////////////////////////////////// + // an object representing a file position + // used for example when reporting errors when parsing a text file + + class message_position + { + public: + message_position(void); + message_position(const std::string& filename, unsigned line, unsigned column); + ~message_position(void); + + // access the elements of the position + const std::string& filename(void) const; + // line number in the range 1..n + // so line 0 means uninitialised + unsigned line(void) const; + // column number in the range 0..m-1 + unsigned column(void) const; + + // add a column offset to a position + message_position operator + (unsigned) const; + message_position& operator += (unsigned); + + // tests for valid position + bool empty(void) const; + bool valid(void) const; + + // vector of two strings + // - the first reproducing the source line + // - the second an arrow pointing to the correct column + // the vector will be empty if the position can't be found + std::vector show(void) const; + + private: + std::string m_filename; + unsigned m_line; + unsigned m_column; + }; + + std::string to_string(const message_position& where); + + ////////////////////////////////////////////////////////////////////////////// + // an object representing an message context + // used to control the context stack + // on initialisation, the message_context stores the state of the context stack + // on destruction it restores the state by popping any context that has been pushed since creation + + class message_context + { + public: + message_context(message_handler_base& handler); + + void set(message_handler_base& handler); + void pop(void); + + private: + friend class message_context_body; + friend class message_handler_base; + smart_ptr_nocopy m_body; + }; + + //////////////////////////////////////////////////////////////////////////////// + // exception classes which can be thrown by the message handler + + // read_error is thrown if the message file read fails + class message_handler_read_error : public std::runtime_error + { + public: + message_handler_read_error(const message_position& position, const std::string& reason); + ~message_handler_read_error(void) throw(); + + const message_position& where(void) const; + + private: + message_position m_position; + }; + + // format_error is thrown if a formatting error occurs trying to create the text for the message + + class message_handler_format_error : public std::runtime_error + { + public: + message_handler_format_error(const std::string& format, unsigned offset); + message_handler_format_error(const message_position& pos, const std::string& format, unsigned offset); + ~message_handler_format_error(void) throw(); + + const message_position& where(void) const; + const std::string& format(void) const; + unsigned offset(void) const; + + private: + message_position m_position; + std::string m_format; + unsigned m_offset; + }; + + // id_error is thrown if an error id is requested that could not be found in the message file + + class message_handler_id_error : public std::runtime_error + { + public: + message_handler_id_error(const std::string& id); + ~message_handler_id_error(void) throw(); + + const std::string& id(void) const; + + private: + std::string m_id; + }; + + // limit_error is thrown when the number of errors reaches the error limit + + class message_handler_limit_error : public std::runtime_error + { + public: + message_handler_limit_error(unsigned limit); + ~message_handler_limit_error(void) throw(); + + unsigned limit(void) const; + + private: + unsigned m_limit; // the limit that was exceeded + }; + + // fatal_error is thrown when a fatal error is reported + + class message_handler_fatal_error : public std::runtime_error + { + public: + message_handler_fatal_error(const std::string& id); + ~message_handler_fatal_error(void) throw(); + + const std::string& id(void) const; + + private: + std::string m_id; + }; + + //////////////////////////////////////////////////////////////////////////////// + // base version returns message objects as vectors of strings + // - it is then up to the user to decide what to do with them + // - suitable for use in a GUI for example where the message is displayed in a dialog + + class message_handler_base + { + public: + ////////////////////////////////////////////////////////////////////////////// + // constructor + + // The first form sets the show flag but doesn't load any message files. + // The second and third forms also read message file(s) by calling + // add_message_file and therefore can throw exceptions. The first form + // defers file reading to explicit calls of add_message_file so does not + // throw any exceptions. + + // show determines whether the source file line containing the source of a problem should also be shown + + message_handler_base(bool show = true) + throw(); + + message_handler_base(const std::string& message_file, bool show = true) + throw(message_handler_read_error); + + message_handler_base(const std::vector& message_files, bool show = true) + throw(message_handler_read_error); + + virtual ~message_handler_base(void) + throw(); + + ////////////////////////////////////////////////////////////////////////////// + // message file handling + + // The message file format contains lines of the form: + // + // + // + // In is a unique mnemonic for the message. It starts with an + // alphabetic character and may contain alphanumerics and underscores only. + // The can be one or more space or tab characters. The is the + // remainder of the line and is plain text (not a quoted string). All lines + // starting with a non-alphabetic character are assumed to be comments and are + // ignored + + // If the message file is missing the function throws read_error with no line + // number. If formatting errors were found in the file,then it throws a + // read_error with valid line information. + + // Any number of message files can be added and they accumulate + + void add_message_file(const std::string& message_file) + throw(message_handler_read_error); + + void add_message_files(const std::vector& message_files) + throw(message_handler_read_error); + + void add_message(const std::string& id, const std::string& text) + throw(); + + bool message_present(const std::string& id) const + throw(); + + ////////////////////////////////////////////////////////////////////////////// + // format control + + // The status formats - that is, information/warning/error/fatal/context/supplement + // formats take a single argument which is the formatted message + // For example: "warning: @0" + // + // Messages may be printed as either simple or positional + // simple: just a text message, such as a progress report + // positional: a message relating to a source file line + // The formatted message text is generated directly for simple messages + // However, for positional messages, this text is further substituted + // into a positional format string. + // The positional format string takes up to 4 arguments: + // @0: simple message text + // @1: filename + // @2: line number + // @3: column number + // You can miss out a part of this (e.g. the column number) + // by simply not including the argument number in the format string + // For example: "file: @1, line: @2: @0" + // + // The default formats are: + // information: "@0" + // warning: "warning: @0" + // error: "error: @0" + // fatal: "FATAL: @0" + // context: "context: @0" + // supplement: "supplement: @0" + // + // positional: "\"@1\" (@2,@3) : @0" + + void set_information_format(const std::string& format) + throw(); + + void set_warning_format(const std::string& format) + throw(); + + void set_error_format(const std::string& format) + throw(); + + void set_fatal_format(const std::string& format) + throw(); + + void set_context_format(const std::string& format) + throw(); + + void set_supplement_format(const std::string& format) + throw(); + + void set_position_format(const std::string& format) + throw(); + + ////////////////////////////////////////////////////////////////////////////// + // source file position display control + // show_position indicates that the source file line containing the error + // should be shown with the message on subsequent lines + // hide_position indicates that the source file line should not be shown + + void show_position(void) + throw(); + + void hide_position(void) + throw(); + + ////////////////////////////////////////////////////////////////////////////// + // Message formatting functions + // These functions return a vector of strings containing the completed message + + // There are 6 classes of message: information, context, supplement, warning, error, fatal + // The 4 main classes are: + // - information: progress messages, status messages etc. + // - warning: a problem has been found but there is a sensible way of proceeding + // - error: a problem has been found and the operation will fail + // processing may continue but only to find further errors + // - fatal: an internal (programming) error has been found and the operation is stopping NOW + // The remaining two always follow one of the above + // - context: give stack-like information of the error context + // e.g. if processing include files, the sequence of includes forms a stack + // - supplement: give extra information of the error context + // e.g. give the set of possible solutions to the problem + + // There are 2 kinds of message: simple, positional + // - simple: just a text message + // - positional: a message relating to a source file and a specific position in that file + // This gives 8 variants. + // Note: a positional message with an empty position is treated as a simple message + + // Messages can have arguments. + // All arguments are strings. + // For each variant there are 5 functions relating to different numbers of arguments. + // - general form: takes any number of arguments as a vector of strings + // - 0 arguments: takes no arguments + // - 1 argument: allows a single argument + // - 2 arguments: allows two arguments + // - 3 arguments: allows three arguments + // For more than 3 arguments, use the general form + + // information messages + + // simple messages + std::vector information_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional messages + std::vector information_message(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector information_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // warning messages + + // simple messages + std::vector warning_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional messages + std::vector warning_message(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector warning_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // error messages + + // simple messages + std::vector error_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional messages + std::vector error_message(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector error_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // fatal messages + // Note that these do not throw the fatal_error exception because that would prevent the message being reported + // the caller should throw the exception after reporting the message + + // simple messages + std::vector fatal_message(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional messages + std::vector fatal_message(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + std::vector fatal_message(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // supplement messages - these must be pushed *before* the message that they apply to + + // simple messages + void push_supplement(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional messages + void push_supplement(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + void push_supplement(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + ////////////////////////////////////////////////////////////////////////////// + // context stack - allows supplementary messages to be printed after each message showing where it came from + // for example, an message whilst inlining a function could be followed by a "function called from..." message + + // simple context messages + void push_context(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional context messages + void push_context(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + void push_context(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + unsigned context_depth(void) const + throw(); + + // remove the last level of context if there is one + void pop_context(void) + throw(); + // remove context messages to the specified depth + void pop_context(unsigned) + throw(); + + // push the context and save it in the message_context handle. When the + // message_context handle goes out of scope, the context is popped + // automatically + + // simple context messages + message_context auto_push_context(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional context messages + message_context auto_push_context(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + message_context auto_push_context(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + private: + friend class message_handler_base_body; + smart_ptr_nocopy m_base_body; + }; + + //////////////////////////////////////////////////////////////////////////////// + // iostream-based derivative uses the above base class to generate messages then uses iostream to print them + // Note: since this is a public derivative, all message_handler_base operations are also available + + class message_handler : public message_handler_base + { + public: + ////////////////////////////////////////////////////////////////////////////// + // constructor + + // The device is the output on which to print the error. For command-line tools + // it will be either std::cout (standard output) or std::cerr (standard error) from + // . + + // The second and third form also reads a message file by calling + // add_message_file and therefore can throw exceptions. The first form + // defers file reading to explicit calls of add_message_file so does not + // throw any exceptions. + + // limit sets the error limit - zero disables this feature + // show determines whether the source file line containing the error should also be shown + + message_handler(std::ostream& device,unsigned limit = 0,bool show = true) + throw(); + + message_handler(std::ostream& device, + const std::string& message_file,unsigned limit = 0,bool show = true) + throw(message_handler_read_error); + + message_handler(std::ostream& device, + const std::vector& message_files,unsigned limit = 0,bool show = true) + throw(message_handler_read_error); + + ~message_handler(void) + throw(); + + ////////////////////////////////////////////////////////////////////////////// + // error count and error limits + + void set_error_limit(unsigned error_limit) + throw(); + + unsigned error_limit(void) const + throw(); + + void reset_error_count(void) + throw(); + + unsigned error_count(void) const + throw(); + + ////////////////////////////////////////////////////////////////////////////// + // access the output device for whatever reason (for example, to ensure that + // text output goes wherever the messages go) + + std::ostream& device(void); + + ////////////////////////////////////////////////////////////////////////////// + // Message reporting functions + // These are based on the error formatting functions in the baseclass + + // information messages + + // simple messages + bool information(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional messages + bool information(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + bool information(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // warning messages + + // simple messages + bool warning(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // positional messages + bool warning(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error); + + bool warning(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error); + + // error messages + + // simple messages + bool error(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + // positional messages + bool error(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + bool error(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); + + // fatal messages + // These report the error and then always throw the fatal_error exception + + // simple messages + bool fatal(const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + // positional messages + bool fatal(const message_position&, + const std::string& id, + const std::vector& args) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const message_position&, + const std::string& id) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const message_position&, + const std::string& id, + const std::string& arg1) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + bool fatal(const message_position&, + const std::string& id, + const std::string& arg1, + const std::string& arg2, + const std::string& arg3) + throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); + + ////////////////////////////////////////////////////////////////////////////// + // plain text output + // provides a simple way of outputting text from the program to the same device as the messages + // Each call of plaintext is treated as a line of text and has a newline appended + + bool plaintext (const std::string& text); + + private: + friend class message_handler_body; + smart_ptr_nocopy m_body; + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/subsystems/subsystems.hpp b/src/stlplus/subsystems/subsystems.hpp new file mode 100644 index 0000000..2bc41b5 --- /dev/null +++ b/src/stlplus/subsystems/subsystems.hpp @@ -0,0 +1,21 @@ +#ifndef STLPLUS_SUBSYSTEMS +#define STLPLUS_SUBSYSTEMS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Allows all the STLplus subsystems to be included in one go + +//////////////////////////////////////////////////////////////////////////////// + +#include "cli_parser.hpp" +#include "ini_manager.hpp" +#include "library_manager.hpp" +#include "message_handler.hpp" +#include "timer.hpp" + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/subsystems/subsystems_fixes.hpp b/src/stlplus/subsystems/subsystems_fixes.hpp new file mode 100644 index 0000000..8fd9f91 --- /dev/null +++ b/src/stlplus/subsystems/subsystems_fixes.hpp @@ -0,0 +1,42 @@ +#ifndef STLPLUS_SUBSYSTEMS_FIXES +#define STLPLUS_SUBSYSTEMS_FIXES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Contains work arounds for OS or Compiler specific problems + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Unnecessary compiler warnings +//////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +// Microsoft Visual Studio +// shut up the following irritating warnings +// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger) +// 4305 - VC6, identifier type was converted to a smaller type +// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger) +// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it +// 4290 - VC6, C++ exception specification ignored +// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning) +// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program +// 4996 - VC8, 'xxxx' was declared deprecated +#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996) +#endif + +#ifdef __BORLANDC__ +// Borland +// Shut up the following irritating warnings +// 8026 - Functions with exception specifications are not expanded inline +// 8027 - Functions with xxx are not expanded inline +#pragma warn -8026 +#pragma warn -8027 +#endif + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/subsystems/timer.cpp b/src/stlplus/subsystems/timer.cpp new file mode 100644 index 0000000..9faac0f --- /dev/null +++ b/src/stlplus/subsystems/timer.cpp @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "timer.hpp" +#include "dprintf.hpp" +#include "time.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + +//////////////////////////////////////////////////////////////////////////////// + + timer::timer(void) + { + reset(); + } + + timer::~timer(void) + { + } + + void timer::reset(void) + { + m_clock = clock(); + m_time = time(0); + } + + float timer::cpu(void) const + { + return ((float)(clock() - m_clock)) / ((float)CLOCKS_PER_SEC); + } + + float timer::elapsed(void) const + { + return ((float)(time(0) - m_time)); + } + + std::string timer::text(void) const + { + return dformat("%4.2fs CPU, %s elapsed", cpu(), delaytime_string(time(0)-m_time).c_str()); + } + +//////////////////////////////////////////////////////////////////////////////// + + std::ostream& operator << (std::ostream& str, const timer& t) + { + return str << t.text(); + } + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/subsystems/timer.hpp b/src/stlplus/subsystems/timer.hpp new file mode 100644 index 0000000..a3ad38d --- /dev/null +++ b/src/stlplus/subsystems/timer.hpp @@ -0,0 +1,56 @@ +#ifndef STLPLUS_TIMER +#define STLPLUS_TIMER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A CPU timer encapsulated as a class. Measures the CPU time used since its +// construction and allows this cumulative time to be reported at any time. + +//////////////////////////////////////////////////////////////////////////////// +#include "subsystems_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + class timer + { + private: + clock_t m_clock; + time_t m_time; + + public: + // constructor resets the timer to zero + timer(void); + ~timer(void); + + // reset the timer to zero without destroying it + void reset(void); + + // get the elapsed time in seconds, expressed as a float + float elapsed(void) const; + // get the CPU time in seconds, expressed as a float + float cpu(void) const; + + // get a printable string representing the elapsed time and CPU time + std::string text(void) const; + }; + + //////////////////////////////////////////////////////////////////////////////// + + // print the elapsed time and CPU time using the same representation as the text method + std::ostream& operator << (std::ostream&, const timer&); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/yoink.rc b/src/yoink.rc index 64eee3d..caf96bb 100644 --- a/src/yoink.rc +++ b/src/yoink.rc @@ -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 -#include "config.h" 1 VERSIONINFO FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_REVISION,0