There will be maintenance performed on git.ligo.org, chat.ligo.org, containers.lig.org, and docs.ligo.org starting at 9am PDT on Tuesday 18th August 2020. There will be an extremely small period of downtime at the start of the maintenance window as various services are restarted. Please address any comments, questions, or concerns to computing-help@igwn.org.

Commit 93bba4cd authored by Karl Wette's avatar Karl Wette

SWIG: build system Mk. III

- Use single Makefile to generate interface and build Octave/Python bindings
- SWIG-generated sources are now distributed, and can be built without SWIG
Original: 0072d706dce6648b6d75e540cd254195f9f98237
parent 87993b49
.PHONY: header-links
header-links: $(pkginclude_HEADERS) $(swiginclude_HEADERS)
header-links: $(pkginclude_HEADERS)
$(AM_V_at)for dir in $(SUBDIRS); do \
if test "x$$dir" != "x."; then \
( cd "$$dir" && $(MAKE) $(AM_MAKEFLAGS) header-links ) || exit 1; \
......@@ -17,5 +17,3 @@ header-links: $(pkginclude_HEADERS) $(swiginclude_HEADERS)
fi; \
done; \
fi
include $(top_srcdir)/swig/swiglal_header.am
# Common SWIG build rules
# Author: Karl Wette, 2011--2014
include $(top_srcdir)/gnuscripts/lallibs_header_links.am
.PHONY: force-swig-preproc-generation
.PHONY: run-octave
.PHONY: run-python
.PHONY: swig-distclean-generate-local
.PHONY: swig-octave-all-local
.PHONY: swig-octave-install-hook
.PHONY: swig-octave-uninstall-hook
.PHONY: swig-python-all-local
.PHONY: swig-python-install-hook
.PHONY: swig-python-uninstall-hook
swig_iface = swig_$(PACKAGE).i
swig_preproc_flags = $(swig_flags) -xml -xmllite
swig_flags = -Wextra -Werror -MP -MD -I$(top_builddir)/include $(SWIG_CPPFLAGS) $(LAL_SYSTEM_INCLUDES)
swig_cppflags = -shared -I$(top_builddir)/include $(SWIG_CPPFLAGS)
swig_ldflags = -module -avoid-version -version-info 0:0:0 $(SWIG_LDFLAGS)
swig_libtoolflags = --tag=disable-static
swig_deps_filter = $(SED) -e '/^ *swig_[a-z0-9][a-z0-9]*\.[a-z0-9][a-z0-9]* *\\$$/d'
swig_octave_flags = $(swig_flags) -octave -outdir octave/ -globals $(PACKAGE)cvar -DSWIG_CPLUSPLUS_CAST
swig_octave_sources = swig_octave.cpp
swig_octave_test = $(swig_test_runner) OCTAVE_PATH '$(abs_builddir)/octave:$(abs_srcdir)/octave:$(LAL_OCTAVE_PATH)'
swig_python_flags = $(swig_flags) -python -outdir python/ -O -builtin -globals cvar
swig_python_pysource = python/$(PACKAGE).py
swig_python_sources = swig_python.c
swig_python_test = $(swig_test_runner) PYTHONPATH '$(abs_builddir)/python:$(abs_srcdir)/python:$(LAL_PYTHON_PATH)'
swig_test_runner = $(SHELL) $(srcdir)/run_swig_tests.sh '$(LIBTOOL)' '$(swig_module_lal_libs)'
BUILT_SOURCES =
CLEANFILES =
EXTRA_HEADERS =
TESTS =
nodist_pkginclude_HEADERS =
TEST_EXTENSIONS = .m .py
pkginclude_HEADERS = \
SWIG$(PACKAGE_NAME)Alpha.i \
SWIG$(PACKAGE_NAME)Omega.i \
SWIG$(PACKAGE_NAME)Test.h \
$(END_OF_LIST)
EXTRA_DIST = \
generate_swig_iface.py \
run_swig_tests.sh \
$(TESTS)
if SWIG_BUILD
BUILT_SOURCES += \
swig_config.h \
$(END_OF_LIST)
swig_config.h: $(CONFIG_HEADER)
$(AM_V_GEN)echo '// $@: generated from $< by $(subdir)/Makefile' >$@; \
$(SED) -n 's/^#define */#define SWIGLAL_/p' $< >>$@
CLEANFILES += \
swig_config.h \
$(END_OF_LIST)
if SWIG_BUILD_OCTAVE
@SWIG_include_deps@/swig_octave.deps
octexec_LTLIBRARIES = octave/swiglal.la
nodist_octave_swiglal_la_SOURCES = $(swig_octave_sources)
octave_swiglal_la_CPPFLAGS = $(swig_cppflags) $(SWIG_OCTAVE_CPPFLAGS)
octave_swiglal_la_CXXFLAGS = $(SWIG_OCTAVE_CXXFLAGS)
octave_swiglal_la_LDFLAGS = $(swig_ldflags) $(SWIG_OCTAVE_LDFLAGS)
octave_swiglal_la_LIBADD = $(swig_module_lal_libs)
octave_swiglal_la_LIBTOOLFLAGS = $(swig_libtoolflags)
all-local: swig-octave-all-local
swig-octave-all-local: octave/$(am__dirstamp)
$(AM_V_at)cd octave/ && rm -f $(PACKAGE).oct && $(LN_S) $(SWIG_LTLIBDIR)/swiglal$(SWIG_SOEXT) $(PACKAGE).oct
install-exec-hook: swig-octave-install-hook
swig-octave-install-hook:
cd $(DESTDIR)$(octexecdir) && rm -f swiglal.la $(PACKAGE).oct* && mv -f swiglal$(SWIG_SOEXT) $(PACKAGE).oct
uninstall-hook: swig-octave-uninstall-hook
swig-octave-uninstall-hook:
cd $(DESTDIR)$(octexecdir) && rm -f $(PACKAGE).oct
TESTS += SWIGTest$(PACKAGE_NAME)Octave.m
M_LOG_COMPILER = $(swig_octave_test) $(OCTAVE) -fH
run-octave: $(octexec_LTLIBRARIES)
$(AM_V_at)$(swig_octave_test) $(RUNTOOL) $(OCTAVE) -f
EXTRA_DIST += \
$(swig_octave_sources) \
swig_octave.deps \
$(END_OF_LIST)
endif # SWIG_BUILD_OCTAVE
if SWIG_BUILD_PYTHON
@SWIG_include_deps@/swig_python.deps
pkgpyexec_LTLIBRARIES = python/swiglal.la
nodist_pkgpyexec_PYTHON = $(swig_python_pysource)
nodist_python_swiglal_la_SOURCES = $(swig_python_sources)
python_swiglal_la_CPPFLAGS = $(swig_cppflags) $(SWIG_PYTHON_CPPFLAGS)
python_swiglal_la_CFLAGS = $(SWIG_PYTHON_CFLAGS)
python_swiglal_la_LDFLAGS = $(swig_ldflags) $(SWIG_PYTHON_LDFLAGS)
python_swiglal_la_LIBADD = $(swig_module_lal_libs)
python_swiglal_la_LIBTOOLFLAGS = $(swig_libtoolflags)
all-local: swig-python-all-local
swig-python-all-local: python/$(am__dirstamp)
$(AM_V_at)cd python/ && rm -f _$(PACKAGE)$(SWIG_SOEXT) && $(LN_S) $(SWIG_LTLIBDIR)/swiglal$(SWIG_SOEXT) _$(PACKAGE)$(SWIG_SOEXT)
install-exec-hook: swig-python-install-hook
swig-python-install-hook:
cd $(DESTDIR)$(pkgpyexecdir) && rm -f swiglal.la _$(PACKAGE)$(SWIG_SOEXT)* && mv -f swiglal$(SWIG_SOEXT) _$(PACKAGE)$(SWIG_SOEXT)
uninstall-hook: swig-python-uninstall-hook
swig-python-uninstall-hook:
cd $(DESTDIR)$(pkgpyexecdir) && rm -f _$(PACKAGE)$(SWIG_SOEXT)
TESTS += SWIGTest$(PACKAGE_NAME)Python.py
PY_LOG_COMPILER = $(swig_python_test) $(PYTHON)
run-python: $(pkgpyexec_LTLIBRARIES)
$(AM_V_at)$(swig_python_test) $(RUNTOOL) $(PYTHON) $(SWIG_IPYTHON_CMD)
EXTRA_DIST += \
$(swig_python_sources) \
$(swig_python_pysource) \
swig_python.deps \
$(END_OF_LIST)
endif # SWIG_BUILD_PYTHON
if SWIG_GENERATE
@SWIG_include_deps@/swig_preproc.deps
# swig_preproc.i.tmp is always generated, but replaces swig_preproc.i only if they differ
swig_preproc.i: force-swig-preproc-generation
$(AM_V_at)echo '// $@: generated by $(subdir)/Makefile' >$@.tmp; \
headers=`find $(top_builddir)/include/lal -name '*.h' ! -name '*VCSInfoHeader.h'`; \
headers=`printf '%s\n' $${headers} | $(SED) 's|^.*/\([^/]*\)$$|\1|' | sort`; \
echo "%module swiglal_preproc;" >>$@.tmp; \
echo "#pragma SWIG nowarn=SWIGWARN_PP_MISSING_FILE" >>$@.tmp; \
echo "#pragma SWIG nowarn=SWIGWARN_PARSE_REDEFINED" >>$@.tmp; \
echo "%warnfilter(SWIGWARN_PARSE_REDUNDANT) __swiglal__;" >>$@.tmp; \
echo "%warnfilter(SWIGWARN_PARSE_REDUNDANT) __swiglal_clear__;" >>$@.tmp; \
echo "%include <lal/LALStddef.h>" >>$@.tmp; \
printf "%%include <lal/%s>\n" $${headers} >>$@.tmp; \
if test -f $@ && diff -s $@ $@.tmp >/dev/null 2>&1; then \
rm -f $@.tmp; \
else \
mv -f $@.tmp $@; \
fi
swig_preproc.xml: swig_preproc.i Makefile
$(AM_V_GEN)if $(SWIG) -MF swig_preproc.deps.tmp $(swig_preproc_flags) -o $@ $<; then \
mv -f swig_preproc.deps.tmp swig_preproc.deps; \
exit 0; \
else \
rm -f swig_preproc.deps.tmp $@; \
exit 1; \
fi
$(swig_iface): swig_preproc.xml $(srcdir)/generate_swig_iface.py
$(AM_V_GEN)if $(PYTHON) $(srcdir)/generate_swig_iface.py '$(PACKAGE_NAME)' '$(SWIG_DEPENDENCIES)' '$(swig_function_prefixes)' $< $@; then \
rm -f $(top_builddir)/include/lal/$(swig_iface); \
$(LN_S) $(abs_top_builddir)/swig/$(swig_iface) $(top_builddir)/include/lal/$(swig_iface); \
exit 0; \
else \
rm -f $@; \
exit 1; \
fi
$(swig_octave_sources): $(swig_iface) swig_config.h octave/$(am__dirstamp)
$(AM_V_GEN)if $(SWIG) -MF swig_octave.deps.tmp $(swig_octave_flags) -o $@ $<; then \
$(swig_deps_filter) swig_octave.deps.tmp > swig_octave.deps; \
rm -f swig_octave.deps.tmp; \
exit 0; \
else \
rm -f swig_octave.deps.tmp $@; \
exit 1; \
fi
$(swig_python_sources): $(swig_iface) swig_config.h python/$(am__dirstamp)
$(AM_V_GEN)if $(SWIG) -MF swig_python.deps.tmp $(swig_python_flags) -o $@ $<; then \
$(swig_deps_filter) swig_python.deps.tmp > swig_python.deps; \
rm -f swig_python.deps.tmp; \
exit 0; \
else \
rm -f swig_python.deps.tmp $@; \
exit 1; \
fi
$(swig_python_pysource): $(swig_python_sources)
nodist_pkginclude_HEADERS += \
$(swig_iface) \
$(END_OF_LIST)
CLEANFILES += \
$(swig_iface) \
$(swig_octave_sources) \
$(swig_python_pysource) \
$(swig_python_pysource)c \
$(swig_python_pysource)o \
$(swig_python_sources) \
swig_preproc.i \
swig_preproc.i.tmp \
swig_preproc.xml \
$(END_OF_LIST)
distclean-local: swig-distclean-generate-local
swig-distclean-generate-local:
-rm -f swig_*.deps
else # !SWIG_GENERATE
$(swig_octave_sources) $(swig_python_sources):
@echo "################################################################" >&2; \
echo "You are trying to re-create the SWIG-generated source file" >&2; \
echo " $@" >&2; \
echo "but $(PACKAGE_NAME) was not configured with SWIG generation" >&2; \
echo "enabled. Please ensure SWIG is installed and re-run ./configure" >&2; \
echo "with --enable-swig=generate or --enable-swig-<lang>=generate." >&2; \
echo "################################################################" >&2; \
exit 1
endif # SWIG_GENERATE
endif # SWIG_BUILD
tags-am:
@echo > swig_dummy.c && $(ETAGS) swig_dummy.c && rm -f swig_dummy.c
ctags-am:
@echo > swig_dummy.c && $(CTAGS) swig_dummy.c && rm -f swig_dummy.c
cscopelist-am:
@:
This diff is collapsed.
......@@ -189,3 +189,6 @@ packages/vectorops/test/VectorOpsTest
packages/window/test/WindowTest
python/__init__.py
python/git_version.py
swig/octave/
swig/python/
swig/swig_*
......@@ -28,8 +28,6 @@ AC_CONFIG_FILES([ \
python/utils/Makefile \
python/spectrum/Makefile \
swig/Makefile \
swig/octave/Makefile \
swig/python/Makefile \
packages/Makefile \
packages/std/Makefile \
packages/std/doc/Makefile \
......@@ -319,7 +317,7 @@ if test "${boinc}" = "true"; then
fi
# configure SWIG wrapping modules
LALSUITE_USE_SWIG([XLAL LAL])
LALSUITE_USE_SWIG
# checks for typedefs, structures, and compiler characteristics
AC_C_BIGENDIAN
......@@ -365,8 +363,9 @@ LAL has now been successfully configured:
* CUDA support is $CUDA_ENABLE_VAL
* BOINC support is $BOINC_ENABLE_VAL
* SWIG wrapping module for Octave is $SWIG_OCTAVE_ENABLE_VAL
* SWIG wrapping module for Python is $SWIG_PYTHON_ENABLE_VAL
* SWIG binding generation is $SWIG_GENERATE_ENABLE_VAL
* SWIG bindings for Octave are $SWIG_BUILD_OCTAVE_ENABLE_VAL
* SWIG bindings for Python are $SWIG_BUILD_PYTHON_ENABLE_VAL
and will be installed under the directory:
......
../../gnuscripts/lallibs_swig.am
\ No newline at end of file
......@@ -13,7 +13,7 @@ header-link-stamp: force-header-link-generation
else \
srcdirs="src"; \
fi; \
for dir in $$srcdirs; do \
for dir in $$srcdirs swig; do \
( cd "$(top_builddir)/$$dir" && $(MAKE) $(AM_MAKEFLAGS) header-links ) || exit 1; \
done; \
echo "$@" > $@
abs_top_srcdir=@abs_top_srcdir@
abs_top_builddir=@abs_top_builddir@
LAL_OCTAVE_PATH=@abs_top_builddir@/swig/octave/.libs
LAL_PYTHON_PATH=@abs_top_builddir@/swig/python/.libs
LAL_OCTAVE_PATH=@abs_top_builddir@/swig/octave:@abs_top_srcdir@/swig/octave
LAL_PYTHON_PATH=@abs_top_builddir@/swig/python:@abs_top_srcdir@/swig/python
Name: LAL
Description: (uninstalled)
......
......@@ -24,18 +24,7 @@
#include <stdint.h>
#include <lal/LALConfig.h>
/* macros for certain keywords */
#if __STDC_VERSION__ >= 199901L
# define _LAL_RESTRICT_ restrict
# define _LAL_INLINE_ inline
#elif defined __GNUC__
# define _LAL_RESTRICT_ __restrict__
# define _LAL_INLINE_ __inline__
#else
# define _LAL_RESTRICT_
# define _LAL_INLINE_
#endif
#include <lal/LALStddef.h>
#if defined(__cplusplus)
extern "C" {
......
......@@ -451,13 +451,6 @@ typedef struct tagCOMPLEX16ArraySequence {
/* ---------- Structured datatypes ---------- */
#ifdef SWIG
/* *INDENT-OFF* */
/* SWIG interface directives: must appear *before* the definition of LIGOTimeGPS */
%include <lal/SWIGLIGOTimeGPSPre.i>
/* *INDENT-ON* */
#endif /* SWIG */
/** Epoch relative to GPS epoch, see \ref ss_LIGOTimeGPS for more details */
typedef struct tagLIGOTimeGPS {
INT4 gpsSeconds; /**< Seconds since 0h UTC 6 Jan 1980. */
......@@ -467,13 +460,6 @@ typedef struct tagLIGOTimeGPS {
/** Zero-initializer for LIGOTimeGPS structs */
#define LIGOTIMEGPSZERO { 0, 0 }
#ifdef SWIG /* SWIG interface directives */
/* *INDENT-OFF* */
/* SWIG interface directives: must appear *after* the definition of LIGOTimeGPS */
%include <lal/SWIGLIGOTimeGPSPost.i>
/* *INDENT-ON* */
#endif /* SWIG */
/**
* Indices of arrays corresponding to particular units.
* The ::LALUnit structure has arrays giving the numerators
......
/*
* Copyright (C) 2013 Jolien Creighton
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with with program; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef _LALSTDDEF_H
#define _LALSTDDEF_H
/* macros for certain keywords */
#if __STDC_VERSION__ >= 199901L
# define _LAL_RESTRICT_ restrict
# define _LAL_INLINE_ inline
#elif defined __GNUC__
# define _LAL_RESTRICT_ __restrict__
# define _LAL_INLINE_ __inline__
#else
# define _LAL_RESTRICT_
# define _LAL_INLINE_
#endif
#endif /* _LALSTDDEF_H */
......@@ -17,6 +17,7 @@ pkginclude_HEADERS = \
LALGSL.h \
LALMalloc.h \
LALStatusMacros.h \
LALStddef.h \
LALStdio.h \
LALStdlib.h \
LALString.h \
......@@ -24,15 +25,4 @@ pkginclude_HEADERS = \
LALVersion.h \
StringInput.h \
XLALError.h \
XLALGSL.h \
lalswig_error.i \
lalswig_gsl.i \
lalswig_test.i
swigincludedir = $(pkgincludedir)
swiginclude_HEADERS = \
SWIGLIGOTimeGPSPre.i \
SWIGLIGOTimeGPSPost.i \
swiglal_common.i \
swiglal_octave.i \
swiglal_python.i
XLALGSL.h
//
// Copyright (C) 2011--2014 Karl Wette
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with with program; see the file COPYING. If not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
// MA 02111-1307 USA
//
// SWIG typemaps, methods and operators for LIGOTimeGPS.
// Author: Karl Wette
// This file contains SWIG code which must appear *before*
// the definition of the LIGOTimeGPS struct.
// Only in SWIG interface.
#if defined(SWIG) && !defined(SWIGXML)
// Specialised input typemaps for LIGOTimeGPS structs.
// Accepts a SWIG-wrapped LIGOTimeGPS or a double as input.
%typemap(in, noblock=1, fragment=SWIG_AsVal_frag(double))
struct tagLIGOTimeGPS (void *argp = 0, int res = 0),
const struct tagLIGOTimeGPS (void *argp = 0, int res = 0)
{
res = SWIG_ConvertPtr($input, &argp, $&descriptor, $disown | %convertptr_flags);
if (!SWIG_IsOK(res)) {
double val = 0;
res = SWIG_AsVal(double)($input, &val);
if (!SWIG_IsOK(res)) {
%argument_fail(res, "$type", $symname, $argnum);
} else {
XLALGPSSetREAL8(&$1, val);
}
} else {
if (!argp) {
%argument_nullref("$type", $symname, $argnum);
} else {
$&ltype temp = %reinterpret_cast(argp, $&ltype);
$1 = *temp;
if (SWIG_IsNewObj(res)) {
%delete(temp);
}
}
}
}
%typemap(freearg) struct tagLIGOTimeGPS, const struct tagLIGOTimeGPS "";
// Specialised input typemaps for pointers to LIGOTimeGPS.
// Accepts a SWIG-wrapped LIGOTimeGPS or a double as input.
%typemap(in, noblock=1, fragment=SWIG_AsVal_frag(double))
struct tagLIGOTimeGPS* (LIGOTimeGPS tmp, void *argp = 0, int res = 0),
const struct tagLIGOTimeGPS* (LIGOTimeGPS tmp, void *argp = 0, int res = 0)
{
res = SWIG_ConvertPtr($input, &argp, $descriptor, $disown | %convertptr_flags);
if (!SWIG_IsOK(res)) {
double val = 0;
res = SWIG_AsVal(double)($input, &val);
if (!SWIG_IsOK(res)) {
%argument_fail(res, "$type", $symname, $argnum);
} else {
$1 = %reinterpret_cast(XLALGPSSetREAL8(&tmp, val), $ltype);
}
} else {
$1 = %reinterpret_cast(argp, $ltype);
}
}
%typemap(freearg) struct tagLIGOTimeGPS*, const struct tagLIGOTimeGPS* "";
#endif // SWIG && !SWIGXML
//
// Copyright (C) 2011, 2012 Karl Wette
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with with program; see the file COPYING. If not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
// MA 02111-1307 USA
//
// SWIG interface LAL error handling
// Author: Karl Wette
// Only in SWIG interface.
#ifdef SWIG
// Replace default LAL raise/abort hooks with custom handlers which
// translate them into an XLAL error, so that they will be caught
// by the SWIG %exception handler.
%header %{
#include <signal.h>
// Print the supplied error message, then raise an XLAL error.
static int lalswig_LALRaise(int sig, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
(void) fprintf(stderr, "LALRaise: %s\n", strsignal(sig));
XLALSetErrno(XLAL_EFAILED);
return 0;
}
// Print the supplied error message, then raise an XLAL error.
static void lalswig_LALAbort(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
XLALSetErrno(XLAL_EFAILED);
}
%}
%init %{
lalRaiseHook = lalswig_LALRaise;
lalAbortHook = lalswig_LALAbort;
%}
#endif // SWIG
//
// Copyright (C) 2011, 2012 Karl Wette
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with with program; see the file COPYING. If not, write to the