aboutsummaryrefslogtreecommitdiff
path: root/lib/cpluff/libcpluff
diff options
context:
space:
mode:
authoralcoheca <alcoheca@svn>2010-03-26 13:35:35 +0000
committeralcoheca <alcoheca@svn>2010-03-26 13:35:35 +0000
commitb632a42d5cacdda7bb07a3fee995a4f0de1deba9 (patch)
treeb174ecd7ce4830785293fa6d18ab65d30c0cc375 /lib/cpluff/libcpluff
parent5ccef76d00e1dc22d6ae5d1c08172630f5972e58 (diff)
copied cpluff-0.1.3 from vendor branch
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@28834 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'lib/cpluff/libcpluff')
-rw-r--r--lib/cpluff/libcpluff/Makefile.am63
-rw-r--r--lib/cpluff/libcpluff/Makefile.in721
-rw-r--r--lib/cpluff/libcpluff/context.c526
-rw-r--r--lib/cpluff/libcpluff/cpluff.c182
-rw-r--r--lib/cpluff/libcpluff/cpluff.h1497
-rw-r--r--lib/cpluff/libcpluff/cpluffdef.h200
-rw-r--r--lib/cpluff/libcpluff/cpluffdef.h.in200
-rw-r--r--lib/cpluff/libcpluff/defines.h69
-rw-r--r--lib/cpluff/libcpluff/docsrc/Doxyfile-impl.in1256
-rw-r--r--lib/cpluff/libcpluff/docsrc/Doxyfile-ref.in1256
-rw-r--r--lib/cpluff/libcpluff/docsrc/Makefile.am7
-rw-r--r--lib/cpluff/libcpluff/docsrc/Makefile.in359
-rw-r--r--lib/cpluff/libcpluff/docsrc/architecture.dox66
-rw-r--r--lib/cpluff/libcpluff/docsrc/mainpage.dox57
-rw-r--r--lib/cpluff/libcpluff/docsrc/mainprog.dox338
-rw-r--r--lib/cpluff/libcpluff/docsrc/plugin.dox286
-rw-r--r--lib/cpluff/libcpluff/internal.h566
-rw-r--r--lib/cpluff/libcpluff/logging.c258
-rw-r--r--lib/cpluff/libcpluff/pcontrol.c1240
-rw-r--r--lib/cpluff/libcpluff/pinfo.c722
-rw-r--r--lib/cpluff/libcpluff/ploader.c1186
-rw-r--r--lib/cpluff/libcpluff/pscan.c308
-rw-r--r--lib/cpluff/libcpluff/psymbol.c336
-rw-r--r--lib/cpluff/libcpluff/serial.c201
-rw-r--r--lib/cpluff/libcpluff/thread.h120
-rw-r--r--lib/cpluff/libcpluff/thread_posix.c228
-rw-r--r--lib/cpluff/libcpluff/thread_windows.c268
-rw-r--r--lib/cpluff/libcpluff/util.c211
-rw-r--r--lib/cpluff/libcpluff/util.h131
29 files changed, 12858 insertions, 0 deletions
diff --git a/lib/cpluff/libcpluff/Makefile.am b/lib/cpluff/libcpluff/Makefile.am
new file mode 100644
index 0000000000..455a5d7f05
--- /dev/null
+++ b/lib/cpluff/libcpluff/Makefile.am
@@ -0,0 +1,63 @@
+## Process this file with automake to produce Makefile.in.
+
+# Copyright 2007 Johannes Lehtinen
+# This Makefile is free software; Johannes Lehtinen gives unlimited
+# permission to copy, distribute and modify it.
+
+SUBDIRS = docsrc
+
+LIBS = @LIBS_LIBCPLUFF@ @LTLIBINTL@ @LIBS@
+
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS += -I. -DCP_C_API=CP_EXPORT -DCP_HOST="\"$(host)\"" -DCP_DATADIR="\"$(datadir)\""
+
+DOXYGEN = doxygen
+DOXYGEN_SOURCE = cpluffdef.h $(srcdir)/cpluff.h $(srcdir)/docsrc/*.dox
+DOXYGEN_STYLE = $(top_srcdir)/docsrc/doxygen.footer $(top_srcdir)/docsrc/doxygen.css
+
+lib_LTLIBRARIES = libcpluff.la
+libcpluff_la_SOURCES = psymbol.c pscan.c ploader.c pinfo.c pcontrol.c serial.c logging.c context.c cpluff.c util.c ../kazlib/list.c ../kazlib/list.h ../kazlib/hash.c ../kazlib/hash.h internal.h thread.h util.h defines.h
+if POSIX_THREADS
+libcpluff_la_SOURCES += thread_posix.c
+endif
+if WINDOWS_THREADS
+libcpluff_la_SOURCES += thread_windows.c
+endif
+libcpluff_la_LDFLAGS = -no-undefined -version-info $(CP_C_LIB_VERSION)
+
+include_HEADERS = cpluff.h cpluffdef.h
+
+doc: refdoc
+
+refdoc: doc/reference/c-api/index.html
+
+doc/reference/c-api/index.html: $(DOXYGEN_SOURCE) $(top_srcdir)/doc/img/architecture.png docsrc/Doxyfile-ref $(DOXYGEN_STYLE)
+ rm -rf doxygen-ref
+ mkdir -p doxygen-ref
+ cp -p $^ doxygen-ref
+ cd doxygen-ref && $(DOXYGEN) Doxyfile-ref
+ mkdir -p doc/reference
+ rm -rf doc/reference/c-api
+ mv doxygen-ref/html doc/reference/c-api
+ rm -rf doxygen-ref
+
+impldoc: doc/implementation/c-api/index.html
+
+doc/implementation/c-api/index.html: $(srcdir)/*.h $(srcdir)/*.c cpluffdef.h ../config.h docsrc/Doxyfile-impl $(DOXYGEN_STYLE)
+ rm -rf doxygen-impl
+ mkdir -p doxygen-impl
+ cp -p $^ doxygen-impl
+ cd doxygen-impl && $(DOXYGEN) Doxyfile-impl
+ mkdir -p doc/implementation
+ rm -rf doc/implementation/c-api
+ mv doxygen-impl/html doc/implementation/c-api
+ rm -rf doxygen-impl
+
+dist-hook: refdoc
+ mkdir -p $(top_distdir)/doc/reference
+ cp -rp doc/reference/c-api $(top_distdir)/doc/reference
+
+clean-local:
+ rm -rf doc
+
+.PHONY: doc refdoc impldoc
diff --git a/lib/cpluff/libcpluff/Makefile.in b/lib/cpluff/libcpluff/Makefile.in
new file mode 100644
index 0000000000..a47a8d95b4
--- /dev/null
+++ b/lib/cpluff/libcpluff/Makefile.in
@@ -0,0 +1,721 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright 2007 Johannes Lehtinen
+# This Makefile is free software; Johannes Lehtinen gives unlimited
+# permission to copy, distribute and modify it.
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@POSIX_THREADS_TRUE@am__append_1 = thread_posix.c
+@WINDOWS_THREADS_TRUE@am__append_2 = thread_windows.c
+subdir = libcpluff
+DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(srcdir)/cpluffdef.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = cpluffdef.h
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libcpluff_la_LIBADD =
+am__libcpluff_la_SOURCES_DIST = psymbol.c pscan.c ploader.c pinfo.c \
+ pcontrol.c serial.c logging.c context.c cpluff.c util.c \
+ ../kazlib/list.c ../kazlib/list.h ../kazlib/hash.c \
+ ../kazlib/hash.h internal.h thread.h util.h defines.h \
+ thread_posix.c thread_windows.c
+@POSIX_THREADS_TRUE@am__objects_1 = thread_posix.lo
+@WINDOWS_THREADS_TRUE@am__objects_2 = thread_windows.lo
+am_libcpluff_la_OBJECTS = psymbol.lo pscan.lo ploader.lo pinfo.lo \
+ pcontrol.lo serial.lo logging.lo context.lo cpluff.lo util.lo \
+ list.lo hash.lo $(am__objects_1) $(am__objects_2)
+libcpluff_la_OBJECTS = $(am_libcpluff_la_OBJECTS)
+libcpluff_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libcpluff_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/auxliary/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libcpluff_la_SOURCES)
+DIST_SOURCES = $(am__libcpluff_la_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+includeHEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(include_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPLUFF_LOADER = @CPLUFF_LOADER@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@ -I. -DCP_C_API=CP_EXPORT -DCP_HOST="\"$(host)\"" \
+ -DCP_DATADIR="\"$(datadir)\""
+CP_CXX_LIB_VERSION = @CP_CXX_LIB_VERSION@
+CP_C_LIB_VERSION = @CP_C_LIB_VERSION@
+CP_VERSION_MAJOR = @CP_VERSION_MAJOR@
+CP_VERSION_MINOR = @CP_VERSION_MINOR@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS_LIBCPLUFF@ @LTLIBINTL@ @LIBS@
+LIBS_LIBCPLUFF = @LIBS_LIBCPLUFF@
+LIBS_OTHER = @LIBS_OTHER@
+LIBTOOL = @LIBTOOL@
+LIB_READLINE = @LIB_READLINE@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = docsrc
+DOXYGEN = doxygen
+DOXYGEN_SOURCE = cpluffdef.h $(srcdir)/cpluff.h $(srcdir)/docsrc/*.dox
+DOXYGEN_STYLE = $(top_srcdir)/docsrc/doxygen.footer $(top_srcdir)/docsrc/doxygen.css
+lib_LTLIBRARIES = libcpluff.la
+libcpluff_la_SOURCES = psymbol.c pscan.c ploader.c pinfo.c pcontrol.c \
+ serial.c logging.c context.c cpluff.c util.c ../kazlib/list.c \
+ ../kazlib/list.h ../kazlib/hash.c ../kazlib/hash.h internal.h \
+ thread.h util.h defines.h $(am__append_1) $(am__append_2)
+libcpluff_la_LDFLAGS = -no-undefined -version-info $(CP_C_LIB_VERSION)
+include_HEADERS = cpluff.h cpluffdef.h
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign libcpluff/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign libcpluff/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+cpluffdef.h: $(top_builddir)/config.status $(srcdir)/cpluffdef.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcpluff.la: $(libcpluff_la_OBJECTS) $(libcpluff_la_DEPENDENCIES)
+ $(libcpluff_la_LINK) -rpath $(libdir) $(libcpluff_la_OBJECTS) $(libcpluff_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpluff.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logging.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcontrol.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pinfo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ploader.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pscan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psymbol.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serial.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread_posix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread_windows.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+list.lo: ../kazlib/list.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT list.lo -MD -MP -MF $(DEPDIR)/list.Tpo -c -o list.lo `test -f '../kazlib/list.c' || echo '$(srcdir)/'`../kazlib/list.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/list.Tpo $(DEPDIR)/list.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../kazlib/list.c' object='list.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o list.lo `test -f '../kazlib/list.c' || echo '$(srcdir)/'`../kazlib/list.c
+
+hash.lo: ../kazlib/hash.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hash.lo -MD -MP -MF $(DEPDIR)/hash.Tpo -c -o hash.lo `test -f '../kazlib/hash.c' || echo '$(srcdir)/'`../kazlib/hash.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/hash.Tpo $(DEPDIR)/hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='../kazlib/hash.c' object='hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hash.lo `test -f '../kazlib/hash.c' || echo '$(srcdir)/'`../kazlib/hash.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
+ $(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
+ rm -f "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-includeHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+ install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool clean-local ctags \
+ ctags-recursive dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-recursive uninstall uninstall-am \
+ uninstall-includeHEADERS uninstall-libLTLIBRARIES
+
+
+doc: refdoc
+
+refdoc: doc/reference/c-api/index.html
+
+doc/reference/c-api/index.html: $(DOXYGEN_SOURCE) $(top_srcdir)/doc/img/architecture.png docsrc/Doxyfile-ref $(DOXYGEN_STYLE)
+ rm -rf doxygen-ref
+ mkdir -p doxygen-ref
+ cp -p $^ doxygen-ref
+ cd doxygen-ref && $(DOXYGEN) Doxyfile-ref
+ mkdir -p doc/reference
+ rm -rf doc/reference/c-api
+ mv doxygen-ref/html doc/reference/c-api
+ rm -rf doxygen-ref
+
+impldoc: doc/implementation/c-api/index.html
+
+doc/implementation/c-api/index.html: $(srcdir)/*.h $(srcdir)/*.c cpluffdef.h ../config.h docsrc/Doxyfile-impl $(DOXYGEN_STYLE)
+ rm -rf doxygen-impl
+ mkdir -p doxygen-impl
+ cp -p $^ doxygen-impl
+ cd doxygen-impl && $(DOXYGEN) Doxyfile-impl
+ mkdir -p doc/implementation
+ rm -rf doc/implementation/c-api
+ mv doxygen-impl/html doc/implementation/c-api
+ rm -rf doxygen-impl
+
+dist-hook: refdoc
+ mkdir -p $(top_distdir)/doc/reference
+ cp -rp doc/reference/c-api $(top_distdir)/doc/reference
+
+clean-local:
+ rm -rf doc
+
+.PHONY: doc refdoc impldoc
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/cpluff/libcpluff/context.c b/lib/cpluff/libcpluff/context.c
new file mode 100644
index 0000000000..784d1b45b7
--- /dev/null
+++ b/lib/cpluff/libcpluff/context.c
@@ -0,0 +1,526 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Plug-in context implementation
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+#include "../kazlib/list.h"
+#include "cpluff.h"
+#include "util.h"
+#ifdef CP_THREADS
+#include "thread.h"
+#endif
+#include "internal.h"
+
+
+/* ------------------------------------------------------------------------
+ * Variables
+ * ----------------------------------------------------------------------*/
+
+/// Existing contexts
+static list_t *contexts = NULL;
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+// Generic
+
+static void free_plugin_env(cp_plugin_env_t *env) {
+ assert(env != NULL);
+
+ // Free environment data
+ if (env->plugin_listeners != NULL) {
+ cpi_unregister_plisteners(env->plugin_listeners, NULL);
+ list_destroy(env->plugin_listeners);
+ env->plugin_listeners = NULL;
+ }
+ if (env->loggers != NULL) {
+ cpi_unregister_loggers(env->loggers, NULL);
+ list_destroy(env->loggers);
+ env->loggers = NULL;
+ }
+ if (env->plugin_dirs != NULL) {
+ list_process(env->plugin_dirs, NULL, cpi_process_free_ptr);
+ list_destroy(env->plugin_dirs);
+ env->plugin_dirs = NULL;
+ }
+ if (env->infos != NULL) {
+ assert(hash_isempty(env->infos));
+ hash_destroy(env->infos);
+ env->infos = NULL;
+ }
+ if (env->plugins != NULL) {
+ assert(hash_isempty(env->plugins));
+ hash_destroy(env->plugins);
+ env->plugins = NULL;
+ }
+ if (env->started_plugins != NULL) {
+ assert(list_isempty(env->started_plugins));
+ list_destroy(env->started_plugins);
+ env->started_plugins = NULL;
+ }
+ if (env->ext_points != NULL) {
+ assert(hash_isempty(env->ext_points));
+ hash_destroy(env->ext_points);
+ }
+ if (env->extensions != NULL) {
+ assert(hash_isempty(env->extensions));
+ hash_destroy(env->extensions);
+ }
+ if (env->run_funcs != NULL) {
+ assert(list_isempty(env->run_funcs));
+ list_destroy(env->run_funcs);
+ }
+
+ // Destroy mutex
+#ifdef CP_THREADS
+ if (env->mutex != NULL) {
+ cpi_destroy_mutex(env->mutex);
+ }
+#endif
+
+ // Free environment
+ free(env);
+
+}
+
+CP_HIDDEN void cpi_free_context(cp_context_t *context) {
+ assert(context != NULL);
+
+ // Free environment if this is the client program context
+ if (context->plugin == NULL && context->env != NULL) {
+ free_plugin_env(context->env);
+ }
+
+ // Destroy symbol lists
+ if (context->resolved_symbols != NULL) {
+ assert(hash_isempty(context->resolved_symbols));
+ hash_destroy(context->resolved_symbols);
+ }
+ if (context->symbol_providers != NULL) {
+ assert(hash_isempty(context->symbol_providers));
+ hash_destroy(context->symbol_providers);
+ }
+
+ // Free context
+ free(context);
+}
+
+CP_HIDDEN cp_context_t * cpi_new_context(cp_plugin_t *plugin, cp_plugin_env_t *env, cp_status_t *error) {
+ cp_context_t *context = NULL;
+ cp_status_t status = CP_OK;
+
+ assert(env != NULL);
+ assert(error != NULL);
+
+ do {
+
+ // Allocate memory for the context
+ if ((context = malloc(sizeof(cp_context_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Initialize context
+ context->plugin = plugin;
+ context->env = env;
+ context->resolved_symbols = NULL;
+ context->symbol_providers = NULL;
+
+ } while (0);
+
+ // Free context on error
+ if (status != CP_OK && context != NULL) {
+ free(context);
+ context = NULL;
+ }
+
+ *error = status;
+ return context;
+}
+
+CP_C_API cp_context_t * cp_create_context(cp_status_t *error) {
+ cp_plugin_env_t *env = NULL;
+ cp_context_t *context = NULL;
+ cp_status_t status = CP_OK;
+
+ // Initialize internal state
+ do {
+
+ // Allocate memory for the plug-in environment
+ if ((env = malloc(sizeof(cp_plugin_env_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Initialize plug-in environment
+ memset(env, 0, sizeof(cp_plugin_env_t));
+#ifdef CP_THREADS
+ env->mutex = cpi_create_mutex();
+#endif
+ env->argc = 0;
+ env->argv = NULL;
+ env->plugin_listeners = list_create(LISTCOUNT_T_MAX);
+ env->loggers = list_create(LISTCOUNT_T_MAX);
+ env->log_min_severity = CP_LOG_NONE;
+ env->plugin_dirs = list_create(LISTCOUNT_T_MAX);
+ env->infos = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
+ env->plugins = hash_create(HASHCOUNT_T_MAX,
+ (int (*)(const void *, const void *)) strcmp, NULL);
+ env->started_plugins = list_create(LISTCOUNT_T_MAX);
+ env->ext_points = hash_create(HASHCOUNT_T_MAX,
+ (int (*)(const void *, const void *)) strcmp, NULL);
+ env->extensions = hash_create(HASHCOUNT_T_MAX,
+ (int (*)(const void *, const void *)) strcmp, NULL);
+ env->run_funcs = list_create(LISTCOUNT_T_MAX);
+ env->run_wait = NULL;
+ if (env->plugin_listeners == NULL
+ || env->loggers == NULL
+#ifdef CP_THREADS
+ || env->mutex == NULL
+#endif
+ || env->plugin_dirs == NULL
+ || env->infos == NULL
+ || env->plugins == NULL
+ || env->started_plugins == NULL
+ || env->ext_points == NULL
+ || env->extensions == NULL
+ || env->run_funcs == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Create the plug-in context
+ if ((context = cpi_new_context(NULL, env, &status)) == NULL) {
+ break;
+ }
+ env = NULL;
+
+ // Create a context list, if necessary, and add context to the list
+ cpi_lock_framework();
+ if (contexts == NULL) {
+ if ((contexts = list_create(LISTCOUNT_T_MAX)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ }
+ }
+ if (status == CP_OK) {
+ lnode_t *node;
+
+ if ((node = lnode_create(context)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ } else {
+ list_append(contexts, node);
+ }
+ }
+ cpi_unlock_framework();
+
+ } while (0);
+
+ // Release resources on failure
+ if (status != CP_OK) {
+ if (env != NULL) {
+ free_plugin_env(env);
+ }
+ if (context != NULL) {
+ cpi_free_context(context);
+ }
+ context = NULL;
+ }
+
+ // Return the final status
+ if (error != NULL) {
+ *error = status;
+ }
+
+ // Return the context (or NULL on failure)
+ return context;
+}
+
+CP_C_API void cp_destroy_context(cp_context_t *context) {
+ CHECK_NOT_NULL(context);
+ if (context->plugin != NULL) {
+ cpi_fatalf(_("Only the main program can destroy a plug-in context."));
+ }
+
+ // Check invocation
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ cpi_unlock_context(context);
+
+#ifdef CP_THREADS
+ assert(context->env->mutex == NULL || !cpi_is_mutex_locked(context->env->mutex));
+#else
+ assert(!context->env->locked);
+#endif
+
+ // Remove context from the context list
+ cpi_lock_framework();
+ if (contexts != NULL) {
+ lnode_t *node;
+
+ if ((node = list_find(contexts, context, cpi_comp_ptr)) != NULL) {
+ list_delete(contexts, node);
+ lnode_destroy(node);
+ }
+ }
+ cpi_unlock_framework();
+
+ // Unload all plug-ins
+ cp_uninstall_plugins(context);
+
+ // Release remaining information objects
+ cpi_release_infos(context);
+
+ // Free context
+ cpi_free_context(context);
+}
+
+CP_HIDDEN void cpi_destroy_all_contexts(void) {
+ cpi_lock_framework();
+ if (contexts != NULL) {
+ lnode_t *node;
+
+ while ((node = list_last(contexts)) != NULL) {
+ cpi_unlock_framework();
+ cp_destroy_context(lnode_get(node));
+ cpi_lock_framework();
+ }
+ list_destroy(contexts);
+ contexts = NULL;
+ }
+ cpi_unlock_framework();
+}
+
+
+// Plug-in directories
+
+CP_C_API cp_status_t cp_register_pcollection(cp_context_t *context, const char *dir) {
+ char *d = NULL;
+ lnode_t *node = NULL;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(dir);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ do {
+
+ // Check if directory has already been registered
+ if (list_find(context->env->plugin_dirs, dir, (int (*)(const void *, const void *)) strcmp) != NULL) {
+ break;
+ }
+
+ // Allocate resources
+ d = malloc(sizeof(char) * (strlen(dir) + 1));
+ node = lnode_create(d);
+ if (d == NULL || node == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Register directory
+ strcpy(d, dir);
+ list_append(context->env->plugin_dirs, node);
+
+ } while (0);
+
+ // Report error or success
+ if (status != CP_OK) {
+ cpi_errorf(context, N_("The plug-in collection in path %s could not be registered due to insufficient memory."), dir);
+ } else {
+ cpi_debugf(context, N_("The plug-in collection in path %s was registered."), dir);
+ }
+ cpi_unlock_context(context);
+
+ // Release resources on failure
+ if (status != CP_OK) {
+ if (d != NULL) {
+ free(d);
+ }
+ if (node != NULL) {
+ lnode_destroy(node);
+ }
+ }
+
+ return status;
+}
+
+CP_C_API void cp_unregister_pcollection(cp_context_t *context, const char *dir) {
+ char *d;
+ lnode_t *node;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(dir);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ node = list_find(context->env->plugin_dirs, dir, (int (*)(const void *, const void *)) strcmp);
+ if (node != NULL) {
+ d = lnode_get(node);
+ list_delete(context->env->plugin_dirs, node);
+ lnode_destroy(node);
+ free(d);
+ }
+ cpi_debugf(context, N_("The plug-in collection in path %s was unregistered."), dir);
+ cpi_unlock_context(context);
+}
+
+CP_C_API void cp_unregister_pcollections(cp_context_t *context) {
+ CHECK_NOT_NULL(context);
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ list_process(context->env->plugin_dirs, NULL, cpi_process_free_ptr);
+ cpi_debug(context, N_("All plug-in collections were unregistered."));
+ cpi_unlock_context(context);
+}
+
+
+// Startup arguments
+
+CP_C_API void cp_set_context_args(cp_context_t *ctx, char **argv) {
+ int argc;
+
+ CHECK_NOT_NULL(ctx);
+ CHECK_NOT_NULL(argv);
+ for (argc = 0; argv[argc] != NULL; argc++);
+ if (argc < 1) {
+ cpi_fatalf(_("At least one startup argument must be given in call to function %s."), __func__);
+ }
+ cpi_lock_context(ctx);
+ ctx->env->argc = argc;
+ ctx->env->argv = argv;
+ cpi_unlock_context(ctx);
+}
+
+CP_C_API char **cp_get_context_args(cp_context_t *ctx, int *argc) {
+ char **argv;
+
+ CHECK_NOT_NULL(ctx);
+ cpi_lock_context(ctx);
+ if (argc != NULL) {
+ *argc = ctx->env->argc;
+ }
+ argv = ctx->env->argv;
+ cpi_unlock_context(ctx);
+ return argv;
+}
+
+
+// Checking API call invocation
+
+CP_HIDDEN void cpi_check_invocation(cp_context_t *ctx, int funcmask, const char *func) {
+ assert(ctx != NULL);
+ assert(funcmask != 0);
+ assert(func != NULL);
+ assert(cpi_is_context_locked(ctx));
+ if ((funcmask & CPI_CF_LOGGER)
+ &&ctx->env->in_logger_invocation) {
+ cpi_fatalf(_("Function %s was called from within a logger invocation."), func);
+ }
+ if ((funcmask & CPI_CF_LISTENER)
+ && ctx->env->in_event_listener_invocation) {
+ cpi_fatalf(_("Function %s was called from within an event listener invocation."), func);
+ }
+ if ((funcmask & CPI_CF_START)
+ && ctx->env->in_start_func_invocation) {
+ cpi_fatalf(_("Function %s was called from within a plug-in start function invocation."), func);
+ }
+ if ((funcmask & CPI_CF_STOP)
+ && ctx->env->in_stop_func_invocation) {
+ cpi_fatalf(_("Function %s was called from within a plug-in stop function invocation."), func);
+ }
+ if (ctx->env->in_create_func_invocation) {
+ cpi_fatalf(_("Function %s was called from within a plug-in create function invocation."), func);
+ }
+ if (ctx->env->in_destroy_func_invocation) {
+ cpi_fatalf(_("Function %s was called from within a plug-in destroy function invocation."), func);
+ }
+}
+
+
+// Locking
+
+#if defined(CP_THREADS) || !defined(NDEBUG)
+
+CP_HIDDEN void cpi_lock_context(cp_context_t *context) {
+#if defined(CP_THREADS)
+ cpi_lock_mutex(context->env->mutex);
+#elif !defined(NDEBUG)
+ context->env->locked++;
+#endif
+}
+
+CP_HIDDEN void cpi_unlock_context(cp_context_t *context) {
+#if defined(CP_THREADS)
+ cpi_unlock_mutex(context->env->mutex);
+#elif !defined(NDEBUG)
+ assert(context->env->locked > 0);
+ context->env->locked--;
+#endif
+}
+
+CP_HIDDEN void cpi_wait_context(cp_context_t *context) {
+#if defined(CP_THREADS)
+ cpi_wait_mutex(context->env->mutex);
+#elif !defined(NDEBUG)
+ assert(context->env->locked > 0);
+ assert(0);
+#endif
+}
+
+CP_HIDDEN void cpi_signal_context(cp_context_t *context) {
+#if defined(CP_THREADS)
+ cpi_signal_mutex(context->env->mutex);
+#elif !defined(NDEBUG)
+ assert(context->env->locked > 0);
+#endif
+}
+
+
+// Debug helpers
+
+CP_HIDDEN char *cpi_context_owner(cp_context_t *ctx, char *name, size_t size) {
+ if (ctx->plugin != NULL) {
+ /* TRANSLATORS: The context owner (when it is a plug-in) used in some strings.
+ Search for "context owner" to find these strings. */
+ snprintf(name, size, _("Plug-in %s"), ctx->plugin->plugin->identifier);
+ } else {
+ /* TRANSLATORS: The context owner (when it is the main program) used in some strings.
+ Search for "context owner" to find these strings. */
+ strncpy(name, _("The main program"), size);
+ }
+ assert(size >= 4);
+ strcpy(name + size - 4, "...");
+ return name;
+}
+
+#endif
diff --git a/lib/cpluff/libcpluff/cpluff.c b/lib/cpluff/libcpluff/cpluff.c
new file mode 100644
index 0000000000..a7e872542c
--- /dev/null
+++ b/lib/cpluff/libcpluff/cpluff.c
@@ -0,0 +1,182 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Core framework functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#ifdef DLOPEN_LIBTOOL
+#include <ltdl.h>
+#endif
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#ifdef CP_THREADS
+#include "thread.h"
+#endif
+#include "internal.h"
+
+
+/* ------------------------------------------------------------------------
+ * Variables
+ * ----------------------------------------------------------------------*/
+
+/// Number of initializations
+static int initialized = 0;
+
+#ifdef CP_THREADS
+
+/// Framework mutex
+static cpi_mutex_t *framework_mutex = NULL;
+
+#elif !defined(NDEBUG)
+
+/// Framework locking count
+static int framework_locked = 0;
+
+#endif
+
+/// Fatal error handler, or NULL for default
+static cp_fatal_error_func_t fatal_error_handler = NULL;
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+CP_C_API const char *cp_get_version(void) {
+ return CP_VERSION;
+}
+
+CP_C_API const char *cp_get_host_type(void) {
+ return CP_HOST;
+}
+
+CP_HIDDEN void cpi_lock_framework(void) {
+#if defined(CP_THREADS)
+ cpi_lock_mutex(framework_mutex);
+#elif !defined(NDEBUG)
+ framework_locked++;
+#endif
+}
+
+CP_HIDDEN void cpi_unlock_framework(void) {
+#if defined(CP_THREADS)
+ cpi_unlock_mutex(framework_mutex);
+#elif !defined(NDEBUG)
+ assert(framework_locked > 0);
+ framework_locked--;
+#endif
+}
+
+static void reset(void) {
+#ifdef CP_THREADS
+ if (framework_mutex != NULL) {
+ cpi_destroy_mutex(framework_mutex);
+ framework_mutex = NULL;
+ }
+#endif
+}
+
+CP_C_API cp_status_t cp_init(void) {
+ cp_status_t status = CP_OK;
+
+ // Initialize if necessary
+ do {
+ if (!initialized) {
+ bindtextdomain(PACKAGE, CP_DATADIR CP_FNAMESEP_STR "locale");
+#ifdef CP_THREADS
+ if ((framework_mutex = cpi_create_mutex()) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+#endif
+#ifdef DLOPEN_LIBTOOL
+ if (lt_dlinit()) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+#endif
+ }
+ initialized++;
+ } while (0);
+
+ // Rollback on failure
+ if (status != CP_OK) {
+ reset();
+ }
+
+ return status;
+}
+
+CP_C_API void cp_destroy(void) {
+ assert(initialized > 0);
+ initialized--;
+ if (!initialized) {
+#ifdef CP_THREADS
+ assert(framework_mutex == NULL || !cpi_is_mutex_locked(framework_mutex));
+#else
+ assert(!framework_locked);
+#endif
+ cpi_destroy_all_contexts();
+#ifdef DLOPEN_LIBTOOL
+ lt_dlexit();
+#endif
+ reset();
+ }
+}
+
+CP_C_API void cp_set_fatal_error_handler(cp_fatal_error_func_t error_handler) {
+ fatal_error_handler = error_handler;
+}
+
+CP_HIDDEN void cpi_fatalf(const char *msg, ...) {
+ va_list params;
+ char fmsg[256];
+
+ // Format message
+ assert(msg != NULL);
+ va_start(params, msg);
+ vsnprintf(fmsg, sizeof(fmsg), msg, params);
+ va_end(params);
+ fmsg[sizeof(fmsg)/sizeof(char) - 1] = '\0';
+
+ // Call error handler or print the error message
+ if (fatal_error_handler != NULL) {
+ fatal_error_handler(fmsg);
+ } else {
+ fprintf(stderr, _("C-Pluff: FATAL ERROR: %s\n"), fmsg);
+ }
+
+ // Abort if still alive
+ abort();
+}
+
+CP_HIDDEN void cpi_fatal_null_arg(const char *arg, const char *func) {
+ cpi_fatalf(_("Argument %s has illegal NULL value in call to function %s."), arg, func);
+}
diff --git a/lib/cpluff/libcpluff/cpluff.h b/lib/cpluff/libcpluff/cpluff.h
new file mode 100644
index 0000000000..0f9797d37a
--- /dev/null
+++ b/lib/cpluff/libcpluff/cpluff.h
@@ -0,0 +1,1497 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * C-Pluff C API header file.
+ * The elements declared here constitute the C-Pluff C API. To use the
+ * API include this file and link the main program and plug-in runtime
+ * libraries with the C-Pluff C library. In addition to local declarations,
+ * this file also includes cpluffdef.h header file for defines common to C
+ * and C++ API.
+ */
+
+#ifndef CPLUFF_H_
+#define CPLUFF_H_
+
+/**
+ * @defgroup cDefines Defines
+ * Preprocessor defines.
+ */
+
+#include <cpluffdef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /*__cplusplus*/
+
+
+/* ------------------------------------------------------------------------
+ * Defines
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @def CP_C_API
+ * @ingroup cDefines
+ *
+ * Marks a symbol declaration to be part of the C-Pluff C API.
+ * This macro declares the symbol to be imported from the C-Pluff library.
+ */
+
+#ifndef CP_C_API
+#define CP_C_API CP_IMPORT
+#endif
+
+
+/**
+ * @defgroup cScanFlags Flags for plug-in scanning
+ * @ingroup cDefines
+ *
+ * These constants can be orred together for the flags
+ * parameter of ::cp_scan_plugins.
+ */
+/*@{*/
+
+/**
+ * This flag enables upgrades of installed plug-ins by unloading
+ * the old version and installing the new version.
+ */
+#define CP_SP_UPGRADE 0x01
+
+/**
+ * This flag causes all plug-ins to be stopped before any
+ * plug-ins are to be upgraded.
+ */
+#define CP_SP_STOP_ALL_ON_UPGRADE 0x02
+
+/**
+ * This flag causes all plug-ins to be stopped before any
+ * plugins are to be installed (also if new version is to be installed
+ * as part of an upgrade).
+ */
+#define CP_SP_STOP_ALL_ON_INSTALL 0x04
+
+/**
+ * Setting this flag causes the currently active plug-ins to be restarted
+ * after all changes to the plug-ins have been made (if they were stopped).
+ */
+#define CP_SP_RESTART_ACTIVE 0x08
+
+/*@}*/
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup cEnums Enumerations
+ * Constant value enumerations.
+ */
+
+/**
+ * @defgroup cTypedefs Typedefs
+ * Typedefs of various kind.
+ */
+
+/**
+ * @defgroup cStructs Data structures
+ * Data structure definitions.
+ */
+
+
+/* Enumerations */
+
+/**
+ * @ingroup cEnums
+ *
+ * An enumeration of status codes returned by API functions.
+ * Most of the interface functions return a status code. The returned
+ * status code either indicates successful completion of the operation
+ * or some specific kind of error. Some functions do not return a status
+ * code because they never fail.
+ */
+enum cp_status_t {
+
+ /**
+ * Operation was performed successfully (equals to zero).
+ * @showinitializer
+ */
+ CP_OK = 0,
+
+ /** Not enough memory or other operating system resources available */
+ CP_ERR_RESOURCE,
+
+ /** The specified object is unknown to the framework */
+ CP_ERR_UNKNOWN,
+
+ /** An I/O error occurred */
+ CP_ERR_IO,
+
+ /** Malformed plug-in descriptor was encountered when loading a plug-in */
+ CP_ERR_MALFORMED,
+
+ /** Plug-in or symbol conflicts with another plug-in or symbol. */
+ CP_ERR_CONFLICT,
+
+ /** Plug-in dependencies could not be satisfied. */
+ CP_ERR_DEPENDENCY,
+
+ /** Plug-in runtime signaled an error. */
+ CP_ERR_RUNTIME
+
+};
+
+/**
+ * @ingroup cEnums
+ * An enumeration of possible plug-in states. Plug-in states are controlled
+ * by @ref cFuncsPlugin "plug-in management functions". Plug-in states can be
+ * observed by @ref cp_register_plistener "registering" a
+ * @ref cp_plugin_listener_func_t "plug-in listener function"
+ * or by calling ::cp_get_plugin_state.
+ *
+ * @sa cp_plugin_listener_t
+ * @sa cp_get_plugin_state
+ */
+enum cp_plugin_state_t {
+
+ /**
+ * Plug-in is not installed. No plug-in information has been
+ * loaded.
+ */
+ CP_PLUGIN_UNINSTALLED,
+
+ /**
+ * Plug-in is installed. At this stage the plug-in information has
+ * been loaded but its dependencies to other plug-ins has not yet
+ * been resolved. The plug-in runtime has not been loaded yet.
+ * The extension points and extensions provided by the plug-in
+ * have been registered.
+ */
+ CP_PLUGIN_INSTALLED,
+
+ /**
+ * Plug-in dependencies have been resolved. At this stage it has
+ * been verified that the dependencies of the plug-in are satisfied
+ * and the plug-in runtime has been loaded but it is not active
+ * (it has not been started or it has been stopped).
+ * Plug-in is resolved when a dependent plug-in is being
+ * resolved or before the plug-in is started. Plug-in is put
+ * back to installed stage if its dependencies are being
+ * uninstalled.
+ */
+ CP_PLUGIN_RESOLVED,
+
+ /**
+ * Plug-in is starting. The plug-in has been resolved and the start
+ * function (if any) of the plug-in runtime is about to be called.
+ * A plug-in is started when explicitly requested by the main
+ * program or when a dependent plug-in is about to be started or when
+ * a dynamic symbol defined by the plug-in is being resolved. This state
+ * is omitted and the state changes directly from resolved to active
+ * if the plug-in runtime does not define a start function.
+ */
+ CP_PLUGIN_STARTING,
+
+ /**
+ * Plug-in is stopping. The stop function (if any) of the plug-in
+ * runtime is about to be called. A plug-in is stopped if the start
+ * function fails or when stopping is explicitly
+ * requested by the main program or when its dependencies are being
+ * stopped. This state is omitted and the state changes directly from
+ * active to resolved if the plug-in runtime does not define a stop
+ * function.
+ */
+ CP_PLUGIN_STOPPING,
+
+ /**
+ * Plug-in has been successfully started and it has not yet been
+ * stopped.
+ */
+ CP_PLUGIN_ACTIVE
+
+};
+
+/**
+ * @ingroup cEnums
+ * An enumeration of possible message severities for framework logging. These
+ * constants are used when passing a log message to a
+ * @ref cp_logger_func_t "logger function" and when
+ * @ref cp_register_logger "registering" a logger function.
+ */
+enum cp_log_severity_t {
+
+ /** Used for detailed debug messages */
+ CP_LOG_DEBUG,
+
+ /** Used for informational messages such as plug-in state changes */
+ CP_LOG_INFO,
+
+ /** Used for messages warning about possible problems */
+ CP_LOG_WARNING,
+
+ /** Used for messages reporting errors */
+ CP_LOG_ERROR
+
+};
+
+/*@}*/
+
+
+/* Typedefs */
+
+/**
+ * @defgroup cTypedefsOpaque Opaque types
+ * @ingroup cTypedefs
+ * Opaque data type definitions.
+ */
+/*@{*/
+
+/**
+ * A plug-in context represents the co-operation environment of a set of
+ * plug-ins from the perspective of a particular participating plug-in or
+ * the perspective of the main program. It is used as an opaque handle to
+ * the shared resources but the framework also uses the context to identify
+ * the plug-in or the main program invoking framework functions. Therefore
+ * a plug-in should not generally expose its context instance to other
+ * plug-ins or the main program and neither should the main program
+ * expose its context instance to plug-ins. The main program creates
+ * plug-in contexts using ::cp_create_context and plug-ins receive their
+ * plug-in contexts via @ref cp_plugin_runtime_t::create.
+ */
+typedef struct cp_context_t cp_context_t;
+
+/*@}*/
+
+ /**
+ * @defgroup cTypedefsShorthand Shorthand type names
+ * @ingroup cTypedefs
+ * Shorthand type names for structs and enumerations.
+ */
+/*@{*/
+
+/** A type for cp_plugin_info_t structure. */
+typedef struct cp_plugin_info_t cp_plugin_info_t;
+
+/** A type for cp_plugin_import_t structure. */
+typedef struct cp_plugin_import_t cp_plugin_import_t;
+
+/** A type for cp_ext_point_t structure. */
+typedef struct cp_ext_point_t cp_ext_point_t;
+
+/** A type for cp_extension_t structure. */
+typedef struct cp_extension_t cp_extension_t;
+
+/** A type for cp_cfg_element_t structure. */
+typedef struct cp_cfg_element_t cp_cfg_element_t;
+
+/** A type for cp_plugin_runtime_t structure. */
+typedef struct cp_plugin_runtime_t cp_plugin_runtime_t;
+
+/** A type for cp_status_t enumeration. */
+typedef enum cp_status_t cp_status_t;
+
+/** A type for cp_plugin_state_t enumeration. */
+typedef enum cp_plugin_state_t cp_plugin_state_t;
+
+/** A type for cp_log_severity_t enumeration. */
+typedef enum cp_log_severity_t cp_log_severity_t;
+
+/*@}*/
+
+/**
+ * @defgroup cTypedefsFuncs Callback function types
+ * @ingroup cTypedefs
+ * Typedefs for client supplied callback functions.
+ */
+/*@{*/
+
+/**
+ * A listener function called synchronously after a plugin state change.
+ * The function should return promptly.
+ * @ref cFuncsInit "Library initialization",
+ * @ref cFuncsContext "plug-in context management",
+ * @ref cFuncsPlugin "plug-in management",
+ * listener registration (::cp_register_plistener and ::cp_unregister_plistener)
+ * and @ref cFuncsSymbols "dynamic symbol" functions must not be called from
+ * within a plug-in listener invocation. Listener functions are registered
+ * using ::cp_register_plistener.
+ *
+ * @param plugin_id the plug-in identifier
+ * @param old_state the old plug-in state
+ * @param new_state the new plug-in state
+ * @param user_data the user data pointer supplied at listener registration
+ */
+typedef void (*cp_plugin_listener_func_t)(const char *plugin_id, cp_plugin_state_t old_state, cp_plugin_state_t new_state, void *user_data);
+
+/**
+ * A logger function called to log selected plug-in framework messages. The
+ * messages may be localized. Plug-in framework API functions must not
+ * be called from within a logger function invocation. In a multi-threaded
+ * environment logger function invocations are serialized by the framework.
+ * Logger functions are registered using ::cp_register_logger.
+ *
+ * @param severity the severity of the message
+ * @param msg the message to be logged, possibly localized
+ * @param apid the identifier of the activating plug-in or NULL for the main program
+ * @param user_data the user data pointer given when the logger was registered
+ */
+typedef void (*cp_logger_func_t)(cp_log_severity_t severity, const char *msg, const char *apid, void *user_data);
+
+/**
+ * A fatal error handler for handling unrecoverable errors. If the error
+ * handler returns then the framework aborts the program. Plug-in framework
+ * API functions must not be called from within a fatal error handler
+ * invocation. The fatal error handler function is set using
+ * ::cp_set_fatal_error_handler.
+ *
+ * @param msg the possibly localized error message
+ */
+typedef void (*cp_fatal_error_func_t)(const char *msg);
+
+/**
+ * A run function registered by a plug-in to perform work.
+ * The run function should perform a finite chunk of work and it should
+ * return a non-zero value if there is more work to be done. Run functions
+ * are registered using ::cp_run_function and the usage is discussed in
+ * more detail in the @ref cFuncsPluginExec "serial execution" section.
+ *
+ * @param plugin_data the plug-in instance data pointer
+ * @return non-zero if there is more work to be done or zero if finished
+ */
+typedef int (*cp_run_func_t)(void *plugin_data);
+
+/*@}*/
+
+
+/* Data structures */
+
+/**
+ * @ingroup cStructs
+ * Plug-in information structure captures information about a plug-in. This
+ * information can be loaded from a plug-in descriptor using
+ * ::cp_load_plugin_descriptor. Information about installed plug-ins can
+ * be obtained using ::cp_get_plugin_info and ::cp_get_plugins_info. This
+ * structure corresponds to the @a plugin element in a plug-in descriptor.
+ */
+struct cp_plugin_info_t {
+
+ /**
+ * The obligatory unique identifier of the plugin. A recommended way
+ * to generate identifiers is to use domain name service (DNS) prefixes
+ * (for example, org.cpluff.ExamplePlugin) to avoid naming conflicts. This
+ * corresponds to the @a id attribute of the @a plugin element in a plug-in
+ * descriptor.
+ */
+ char *identifier;
+
+ /**
+ * An optional plug-in name. NULL if not available. The plug-in name is
+ * intended only for display purposes and the value can be localized.
+ * This corresponds to the @a name attribute of the @a plugin element in
+ * a plug-in descriptor.
+ */
+ char *name;
+
+ /**
+ * An optional release version string. NULL if not available. This
+ * corresponds to the @a version attribute of the @a plugin element in
+ * a plug-in descriptor.
+ */
+ char *version;
+
+ /**
+ * An optional provider name. NULL if not available. This is the name of
+ * the author or the organization providing the plug-in. The
+ * provider name is intended only for display purposes and the value can
+ * be localized. This corresponds to the @a provider-name attribute of the
+ * @a plugin element in a plug-in descriptor.
+ */
+ char *provider_name;
+
+ /**
+ * Path of the plugin directory or NULL if not known. This is the
+ * (absolute or relative) path to the plug-in directory containing
+ * plug-in data and the plug-in runtime library. The value corresponds
+ * to the path specified to ::cp_load_plugin_descriptor when loading
+ * the plug-in.
+ */
+ char *plugin_path;
+
+ /**
+ * Optional ABI compatibility information. NULL if not available.
+ * This is the earliest version of the plug-in interface the current
+ * interface is backwards compatible with when it comes to the application
+ * binary interface (ABI) of the plug-in. That is, plug-in clients compiled against
+ * any plug-in interface version from @a abi_bw_compatibility to
+ * @ref version (inclusive) can use the current version of the plug-in
+ * binary. This describes binary or runtime compatibility.
+ * The value corresponds to the @a abi-compatibility
+ * attribute of the @a backwards-compatibility element in a plug-in descriptor.
+ */
+ char *abi_bw_compatibility;
+
+ /**
+ * Optional API compatibility information. NULL if not available.
+ * This is the earliest version of the plug-in interface the current
+ * interface is backwards compatible with when it comes to the
+ * application programming interface (API) of the plug-in. That is,
+ * plug-in clients written for any plug-in interface version from
+ * @a api_bw_compatibility to @ref version (inclusive) can be compiled
+ * against the current version of the plug-in API. This describes
+ * source or build time compatibility. The value corresponds to the
+ * @a api-compatibility attribute of the @a backwards-compatibility
+ * element in a plug-in descriptor.
+ */
+ char *api_bw_compatibility;
+
+ /**
+ * Optional C-Pluff version requirement. NULL if not available.
+ * This is the version of the C-Pluff implementation the plug-in was
+ * compiled against. It is used to determine the compatibility of
+ * the plug-in runtime and the linked in C-Pluff implementation. Any
+ * C-Pluff version that is backwards compatible on binary level with the
+ * specified version fulfills the requirement.
+ */
+ char *req_cpluff_version;
+
+ /** Number of import entries in the @ref imports array. */
+ unsigned int num_imports;
+
+ /**
+ * An array of @ref num_imports import entries. These correspond to
+ * @a import elements in a plug-in descriptor.
+ */
+ cp_plugin_import_t *imports;
+
+ /**
+ * The base name of the plug-in runtime library, or NULL if none.
+ * A platform specific prefix (for example, "lib") and an extension
+ * (for example, ".dll" or ".so") may be added to the base name.
+ * This corresponds to the @a library attribute of the
+ * @a runtime element in a plug-in descriptor.
+ */
+ char *runtime_lib_name;
+
+ /**
+ * The symbol pointing to the plug-in runtime function information or
+ * NULL if none. The symbol with this name should point to an instance of
+ * @ref cp_plugin_runtime_t structure. This corresponds to the
+ * @a funcs attribute of the @a runtime element in a plug-in descriptor.
+ */
+ char *runtime_funcs_symbol;
+
+ /** Number of extension points in @ref ext_points array. */
+ unsigned int num_ext_points;
+
+ /**
+ * An array of @ref num_ext_points extension points provided by this
+ * plug-in. These correspond to @a extension-point elements in a
+ * plug-in descriptor.
+ */
+ cp_ext_point_t *ext_points;
+
+ /** Number of extensions in @ref extensions array. */
+ unsigned int num_extensions;
+
+ /**
+ * An array of @ref num_extensions extensions provided by this
+ * plug-in. These correspond to @a extension elements in a plug-in
+ * descriptor.
+ */
+ cp_extension_t *extensions;
+
+};
+
+/**
+ * @ingroup cStructs
+ * Information about plug-in import. Plug-in import structures are
+ * contained in @ref cp_plugin_info_t::imports.
+ */
+struct cp_plugin_import_t {
+
+ /**
+ * The identifier of the imported plug-in. This corresponds to the
+ * @a plugin attribute of the @a import element in a plug-in descriptor.
+ */
+ char *plugin_id;
+
+ /**
+ * An optional version requirement. NULL if no version requirement.
+ * This is the version of the imported plug-in the importing plug-in was
+ * compiled against. Any version of the imported plug-in that is
+ * backwards compatible with this version fulfills the requirement.
+ * This corresponds to the @a if-version attribute of the @a import
+ * element in a plug-in descriptor.
+ */
+ char *version;
+
+ /**
+ * Is this import optional. 1 for optional and 0 for mandatory import.
+ * An optional import causes the imported plug-in to be started if it is
+ * available but does not stop the importing plug-in from starting if the
+ * imported plug-in is not available. If the imported plug-in is available
+ * but the API version conflicts with the API version requirement then the
+ * importing plug-in fails to start. This corresponds to the @a optional
+ * attribute of the @a import element in a plug-in descriptor.
+ */
+ int optional;
+};
+
+/**
+ * @ingroup cStructs
+ * Extension point structure captures information about an extension
+ * point. Extension point structures are contained in
+ * @ref cp_plugin_info_t::ext_points.
+ */
+struct cp_ext_point_t {
+
+ /**
+ * A pointer to plug-in information containing this extension point.
+ * This reverse pointer is provided to make it easy to get information
+ * about the plug-in which is hosting a particular extension point.
+ */
+ cp_plugin_info_t *plugin;
+
+ /**
+ * The local identifier uniquely identifying the extension point within the
+ * host plug-in. This corresponds to the @name id attribute of an
+ * @a extension-point element in a plug-in descriptor.
+ */
+ char *local_id;
+
+ /**
+ * The unique identifier of the extension point. This is automatically
+ * constructed by concatenating the identifier of the host plug-in and
+ * the local identifier of the extension point.
+ */
+ char *identifier;
+
+ /**
+ * An optional extension point name. NULL if not available. The extension
+ * point name is intended for display purposes only and the value can be
+ * localized. This corresponds to the @a name attribute of
+ * an @a extension-point element in a plug-in descriptor.
+ */
+ char *name;
+
+ /**
+ * An optional path to the extension schema definition.
+ * NULL if not available. The path is relative to the plug-in directory.
+ * This corresponds to the @a schema attribute
+ * of an @a extension-point element in a plug-in descriptor.
+ */
+ char *schema_path;
+};
+
+/**
+ * @ingroup cStructs
+ * Extension structure captures information about an extension. Extension
+ * structures are contained in @ref cp_plugin_info_t::extensions.
+ */
+struct cp_extension_t {
+
+ /**
+ * A pointer to plug-in information containing this extension.
+ * This reverse pointer is provided to make it easy to get information
+ * about the plug-in which is hosting a particular extension.
+ */
+ cp_plugin_info_t *plugin;
+
+ /**
+ * The unique identifier of the extension point this extension is
+ * attached to. This corresponds to the @a point attribute of an
+ * @a extension element in a plug-in descriptor.
+ */
+ char *ext_point_id;
+
+ /**
+ * An optional local identifier uniquely identifying the extension within
+ * the host plug-in. NULL if not available. This corresponds to the
+ * @a id attribute of an @a extension element in a plug-in descriptor.
+ */
+ char *local_id;
+
+ /**
+ * An optional unique identifier of the extension. NULL if not available.
+ * This is automatically constructed by concatenating the identifier
+ * of the host plug-in and the local identifier of the extension.
+ */
+ char *identifier;
+
+ /**
+ * An optional extension name. NULL if not available. The extension name
+ * is intended for display purposes only and the value can be localized.
+ * This corresponds to the @a name attribute
+ * of an @a extension element in a plug-in descriptor.
+ **/
+ char *name;
+
+ /**
+ * Extension configuration starting with the extension element.
+ * This includes extension configuration information as a tree of
+ * configuration elements. These correspond to the @a extension
+ * element and its contents in a plug-in descriptor.
+ */
+ cp_cfg_element_t *configuration;
+};
+
+/**
+ * @ingroup cStructs
+ * A configuration element contains configuration information for an
+ * extension. Utility functions ::cp_lookup_cfg_element and
+ * ::cp_lookup_cfg_value can be used for traversing the tree of
+ * configuration elements. Pointer to the root configuration element is
+ * stored at @ref cp_extension_t::configuration and others are contained as
+ * @ref cp_cfg_element_t::children "children" of parent elements.
+ */
+struct cp_cfg_element_t {
+
+ /**
+ * The name of the configuration element. This corresponds to the name of
+ * the element in a plug-in descriptor.
+ */
+ char *name;
+
+ /** Number of attribute name, value pairs in the @ref atts array. */
+ unsigned int num_atts;
+
+ /**
+ * An array of pointers to alternating attribute names and values.
+ * Attribute values can be localized.
+ */
+ char **atts;
+
+ /**
+ * An optional value of this configuration element. NULL if not available.
+ * The value can be localized. This corresponds to the
+ * text contents of the element in a plug-in descriptor.
+ */
+ char *value;
+
+ /** A pointer to the parent element or NULL if this is a root element. */
+ cp_cfg_element_t *parent;
+
+ /** The index of this element among its siblings (0-based). */
+ unsigned int index;
+
+ /** Number of children in the @ref children array. */
+ unsigned int num_children;
+
+ /**
+ * An array of @ref num_children childrens of this element. These
+ * correspond to child elements in a plug-in descriptor.
+ */
+ cp_cfg_element_t *children;
+};
+
+/**
+ * @ingroup cStructs
+ * Container for plug-in runtime information. A plug-in runtime defines a
+ * static instance of this structure to pass information to the plug-in
+ * framework. The plug-in framework then uses the information
+ * to create and control plug-in instances. The symbol pointing
+ * to the runtime information instance is named by the @a funcs
+ * attribute of the @a runtime element in a plug-in descriptor.
+ *
+ * The following graph displays how these functions are used to control the
+ * state of the plug-in instance.
+ *
+ * @dot
+ * digraph lifecycle {
+ * rankdir=LR;
+ * node [shape=ellipse, fontname=Helvetica, fontsize=10];
+ * edge [fontname=Helvetica, fontsize=10];
+ * none [label="no instance"];
+ * inactive [label="inactive"];
+ * active [label="active"];
+ * none -> inactive [label="create", URL="\ref create"];
+ * inactive -> active [label="start", URL="\ref start"];
+ * active -> inactive [label="stop", URL="\ref stop"];
+ * inactive -> none [label="destroy", URL="\ref destroy"];
+ * }
+ * @enddot
+ */
+struct cp_plugin_runtime_t {
+
+ /**
+ * An initialization function called to create a new plug-in
+ * runtime instance. The initialization function initializes and
+ * returns an opaque plug-in instance data pointer which is then
+ * passed on to other control functions. This data pointer should
+ * be used to access plug-in instance specific data. For example,
+ * the context reference must be stored as part of plug-in instance
+ * data if the plug-in runtime needs it. On failure, the function
+ * must return NULL.
+ *
+ * C-pluff API functions must not be called from within a create
+ * function invocation and symbols from imported plug-ins must not be
+ * used because they may not available yet.
+ *
+ * @param ctx the plug-in context of the new plug-in instance
+ * @return an opaque pointer to plug-in instance data or NULL on failure
+ */
+ void *(*create)(cp_context_t *ctx);
+
+ /**
+ * A start function called to start a plug-in instance.
+ * The start function must return zero (CP_OK) on success and non-zero
+ * on failure. If the start fails then the stop function (if any) is
+ * called to clean up plug-in state. @ref cFuncsInit "Library initialization",
+ * @ref cFuncsContext "plug-in context management" and
+ * @ref cFuncsPlugin "plug-in management" functions must not be
+ * called from within a start function invocation. The start function
+ * pointer can be NULL if the plug-in runtime does not have a start
+ * function.
+ *
+ * The start function implementation should set up plug-in and return
+ * promptly. If there is further work to be done then a plug-in can
+ * start a thread or register a run function using ::cp_run_function.
+ * Symbols from imported plug-ins are guaranteed to be available for
+ * the start function.
+ *
+ * @param data an opaque pointer to plug-in instance data
+ * @return non-zero on success, or zero on failure
+ */
+ int (*start)(void *data);
+
+ /**
+ * A stop function called to stop a plugin instance.
+ * This function must cease all plug-in runtime activities.
+ * @ref cFuncsInit "Library initialization",
+ * @ref cFuncsContext "plug-in context management",
+ * @ref cFuncsPlugin "plug-in management"
+ * functions, ::cp_run_function and ::cp_resolve_symbol must not be called
+ * from within a stop function invocation. The stop function pointer can
+ * be NULL if the plug-in runtime does not have a stop function.
+ * It is guaranteed that no run functions registered by the plug-in are
+ * called simultaneously or after the call to the stop function.
+ *
+ * The stop function should release any external resources hold by
+ * the plug-in. Dynamically resolved symbols are automatically released
+ * and dynamically defined symbols and registered run functions are
+ * automatically unregistered after the call to stop function.
+ * Resolved external symbols are still available for the stop function
+ * and symbols provided by the plug-in should remain available
+ * after the call to stop function (although functionality might be
+ * limited). Final cleanup can be safely done in the destroy function.
+ *
+ * @param data an opaque pointer to plug-in instance data
+ */
+ void (*stop)(void *data);
+
+ /**
+ * A destroy function called to destroy a plug-in instance.
+ * This function should release any plug-in instance data.
+ * The plug-in is stopped before this function is called.
+ * C-Pluff API functions must not be called from within a destroy
+ * function invocation and symbols from imported plug-ins must not be
+ * used because they may not be available anymore. Correspondingly,
+ * it is guaranteed that the symbols provided by the plug-in are not
+ * used by other plug-ins when destroy function has been called.
+ *
+ * @param data an opaque pointer to plug-in instance data
+ */
+ void (*destroy)(void *data);
+
+};
+
+/*@}*/
+
+
+/* ------------------------------------------------------------------------
+ * Function declarations
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup cFuncs Functions
+ *
+ * C API functions. The C-Pluff C API functions and
+ * any data exposed by them are generally thread-safe if the library has been
+ * compiled with multi-threading support. The
+ * @ref cFuncsInit "framework initialization functions"
+ * are exceptions, they are not thread-safe.
+ */
+
+/**
+ * @defgroup cFuncsFrameworkInfo Framework information
+ * @ingroup cFuncs
+ *
+ * These functions can be used to query runtime information about the
+ * linked in C-Pluff implementation. They may be used by the main program or
+ * by a plug-in runtime.
+ */
+/*@{*/
+
+/**
+ * Returns the release version string of the linked in C-Pluff
+ * implementation.
+ *
+ * @return the C-Pluff release version string
+ */
+CP_C_API const char *cp_get_version(void) CP_GCC_PURE;
+
+/**
+ * Returns the canonical host type associated with the linked in C-Pluff implementation.
+ * A multi-platform installation manager could use this information to
+ * determine what plug-in versions to install.
+ *
+ * @return the canonical host type
+ */
+CP_C_API const char *cp_get_host_type(void) CP_GCC_PURE;
+
+/*@}*/
+
+
+/**
+ * @defgroup cFuncsInit Framework initialization
+ * @ingroup cFuncs
+ *
+ * These functions are used for framework initialization.
+ * They are intended to be used by the main program. These functions are
+ * not thread safe.
+ */
+/*@{*/
+
+/**
+ * Sets the fatal error handler called on non-recoverable errors. The default
+ * error handler prints the error message out to standard error and aborts
+ * the program. If the user specified error handler returns then the framework
+ * will abort the program. Setting NULL error handler will restore the default
+ * handler. This function is not thread-safe and it should be called
+ * before initializing the framework to catch all fatal errors.
+ *
+ * @param error_handler the fatal error handler
+ */
+CP_C_API void cp_set_fatal_error_handler(cp_fatal_error_func_t error_handler);
+
+/**
+ * Initializes the plug-in framework. This function must be called
+ * by the main program before calling any other plug-in framework
+ * functions except @ref cFuncsFrameworkInfo "framework information" functions and
+ * ::cp_set_fatal_error_handler. This function may be
+ * called several times but it is not thread-safe. Library resources
+ * should be released by calling ::cp_destroy when the framework is
+ * not needed anymore.
+ *
+ * Additionally, to enable localization support, the main program should
+ * set the current locale using @code setlocale(LC_ALL, "") @endcode
+ * before calling this function.
+ *
+ * @return @ref CP_OK (zero) on success or error code on failure
+ */
+CP_C_API cp_status_t cp_init(void);
+
+/**
+ * Destroys the plug-in framework and releases the resources used by it.
+ * The plug-in framework is only destroyed after this function has
+ * been called as many times as ::cp_init. This function is not
+ * thread-safe. Plug-in framework functions other than ::cp_init,
+ * ::cp_get_framework_info and ::cp_set_fatal_error_handler
+ * must not be called after the plug-in framework has been destroyed.
+ * All contexts are destroyed and all data references returned by the
+ * framework become invalid.
+ */
+CP_C_API void cp_destroy(void);
+
+/*@}*/
+
+
+/**
+ * @defgroup cFuncsContext Plug-in context initialization
+ * @ingroup cFuncs
+ *
+ * These functions are used to manage plug-in contexts from the main
+ * program perspective. They are not intended to be used by a plug-in runtime.
+ * From the main program perspective a plug-in context is a container for
+ * installed plug-ins. There can be several plug-in context instances if there
+ * are several independent sets of plug-ins. However, different plug-in
+ * contexts are not very isolated from each other in practice because the
+ * global symbols exported by a plug-in runtime in one context are visible to
+ * all plug-ins in all context instances.
+ */
+/*@{*/
+
+/**
+ * Creates a new plug-in context which can be used as a container for plug-ins.
+ * Plug-ins are loaded and installed into a specific context. The main
+ * program may have more than one plug-in context but the plug-ins that
+ * interact with each other should be placed in the same context. The
+ * resources associated with the context are released by calling
+ * ::cp_destroy_context when the context is not needed anymore. Remaining
+ * contexts are automatically destroyed when the plug-in framework is
+ * destroyed.
+ *
+ * @param status pointer to the location where status code is to be stored, or NULL
+ * @return the newly created plugin context, or NULL on failure
+ */
+CP_C_API cp_context_t * cp_create_context(cp_status_t *status);
+
+/**
+ * Destroys the specified plug-in context and releases the associated resources.
+ * Stops and uninstalls all plug-ins in the context. The context must not be
+ * accessed after calling this function.
+ *
+ * @param ctx the context to be destroyed
+ */
+CP_C_API void cp_destroy_context(cp_context_t *ctx) CP_GCC_NONNULL(1);
+
+/**
+ * Registers a plug-in collection with a plug-in context. A plug-in collection
+ * is a directory that has plug-ins as its immediate subdirectories. The
+ * plug-in context will scan the directory when ::cp_scan_plugins is called.
+ * Returns @ref CP_OK if the directory has already been registered. A plug-in
+ * collection can be unregistered using ::cp_unregister_pcollection or
+ * ::cp_unregister_pcollections.
+ *
+ * @param ctx the plug-in context
+ * @param dir the directory
+ * @return @ref CP_OK (zero) on success or @ref CP_ERR_RESOURCE if insufficient memory
+ */
+CP_C_API cp_status_t cp_register_pcollection(cp_context_t *ctx, const char *dir) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Unregisters a previously registered plug-in collection from a
+ * plug-in context. Plug-ins already loaded from the collection are not
+ * affected. Does nothing if the directory has not been registered.
+ * Plug-in collections can be registered using ::cp_register_pcollection.
+ *
+ * @param ctx the plug-in context
+ * @param dir the previously registered directory
+ */
+CP_C_API void cp_unregister_pcollection(cp_context_t *ctx, const char *dir) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Unregisters all plug-in collections from a plug-in context.
+ * Plug-ins already loaded are not affected. Plug-in collections can
+ * be registered using ::cp_register_pcollection.
+ *
+ * @param ctx the plug-in context
+ */
+CP_C_API void cp_unregister_pcollections(cp_context_t *ctx) CP_GCC_NONNULL(1);
+
+/*@}*/
+
+
+/**
+ * @defgroup cFuncsLogging Logging
+ * @ingroup cFuncs
+ *
+ * These functions can be used to receive and emit log messages related
+ * to a particular plug-in context. They can be used by the main program
+ * or by a plug-in runtime.
+ */
+/*@{*/
+
+/**
+ * Registers a logger with a plug-in context or updates the settings of a
+ * registered logger. The logger will receive selected log messages.
+ * If the specified logger is not yet known, a new logger registration
+ * is made, otherwise the settings for the existing logger are updated.
+ * The logger can be unregistered using ::cp_unregister_logger and it is
+ * automatically unregistered when the registering plug-in is stopped or
+ * when the context is destroyed.
+ *
+ * @param ctx the plug-in context to log
+ * @param logger the logger function to be called
+ * @param user_data the user data pointer passed to the logger
+ * @param min_severity the minimum severity of messages passed to logger
+ * @return @ref CP_OK (zero) on success or @ref CP_ERR_RESOURCE if insufficient memory
+ */
+CP_C_API cp_status_t cp_register_logger(cp_context_t *ctx, cp_logger_func_t logger, void *user_data, cp_log_severity_t min_severity) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Removes a logger registration.
+ *
+ * @param ctx the plug-in context
+ * @param logger the logger function to be unregistered
+ */
+CP_C_API void cp_unregister_logger(cp_context_t *ctx, cp_logger_func_t logger) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Emits a new log message.
+ *
+ * @param ctx the plug-in context
+ * @param severity the severity of the event
+ * @param msg the log message (possibly localized)
+ */
+CP_C_API void cp_log(cp_context_t *ctx, cp_log_severity_t severity, const char *msg) CP_GCC_NONNULL(1, 3);
+
+/**
+ * Returns whether a message of the specified severity would get logged.
+ *
+ * @param ctx the plug-in context
+ * @param severity the target logging severity
+ * @return whether a message of the specified severity would get logged
+ */
+CP_C_API int cp_is_logged(cp_context_t *ctx, cp_log_severity_t severity) CP_GCC_NONNULL(1);
+
+/*@}*/
+
+
+/**
+ * @defgroup cFuncsPlugin Plug-in management
+ * @ingroup cFuncs
+ *
+ * These functions can be used to manage plug-ins. They are intended to be
+ * used by the main program.
+ */
+/*@{*/
+
+/**
+ * Loads a plug-in descriptor from the specified plug-in installation
+ * path and returns information about the plug-in. The plug-in descriptor
+ * is validated during loading. Possible loading errors are reported via the
+ * specified plug-in context. The plug-in is not installed to the context.
+ * If operation fails or the descriptor
+ * is invalid then NULL is returned. The caller must release the returned
+ * information by calling ::cp_release_plugin_info when it does not
+ * need the information anymore, typically after installing the plug-in.
+ * The returned plug-in information must not be modified.
+ *
+ * @param ctx the plug-in context
+ * @param path the installation path of the plug-in
+ * @param status a pointer to the location where status code is to be stored, or NULL
+ * @return pointer to the information structure or NULL if error occurs
+ */
+CP_C_API cp_plugin_info_t * cp_load_plugin_descriptor(cp_context_t *ctx, const char *path, cp_status_t *status) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Installs the plug-in described by the specified plug-in information
+ * structure to the specified plug-in context. The plug-in information
+ * must have been loaded using ::cp_load_plugin_descriptor with the same
+ * plug-in context.
+ * The installation fails on #CP_ERR_CONFLICT if the context already
+ * has an installed plug-in with the same plug-in identifier. Installation
+ * also fails if the plug-in tries to install an extension point which
+ * conflicts with an already installed extension point.
+ * The plug-in information must not be modified but it is safe to call
+ * ::cp_release_plugin_info after the plug-in has been installed.
+ *
+ * @param ctx the plug-in context
+ * @param pi plug-in information structure
+ * @return @ref CP_OK (zero) on success or an error code on failure
+ */
+CP_C_API cp_status_t cp_install_plugin(cp_context_t *ctx, cp_plugin_info_t *pi) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Scans for plug-ins in the registered plug-in directories, installing
+ * new plug-ins and upgrading installed plug-ins. This function can be used to
+ * initially load the plug-ins and to later rescan for new plug-ins.
+ *
+ * When several versions of the same plug-in is available the most recent
+ * version will be installed. The upgrade behavior depends on the specified
+ * @ref cScanFlags "flags". If #CP_SP_UPGRADE is set then upgrades to installed plug-ins are
+ * allowed. The old version is unloaded and the new version installed instead.
+ * If #CP_SP_STOP_ALL_ON_UPGRADE is set then all active plug-ins are stopped
+ * if any plug-ins are to be upgraded. If #CP_SP_STOP_ALL_ON_INSTALL is set then
+ * all active plug-ins are stopped if any plug-ins are to be installed or
+ * upgraded. Finally, if #CP_SP_RESTART_ACTIVE is set all currently active
+ * plug-ins will be restarted after the changes (if they were stopped).
+ *
+ * When removing plug-in files from the plug-in directories, the
+ * plug-ins to be removed must be first unloaded. Therefore this function
+ * does not check for removed plug-ins.
+ *
+ * @param ctx the plug-in context
+ * @param flags the bitmask of flags
+ * @return @ref CP_OK (zero) on success or an error code on failure
+ */
+CP_C_API cp_status_t cp_scan_plugins(cp_context_t *ctx, int flags) CP_GCC_NONNULL(1);
+
+/**
+ * Starts a plug-in. Also starts any imported plug-ins. If the plug-in is
+ * already starting then
+ * this function blocks until the plug-in has started or failed to start.
+ * If the plug-in is already active then this function returns immediately.
+ * If the plug-in is stopping then this function blocks until the plug-in
+ * has stopped and then starts the plug-in.
+ *
+ * @param ctx the plug-in context
+ * @param id identifier of the plug-in to be started
+ * @return @ref CP_OK (zero) on success or an error code on failure
+ */
+CP_C_API cp_status_t cp_start_plugin(cp_context_t *ctx, const char *id) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Stops a plug-in. First stops any dependent plug-ins that are currently
+ * active. Then stops the specified plug-in. If the plug-in is already
+ * stopping then this function blocks until the plug-in has stopped. If the
+ * plug-in is already stopped then this function returns immediately. If the
+ * plug-in is starting then this function blocks until the plug-in has
+ * started (or failed to start) and then stops the plug-in.
+ *
+ * @param ctx the plug-in context
+ * @param id identifier of the plug-in to be stopped
+ * @return @ref CP_OK (zero) on success or @ref CP_ERR_UNKNOWN if unknown plug-in
+ */
+CP_C_API cp_status_t cp_stop_plugin(cp_context_t *ctx, const char *id) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Stops all active plug-ins.
+ *
+ * @param ctx the plug-in context
+ */
+CP_C_API void cp_stop_plugins(cp_context_t *ctx) CP_GCC_NONNULL(1);
+
+/**
+ * Uninstalls the specified plug-in. The plug-in is first stopped if it is active.
+ * Then uninstalls the plug-in and any dependent plug-ins.
+ *
+ * @param ctx the plug-in context
+ * @param id identifier of the plug-in to be unloaded
+ * @return @ref CP_OK (zero) on success or @ref CP_ERR_UNKNOWN if unknown plug-in
+ */
+CP_C_API cp_status_t cp_uninstall_plugin(cp_context_t *ctx, const char *id) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Uninstalls all plug-ins. All plug-ins are first stopped and then
+ * uninstalled.
+ *
+ * @param ctx the plug-in context
+ */
+CP_C_API void cp_uninstall_plugins(cp_context_t *ctx) CP_GCC_NONNULL(1);
+
+/*@}*/
+
+
+/**
+ * @defgroup cFuncsPluginInfo Plug-in and extension information
+ * @ingroup cFuncs
+ *
+ * These functions can be used to query information about the installed
+ * plug-ins, extension points and extensions or to listen for plug-in state
+ * changes. They may be used by the main program or by a plug-in runtime.
+ */
+/*@{*/
+
+/**
+ * Returns static information about the specified plug-in. The returned
+ * information must not be modified and the caller must
+ * release the information by calling ::cp_release_info when the
+ * information is not needed anymore. When a plug-in runtime calls this
+ * function it may pass NULL as the identifier to get information about the
+ * plug-in itself.
+ *
+ * @param ctx the plug-in context
+ * @param id identifier of the plug-in to be examined or NULL for self
+ * @param status a pointer to the location where status code is to be stored, or NULL
+ * @return pointer to the information structure or NULL on failure
+ */
+CP_C_API cp_plugin_info_t * cp_get_plugin_info(cp_context_t *ctx, const char *id, cp_status_t *status) CP_GCC_NONNULL(1);
+
+/**
+ * Returns static information about the installed plug-ins. The returned
+ * information must not be modified and the caller must
+ * release the information by calling ::cp_release_info when the
+ * information is not needed anymore.
+ *
+ * @param ctx the plug-in context
+ * @param status a pointer to the location where status code is to be stored, or NULL
+ * @param num a pointer to the location where the number of returned plug-ins is stored, or NULL
+ * @return pointer to a NULL-terminated list of pointers to plug-in information
+ * or NULL on failure
+ */
+CP_C_API cp_plugin_info_t ** cp_get_plugins_info(cp_context_t *ctx, cp_status_t *status, int *num) CP_GCC_NONNULL(1);
+
+/**
+ * Returns static information about the currently installed extension points.
+ * The returned information must not be modified and the caller must
+ * release the information by calling ::cp_release_info when the
+ * information is not needed anymore.
+ *
+ * @param ctx the plug-in context
+ * @param status a pointer to the location where status code is to be stored, or NULL
+ * @param num filled with the number of returned extension points, if non-NULL
+ * @return pointer to a NULL-terminated list of pointers to extension point
+ * information or NULL on failure
+ */
+CP_C_API cp_ext_point_t ** cp_get_ext_points_info(cp_context_t *ctx, cp_status_t *status, int *num) CP_GCC_NONNULL(1);
+
+/**
+ * Returns static information about the currently installed extension points.
+ * The returned information must not be modified and the caller must
+ * release the information by calling ::cp_release_info when the
+ * information is not needed anymore.
+ *
+ * @param ctx the plug-in context
+ * @param extpt_id the extension point identifier or NULL for all extensions
+ * @param status a pointer to the location where status code is to be stored, or NULL
+ * @param num a pointer to the location where the number of returned extension points is to be stored, or NULL
+ * @return pointer to a NULL-terminated list of pointers to extension
+ * information or NULL on failure
+ */
+CP_C_API cp_extension_t ** cp_get_extensions_info(cp_context_t *ctx, const char *extpt_id, cp_status_t *status, int *num) CP_GCC_NONNULL(1);
+
+/**
+ * Releases a previously obtained reference counted information object. The
+ * documentation for functions returning such information refers
+ * to this function. The information must not be accessed after it has
+ * been released. The framework uses reference counting to deallocate
+ * the information when it is not in use anymore.
+ *
+ * @param ctx the plug-in context
+ * @param info the information to be released
+ */
+CP_C_API void cp_release_info(cp_context_t *ctx, void *info) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Returns the current state of the specified plug-in. Returns
+ * #CP_PLUGIN_UNINSTALLED if the specified plug-in identifier is unknown.
+ *
+ * @param ctx the plug-in context
+ * @param id the plug-in identifier
+ * @return the current state of the plug-in
+ */
+CP_C_API cp_plugin_state_t cp_get_plugin_state(cp_context_t *ctx, const char *id) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Registers a plug-in listener with a plug-in context. The listener is called
+ * synchronously immediately after a plug-in state change. There can be several
+ * listeners registered with the same context. A plug-in listener can be
+ * unregistered using ::cp_unregister_plistener and it is automatically
+ * unregistered when the registering plug-in is stopped or when the context
+ * is destroyed.
+ *
+ * @param ctx the plug-in context
+ * @param listener the plug-in listener to be added
+ * @param user_data user data pointer supplied to the listener
+ * @return @ref CP_OK (zero) on success or @ref CP_ERR_RESOURCE if out of resources
+ */
+CP_C_API cp_status_t cp_register_plistener(cp_context_t *ctx, cp_plugin_listener_func_t listener, void *user_data) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Removes a plug-in listener from a plug-in context. Does nothing if the
+ * specified listener was not registered.
+ *
+ * @param ctx the plug-in context
+ * @param listener the plug-in listener to be removed
+ */
+CP_C_API void cp_unregister_plistener(cp_context_t *ctx, cp_plugin_listener_func_t listener) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Traverses a configuration element tree and returns the specified element.
+ * The target element is specified by a base element and a relative path from
+ * the base element to the target element. The path includes element names
+ * separated by slash '/'. Two dots ".." can be used to designate a parent
+ * element. Returns NULL if the specified element does not exist. If there are
+ * several subelements with the same name, this function chooses the first one
+ * when traversing the tree.
+ *
+ * @param base the base configuration element
+ * @param path the path to the target element
+ * @return the target element or NULL if nonexisting
+ */
+CP_C_API cp_cfg_element_t * cp_lookup_cfg_element(cp_cfg_element_t *base, const char *path) CP_GCC_PURE CP_GCC_NONNULL(1, 2);
+
+/**
+ * Traverses a configuration element tree and returns the value of the
+ * specified element or attribute. The target element or attribute is specified
+ * by a base element and a relative path from the base element to the target
+ * element or attributes. The path includes element names
+ * separated by slash '/'. Two dots ".." can be used to designate a parent
+ * element. The path may end with '@' followed by an attribute name
+ * to select an attribute. Returns NULL if the specified element or attribute
+ * does not exist or does not have a value. If there are several subelements
+ * with the same name, this function chooses the first one when traversing the
+ * tree.
+ *
+ * @param base the base configuration element
+ * @param path the path to the target element
+ * @return the value of the target element or attribute or NULL
+ */
+CP_C_API char * cp_lookup_cfg_value(cp_cfg_element_t *base, const char *path) CP_GCC_PURE CP_GCC_NONNULL(1, 2);
+
+/*@}*/
+
+
+/**
+ * @defgroup cFuncsPluginExec Plug-in execution
+ * @ingroup cFuncs
+ *
+ * These functions support a plug-in controlled execution model. Started plug-ins can
+ * use ::cp_run_function to register @ref cp_run_func_t "a run function" which is called when the
+ * main program calls ::cp_run_plugins or ::cp_run_plugins_step. A run
+ * function should do a finite chunk of work and then return telling whether
+ * there is more work to be done. A run function is automatically unregistered
+ * when the plug-in is stopped. Run functions make it possible for plug-ins
+ * to control the flow of execution or they can be used as a coarse
+ * way of task switching if there is no multi-threading support.
+ *
+ * The C-Pluff distribution includes a generic main program, cpluff-loader,
+ * which only acts as a plug-in loader. It loads and starts up the
+ * specified plug-ins, passing any additional startup arguments to them and
+ * then just calls run functions of the plug-ins. This
+ * makes it is possible to put all the application specific logic in
+ * plug-ins. Application does not necessarily need a main program of its own.
+ *
+ * It is also safe, from framework perspective, to call these functions from
+ * multiple threads. Run functions may then be executed in parallel threads.
+ */
+/*@{*/
+
+/**
+ * Registers a new run function. The plug-in instance data pointer is given to
+ * the run function as a parameter. The run function must return zero if it has
+ * finished its work or non-zero if it should be called again later. The run
+ * function is unregistered when it returns zero. Plug-in framework functions
+ * stopping the registering plug-in must not be called from within a run
+ * function. This function does nothing if the specified run
+ * function is already registered for the calling plug-in instance.
+ *
+ * @param ctx the plug-in context of the registering plug-in
+ * @param runfunc the run function to be registered
+ * @return @ref CP_OK (zero) on success or an error code on failure
+ */
+CP_C_API cp_status_t cp_run_function(cp_context_t *ctx, cp_run_func_t runfunc) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Runs the started plug-ins as long as there is something to run.
+ * This function calls repeatedly run functions registered by started plug-ins
+ * until there are no more active run functions. This function is normally
+ * called by a thin main proram, a loader, which loads plug-ins, starts some
+ * plug-ins and then passes control over to the started plug-ins.
+ *
+ * @param ctx the plug-in context containing the plug-ins
+ */
+CP_C_API void cp_run_plugins(cp_context_t *ctx) CP_GCC_NONNULL(1);
+
+/**
+ * Runs one registered run function. This function calls one
+ * active run function registered by a started plug-in. When the run function
+ * returns this function also returns and passes control back to the main
+ * program. The return value can be used to determine whether there are any
+ * active run functions left. This function does nothing if there are no active
+ * registered run functions.
+ *
+ * @param ctx the plug-in context containing the plug-ins
+ * @return whether there are active run functions waiting to be run
+ */
+CP_C_API int cp_run_plugins_step(cp_context_t *ctx) CP_GCC_NONNULL(1);
+
+/**
+ * Sets startup arguments for the specified plug-in context. Like for usual
+ * C main functions, the first argument is expected to be the name of the
+ * program being executed or an empty string and the argument array should be
+ * terminated by NULL entry. If the main program is
+ * about to pass startup arguments to plug-ins it should call this function
+ * before starting any plug-ins in the context. The arguments are not copied
+ * and the caller is responsible for keeping the argument data available once
+ * the arguments have been set until the context is destroyed. Plug-ins can
+ * access the startup arguments using ::cp_get_context_args.
+ *
+ * @param ctx the plug-in context
+ * @param argv a NULL-terminated array of arguments
+ */
+CP_C_API void cp_set_context_args(cp_context_t *ctx, char **argv) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Returns the startup arguments associated with the specified
+ * plug-in context. This function is intended to be used by a plug-in runtime.
+ * Startup arguments are set by the main program using ::cp_set_context_args.
+ * The returned argument count is zero and the array pointer is NULL if no
+ * arguments have been set.
+ *
+ * @param ctx the plug-in context
+ * @param argc a pointer to a location where the number of startup arguments is stored, or NULL for none
+ * @return an argument array terminated by NULL or NULL if not set
+ */
+CP_C_API char **cp_get_context_args(cp_context_t *ctx, int *argc) CP_GCC_NONNULL(1);
+
+/*@}*/
+
+
+/**
+ * @defgroup cFuncsSymbols Dynamic symbols
+ * @ingroup cFuncs
+ *
+ * These functions can be used to dynamically access symbols exported by the
+ * plug-ins. They are intended to be used by a plug-in runtime or by the main
+ * program.
+ */
+/*@{*/
+
+/**
+ * Defines a context specific symbol. If a plug-in has symbols which have
+ * a plug-in instance specific value then the plug-in should define those
+ * symbols when it is started. The defined symbols are cleared
+ * automatically when the plug-in instance is stopped. Symbols can not be
+ * redefined.
+ *
+ * @param ctx the plug-in context
+ * @param name the name of the symbol
+ * @param ptr pointer value for the symbol
+ * @return @ref CP_OK (zero) on success or a status code on failure
+ */
+CP_C_API cp_status_t cp_define_symbol(cp_context_t *ctx, const char *name, void *ptr) CP_GCC_NONNULL(1, 2, 3);
+
+/**
+ * Resolves a symbol provided by the specified plug-in. The plug-in is started
+ * automatically if it is not already active. The symbol may be context
+ * specific or global. The framework first looks for a context specific
+ * symbol and then falls back to resolving a global symbol exported by the
+ * plug-in runtime library. The symbol can be released using
+ * ::cp_release_symbol when it is not needed anymore. Pointers obtained from
+ * this function must not be passed on to other plug-ins or the main
+ * program.
+ *
+ * When a plug-in runtime calls this function the plug-in framework creates
+ * a dynamic dependency from the symbol using plug-in to the symbol
+ * defining plug-in. The symbol using plug-in is stopped automatically if the
+ * symbol defining plug-in is about to be stopped. If the symbol using plug-in
+ * does not explicitly release the symbol then it is automatically released
+ * after a call to the stop function. It is not safe to refer to a dynamically
+ * resolved symbol in the stop function except to release it using
+ * ::cp_release_symbol.
+ *
+ * When the main program calls this function it is the responsibility of the
+ * main program to always release the symbol before the symbol defining plug-in
+ * is stopped. It is a fatal error if the symbol is not released before the
+ * symbol defining plug-in is stopped.
+ *
+ * @param ctx the plug-in context
+ * @param id the identifier of the symbol defining plug-in
+ * @param name the name of the symbol
+ * @param status a pointer to the location where the status code is to be stored, or NULL
+ * @return the pointer associated with the symbol or NULL on failure
+ */
+CP_C_API void *cp_resolve_symbol(cp_context_t *ctx, const char *id, const char *name, cp_status_t *status) CP_GCC_NONNULL(1, 2, 3);
+
+/**
+ * Releases a previously obtained symbol. The pointer must not be used after
+ * the symbol has been released. The symbol is released
+ * only after as many calls to this function as there have been for
+ * ::cp_resolve_symbol for the same plug-in and symbol.
+ *
+ * @param ctx the plug-in context
+ * @param ptr the pointer associated with the symbol
+ */
+CP_C_API void cp_release_symbol(cp_context_t *ctx, const void *ptr) CP_GCC_NONNULL(1, 2);
+
+/*@}*/
+
+
+#ifdef __cplusplus
+}
+#endif /*__cplusplus*/
+
+#endif /*CPLUFF_H_*/
diff --git a/lib/cpluff/libcpluff/cpluffdef.h b/lib/cpluff/libcpluff/cpluffdef.h
new file mode 100644
index 0000000000..c529386702
--- /dev/null
+++ b/lib/cpluff/libcpluff/cpluffdef.h
@@ -0,0 +1,200 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Common defines shared by C-Pluff C and C++ APIs.
+ * This file is automatically included by the top level C and C++
+ * API header files. There should be no need to include it explicitly.
+ */
+
+#ifndef CPLUFFDEF_H_
+#define CPLUFFDEF_H_
+
+
+/* ------------------------------------------------------------------------
+ * Version information
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup versionInfo Version information
+ * @ingroup cDefines cxxDefines
+ *
+ * C-Pluff version information. Notice that this version information
+ * is static version information included in header files. The
+ * macros introduced here can be used for compile time checks.
+ */
+/*@{*/
+
+/**
+ * The C-Pluff release version string. This string identifies a specific
+ * version of the C-Pluff distribution. Compile time software compatibility
+ * checks should use #CP_VERSION_MAJOR and #CP_VERSION_MINOR instead.
+ */
+#define CP_VERSION "0.1.3"
+
+/**
+ * The major version number component of the release version. This is an
+ * integer.
+ */
+#define CP_VERSION_MAJOR 0
+
+/**
+ * The minor version number component of the release version. This is an
+ * integer.
+ */
+#define CP_VERSION_MINOR 1
+
+/*@}*/
+
+
+/* ------------------------------------------------------------------------
+ * Symbol visibility
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup symbolVisibility Symbol visibility
+ * @ingroup cDefines cxxDefines
+ *
+ * Macros for controlling inter-module symbol visibility and linkage. These
+ * macros have platform specific values. #CP_EXPORT, #CP_IMPORT and #CP_HIDDEN
+ * can be reused by plug-in implementations for better portability. The
+ * complexity is mostly due to Windows DLL exports and imports.
+ *
+ * @anchor symbolVisibilityExample
+ * Each module should usually define its own macro to declare API symbols with
+ * #CP_EXPORT and #CP_IMPORT as necessary. For example, a mobule could define
+ * a macro @c MY_API in the API header file as follows.
+ *
+ * @code
+ * #ifndef MY_API
+ * # define MY_API CP_IMPORT
+ * #endif
+ * @endcode
+ *
+ * By default the API symbols would then be marked for import which is correct
+ * when client modules are including the API header file. When compiling the
+ * module itself the option @c -DMY_API=CP_EXPORT would be passed to the compiler to
+ * override the API header file and to mark the API symbols for export.
+ * The overriding definition could also be included in module source files or
+ * in an internal header file before including the API header file.
+ */
+/*@{*/
+
+/**
+ * @def CP_EXPORT
+ *
+ * Declares a symbol to be exported for inter-module usage. When compiling the
+ * module which defines the symbol this macro should be placed
+ * at the start of the symbol declaration to ensure that the symbol is exported
+ * to other modules. However, when compiling other modules the declaration of
+ * the symbol should start with #CP_IMPORT.
+ * See @ref symbolVisibilityExample "the example" of how to do this.
+ */
+
+/**
+ * @def CP_IMPORT
+ *
+ * Declares a symbol to be imported from another module. When compiling a
+ * module which uses the symbol this macro should be placed at the start of
+ * the symbol declaration to ensure that the symbol is imported from the
+ * defining module. However, when compiling the defining module the declaration
+ * of the symbol should start with #CP_EXPORT.
+ * See @ref symbolVisibilityExample "the example" of how to do this.
+ */
+
+/**
+ * @def CP_HIDDEN
+ *
+ * Declares a symbol hidden from other modules. This macro should be
+ * placed at the start of the symbol declaration to hide the symbol from other
+ * modules (if supported by the platform). This macro is not intended to be
+ * used with symbols declared as "static" which are already internal to the
+ * object file. Some platforms do not support hiding of symbols and therefore
+ * unique prefixes should be used for global symbols internal to the module
+ * even when they are declared using this macro.
+ */
+
+#if defined(_WIN32)
+# define CP_EXPORT __declspec(dllexport)
+# define CP_IMPORT extern __declspec(dllimport)
+# define CP_HIDDEN
+#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))
+# define CP_EXPORT
+# define CP_IMPORT extern
+# define CP_HIDDEN __attribute__ ((visibility ("hidden")))
+#else
+# define CP_EXPORT
+# define CP_IMPORT extern
+# define CP_HIDDEN
+#endif
+
+/*@}*/
+
+
+/* ------------------------------------------------------------------------
+ * GCC attributes
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup cDefinesGCCAttributes GCC attributes
+ * @ingroup cDefines cxxDefines
+ *
+ * These macros conditionally define GCC attributes for declarations.
+ * They are used in C-Pluff API declarations to enable better optimization
+ * and error checking when using GCC. In non-GCC platforms they have
+ * empty values.
+ */
+/*@{*/
+
+/**
+ * @def CP_GCC_PURE
+ *
+ * Declares a function as pure function having no side effects.
+ * This attribute is supported in GCC since version 2.96.
+ * Such functions can be subject to common subexpression elimination
+ * and loop optimization.
+ */
+
+/**
+ * @def CP_GCC_NONNULL
+ *
+ * Specifies that some pointer arguments to a function should have
+ * non-NULL values. Takes a variable length list of argument indexes as
+ * arguments. This attribute is supported in GCC since version 3.3.
+ * It can be used for enhanced error checking and some optimizations.
+ */
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+#define CP_GCC_PURE __attribute__((pure))
+#else
+#define CP_GCC_PURE
+#endif
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define CP_GCC_NONNULL(...) __attribute__((nonnull (__VA_ARGS__)))
+#else
+#define CP_GCC_NONNULL(...)
+#endif
+
+/*@}*/
+
+#endif /*CPLUFFDEF_H_*/
diff --git a/lib/cpluff/libcpluff/cpluffdef.h.in b/lib/cpluff/libcpluff/cpluffdef.h.in
new file mode 100644
index 0000000000..1c089f4e4d
--- /dev/null
+++ b/lib/cpluff/libcpluff/cpluffdef.h.in
@@ -0,0 +1,200 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Common defines shared by C-Pluff C and C++ APIs.
+ * This file is automatically included by the top level C and C++
+ * API header files. There should be no need to include it explicitly.
+ */
+
+#ifndef CPLUFFDEF_H_
+#define CPLUFFDEF_H_
+
+
+/* ------------------------------------------------------------------------
+ * Version information
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup versionInfo Version information
+ * @ingroup cDefines cxxDefines
+ *
+ * C-Pluff version information. Notice that this version information
+ * is static version information included in header files. The
+ * macros introduced here can be used for compile time checks.
+ */
+/*@{*/
+
+/**
+ * The C-Pluff release version string. This string identifies a specific
+ * version of the C-Pluff distribution. Compile time software compatibility
+ * checks should use #CP_VERSION_MAJOR and #CP_VERSION_MINOR instead.
+ */
+#define CP_VERSION "@PACKAGE_VERSION@"
+
+/**
+ * The major version number component of the release version. This is an
+ * integer.
+ */
+#define CP_VERSION_MAJOR @CP_VERSION_MAJOR@
+
+/**
+ * The minor version number component of the release version. This is an
+ * integer.
+ */
+#define CP_VERSION_MINOR @CP_VERSION_MINOR@
+
+/*@}*/
+
+
+/* ------------------------------------------------------------------------
+ * Symbol visibility
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup symbolVisibility Symbol visibility
+ * @ingroup cDefines cxxDefines
+ *
+ * Macros for controlling inter-module symbol visibility and linkage. These
+ * macros have platform specific values. #CP_EXPORT, #CP_IMPORT and #CP_HIDDEN
+ * can be reused by plug-in implementations for better portability. The
+ * complexity is mostly due to Windows DLL exports and imports.
+ *
+ * @anchor symbolVisibilityExample
+ * Each module should usually define its own macro to declare API symbols with
+ * #CP_EXPORT and #CP_IMPORT as necessary. For example, a mobule could define
+ * a macro @c MY_API in the API header file as follows.
+ *
+ * @code
+ * #ifndef MY_API
+ * # define MY_API CP_IMPORT
+ * #endif
+ * @endcode
+ *
+ * By default the API symbols would then be marked for import which is correct
+ * when client modules are including the API header file. When compiling the
+ * module itself the option @c -DMY_API=CP_EXPORT would be passed to the compiler to
+ * override the API header file and to mark the API symbols for export.
+ * The overriding definition could also be included in module source files or
+ * in an internal header file before including the API header file.
+ */
+/*@{*/
+
+/**
+ * @def CP_EXPORT
+ *
+ * Declares a symbol to be exported for inter-module usage. When compiling the
+ * module which defines the symbol this macro should be placed
+ * at the start of the symbol declaration to ensure that the symbol is exported
+ * to other modules. However, when compiling other modules the declaration of
+ * the symbol should start with #CP_IMPORT.
+ * See @ref symbolVisibilityExample "the example" of how to do this.
+ */
+
+/**
+ * @def CP_IMPORT
+ *
+ * Declares a symbol to be imported from another module. When compiling a
+ * module which uses the symbol this macro should be placed at the start of
+ * the symbol declaration to ensure that the symbol is imported from the
+ * defining module. However, when compiling the defining module the declaration
+ * of the symbol should start with #CP_EXPORT.
+ * See @ref symbolVisibilityExample "the example" of how to do this.
+ */
+
+/**
+ * @def CP_HIDDEN
+ *
+ * Declares a symbol hidden from other modules. This macro should be
+ * placed at the start of the symbol declaration to hide the symbol from other
+ * modules (if supported by the platform). This macro is not intended to be
+ * used with symbols declared as "static" which are already internal to the
+ * object file. Some platforms do not support hiding of symbols and therefore
+ * unique prefixes should be used for global symbols internal to the module
+ * even when they are declared using this macro.
+ */
+
+#if defined(_WIN32)
+# define CP_EXPORT __declspec(dllexport)
+# define CP_IMPORT extern __declspec(dllimport)
+# define CP_HIDDEN
+#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))
+# define CP_EXPORT
+# define CP_IMPORT extern
+# define CP_HIDDEN __attribute__ ((visibility ("hidden")))
+#else
+# define CP_EXPORT
+# define CP_IMPORT extern
+# define CP_HIDDEN
+#endif
+
+/*@}*/
+
+
+/* ------------------------------------------------------------------------
+ * GCC attributes
+ * ----------------------------------------------------------------------*/
+
+/**
+ * @defgroup cDefinesGCCAttributes GCC attributes
+ * @ingroup cDefines cxxDefines
+ *
+ * These macros conditionally define GCC attributes for declarations.
+ * They are used in C-Pluff API declarations to enable better optimization
+ * and error checking when using GCC. In non-GCC platforms they have
+ * empty values.
+ */
+/*@{*/
+
+/**
+ * @def CP_GCC_PURE
+ *
+ * Declares a function as pure function having no side effects.
+ * This attribute is supported in GCC since version 2.96.
+ * Such functions can be subject to common subexpression elimination
+ * and loop optimization.
+ */
+
+/**
+ * @def CP_GCC_NONNULL
+ *
+ * Specifies that some pointer arguments to a function should have
+ * non-NULL values. Takes a variable length list of argument indexes as
+ * arguments. This attribute is supported in GCC since version 3.3.
+ * It can be used for enhanced error checking and some optimizations.
+ */
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+#define CP_GCC_PURE __attribute__((pure))
+#else
+#define CP_GCC_PURE
+#endif
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define CP_GCC_NONNULL(...) __attribute__((nonnull (__VA_ARGS__)))
+#else
+#define CP_GCC_NONNULL(...)
+#endif
+
+/*@}*/
+
+#endif /*CPLUFFDEF_H_*/
diff --git a/lib/cpluff/libcpluff/defines.h b/lib/cpluff/libcpluff/defines.h
new file mode 100644
index 0000000000..3d91a6bfa1
--- /dev/null
+++ b/lib/cpluff/libcpluff/defines.h
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Core internal defines
+ */
+
+#ifndef DEFINES_H_
+#define DEFINES_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#endif
+
+
+/* ------------------------------------------------------------------------
+ * Defines
+ * ----------------------------------------------------------------------*/
+
+// Gettext defines
+#ifdef ENABLE_NLS
+#define _(String) dgettext(PACKAGE, String)
+#define gettext_noop(String) String
+#define N_(String) gettext_noop(String)
+#else
+#define _(String) (String)
+#define N_(String) String
+#define textdomain(Domain)
+#define bindtextdomain(Package, Directory)
+#endif //HAVE_GETTEXT
+
+
+// Additional defines for function attributes (under GCC).
+#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) && ! defined(printf)
+#define CP_GCC_PRINTF(format_idx, arg_idx) \
+ __attribute__((format (printf, format_idx, arg_idx)))
+#define CP_GCC_CONST __attribute__((const))
+#define CP_GCC_NORETURN __attribute__((noreturn))
+#else
+#define CP_GCC_PRINTF(format_idx, arg_idx)
+#define CP_GCC_CONST
+#define CP_GCC_NORETURN
+#endif
+
+
+#endif //DEFINES_H_
diff --git a/lib/cpluff/libcpluff/docsrc/Doxyfile-impl.in b/lib/cpluff/libcpluff/docsrc/Doxyfile-impl.in
new file mode 100644
index 0000000000..3bc1858041
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/Doxyfile-impl.in
@@ -0,0 +1,1256 @@
+# Doxyfile 1.5.1
+
+# Copyright 2007 Johannes Lehtinen
+# This configuration file is free software; Johannes Lehtinen gives unlimited
+# permission to copy, distribute and modify it.
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "@PACKAGE_NAME@ C Implementation"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = "@PACKAGE_VERSION@ (API version @CP_CORE_API_CURRENT@)"
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER = doxygen.footer
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET = doxygen.css
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = NO
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that a graph may be further truncated if the graph's
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/lib/cpluff/libcpluff/docsrc/Doxyfile-ref.in b/lib/cpluff/libcpluff/docsrc/Doxyfile-ref.in
new file mode 100644
index 0000000000..aedb7f66a2
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/Doxyfile-ref.in
@@ -0,0 +1,1256 @@
+# Doxyfile 1.5.1
+
+# Copyright 2007 Johannes Lehtinen
+# This configuration file is free software; Johannes Lehtinen gives unlimited
+# permission to copy, distribute and modify it.
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "@PACKAGE_NAME@ C API"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = "@PACKAGE_VERSION@"
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 0
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = cpluffdef.h cpluff.h mainpage.dox architecture.dox mainprog.dox plugin.dox
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = .
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER = doxygen.footer
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET = doxygen.css
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED = CP_GCC_PURE CP_GCC_NONNULL CP_C_API
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = NO
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that a graph may be further truncated if the graph's
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/lib/cpluff/libcpluff/docsrc/Makefile.am b/lib/cpluff/libcpluff/docsrc/Makefile.am
new file mode 100644
index 0000000000..5a2f304b3c
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/Makefile.am
@@ -0,0 +1,7 @@
+## Process this file with automake to produce Makefile.in.
+
+# Copyright 2007 Johannes Lehtinen
+# This Makefile is free software; Johannes Lehtinen gives unlimited
+# permission to copy, distribute and modify it.
+
+EXTRA_DIST = mainpage.dox architecture.dox mainprog.dox plugin.dox
diff --git a/lib/cpluff/libcpluff/docsrc/Makefile.in b/lib/cpluff/libcpluff/docsrc/Makefile.in
new file mode 100644
index 0000000000..4da9da6e44
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/Makefile.in
@@ -0,0 +1,359 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright 2007 Johannes Lehtinen
+# This Makefile is free software; Johannes Lehtinen gives unlimited
+# permission to copy, distribute and modify it.
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libcpluff/docsrc
+DIST_COMMON = $(srcdir)/Doxyfile-impl.in $(srcdir)/Doxyfile-ref.in \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = Doxyfile-ref Doxyfile-impl
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPLUFF_LOADER = @CPLUFF_LOADER@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CP_CXX_LIB_VERSION = @CP_CXX_LIB_VERSION@
+CP_C_LIB_VERSION = @CP_C_LIB_VERSION@
+CP_VERSION_MAJOR = @CP_VERSION_MAJOR@
+CP_VERSION_MINOR = @CP_VERSION_MINOR@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBS_LIBCPLUFF = @LIBS_LIBCPLUFF@
+LIBS_OTHER = @LIBS_OTHER@
+LIBTOOL = @LIBTOOL@
+LIB_READLINE = @LIB_READLINE@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = mainpage.dox architecture.dox mainprog.dox plugin.dox
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign libcpluff/docsrc/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign libcpluff/docsrc/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+Doxyfile-ref: $(top_builddir)/config.status $(srcdir)/Doxyfile-ref.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+Doxyfile-impl: $(top_builddir)/config.status $(srcdir)/Doxyfile-impl.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ distclean distclean-generic distclean-libtool distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/cpluff/libcpluff/docsrc/architecture.dox b/lib/cpluff/libcpluff/docsrc/architecture.dox
new file mode 100644
index 0000000000..e9e6de5386
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/architecture.dox
@@ -0,0 +1,66 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/**
+ * @page architecture Plug-in architecture
+ *
+ * @section architectureOverview Overview
+ *
+ * The plug-in architecture supported by C-Pluff is presented in the
+ * following figure. There is a thin main program controlling the plug-in
+ * framework. The main program is responsible for initializing and setting
+ * up the plug-in environment. Most of the application logic is contained in
+ * plug-ins which are independent components and can be developed and
+ * distributed separately. Plug-ins integrate with each other by providing
+ * extension points and extensions. An extension point is a point into which
+ * other plug-ins can attach extensions. An extension can be just
+ * information, expressed in XML format, or the plug-in may also provide
+ * program logic as part of the plug-in runtime library. The framework
+ * provides services for accessing extensions and for managing plug-in
+ * dependencies.
+ *
+ * @image html architecture.png "C-Pluff plug-in architecture"
+ *
+ * @section architectureExtensions Extensions
+ *
+ * The idea behind extension points and extensions is that the extensibility
+ * is not limited only to few fixed plug-in types supported by the
+ * core application. Although the core plug-ins typically define the extension
+ * points for the core application logic, it is possible for any plug-in
+ * to specify additional extension points.
+ *
+ * For example, let us assume that we are developing an extensible text
+ * editor. One extension point defined by core editor plug-in could be
+ * auto-completion extension point. A plug-in providing basic Java source code
+ * support could provide an extension for auto-completing Java code.
+ * Now, while this extension could do basic auto-completion of plain Java code,
+ * it is customary that Java source code also includes embedded documentation,
+ * such as JavaDoc comments and tags, or annotations, such as XDoclet tags, as
+ * part of doc comments. Instead of trying to support all known tags and their
+ * semantics, the plug-in providing basic Java support could define another
+ * extension point for additional plug-ins that know how to perform
+ * auto-completion of different kind of tags in doc comments.
+ * This way the extensibility of the application is not limited to the
+ * extension points defined by the core application but the plug-ins can
+ * incrementally increase the extensibility of the application.
+ */
diff --git a/lib/cpluff/libcpluff/docsrc/mainpage.dox b/lib/cpluff/libcpluff/docsrc/mainpage.dox
new file mode 100644
index 0000000000..7376c324e3
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/mainpage.dox
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/**
+ * @mainpage
+ *
+ * This is reference documentation for the
+ * <a href="http://www.c-pluff.org/">C-Pluff</a> C API.
+ * C-Pluff is a plug-in framework for C programs.
+ * It has been strongly inspired by the Java plug-in framework in
+ * <a href="http://www.eclipse.org/">Eclipse</a>. C-Pluff focuses on
+ * providing core services for plug-in interaction and plug-in
+ * management. It aims to be platform neutral and supports dynamic changes to
+ * plug-in configuration without stopping the whole application or framework.
+ * It does not yet provide special facilities for distribution such as
+ * signed plug-in packages or remote plug-in repositories but it is
+ * possible to build such features on top of the current framework.
+ *
+ * Here are links to main topics.
+ *
+ * - @ref architecture "Plug-in architecture"
+ * - @ref cMainProgram "Main program"
+ * - @ref plugin "Plug-in"
+ *
+ * - <a class="el" href="modules.html">API elements</a>
+ * - <a class="el" href="files.html">Header files</a>
+ *
+ * For a "quick start guide" in developing C-Pluff based applications,
+ * see the example in the examples directory of the source distribution.
+ *
+ * The latest version of the framework implementation
+ * and documentation is available at
+ * <a href="http://www.c-pluff.org/">C-Pluff web site</a>.
+ *
+ * This documentation has been generated from the C-Pluff source code
+ * using <a href="http://www.doxygen.org">Doxygen</a>.
+ */
diff --git a/lib/cpluff/libcpluff/docsrc/mainprog.dox b/lib/cpluff/libcpluff/docsrc/mainprog.dox
new file mode 100644
index 0000000000..78407d032e
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/mainprog.dox
@@ -0,0 +1,338 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/**
+ * @page cMainProgram Main program
+ *
+ * @section cMainProgramOverview Overview
+ *
+ * The main program is the part of executable that is located outside the
+ * plug-in framework. The main program is responsible for setting up
+ * the plug-in framework and for loading the desired set of
+ * @ref plugin "plug-ins". The main program should preferably be very
+ * thin, a mere plug-in loader, because it can not fully participate in plug-in
+ * interaction. C-Pluff distribution provides a plug-in loader,
+ * @ref cpluff-loader, which can be used as a generic main program for
+ * arbitrary plug-in collections.
+ *
+ * @section cMainProgramResponsibilities Responsibilities
+ *
+ * The main program has several responsibilities:
+ *
+ * - @ref cMainProgramInitFramework "initializing the plug-in framework"
+ * - @ref cMainProgramCreateContext "creating a plug-in context"
+ * - @ref cMainProgramLoad "loading plug-ins"
+ * - @ref cMainProgramExec "controlling plug-in execution"
+ * - @ref cMainProgramChange "changing plug-in configuration" (opt.)
+ * - @ref cMainProgramDestroyFramework "destroying the plug-in framework"
+ *
+ * @subsection cMainProgramInitFramework Initializing the plug-in framework
+ *
+ * Plug-in framework, or the C-Pluff library, must be initialized before its
+ * services can be used.
+ * Initialization is not a thread-safe operation and should generally be
+ * done by the main program before any additional plug-in framework accessing
+ * threads are started. Initialization is done by calling ::cp_init.
+ * Additionally, the main program can use ::cp_set_fatal_error_handler to register
+ * a function that is called when a fatal error occurs. A fatal error is one
+ * that prevents the framework from continuing operation. For example,
+ * errors in operating system locking operations and a NULL pointer being
+ * passed as an argument which is expected to have a non-NULL value are fatal
+ * erors.
+ *
+ * Here is an example of possible initialization code.
+ *
+ * @code
+ * #include <locale.h>
+ * #include <cpluff.h>
+ *
+ * void handle_fatal_error(const char *msg) {
+ *
+ * // ... log error, flush logs, send bug report, etc. ...
+ *
+ * fprintf(stderr, "A fatal error occurred: %s\n", msg);
+ * abort();
+ * }
+ *
+ * void initialize(void) {
+ * cp_status_t status;
+ *
+ * setlocale(LC_ALL, "");
+ * cp_set_fatal_error_handler(handle_fatal_error);
+ * status = cp_init();
+ * if (status != CP_OK) {
+ * // ... handle initialization failure ...
+ * }
+ * }
+ * @endcode
+ *
+ * @subsection cMainProgramCreateContext Creating a plug-in context
+ *
+ * A plug-in context represents the co-operation environment of a set of
+ * plug-ins from the perspective of a particular participating plug-in or
+ * the perspective of the main program. From main program perspective, a
+ * plug-in context is a container for a set of plug-ins. A plug-in can interact
+ * with other plug-ins in the same container.
+ *
+ * An extensible application can have more than one plug-in container but
+ * usually one container should suffice. Due to the nature of C programs,
+ * plug-ins deployed to different containers are not very well insulated from
+ * each other. For example, global variables provided by a plug-in in one
+ * container are visible to all plug-ins in all containers. Also, by placing
+ * all plug-ins in the same container they can more efficiently share common
+ * base components which themselves might provide extensibility.
+ *
+ * A main program creates a plug-in context, to be used as a container for
+ * plugins, using ::cp_create_context.
+ *
+ * @code
+ * #include <cpluff.h>
+ *
+ * cp_context_t *ctx;
+ *
+ * void create_context(void) {
+ * cp_status_t status;
+ *
+ * ctx = cp_create_context(&status);
+ * if (ctx == NULL) {
+ * // ... handle initialization failure ...
+ * }
+ * }
+ * @endcode
+ *
+ * @subsection cMainProgramLoad Loading plug-ins
+ *
+ * An extensible application is made of plug-ins that can be added and removed
+ * dynamically. The plug-ins are loaded by the main program using the services
+ * provided by the framework. The framework provides couple of alternative
+ * ways of loading plug-ins.
+ *
+ * As a lowest level operation, the main program can
+ * load individual plug-ins from known locations using
+ * ::cp_load_plugin_descriptor and ::cp_install_plugin. Here is example code
+ * that loads a set of plug-ins from file system locations listed in a file.
+ *
+ * @code
+ * #include <stdio.h>
+ * #include <cpluff.h>
+ *
+ * extern cp_context_t *ctx;
+ * static const char pluginListFile[] = "/etc/example/plugins.list";
+ *
+ * void load_plugins(void) {
+ * char plugindir[128];
+ * FILE *lf;
+ *
+ * // Open plug-in list file
+ * lf = fopen(pluginListFile, "r");
+ * if (lf == NULL) {
+ * // ... handle loading failure ...
+ * }
+ *
+ * // Load each listed plug-in
+ * while (fgets(plugindir, 128, lf) != NULL) {
+ * cp_plugin_info_t *plugininfo;
+ * cp_status_t status;
+ * int i;
+ *
+ * // Remove possible trailing newline from plug-in location
+ * for (i = 0; plugindir[i + 1] != '\0'; i++);
+ * if (plugindir[i] == '\n') {
+ * plugindir[i] = '\0';
+ * }
+ *
+ * // Load plug-in descriptor
+ * plugininfo = cp_load_plugin_descriptor(ctx, plugindir, &status);
+ * if (pinfo == NULL) {
+ * // ... handle loading failure ...
+ * }
+ *
+ * // Install plug-in descriptor
+ * status = cp_install_plugin(ctx, plugininfo);
+ * if (status != CP_OK) {
+ * // ... handle loading failure ...
+ * }
+ *
+ * // Release plug-in descriptor information
+ * cp_release_info(ctx, plugininfo);
+ * }
+ *
+ * // Close plug-in list file
+ * fclose(lf);
+ * }
+ * @endcode
+ *
+ * Alternatively, the main program can register and load plug-in collections.
+ * A plug-in collection is a file system directory which includes individual
+ * plug-ins in subdirectories, one plug-in in each subdirectory. Plug-in
+ * collections can be registered with a plug-in context using
+ * ::cp_register_pcollection. Plug-ins of the collection can then be scanned
+ * and loaded using ::cp_scan_plugins. Here is example code loading plug-ins
+ * from a plug-in collection.
+ *
+ * @code
+ * #include <cpluff.h>
+ *
+ * extern cp_context_t *ctx;
+ * static const char pluginCollectionDir[] = "/etc/example/plugins";
+ *
+ * void load_plugins(void) {
+ * cp_status_t status;
+ *
+ * status = cp_register_pcollection(ctx, pluginCollectionDir);
+ * if (status != CP_OK) {
+ * // ... handle loading failure ...
+ * }
+ * status = cp_scan_plugins(ctx, 0);
+ * if (status != CP_OK) {
+ * // ... handle loading failure ...
+ * // (notice that some plug-ins might have been loaded)
+ * }
+ * }
+ * @endcode
+ *
+ * @subsection cMainProgramExec Controlling plug-in execution
+ *
+ * The main program controls plug-in execution by starting and stopping
+ * plug-ins and by executing run functions registered by plug-ins.
+ * Additionally, the main program can pass startup arguments to plug-ins.
+ *
+ * When plug-ins are installed they are not yet activated and their
+ * runtime library is not even loaded at that point. The main program
+ * typically activates plug-ins by starting a main plug-in
+ * responsible for user interface or core application logic. This plug-in
+ * then implicitly causes other plug-ins to be activated via dependencies and
+ * by dynamically resolving symbols provided by other plug-ins. Plug-ins
+ * recursively activate each other until all initially needed plug-ins have
+ * been started. Some plug-ins might be activated at a later time when their
+ * functionality is needed, for example due to user action.
+ *
+ * If a plug-in needs to perform background operations, that is operations
+ * executed outside the invocation of plug-in provided interface functions,
+ * then it can either start a new thread or it can register a run function.
+ * A run function is a function that is typically executed as part of the
+ * main loop by the main program.
+ *
+ * The following example code shows how a main program might initialize
+ * plug-in startup arguments using ::cp_set_context_args, start the core
+ * plug-in using ::cp_start_plugin and then execute plug-in run functions
+ * using ::cp_run_plugins.
+ *
+ * @code
+ * #include <cpluff.h>
+ *
+ * extern cp_context_t *ctx;
+ * static const char corePluginId[] = "org.example.core";
+ *
+ * void run_plugins(char *argv[]) {
+ * cp_status_t status;
+ *
+ * // Set plug-in startup arguments
+ * cp_set_context_args(ctx, argv);
+ *
+ * // Start the core plug-in, possibly activating other plug-ins as well
+ * status = cp_start_plugin(ctx, corePluginId);
+ * if (status != CP_OK) {
+ * // ... handle startup failure ...
+ * }
+ *
+ * // Execute plug-ins until there is no more work to be done
+ * cp_run_plugins(ctx);
+ * }
+ *
+ * int main(int argc, char *argv[]) {
+ * // ... do initialization and load plug-ins ...
+ *
+ * run_plugins(argv);
+ *
+ * // ... do destruction ...
+ * }
+ * @endcode
+ *
+ * Alternatively, if the main program has some operations it must perform
+ * as part of the main loop, the call to ::cp_run_plugins can be replaced
+ * by code using ::cp_run_plugins_step like in the following example.
+ *
+ * @code
+ * void mainloop(void) {
+ * int finished = 0;
+ *
+ * while (!finished) {
+ * // ... do main program specific operations ...
+ *
+ * finished = !cp_run_plugins_step(ctx);
+ * }
+ * }
+ * @endcode
+ *
+ * @subsection cMainProgramChange Changing plug-in configuration
+ *
+ * C-Pluff has been designed to allow dynamic changes to the plug-in
+ * configuration, that is plug-ins being added or removed without shutting
+ * down the application or the framework. It is the responsibility of the
+ * main program to manage such changes if the application is to support
+ * dynamic configuration changes.
+ *
+ * Adding plug-ins is straightforward because there is no need to
+ * consider dependencies of active plug-ins. For example, if one uses
+ * plug-in collections as introduced above then new plug-ins can be
+ * deployed under the plug-in collection directory while the application is
+ * running and the main program can load them incrementally by calling
+ * ::cp_scan_plugins again. This call might be activated by some user interface
+ * element, for example a plug-in manager component which just downloaded and
+ * installed new plug-ins as requested by the user. The flags
+ * #CP_SP_STOP_ALL_ON_INSTALL and #CP_SP_RESTART_ACTIVE
+ * orred together can be used to cause all active plug-ins to be restarted
+ * if they do not otherwise notice the extensions provided by new plug-ins.
+ *
+ * Upgrading plug-ins is almost as straightforward because the C-Pluff
+ * framework manages plug-in dependencies (assuming the plug-ins have
+ * declared their dependencies properly). The new version of a plug-in
+ * can be deployed under the plug-in collection directory in a
+ * new subdirectory parallel to the old version while the application is
+ * running. The main program can then call ::cp_scan_plugins with
+ * #CP_SP_UPGRADE and #CP_SP_RESTART_ACTIVE orred together. This will stop
+ * the old version of the upgraded plug-in (implicitly stopping all plug-ins
+ * that depend on it), unload the plug-in from the framework, install the
+ * new version of the plug-in and finally restart plug-ins that were
+ * active before the operation. The old version of the plug-in can now
+ * be removed from the plug-in collection. Again, #CP_SP_STOP_ALL_ON_UPGRADE
+ * can be added to restart all active plug-ins.
+ *
+ * Deleting plug-ins must be done by first stopping and unloading the
+ * plug-in to be deleted using ::cp_uninstall_plugin. The the plug-in can
+ * be removed from the plug-in collection.
+ *
+ * @subsection cMainProgramDestroyFramework Destroying the plug-in framework
+ *
+ * The plug-in framework can be destroyed and all resources released by
+ * calling ::cp_destroy as many times as ::cp_init has been called. This
+ * is not a thread-safe operation and should generally be done by the main
+ * program just before application exits. The destroy function
+ * stops and unloads all plug-ins and destroys all plug-in contexts before
+ * destroying the core framework.
+ *
+ * Individual plug-in contexts can be destroyed by calling
+ * ::cp_destroy_context. The destroy function stops and unloads all plug-ins
+ * before destroying the context itself.
+ */
diff --git a/lib/cpluff/libcpluff/docsrc/plugin.dox b/lib/cpluff/libcpluff/docsrc/plugin.dox
new file mode 100644
index 0000000000..9b7e954896
--- /dev/null
+++ b/lib/cpluff/libcpluff/docsrc/plugin.dox
@@ -0,0 +1,286 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/**
+ * @page plugin Plug-in
+ *
+ * @section pluginOverview Overview
+ *
+ * Plug-in is the core element of an extensible application. The extensions and
+ * also the main application logic are implemented as plug-ins. Plug-ins can
+ * be developed, distributed and deployed independently, subject to
+ * inter-plugin dependencies. Deploying a new plug-in does not require
+ * recompilation or relinking if the operating system platform supports
+ * required dynamic linking facilities.
+ *
+ * @section pluginStructure Structure
+ *
+ * A plug-in includes the following structural elements.
+ *
+ * - @ref pluginDescriptor "Plug-in descriptor"
+ * - @ref pluginRuntime "Plug-in runtime library"
+ * - @ref pluginData "Static plug-in data"
+ *
+ * @subsection pluginDescriptor Plug-in descriptor
+ *
+ * A plug-in descriptor is an XML document describing a plug-in. It includes
+ * information about the contents of the plug-in, the features provided by
+ * the plug-in, plug-in version information and static dependencies of the
+ * plug-in. Most of the elements are optional. Most of the descriptor
+ * information described here is available to software via
+ * @ref cp_plugin_info_t structure. The plug-in descriptor must be located
+ * in the plug-in directory as @c plugin.xml.
+ *
+ * The formal declaration of plug-in descriptor is available as
+ * XML Schema Definition in @c plugin.xsd located in the top level source
+ * directory. Currently there is no namespace associated with the plug-in
+ * descriptor. Here is an example of a plug-in descriptor. Click element name
+ * to jump into documentation for that element.
+ *
+ * <div class="fragment">
+ * <pre class="fragment">
+ * &lt;<a class="code" href="#pluginDescPlugin">plugin</a> id=<span class="charliteral">"org.c-pluff.example"</span> name=<span class="charliteral">"Example Plug-in"</span> version=<span class="charliteral">"0.3.2"</span> provider-name=<span class="charliteral">"Johannes Lehtinen"</span>&gt;
+ * &lt;<a class="code" href="#pluginDescPluginBWC">backwards-compatibility</a> abi=<span class="charliteral">"0.3"</span> api=<span class="charliteral">"0.2.8"</span>/&gt;
+ * &lt;<a class="code" href="#pluginDescPluginRequires">requires</a>&gt;
+ * &lt;<a class="code" href="#pluginDescPluginReqCP">c-pluff</a> version=<span class="charliteral">"0.1"</span>/&gt;
+ * &lt;<a class="code" href="#pluginDescPluginReqImport">import</a> plugin=<span class="charliteral">"org.c-pluff.util"</span> version=<span class="charliteral">"0.2"</span>/&gt;
+ * &lt;<a class="code" href="#pluginDescPluginReqImport">import</a> plugin=<span class="charliteral">"org.c-pluff.extra"</span> optional=<span class="charliteral">"true"</span>/&gt;
+ * &lt;/<a class="code" href="#pluginDescPluginRequires">requires</a>&gt;
+ * &lt;<a class="code" href="#pluginDescPluginRuntime">runtime</a> library=<span class="charliteral">"libruntime"</span> funcs=<span class="charliteral">"org_cpluff_example_funcs"</span>/&gt;
+ * &lt;<a class="code" href="#pluginDescPluginEP">extension-point</a> id=<span class="charliteral">"editors"</span> name=<span class="charliteral">"Text Editors"</span> schema=<span class="charliteral">"editors_schema.xsd"</span>/&gt;
+ * &lt;<a class="code" href="#pluginDescPluginEP">extension-point</a> id=<span class="charliteral">"url-families"</span>/&gt;
+ * &lt;<a class="code" href="#pluginDescPluginE">extension</a> point=<span class="charliteral">"org.c-pluff.util.archivers"</span> id=<span class="charliteral">"tar"</span> name=<span class="charliteral">"Tar Archiver Support"</span>&gt;
+ * &lt;type random-access=<span class="charliteral">"false"</span>/&gt;
+ * &lt;exec bin=<span class="charliteral">"tar"</span>/&gt;
+ * &lt;/<a class="code" href="#pluginDescPluginE">extension</a>&gt;
+ * &lt;<a class="code" href="#pluginDescPluginE">extension</a> point=<span class="charliteral">"org.c-pluff.example.editors</span>&gt;
+ * &lt;editor name=<span class="charliteral">"Text Editor"</span> runtime=<span class="charliteral">"org_cpluff_example_txteditor_runtime"</span>&gt;
+ * &lt;file-types&gt;
+ * &lt;file-type mime-type=<span class="charliteral">"text/plain"</span>/&gt;
+ * &lt;/file-types&gt;
+ * &lt;/editor&gt;
+ * &lt;/<a class="code" href="#pluginDescPluginE">extension</a>&gt;
+ * &lt;/<a class="code" href="#pluginDescPlugin">plugin</a>&gt;</pre>
+ * </div>
+ *
+ * A descriptor can also be much simpler, depending on the plug-in.
+ * Here is an example of a minimal descriptor (of a useless plug-in).
+ *
+ * <div class="fragment">
+ * <pre class="fragment">
+ * &lt;<a class="code" href="#pluginDescPlugin">plugin</a> id=<span class="charliteral">"org.c-pluff.useless"</span>/&gt;</pre>
+ * </div>
+ *
+ * @subsubsection pluginDescPlugin plugin
+ *
+ * This is the top level element of the plug-in descriptor. It can have
+ * following attributes.
+ *
+ * - @a id: A mandatory unique identifier for the plug-in. Plug-in identifiers
+ * should preferably be generated using a reversed DNS domain name as
+ * prefix to prevent identifier conflicts.
+ * - @a name: An optional human-readable name for the plug-in.
+ * - @a version: An optional version number for the plug-in. Version numbers
+ * are used for checking compatibility when resolving versioned plug-in
+ * dependencies. See also information about
+ * @ref pluginVersions "plug-in versions".
+ * - @a provider-name: The name of the plug-in provider or author. Optional.
+ *
+ * This element can contain following elements.
+ *
+ * - @ref pluginDescPluginBWC "backwards-compatibility": Optional information about backwards
+ * compatibility of this plug-in version.
+ * - @ref pluginDescPluginRequires "requires": Information about static plug-in dependencies. Can be omitted
+ * if the plug-in does not have static dependencies.
+ * - @ref pluginDescPluginRuntime "runtime": Information about the plug-in runtime library. Can be omitted
+ * if the plug-in does not have a runtime library but only data.
+ * - @ref pluginDescPluginEP "extension-point": Information about extension points provided by the
+ * plug-in. This element is repeated if there are multiple extension points
+ * and omitted if there are none.
+ * - @ref pluginDescPluginE "extension": Information about extensions provided by the plug-in.
+ * This element is repeated if there are multiple extensions and omitted
+ * if there are none.
+ *
+ * @subsubsection pluginDescPluginBWC backwards-compatibility
+ *
+ * This element includes optional information about the backwards compatibility
+ * of this plug-in version. It can have following attributes.
+ *
+ * - @a abi: Backwards compatibility of the application binary interface (ABI)
+ * of the plug-in. ABI includes any public symbols exported by the plug-in,
+ * data structures associated with exported symbols and any extension points
+ * provided by the plug-in. The ABI of the current plug-in version is
+ * backwards compatible with any plug-in version from the version specified
+ * here to the current version. This information is used when resolving
+ * versioned plug-in dependencies. See also information about
+ * @ref pluginVersions "plug-in versions".
+ * - @a api: Backwards compatibility of the application programming interface
+ * (API) of the plug-in. API compatibility means that source code developed
+ * against one version of the plug-in also compiles against another version
+ * of the plug-in. This information is not used by framework but it can be
+ * used by a developer developing dependent plug-ins.
+ *
+ * These apply to plug-ins that provide header files and runtime libraries.
+ * For example, a plug-in might export global functions to other plug-ins or it
+ * might provide an extension point where an extension installed by other
+ * plug-in must conform to data structures defined by the plug-in.
+ * Both attributes are optional.
+ *
+ * @subsubsection pluginDescPluginRequires requires
+ *
+ * This element includes information about static plug-in dependencies.
+ * It can be omitted if there are no dependencies. It can contain following
+ * elements.
+ *
+ * - @ref pluginDescPluginReqCP "c-pluff": An optional version dependency
+ * on the C-Pluff implementation.
+ * - @ref pluginDescPluginReqImport "import": Declares a static dependency
+ * on other plug-in. This element is repeated if there are multiple
+ * dependencies and omitted if there are none.
+ *
+ * @subsubsection pluginDescPluginReqCP c-pluff
+ *
+ * This element declares a version dependency on the C-Pluff
+ * implementation. It can be used to ensure that the plug-in is not loaded by
+ * incompatible C-Pluff version. It has the following attribute.
+ *
+ * - @a version: The required version of the C-Pluff implementation.
+ * This is used when resolving the plug-in. It is checked that the used
+ * C-Pluff implementation is backwards compatible with the version specified
+ * here when it comes to the application binary interface (ABI) of C-Pluff.
+ *
+ * @subsubsection pluginDescPluginReqImport import
+ *
+ * This element declares a static dependency on other plug-in. It must be
+ * used when a plug-in uses global symbols or data from other plug-in or when
+ * a plug-in uses an extension point defined by other plug-in or whenever some
+ * other plug-in needs to be there for the plug-in to work. The framework takes
+ * care of resolving and starting the dependencies whenever the plug-in is
+ * resolved or started.
+ *
+ * This element can have following attributes.
+ *
+ * - @a plugin: The identifier of the imported plug-in.
+ * - @a version: An optional version dependency on the imported plug-in.
+ * The plug-in can be resolved only if the version of the imported plug-in
+ * is backwards compatible with the version specified here when it comes
+ * to the application binary interface (ABI) of the imported plug-in.
+ * - @a optional: Is the import optional or not ("true" or "false"). Default is
+ * false, a mandatory import.
+ * An optional import behaves just like the mandatory import as long as the
+ * imported plug-in is present. However, if it is not present then the
+ * import is ignored. Optional import can be used if the plug-in works
+ * in limited capacity even without the specified plug-in.
+ *
+ * @subsubsection pluginDescPluginRuntime runtime
+ *
+ * This element contains information about the plug-in runtime library. It is
+ * omitted if the plug-in does not have a runtime library but contains only
+ * data. It can have following attributes.
+ *
+ * - @a library: The name of the plug-in runtime library in the plug-in
+ * directory. A platform specific extension (for example, ".so" or ".dll")
+ * is added to the value specified here when loading the library.
+ * - @a funcs: The functions to be used to create an instance of the plug-in
+ * runtime. This attribute is optional. It is needed if the plug-in has
+ * a start or stop function. The value specified here is a name of an
+ * exported symbol which contains a pointer to @ref cp_plugin_runtime_t
+ * structure.
+ *
+ * @subsubsection pluginDescPluginEP extension-point
+ *
+ * This element defines an extension point provided by the plug-in.
+ * It can have following attributes.
+ *
+ * - @a id: The local identifier of the extension point. The value specified
+ * here is prefixed with the identifier of the plug-in and dot to construct
+ * the global identifier of the extension point.
+ * - @a name: An optional human-readable name describing the use of the
+ * extension point.
+ * - @a schema: An optional path to the extension point XML schema in
+ * the plug-in directory. This information is not currently used by the
+ * framework. But it can be used by a developer to determine what information
+ * should be provided by extensions attached to this extension point.
+ *
+ * @subsubsection pluginDescPluginE extension
+ *
+ * This element defines an extension installed into a specified extension
+ * point provided by the defining plug-in or some other plug-in.
+ * It can have following attributes.
+ *
+ * - @a point: The global identifier of the associated extension point.
+ * - @a id: An optional local identifier for the extension. The value specified
+ * here is prefixed with the identifier of the plug-in and dot to construct
+ * the global identifier for the extension.
+ * - @a name: An optional human-readable name describing the extension.
+ *
+ * The extension element can contain XML elements specific to the associated
+ * extension point (conforming to the schema defined by the extension point).
+ *
+ * @subsection pluginRuntime Plug-in runtime library
+ *
+ * A plug-in runtime library is an optional plug-in element. Plug-ins only
+ * supplying static data in form of XML data and files do not need a runtime
+ * library. However, a typical plug-in does provide program logic as well.
+ *
+ * The plug-in runtime library includes all program logic and program
+ * data provided by the plug-in. It is simply a shared library, or a
+ * dynamically linked library, which is linked in to the application when
+ * the plug-in is started. When plug-in is unloaded, the runtime library is
+ * unloaded as well. The framework has been designed to manage dependencies
+ * so that unloading of the runtime library does not cause problems, provided
+ * that plug-ins behave properly.
+ *
+ * A plug-in can expose functionality to other plug-ins either as exported
+ * global symbols that are directly resolved by other plug-ins or by supplying
+ * extensions. When other plug-ins are directly using exported symbols the
+ * plug-in acts just like any standard shared library. Nothing special there.
+ * The more interesting case is exposing functionality as extensions. Because
+ * the extension is registered at a specific extension point, the logic in
+ * other plug-ins can use the extension and the associated program logic even
+ * if they are not aware of the existence of the extension supplying plug-in.
+ *
+ * The extension points accepting program logic as extensions define a way
+ * to specify the name of the symbol pointing to the supplied logic. This is
+ * typically an attribute of an XML element contained in the extension
+ * definition. The plug-in supplying the extension can then export the program
+ * logic as a global symbol with arbitrary name and then place the name of the
+ * symbol in extension data. Alternatively, the plug-in can define a virtual
+ * symbol at runtime using ::cp_define_symbol. Other plug-ins that are using
+ * extensions registered at the extension point can then resolve the named
+ * symbol using ::cp_resolve_symbol at runtime. The framework automatically
+ * creates a dependency from the symbol using plug-in to the symbol supplying
+ * plug-in to prevent failures in case the symbol supplying plug-in is stopped
+ * or unloaded.
+ *
+ * @subsection pluginData Static plug-in data
+ *
+ * Plug-in can supply static data to other plug-ins using at least two
+ * different mechanisms. A plug-in can easily provide static XML data as part
+ * of extension elements. Additionally, a plug-in directory can contain
+ * files that may be accessed by other plug-ins. Currently the platform does
+ * not provide assistance in accessing data files provided by other plug-ins.
+ * However, a plug-in can locate the plug-in directory and thus any included
+ * data files by using plug-in path available in @ref cp_plugin_info_t
+ * structure of the data providing plug-in.
+ */
diff --git a/lib/cpluff/libcpluff/internal.h b/lib/cpluff/libcpluff/internal.h
new file mode 100644
index 0000000000..91989e8451
--- /dev/null
+++ b/lib/cpluff/libcpluff/internal.h
@@ -0,0 +1,566 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Internal data structures and declarations
+ */
+
+#ifndef INTERNAL_H_
+#define INTERNAL_H_
+
+
+/* ------------------------------------------------------------------------
+ * Inclusions
+ * ----------------------------------------------------------------------*/
+
+#include "defines.h"
+#include <assert.h>
+#if defined(DLOPEN_POSIX)
+#include <dlfcn.h>
+#elif defined(DLOPEN_LIBTOOL)
+#include <ltdl.h>
+#endif
+#include "../kazlib/list.h"
+#include "../kazlib/hash.h"
+#include "cpluff.h"
+#ifdef CP_THREADS
+#include "thread.h"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif //__cplusplus
+
+
+/* ------------------------------------------------------------------------
+ * Constants
+ * ----------------------------------------------------------------------*/
+
+/// Preliminarily OK
+#define CP_OK_PRELIMINARY (-1)
+
+/// Callback function logger function
+#define CPI_CF_LOGGER 1
+
+/// Callback function plug-in listener function
+#define CPI_CF_LISTENER 2
+
+/// Callback function start function
+#define CPI_CF_START 4
+
+/// Callback function stop function
+#define CPI_CF_STOP 8
+
+/// Bitmask corresponding to any callback function
+#define CPI_CF_ANY (~0)
+
+/// Logging limit for no logging
+#define CP_LOG_NONE 1000
+
+
+/* ------------------------------------------------------------------------
+ * Macros
+ * ----------------------------------------------------------------------*/
+
+#if defined(DLOPEN_POSIX)
+#define DLHANDLE void *
+#define DLOPEN(name) dlopen((name), RTLD_LAZY | RTLD_GLOBAL)
+#define DLSYM(handle, symbol) dlsym((handle), (symbol))
+#define DLCLOSE(handle) dlclose(handle)
+#define DLERROR() dlerror()
+#elif defined(DLOPEN_LIBTOOL)
+#define DLHANDLE lt_dlhandle
+#define DLOPEN(name) lt_dlopen(name)
+#define DLSYM(handle, symbol) lt_dlsym((handle), (symbol))
+#define DLCLOSE(handle) lt_dlclose(handle)
+#define DLERROR() lt_dlerror()
+#endif
+
+
+/**
+ * Checks that the specified function argument is not NULL.
+ * Otherwise, reports a fatal error.
+ *
+ * @param arg the argument
+ */
+#define CHECK_NOT_NULL(arg) do { if ((arg) == NULL) cpi_fatal_null_arg(#arg, __func__); } while (0)
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+typedef struct cp_plugin_t cp_plugin_t;
+typedef struct cp_plugin_env_t cp_plugin_env_t;
+
+// Plug-in context
+struct cp_context_t {
+
+ /// The associated plug-in instance or NULL for the main program
+ cp_plugin_t *plugin;
+
+ /// The associated plug-in environment
+ cp_plugin_env_t *env;
+
+ /// Information about resolved symbols or NULL if not initialized
+ hash_t *resolved_symbols;
+
+ /// Information about symbol providing plugins or NULL if not initialized
+ hash_t *symbol_providers;
+
+};
+
+// Plug-in environment
+struct cp_plugin_env_t {
+
+#if defined(CP_THREADS)
+
+ /// Mutex for accessing this plug-in environment.
+ /// This mutex is signaled when a run function returns.
+ cpi_mutex_t *mutex;
+
+#elif !defined(NDEBUG)
+ int locked;
+#endif
+
+ /// Number of startup arguments
+ int argc;
+
+ /// An array of startup arguments
+ char **argv;
+
+ /// Installed plug-in listeners
+ list_t *plugin_listeners;
+
+ /// Registered loggers
+ list_t *loggers;
+
+ /// Minimum logger selection severity
+ int log_min_severity;
+
+ /// List of registered plug-in directories
+ list_t *plugin_dirs;
+
+ /// Map of in-use reference counter information object
+ hash_t *infos;
+
+ /// Maps plug-in identifiers to plug-in state structures
+ hash_t *plugins;
+
+ /// List of started plug-ins in the order they were started
+ list_t *started_plugins;
+
+ /// Maps extension point names to installed extension points
+ hash_t *ext_points;
+
+ /// Maps extension point names to installed extensions
+ hash_t *extensions;
+
+ /// FIFO queue of run functions, currently running functions at front
+ list_t *run_funcs;
+
+ /// First waiting run function, or NULL if none
+ lnode_t *run_wait;
+
+ /// Is logger currently being invoked
+ int in_logger_invocation;
+
+ /// Whether currently in event listener invocation
+ int in_event_listener_invocation;
+
+ // Whether currently in start function invocation
+ int in_start_func_invocation;
+
+ // Whether currently in stop function invocation
+ int in_stop_func_invocation;
+
+ // Whether currently in create function invocation
+ int in_create_func_invocation;
+
+ // Whether currently in destroy function invocation
+ int in_destroy_func_invocation;
+
+};
+
+// Plug-in instance
+struct cp_plugin_t {
+
+ /// The enclosing context or NULL if none exists
+ cp_context_t *context;
+
+ /// Plug-in information
+ cp_plugin_info_t *plugin;
+
+ /// The current state of the plug-in
+ cp_plugin_state_t state;
+
+ /// The set of imported plug-ins, or NULL if not resolved
+ list_t *imported;
+
+ /// The set of plug-ins importing this plug-in
+ list_t *importing;
+
+ /// The runtime library handle, or NULL if not resolved
+ DLHANDLE runtime_lib;
+
+ /// Plug-in runtime function information, or NULL if not resolved
+ cp_plugin_runtime_t *runtime_funcs;
+
+ /// Plug-in instance data or NULL if instance does not exist
+ void *plugin_data;
+
+ /// Context specific symbols defined by the plug-in
+ hash_t *defined_symbols;
+
+ /// Used by recursive operations: has this plug-in been processed already
+ int processed;
+
+};
+
+
+/**
+ * Deallocates a reference counted resource when the reference count drops
+ * to zero. The plug-in context is locked on call to the function.
+ *
+ * @param ctx the associated plug-in context
+ * @param resource the resource
+ */
+typedef void (*cpi_dealloc_func_t)(cp_context_t *ctx, void *resource);
+
+typedef struct cpi_plugin_event_t cpi_plugin_event_t;
+
+/// Plug-in event information
+struct cpi_plugin_event_t {
+
+ /// The affect plug-in
+ const char *plugin_id;
+
+ /// Old state
+ cp_plugin_state_t old_state;
+
+ /// New state
+ cp_plugin_state_t new_state;
+};
+
+
+/* ------------------------------------------------------------------------
+ * Function declarations
+ * ----------------------------------------------------------------------*/
+
+
+// Locking data structures for exclusive access
+
+#if defined(CP_THREADS) || !defined(NDEBUG)
+
+/**
+ * Acquires exclusive access to the framework. Thread having the framework
+ * lock must not acquire plug-in context lock (it is ok to retain a previously
+ * acquired plug-in context lock).
+ */
+CP_HIDDEN void cpi_lock_framework(void);
+
+/**
+ * Releases exclusive access to the framework.
+ */
+CP_HIDDEN void cpi_unlock_framework(void);
+
+/**
+ * Acquires exclusive access to a plug-in context and the associated
+ * plug-in environment.
+ *
+ * @param context the plug-in context
+ */
+CP_HIDDEN void cpi_lock_context(cp_context_t *context) CP_GCC_NONNULL(1);
+
+/**
+ * Releases exclusive access to a plug-in context.
+ *
+ * @param context the plug-in context
+ */
+CP_HIDDEN void cpi_unlock_context(cp_context_t *context) CP_GCC_NONNULL(1);
+
+/**
+ * Waits until the specified plug-in context is signalled.
+ *
+ * @param context the plug-in context
+ */
+CP_HIDDEN void cpi_wait_context(cp_context_t *context) CP_GCC_NONNULL(1);
+
+/**
+ * Signals the specified plug-in context.
+ *
+ * @param context the plug-in context
+ */
+CP_HIDDEN void cpi_signal_context(cp_context_t *context) CP_GCC_NONNULL(1);
+
+#else
+#define cpi_lock_context(dummy) do {} while (0)
+#define cpi_unlock_context(dummy) do {} while (0)
+#define cpi_wait_context(dummy) do {} while (0)
+#define cpi_signal_context(dummy) do {} while (0)
+#define cpi_lock_framework() do {} while(0)
+#define cpi_unlock_framework() do {} while(0)
+#endif
+
+/**
+ * @def cpi_is_context_locked
+ *
+ * Returns whether the context is locked. This is intended to be used in
+ * assertions only and it is not defined if debugging is not enabled.
+ */
+
+#ifndef NDEBUG
+#ifdef CP_THREADS
+#define cpi_is_context_locked(ctx) cpi_is_mutex_locked((ctx)->env->mutex)
+#else
+#define cpi_is_context_locked(ctx) ((ctx)->env->locked)
+#endif
+#endif
+
+
+// Logging
+
+/**
+ * Logs a message. Calls dgettext for @a msg to localize it before delivering
+ * it to loggers. The caller must have locked the context. This
+ * function logs the message unconditionally. Use convenience macros
+ * @ref cpi_error, @ref cpi_warn, @ref cpi_info and @ref cpi_debug
+ * to log based on the minimum severity level logged.
+ *
+ * @param ctx the related plug-in context
+ * @param severity the severity of the message
+ * @param msg the localized message
+ */
+CP_HIDDEN void cpi_log(cp_context_t *ctx, cp_log_severity_t severity, const char *msg) CP_GCC_NONNULL(1, 3);
+
+/**
+ * Formats and logs a message. Calls dgettext for @a msg to localize it before
+ * formatting the message. The caller must have locked the context. This
+ * function logs the message unconditionally. Use convenience macros
+ * @ref cpi_errorf, @ref cpi_warnf, @ref cpi_infof and @ref cpi_debugf
+ * to log based on the minimum severity level logged.
+ *
+ * @param ctx the related plug-in context
+ * @param severity the severity of the message
+ * @param msg the localized message format
+ * @param ... the message parameters
+ */
+CP_HIDDEN void cpi_logf(cp_context_t *ctx, cp_log_severity_t severity, const char *msg, ...) CP_GCC_PRINTF(3, 4) CP_GCC_NONNULL(1, 3);
+
+/**
+ * Returns whether the messages of the specified severity level are
+ * being logged for the specified context. The caller must have locked the context.
+ *
+ * @param ctx the plug-in context
+ * @param severity the severity
+ * @return whether the messages of the specified severity level are logged
+ */
+#define cpi_is_logged(context, severity) (assert(cpi_is_context_locked(context)), (severity) >= (context)->env->log_min_severity)
+
+// Convenience macros for logging
+#define cpi_log_cond(ctx, level, msg) do { if (cpi_is_logged((ctx), (level))) cpi_log((ctx), (level), (msg)); } while (0)
+#define cpi_logf_cond(ctx, level, msg, ...) do { if (cpi_is_logged((ctx), (level))) cpi_logf((ctx), (level), (msg), __VA_ARGS__); } while (0)
+#define cpi_error(ctx, msg) cpi_log_cond((ctx), CP_LOG_ERROR, (msg))
+#define cpi_errorf(ctx, msg, ...) cpi_logf_cond((ctx), CP_LOG_ERROR, (msg), __VA_ARGS__)
+#define cpi_warn(ctx, msg) cpi_log_cond((ctx), CP_LOG_WARNING, (msg))
+#define cpi_warnf(ctx, msg, ...) cpi_logf_cond((ctx), CP_LOG_WARNING, (msg), __VA_ARGS__)
+#define cpi_info(ctx, msg) cpi_log_cond((ctx), CP_LOG_INFO, (msg))
+#define cpi_infof(ctx, msg, ...) cpi_logf_cond((ctx), CP_LOG_INFO, (msg), __VA_ARGS__)
+#define cpi_debug(ctx, msg) cpi_log_cond((ctx), CP_LOG_DEBUG, (msg))
+#define cpi_debugf(ctx, msg, ...) cpi_logf_cond((ctx), CP_LOG_DEBUG, (msg), __VA_ARGS__)
+
+/**
+ * Unregisters loggers in the specified logger list. Either unregisters all
+ * loggers or only loggers installed by the specified plug-in.
+ *
+ * @param loggers the logger list
+ * @param plugin the plug-in whose loggers to unregister or NULL for all
+ */
+CP_HIDDEN void cpi_unregister_loggers(list_t *loggers, cp_plugin_t *plugin) CP_GCC_NONNULL(1);
+
+/**
+ * Unregisters plug-in listeners in the specified list. Either unregisters all
+ * listeners or only listeners installed by the specified plug-in.
+ *
+ * @param listeners the listener list
+ * @param plugin the plug-in whose listeners to unregister or NULL for all
+ */
+CP_HIDDEN void cpi_unregister_plisteners(list_t *listeners, cp_plugin_t *plugin) CP_GCC_NONNULL(1);
+
+/**
+ * Returns the owner name for a context.
+ *
+ * @param ctx the context
+ * @param name the location where the name of the owner is to be stored
+ * @param size maximum size of the owner string, including the terminating zero
+ * @return the pointer passed in as @a name
+ */
+CP_HIDDEN char *cpi_context_owner(cp_context_t *ctx, char *name, size_t size) CP_GCC_NONNULL(1);
+
+/**
+ * Reports a fatal error. This method does not return.
+ *
+ * @param msg the formatted error message
+ * @param ... parameters
+ */
+CP_HIDDEN void cpi_fatalf(const char *msg, ...) CP_GCC_NORETURN CP_GCC_PRINTF(1, 2) CP_GCC_NONNULL(1);
+
+/**
+ * Reports a fatal NULL argument to an API function.
+ *
+ * @param arg the argument name
+ * @param func the API function name
+ */
+CP_HIDDEN void cpi_fatal_null_arg(const char *arg, const char *func) CP_GCC_NORETURN CP_GCC_NONNULL(1, 2);
+
+/**
+ * Checks that we are currently not in a specific callback function invocation.
+ * Otherwise, reports a fatal error. The caller must have locked the context
+ * before calling this function.
+ *
+ * @param ctx the associated plug-in context
+ * @param funcmask the bitmask of disallowed callback functions
+ * @param func the current plug-in framework function
+ */
+CP_HIDDEN void cpi_check_invocation(cp_context_t *ctx, int funcmask, const char *func) CP_GCC_NONNULL(1, 3);
+
+
+// Context management
+
+/**
+ * Allocates a new plug-in context.
+ *
+ * @param plugin the associated plug-in or NULL for the client program
+ * @param env the associated plug-in environment
+ * @param status a pointer to the location where the status code is to be stored
+ * @return the newly allocated context or NULL on failure
+ */
+CP_HIDDEN cp_context_t * cpi_new_context(cp_plugin_t *plugin, cp_plugin_env_t *env, cp_status_t *status) CP_GCC_NONNULL(2, 3);
+
+/**
+ * Frees the resources associated with a plug-in context. Also frees the
+ * associated plug-in environment if the context is a client program plug-in
+ * context.
+ *
+ * @param context the plug-in context to free
+ */
+CP_HIDDEN void cpi_free_context(cp_context_t *context) CP_GCC_NONNULL(1);
+
+/**
+ * Destroys all contexts and releases the context list resources.
+ */
+CP_HIDDEN void cpi_destroy_all_contexts(void);
+
+
+// Delivering plug-in events
+
+/**
+ * Delivers a plug-in event to registered event listeners.
+ *
+ * @param context the plug-in context
+ * @param event the plug-in event
+ */
+CP_HIDDEN void cpi_deliver_event(cp_context_t *context, const cpi_plugin_event_t *event) CP_GCC_NONNULL(1, 2);
+
+
+// Plug-in management
+
+/**
+ * Frees any resources allocated for a plug-in description.
+ *
+ * @param plugin the plug-in to be freed
+ */
+CP_HIDDEN void cpi_free_plugin(cp_plugin_info_t *plugin) CP_GCC_NONNULL(1);
+
+/**
+ * Starts the specified plug-in and its dependencies.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in
+ * @return @ref CP_OK (zero) on success or an error code on failure
+ */
+CP_HIDDEN cp_status_t cpi_start_plugin(cp_context_t *context, cp_plugin_t *plugin) CP_GCC_NONNULL(1, 2);
+
+
+// Dynamic resource management
+
+/**
+ * Registers a new reference counted information object.
+ * Initializes the reference count to 1. The object is released and
+ * deallocated using the specified deallocation function @a df when its
+ * reference count becomes zero. Reference count is incresed by
+ * ::cpi_use_info and decreased by ::cp_release_info. The caller must have
+ * locked the plug-in context.
+ *
+ * @param ctx the associated plug-in context
+ * @param res the resource
+ * @param df the deallocation function
+ * @return @ref CP_OK (zero) on success or an error code on failure
+ */
+CP_HIDDEN cp_status_t cpi_register_info(cp_context_t *ctx, void *res, cpi_dealloc_func_t df) CP_GCC_NONNULL(1, 2, 3);
+
+/**
+ * Increases the reference count for the specified information object.
+ * The caller must have locked the plug-in context.
+ *
+ * @param ctx the plug-in context
+ * @param res the resource
+ */
+CP_HIDDEN void cpi_use_info(cp_context_t *ctx, void *res) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Decreases the reference count for the specified information object.
+ * The caller must have locked the plug-in context.
+ *
+ * @param ctx the plug-in context
+ * @param res the resource
+ */
+CP_HIDDEN void cpi_release_info(cp_context_t *ctx, void *res) CP_GCC_NONNULL(1, 2);
+
+/**
+ * Checks for remaining information objects in the specified plug-in context.
+ * Does not destroy the infos hash.
+ *
+ * @param ctx the plug-in context
+ */
+CP_HIDDEN void cpi_release_infos(cp_context_t *ctx) CP_GCC_NONNULL(1);
+
+
+// Serialized execution
+
+/**
+ * Waits for all the run functions registered by the specified plug-in to
+ * return and then unregisters them. The caller must have locked the
+ * associated context.
+ *
+ * @param plugin the plug-in to be stopped
+ */
+CP_HIDDEN void cpi_stop_plugin_run(cp_plugin_t *plugin) CP_GCC_NONNULL(1);
+
+
+#ifdef __cplusplus
+}
+#endif //__cplusplus
+
+#endif /*INTERNAL_H_*/
diff --git a/lib/cpluff/libcpluff/logging.c b/lib/cpluff/libcpluff/logging.c
new file mode 100644
index 0000000000..928f0b4bec
--- /dev/null
+++ b/lib/cpluff/libcpluff/logging.c
@@ -0,0 +1,258 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Logging functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#include "internal.h"
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+/// Contains information about installed loggers
+typedef struct logger_t {
+
+ /// Pointer to logger
+ cp_logger_func_t logger;
+
+ /// Pointer to registering plug-in or NULL for the main program
+ cp_plugin_t *plugin;
+
+ /// User data pointer
+ void *user_data;
+
+ /// Minimum severity
+ cp_log_severity_t min_severity;
+
+ /// Selected environment or NULL
+ cp_plugin_env_t *env_selection;
+} logger_t;
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+/**
+ * Updates the context logging limits. The caller must have locked the
+ * context.
+ */
+static void update_logging_limits(cp_context_t *context) {
+ lnode_t *node;
+ int nms = CP_LOG_NONE;
+
+ node = list_first(context->env->loggers);
+ while (node != NULL) {
+ logger_t *lh = lnode_get(node);
+ if (lh->min_severity < nms) {
+ nms = lh->min_severity;
+ }
+ node = list_next(context->env->loggers, node);
+ }
+ context->env->log_min_severity = nms;
+}
+
+static int comp_logger(const void *p1, const void *p2) {
+ const logger_t *l1 = p1;
+ const logger_t *l2 = p2;
+ return l1->logger != l2->logger;
+}
+
+CP_C_API cp_status_t cp_register_logger(cp_context_t *context, cp_logger_func_t logger, void *user_data, cp_log_severity_t min_severity) {
+ logger_t l;
+ logger_t *lh = NULL;
+ lnode_t *node = NULL;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(logger);
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ do {
+
+ // Check if logger already exists and allocate new holder if necessary
+ l.logger = logger;
+ if ((node = list_find(context->env->loggers, &l, comp_logger)) == NULL) {
+ lh = malloc(sizeof(logger_t));
+ node = lnode_create(lh);
+ if (lh == NULL || node == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ lh->logger = logger;
+ lh->plugin = context->plugin;
+ list_append(context->env->loggers, node);
+ } else {
+ lh = lnode_get(node);
+ }
+
+ // Initialize or update the logger holder
+ lh->user_data = user_data;
+ lh->min_severity = min_severity;
+
+ // Update global limits
+ update_logging_limits(context);
+
+ } while (0);
+
+ // Report error
+ if (status == CP_ERR_RESOURCE) {
+ cpi_error(context, N_("Logger could not be registered due to insufficient memory."));
+ } else if (cpi_is_logged(context, CP_LOG_DEBUG)) {
+ char owner[64];
+ /* TRANSLATORS: %s is the context owner */
+ cpi_debugf(context, N_("%s registered a logger."), cpi_context_owner(context, owner, sizeof(owner)));
+ }
+ cpi_unlock_context(context);
+
+ // Release resources on error
+ if (status != CP_OK) {
+ if (node != NULL) {
+ lnode_destroy(node);
+ }
+ if (lh != NULL) {
+ free(lh);
+ }
+ }
+
+ return status;
+}
+
+CP_C_API void cp_unregister_logger(cp_context_t *context, cp_logger_func_t logger) {
+ logger_t l;
+ lnode_t *node;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(logger);
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+
+ l.logger = logger;
+ if ((node = list_find(context->env->loggers, &l, comp_logger)) != NULL) {
+ logger_t *lh = lnode_get(node);
+ list_delete(context->env->loggers, node);
+ lnode_destroy(node);
+ free(lh);
+ update_logging_limits(context);
+ }
+ if (cpi_is_logged(context, CP_LOG_DEBUG)) {
+ char owner[64];
+ /* TRANSLATORS: %s is the context owner */
+ cpi_debugf(context, N_("%s unregistered a logger."), cpi_context_owner(context, owner, sizeof(owner)));
+ }
+ cpi_unlock_context(context);
+}
+
+static void do_log(cp_context_t *context, cp_log_severity_t severity, const char *msg) {
+ lnode_t *node;
+ const char *apid = NULL;
+
+ assert(cpi_is_context_locked(context));
+ if (context->env->in_logger_invocation) {
+ cpi_fatalf(_("Encountered a recursive logging request within a logger invocation."));
+ }
+ if (context->plugin != NULL) {
+ apid = context->plugin->plugin->identifier;
+ }
+ context->env->in_logger_invocation++;
+ node = list_first(context->env->loggers);
+ while (node != NULL) {
+ logger_t *lh = lnode_get(node);
+ if (severity >= lh->min_severity) {
+ lh->logger(severity, msg, apid, lh->user_data);
+ }
+ node = list_next(context->env->loggers, node);
+ }
+ context->env->in_logger_invocation--;
+}
+
+CP_HIDDEN void cpi_log(cp_context_t *context, cp_log_severity_t severity, const char *msg) {
+ assert(context != NULL);
+ assert(msg != NULL);
+ assert(severity >= CP_LOG_DEBUG && severity <= CP_LOG_ERROR);
+ do_log(context, severity, _(msg));
+}
+
+CP_HIDDEN void cpi_logf(cp_context_t *context, cp_log_severity_t severity, const char *msg, ...) {
+ char buffer[256];
+ va_list va;
+
+ assert(context != NULL);
+ assert(msg != NULL);
+ assert(severity >= CP_LOG_DEBUG && severity <= CP_LOG_ERROR);
+
+ va_start(va, msg);
+ vsnprintf(buffer, sizeof(buffer), _(msg), va);
+ va_end(va);
+ strcpy(buffer + sizeof(buffer)/sizeof(char) - 4, "...");
+ do_log(context, severity, buffer);
+}
+
+static void process_unregister_logger(list_t *list, lnode_t *node, void *plugin) {
+ logger_t *lh = lnode_get(node);
+ if (plugin == NULL || lh->plugin == plugin) {
+ list_delete(list, node);
+ lnode_destroy(node);
+ free(lh);
+ }
+}
+
+CP_HIDDEN void cpi_unregister_loggers(list_t *loggers, cp_plugin_t *plugin) {
+ list_process(loggers, plugin, process_unregister_logger);
+}
+
+CP_C_API void cp_log(cp_context_t *context, cp_log_severity_t severity, const char *msg) {
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(msg);
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ if (severity < CP_LOG_DEBUG || severity > CP_LOG_ERROR) {
+ cpi_fatalf(_("Illegal severity value in call to %s."), __func__);
+ }
+ if (cpi_is_logged(context, severity)) {
+ do_log(context, severity, msg);
+ }
+ cpi_unlock_context(context);
+}
+
+CP_C_API int cp_is_logged(cp_context_t *context, cp_log_severity_t severity) {
+ int is_logged;
+
+ CHECK_NOT_NULL(context);
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ is_logged = cpi_is_logged(context, severity);
+ cpi_unlock_context(context);
+ return is_logged;
+}
diff --git a/lib/cpluff/libcpluff/pcontrol.c b/lib/cpluff/libcpluff/pcontrol.c
new file mode 100644
index 0000000000..83a24cbf2c
--- /dev/null
+++ b/lib/cpluff/libcpluff/pcontrol.c
@@ -0,0 +1,1240 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Core plug-in management functions
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stddef.h>
+#include "../kazlib/list.h"
+#include "../kazlib/hash.h"
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#include "internal.h"
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+// Plug-in control
+
+#ifndef NDEBUG
+static void assert_processed_zero(cp_context_t *context) {
+ hscan_t scan;
+ hnode_t *node;
+
+ hash_scan_begin(&scan, context->env->plugins);
+ while ((node = hash_scan_next(&scan)) != NULL) {
+ cp_plugin_t *plugin = hnode_get(node);
+ assert(plugin->processed == 0);
+ }
+}
+#else
+#define assert_processed_zero(c) assert(1)
+#endif
+
+static void unregister_extensions(cp_context_t *context, cp_plugin_info_t *plugin) {
+ int i;
+
+ for (i = 0; i < plugin->num_ext_points; i++) {
+ cp_ext_point_t *ep = plugin->ext_points + i;
+ hnode_t *hnode;
+
+ if ((hnode = hash_lookup(context->env->ext_points, ep->identifier)) != NULL
+ && hnode_get(hnode) == ep) {
+ hash_delete_free(context->env->ext_points, hnode);
+ }
+ }
+ for (i = 0; i < plugin->num_extensions; i++) {
+ cp_extension_t *e = plugin->extensions + i;
+ hnode_t *hnode;
+
+ if ((hnode = hash_lookup(context->env->extensions, e->ext_point_id)) != NULL) {
+ list_t *el = hnode_get(hnode);
+ lnode_t *lnode = list_first(el);
+
+ while (lnode != NULL) {
+ lnode_t *nn = list_next(el, lnode);
+ if (lnode_get(lnode) == e) {
+ list_delete(el, lnode);
+ lnode_destroy(lnode);
+ break;
+ }
+ lnode = nn;
+ }
+ if (list_isempty(el)) {
+ char *epid = (char *) hnode_getkey(hnode);
+ hash_delete_free(context->env->extensions, hnode);
+ free(epid);
+ list_destroy(el);
+ }
+ }
+ }
+}
+
+CP_C_API cp_status_t cp_install_plugin(cp_context_t *context, cp_plugin_info_t *plugin) {
+ cp_plugin_t *rp = NULL;
+ cp_status_t status = CP_OK;
+ cpi_plugin_event_t event;
+ int i;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(plugin);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ do {
+
+ // Check that there is no conflicting plug-in already loaded
+ if (hash_lookup(context->env->plugins, plugin->identifier) != NULL) {
+ cpi_errorf(context,
+ N_("Plug-in %s could not be installed because a plug-in with the same identifier is already installed."),
+ plugin->identifier);
+ status = CP_ERR_CONFLICT;
+ break;
+ }
+
+ // Increase usage count for the plug-in descriptor
+ cpi_use_info(context, plugin);
+
+ // Allocate space for the plug-in state
+ if ((rp = malloc(sizeof(cp_plugin_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Initialize plug-in state
+ memset(rp, 0, sizeof(cp_plugin_t));
+ rp->context = NULL;
+ rp->plugin = plugin;
+ rp->state = CP_PLUGIN_INSTALLED;
+ rp->imported = NULL;
+ rp->runtime_lib = NULL;
+ rp->runtime_funcs = NULL;
+ rp->plugin_data = NULL;
+ rp->importing = list_create(LISTCOUNT_T_MAX);
+ if (rp->importing == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ if (!hash_alloc_insert(context->env->plugins, plugin->identifier, rp)) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Register extension points
+ for (i = 0; status == CP_OK && i < plugin->num_ext_points; i++) {
+ cp_ext_point_t *ep = plugin->ext_points + i;
+ hnode_t *hnode;
+
+ if ((hnode = hash_lookup(context->env->ext_points, ep->identifier)) != NULL) {
+ cpi_errorf(context, N_("Plug-in %s could not be installed because extension point %s conflicts with an already installed extension point."), plugin->identifier, ep->identifier);
+ status = CP_ERR_CONFLICT;
+ } else if (!hash_alloc_insert(context->env->ext_points, ep->identifier, ep)) {
+ status = CP_ERR_RESOURCE;
+ }
+ }
+
+ // Register extensions
+ for (i = 0; status == CP_OK && i < plugin->num_extensions; i++) {
+ cp_extension_t *e = plugin->extensions + i;
+ hnode_t *hnode;
+ lnode_t *lnode;
+ list_t *el;
+
+ if ((hnode = hash_lookup(context->env->extensions, e->ext_point_id)) == NULL) {
+ char *epid;
+ if ((el = list_create(LISTCOUNT_T_MAX)) != NULL
+ && (epid = strdup(e->ext_point_id)) != NULL) {
+ if (!hash_alloc_insert(context->env->extensions, epid, el)) {
+ list_destroy(el);
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ } else {
+ if (el != NULL) {
+ list_destroy(el);
+ }
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ } else {
+ el = hnode_get(hnode);
+ }
+ if ((lnode = lnode_create(e)) != NULL) {
+ list_append(el, lnode);
+ } else {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ }
+
+ // Break if previous loops failed
+ if (status != CP_OK) {
+ break;
+ }
+
+ // Plug-in installed
+ event.plugin_id = plugin->identifier;
+ event.old_state = CP_PLUGIN_UNINSTALLED;
+ event.new_state = rp->state;
+ cpi_deliver_event(context, &event);
+
+ } while (0);
+
+ // Release resources on failure
+ if (status != CP_OK) {
+ if (rp != NULL) {
+ if (rp->importing != NULL) {
+ list_destroy(rp->importing);
+ }
+ free(rp);
+ }
+ unregister_extensions(context, plugin);
+ }
+
+ // Report possible resource error
+ if (status == CP_ERR_RESOURCE) {
+ cpi_errorf(context,
+ N_("Plug-in %s could not be installed due to insufficient system resources."), plugin->identifier);
+ }
+ cpi_unlock_context(context);
+
+ return status;
+}
+
+/**
+ * Unresolves the plug-in runtime information.
+ *
+ * @param plugin the plug-in to unresolve
+ */
+static void unresolve_plugin_runtime(cp_plugin_t *plugin) {
+
+ // Destroy the plug-in instance, if necessary
+ if (plugin->context != NULL) {
+ plugin->context->env->in_destroy_func_invocation++;
+ plugin->runtime_funcs->destroy(plugin->plugin_data);
+ plugin->context->env->in_destroy_func_invocation--;
+ plugin->plugin_data = NULL;
+ cpi_free_context(plugin->context);
+ plugin->context = NULL;
+ }
+
+ // Close plug-in runtime library
+ plugin->runtime_funcs = NULL;
+ if (plugin->runtime_lib != NULL) {
+ DLCLOSE(plugin->runtime_lib);
+ plugin->runtime_lib = NULL;
+ }
+}
+
+/**
+ * Loads and resolves the plug-in runtime library and initialization functions.
+ *
+ * @param context the plug-in context
+ * @param plugin the plugin
+ * @return CP_OK (zero) on success or error code on failure
+ */
+static int resolve_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) {
+ char *rlpath = NULL;
+ int rlpath_len;
+ cp_status_t status = CP_OK;
+
+ assert(plugin->runtime_lib == NULL);
+ if (plugin->plugin->runtime_lib_name == NULL) {
+ return CP_OK;
+ }
+
+ do {
+ int ppath_len, lname_len;
+ int cpluff_compatibility = 1;
+
+ // Check C-Pluff compatibility
+ if (plugin->plugin->req_cpluff_version != NULL) {
+#ifdef CP_ABI_COMPATIBILITY
+ cpluff_compatibility = (
+ cpi_vercmp(plugin->plugin->req_cpluff_version, CP_VERSION) <= 0
+ && cpi_vercmp(plugin->plugin->req_cpluff_version, CP_ABI_COMPATIBILITY) >= 0);
+#else
+ cpluff_compatibility = (cpi_vercmp(plugin->plugin->req_cpluff_version, CP_VERSION) == 0);
+#endif
+ }
+ if (!cpluff_compatibility) {
+ cpi_errorf(context, N_("Plug-in %s could not be resolved due to version incompatibility with C-Pluff."), plugin->plugin->identifier);
+ status = CP_ERR_DEPENDENCY;
+ break;
+ }
+
+ // Construct a path to plug-in runtime library.
+ /// @todo Add platform specific prefix (for example, "lib")
+ ppath_len = strlen(plugin->plugin->plugin_path);
+ lname_len = strlen(plugin->plugin->runtime_lib_name);
+ rlpath_len = ppath_len + lname_len + strlen(CP_SHREXT) + 2;
+ if ((rlpath = malloc(rlpath_len * sizeof(char))) == NULL) {
+ cpi_errorf(context, N_("Plug-in %s runtime library could not be loaded due to insufficient memory."), plugin->plugin->identifier);
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ memset(rlpath, 0, rlpath_len * sizeof(char));
+ strcpy(rlpath, plugin->plugin->plugin_path);
+ rlpath[ppath_len] = CP_FNAMESEP_CHAR;
+ strcpy(rlpath + ppath_len + 1, plugin->plugin->runtime_lib_name);
+ strcpy(rlpath + ppath_len + 1 + lname_len, CP_SHREXT);
+
+ // Open the plug-in runtime library
+ plugin->runtime_lib = DLOPEN(rlpath);
+ if (plugin->runtime_lib == NULL) {
+ const char *error = DLERROR();
+ if (error == NULL) {
+ error = _("Unspecified error.");
+ }
+ cpi_errorf(context, N_("Plug-in %s runtime library %s could not be opened: %s"), plugin->plugin->identifier, rlpath, error);
+ status = CP_ERR_RUNTIME;
+ break;
+ }
+
+ // Resolve plug-in functions
+ if (plugin->plugin->runtime_funcs_symbol != NULL) {
+ plugin->runtime_funcs = (cp_plugin_runtime_t *) DLSYM(plugin->runtime_lib, plugin->plugin->runtime_funcs_symbol);
+ if (plugin->runtime_funcs == NULL) {
+ const char *error = DLERROR();
+ if (error == NULL) {
+ error = _("Unspecified error.");
+ }
+ cpi_errorf(context, N_("Plug-in %s symbol %s containing plug-in runtime information could not be resolved: %s"), plugin->plugin->identifier, plugin->plugin->runtime_funcs_symbol, error);
+ status = CP_ERR_RUNTIME;
+ break;
+ }
+ if (plugin->runtime_funcs->create == NULL
+ || plugin->runtime_funcs->destroy == NULL) {
+ cpi_errorf(context, N_("Plug-in %s is missing a constructor or destructor function."), plugin->plugin->identifier);
+ status = CP_ERR_RUNTIME;
+ break;
+ }
+ }
+
+ } while (0);
+
+ // Release resources
+ free(rlpath);
+ if (status != CP_OK) {
+ unresolve_plugin_runtime(plugin);
+ }
+
+ return status;
+}
+
+/**
+ * Resolves the specified plug-in import into a plug-in pointer. Does not
+ * try to resolve the imported plug-in.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in being resolved
+ * @param import the plug-in import to resolve
+ * @param ipptr filled with pointer to the resolved plug-in or NULL
+ * @return CP_OK on success or error code on failure
+ */
+static int resolve_plugin_import(cp_context_t *context, cp_plugin_t *plugin, cp_plugin_import_t *import, cp_plugin_t **ipptr) {
+ cp_plugin_t *ip = NULL;
+ hnode_t *node;
+
+ // Lookup the plug-in
+ node = hash_lookup(context->env->plugins, import->plugin_id);
+ if (node != NULL) {
+ ip = hnode_get(node);
+ }
+
+ // Check plug-in version
+ if (ip != NULL
+ && import->version != NULL
+ && (ip->plugin->version == NULL
+ || (ip->plugin->abi_bw_compatibility == NULL
+ && cpi_vercmp(import->version, ip->plugin->version) != 0)
+ || (ip->plugin->abi_bw_compatibility != NULL
+ && (cpi_vercmp(import->version, ip->plugin->version) > 0
+ || cpi_vercmp(import->version, ip->plugin->abi_bw_compatibility) < 0)))) {
+ cpi_errorf(context,
+ N_("Plug-in %s could not be resolved due to version incompatibility with plug-in %s."),
+ plugin->plugin->identifier,
+ import->plugin_id);
+ *ipptr = NULL;
+ return CP_ERR_DEPENDENCY;
+ }
+
+ // Check if missing mandatory plug-in
+ if (ip == NULL && !import->optional) {
+ cpi_errorf(context,
+ N_("Plug-in %s could not be resolved because it depends on plug-in %s which is not installed."),
+ plugin->plugin->identifier,
+ import->plugin_id);
+ *ipptr = NULL;
+ return CP_ERR_DEPENDENCY;
+ }
+
+ // Return imported plug-in
+ *ipptr = ip;
+ return CP_OK;
+}
+
+/**
+ * Resolves the specified plug-in and its dependencies while leaving plug-ins
+ * with circular dependencies in a preliminarily resolved state.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in
+ * @return CP_OK (zero) or CP_OK_PRELIMINARY or an error code
+ */
+static int resolve_plugin_prel_rec(cp_context_t *context, cp_plugin_t *plugin) {
+ cp_status_t status = CP_OK;
+ int error_reported = 0;
+ lnode_t *node = NULL;
+ int i;
+
+ // Check if already resolved
+ if (plugin->state >= CP_PLUGIN_RESOLVED) {
+ return CP_OK;
+ }
+
+ // Check for dependency loops
+ if (plugin->processed) {
+ return CP_OK_PRELIMINARY;
+ }
+ plugin->processed = 1;
+
+ do {
+
+ // Recursively resolve the imported plug-ins
+ assert(plugin->imported == NULL);
+ if ((plugin->imported = list_create(LISTCOUNT_T_MAX)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ for (i = 0; i < plugin->plugin->num_imports; i++) {
+ cp_plugin_t *ip;
+ int s;
+
+ if ((node = lnode_create(NULL)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ if ((s = resolve_plugin_import(context, plugin, plugin->plugin->imports + i, &ip)) != CP_OK) {
+ error_reported = 1;
+ status = s;
+ break;
+ }
+ if (ip != NULL) {
+ lnode_put(node, ip);
+ list_append(plugin->imported, node);
+ node = NULL;
+ if (!cpi_ptrset_add(ip->importing, plugin)) {
+ status = CP_ERR_RESOURCE;
+ break;
+ } else if ((s = resolve_plugin_prel_rec(context, ip)) != CP_OK && s != CP_OK_PRELIMINARY) {
+ cpi_errorf(context, N_("Plug-in %s could not be resolved because it depends on plug-in %s which could not be resolved."), plugin->plugin->identifier, ip->plugin->identifier);
+ error_reported = 1;
+ status = s;
+ break;
+ }
+ } else {
+ lnode_destroy(node);
+ node = NULL;
+ }
+ }
+ if (status != CP_OK) {
+ break;
+ }
+
+ // Resolve this plug-in
+ assert(plugin->state == CP_PLUGIN_INSTALLED);
+ if ((i = resolve_plugin_runtime(context, plugin)) != CP_OK) {
+ status = i;
+ error_reported = 1;
+ break;
+ }
+
+ // Notify event listeners and update state if completely resolved
+ if (status == CP_OK) {
+ cpi_plugin_event_t event;
+
+ plugin->processed = 0;
+ event.plugin_id = plugin->plugin->identifier;
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
+ cpi_deliver_event(context, &event);
+ }
+
+ } while (0);
+
+ // Clean up
+ if (node != NULL) {
+ lnode_destroy(node);
+ }
+
+ // Handle errors
+ if (status == CP_ERR_RESOURCE && !error_reported) {
+ cpi_errorf(context, N_("Plug-in %s could not be resolved because of insufficient memory."), plugin->plugin->identifier);
+ }
+
+ return status;
+}
+
+/**
+ * Recursively commits the resolving process for the specified plug-in and
+ * its dependencies.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in
+ */
+static void resolve_plugin_commit_rec(cp_context_t *context, cp_plugin_t *plugin) {
+
+ // Check if already committed
+ if (!plugin->processed) {
+ return;
+ }
+ plugin->processed = 0;
+
+ // Commit if only preliminarily resolved
+ if (plugin->state < CP_PLUGIN_RESOLVED) {
+ cpi_plugin_event_t event;
+ lnode_t *node;
+
+ // Recursively commit dependencies
+ node = list_first(plugin->imported);
+ while (node != NULL) {
+ resolve_plugin_commit_rec(context, (cp_plugin_t *) lnode_get(node));
+ node = list_next(plugin->imported, node);
+ }
+
+ // Notify event listeners and update state
+ event.plugin_id = plugin->plugin->identifier;
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
+ cpi_deliver_event(context, &event);
+ }
+}
+
+/**
+ * Recursively cleans up the specified plug-in and its dependencies after
+ * a failed resolving attempt.
+ *
+ * @param plugin the plug-in
+ */
+static void resolve_plugin_failed_rec(cp_plugin_t *plugin) {
+
+ // Check if already cleaned up
+ if (!plugin->processed) {
+ return;
+ }
+ plugin->processed = 0;
+
+ // Clean up if only preliminarily resolved
+ if (plugin->state < CP_PLUGIN_RESOLVED) {
+ lnode_t *node;
+
+ // Recursively clean up depedencies
+ while ((node = list_first(plugin->imported)) != NULL) {
+ cp_plugin_t *ip = lnode_get(node);
+
+ resolve_plugin_failed_rec(ip);
+ cpi_ptrset_remove(ip->importing, plugin);
+ list_delete(plugin->imported, node);
+ lnode_destroy(node);
+ }
+ list_destroy(plugin->imported);
+ plugin->imported = NULL;
+ }
+}
+
+/**
+ * Resolves the specified plug-in and its dependencies.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in to be resolved
+ * @return CP_OK (zero) on success or an error code on failure
+ */
+static int resolve_plugin(cp_context_t *context, cp_plugin_t *plugin) {
+ cp_status_t status;
+
+ if ((status = resolve_plugin_prel_rec(context, plugin)) == CP_OK || status == CP_OK_PRELIMINARY) {
+ status = CP_OK;
+ resolve_plugin_commit_rec(context, plugin);
+ } else {
+ resolve_plugin_failed_rec(plugin);
+ }
+ assert_processed_zero(context);
+ return status;
+}
+
+/**
+ * Starts the plug-in runtime of the specified plug-in. This function does
+ * not consider dependencies and assumes that the plug-in is resolved but
+ * not yet started.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in
+ * @return CP_OK (zero) on success or an error code on failure
+ */
+static int start_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) {
+ cp_status_t status = CP_OK;
+ cpi_plugin_event_t event;
+ lnode_t *node = NULL;
+
+ event.plugin_id = plugin->plugin->identifier;
+ do {
+
+ // Allocate space for the list node
+ node = lnode_create(plugin);
+ if (node == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Set up plug-in instance
+ if (plugin->runtime_funcs != NULL) {
+
+ // Create plug-in instance if necessary
+ if (plugin->context == NULL) {
+ if ((plugin->context = cpi_new_context(plugin, context->env, &status)) == NULL) {
+ break;
+ }
+ context->env->in_create_func_invocation++;
+ plugin->plugin_data = plugin->runtime_funcs->create(plugin->context);
+ context->env->in_create_func_invocation--;
+ if (plugin->plugin_data == NULL) {
+ status = CP_ERR_RUNTIME;
+ break;
+ }
+ }
+
+ // Start plug-in
+ if (plugin->runtime_funcs->start != NULL) {
+ int s;
+
+ // About to start the plug-in
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_STARTING;
+ cpi_deliver_event(context, &event);
+
+ // Start the plug-in
+ context->env->in_start_func_invocation++;
+ s = plugin->runtime_funcs->start(plugin->plugin_data);
+ context->env->in_start_func_invocation--;
+
+ if (s != CP_OK) {
+
+ // Roll back plug-in state
+ if (plugin->runtime_funcs->stop != NULL) {
+
+ // Update state
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_STOPPING;
+ cpi_deliver_event(context, &event);
+
+ // Call stop function
+ context->env->in_stop_func_invocation++;
+ plugin->runtime_funcs->stop(plugin->plugin_data);
+ context->env->in_stop_func_invocation--;
+ }
+
+ // Destroy plug-in object
+ context->env->in_destroy_func_invocation++;
+ plugin->runtime_funcs->destroy(plugin->plugin_data);
+ context->env->in_destroy_func_invocation--;
+
+ status = CP_ERR_RUNTIME;
+ break;
+ }
+ }
+ }
+
+ // Plug-in active
+ list_append(context->env->started_plugins, node);
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_ACTIVE;
+ cpi_deliver_event(context, &event);
+
+ } while (0);
+
+ // Release resources and roll back plug-in state on failure
+ if (status != CP_OK) {
+ if (node != NULL) {
+ lnode_destroy(node);
+ }
+ if (plugin->context != NULL) {
+ cpi_free_context(plugin->context);
+ plugin->context = NULL;
+ }
+ if (plugin->state != CP_PLUGIN_RESOLVED) {
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
+ cpi_deliver_event(context, &event);
+ }
+ plugin->plugin_data = NULL;
+ }
+
+ // Report error on failure
+ switch (status) {
+ case CP_ERR_RESOURCE:
+ cpi_errorf(context,
+ N_("Plug-in %s could not be started due to insufficient memory."),
+ plugin->plugin->identifier);
+ break;
+ case CP_ERR_RUNTIME:
+ cpi_errorf(context,
+ N_("Plug-in %s failed to start due to plug-in runtime error."),
+ plugin->plugin->identifier);
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+static void warn_dependency_loop(cp_context_t *context, cp_plugin_t *plugin, list_t *importing, int dynamic) {
+ char *msgbase;
+ char *msg;
+ int msgsize;
+ lnode_t *node;
+
+ // Take the message base
+ if (dynamic) {
+ msgbase = N_("Detected a runtime plug-in dependency loop: %s");
+ } else {
+ msgbase = N_("Detected a static plug-in dependency loop: %s");
+ }
+
+ // Calculate the required message space
+ msgsize = 0;
+ msgsize += strlen(plugin->plugin->identifier);
+ msgsize += 2;
+ node = list_last(importing);
+ while (node != NULL) {
+ cp_plugin_t *p = lnode_get(node);
+ if (p == plugin) {
+ break;
+ }
+ msgsize += strlen(p->plugin->identifier);
+ msgsize += 2;
+ node = list_prev(importing, node);
+ }
+ msg = malloc(sizeof(char) * msgsize);
+ if (msg != NULL) {
+ strcpy(msg, plugin->plugin->identifier);
+ node = list_last(importing);
+ while (node != NULL) {
+ cp_plugin_t *p = lnode_get(node);
+ if (p == plugin) {
+ break;
+ }
+ strcat(msg, ", ");
+ strcat(msg, p->plugin->identifier);
+ node = list_prev(importing, node);
+ }
+ strcat(msg, ".");
+ cpi_infof(context, msgbase, msg);
+ free(msg);
+ } else {
+ cpi_infof(context, msgbase, plugin->plugin->identifier);
+ }
+}
+
+/**
+ * Starts the specified plug-in and its dependencies.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in
+ * @param importing stack of importing plug-ins
+ * @return CP_OK (zero) on success or an error code on failure
+ */
+static int start_plugin_rec(cp_context_t *context, cp_plugin_t *plugin, list_t *importing) {
+ cp_status_t status = CP_OK;
+ lnode_t *node;
+
+ // Check if already started or starting
+ if (plugin->state == CP_PLUGIN_ACTIVE) {
+ return CP_OK;
+ } else if (plugin->state == CP_PLUGIN_STARTING) {
+ warn_dependency_loop(context, plugin, importing, 1);
+ return CP_OK;
+ }
+ assert(plugin->state == CP_PLUGIN_RESOLVED);
+
+ // Check for dependency loops
+ if (cpi_ptrset_contains(importing, plugin)) {
+ warn_dependency_loop(context, plugin, importing, 0);
+ return CP_OK;
+ }
+ if (!cpi_ptrset_add(importing, plugin)) {
+ cpi_errorf(context,
+ N_("Plug-in %s could not be started due to insufficient memory."),
+ plugin->plugin->identifier);
+ return CP_ERR_RESOURCE;
+ }
+
+ // Start up dependencies
+ node = list_first(plugin->imported);
+ while (node != NULL) {
+ cp_plugin_t *ip = lnode_get(node);
+
+ if ((status = start_plugin_rec(context, ip, importing)) != CP_OK) {
+ break;
+ }
+ node = list_next(plugin->imported, node);
+ }
+ cpi_ptrset_remove(importing, plugin);
+
+ // Start up this plug-in
+ if (status == CP_OK) {
+ status = start_plugin_runtime(context, plugin);
+ }
+
+ return status;
+}
+
+CP_HIDDEN cp_status_t cpi_start_plugin(cp_context_t *context, cp_plugin_t *plugin) {
+ cp_status_t status;
+
+ if ((status = resolve_plugin(context, plugin)) == CP_OK) {
+ list_t *importing = list_create(LISTCOUNT_T_MAX);
+ if (importing != NULL) {
+ status = start_plugin_rec(context, plugin, importing);
+ assert(list_isempty(importing));
+ list_destroy(importing);
+ } else {
+ cpi_errorf(context,
+ N_("Plug-in %s could not be started due to insufficient memory."),
+ plugin->plugin->identifier);
+ status = CP_ERR_RESOURCE;
+ }
+ }
+ return status;
+}
+
+CP_C_API cp_status_t cp_start_plugin(cp_context_t *context, const char *id) {
+ hnode_t *node;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(id);
+
+ // Look up and start the plug-in
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ node = hash_lookup(context->env->plugins, id);
+ if (node != NULL) {
+ status = cpi_start_plugin(context, hnode_get(node));
+ } else {
+ cpi_warnf(context, N_("Unknown plug-in %s could not be started."), id);
+ status = CP_ERR_UNKNOWN;
+ }
+ cpi_unlock_context(context);
+
+ return status;
+}
+
+/**
+ * Stops the plug-in runtime of the specified plug-in. This function does
+ * not consider dependencies and assumes that the plug-in is active.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in
+ */
+static void stop_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) {
+ cpi_plugin_event_t event;
+
+ // Destroy plug-in instance
+ event.plugin_id = plugin->plugin->identifier;
+ if (plugin->context != NULL) {
+
+ // Wait until possible run functions have stopped
+ cpi_stop_plugin_run(plugin);
+
+ // Stop the plug-in
+ if (plugin->runtime_funcs->stop != NULL) {
+
+ // About to stop the plug-in
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_STOPPING;
+ cpi_deliver_event(context, &event);
+
+ // Invoke stop function
+ context->env->in_stop_func_invocation++;
+ plugin->runtime_funcs->stop(plugin->plugin_data);
+ context->env->in_stop_func_invocation--;
+
+ }
+
+ // Unregister all logger functions
+ cpi_unregister_loggers(plugin->context->env->loggers, plugin);
+
+ // Unregister all plug-in listeners
+ cpi_unregister_plisteners(plugin->context->env->plugin_listeners, plugin);
+
+ // Release resolved symbols
+ if (plugin->context->resolved_symbols != NULL) {
+ while (!hash_isempty(plugin->context->resolved_symbols)) {
+ hscan_t scan;
+ hnode_t *node;
+ const void *ptr;
+
+ hash_scan_begin(&scan, plugin->context->resolved_symbols);
+ node = hash_scan_next(&scan);
+ ptr = hnode_getkey(node);
+ cp_release_symbol(context, ptr);
+ }
+ assert(hash_isempty(plugin->context->resolved_symbols));
+ }
+ if (plugin->context->symbol_providers != NULL) {
+ assert(hash_isempty(plugin->context->symbol_providers));
+ }
+
+ // Release defined symbols
+ if (plugin->defined_symbols != NULL) {
+ hscan_t scan;
+ hnode_t *node;
+
+ hash_scan_begin(&scan, plugin->defined_symbols);
+ while ((node = hash_scan_next(&scan)) != NULL) {
+ char *n = (char *) hnode_getkey(node);
+ hash_scan_delfree(plugin->defined_symbols, node);
+ free(n);
+ }
+ hash_destroy(plugin->defined_symbols);
+ plugin->defined_symbols = NULL;
+ }
+
+ }
+
+ // Plug-in stopped
+ cpi_ptrset_remove(context->env->started_plugins, plugin);
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
+ cpi_deliver_event(context, &event);
+}
+
+/**
+ * Stops the plug-in and all plug-ins depending on it.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in
+ */
+static void stop_plugin_rec(cp_context_t *context, cp_plugin_t *plugin) {
+ lnode_t *node;
+
+ // Check if already stopped
+ if (plugin->state < CP_PLUGIN_ACTIVE) {
+ return;
+ }
+
+ // Check for dependency loops
+ if (plugin->processed) {
+ return;
+ }
+ plugin->processed = 1;
+
+ // Stop the depending plug-ins
+ node = list_first(plugin->importing);
+ while (node != NULL) {
+ stop_plugin_rec(context, lnode_get(node));
+ node = list_next(plugin->importing, node);
+ }
+
+ // Stop this plug-in
+ assert(plugin->state == CP_PLUGIN_ACTIVE);
+ stop_plugin_runtime(context, plugin);
+ assert(plugin->state < CP_PLUGIN_ACTIVE);
+
+ // Clear processed flag
+ plugin->processed = 0;
+}
+
+static void stop_plugin(cp_context_t *context, cp_plugin_t *plugin) {
+ stop_plugin_rec(context, plugin);
+ assert_processed_zero(context);
+}
+
+CP_C_API cp_status_t cp_stop_plugin(cp_context_t *context, const char *id) {
+ hnode_t *node;
+ cp_plugin_t *plugin;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(id);
+
+ // Look up and stop the plug-in
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ node = hash_lookup(context->env->plugins, id);
+ if (node != NULL) {
+ plugin = hnode_get(node);
+ stop_plugin(context, plugin);
+ } else {
+ cpi_warnf(context, N_("Unknown plug-in %s could not be stopped."), id);
+ status = CP_ERR_UNKNOWN;
+ }
+ cpi_unlock_context(context);
+
+ return status;
+}
+
+CP_C_API void cp_stop_plugins(cp_context_t *context) {
+ lnode_t *node;
+
+ CHECK_NOT_NULL(context);
+
+ // Stop the active plug-ins in the reverse order they were started
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ while ((node = list_last(context->env->started_plugins)) != NULL) {
+ stop_plugin(context, lnode_get(node));
+ }
+ cpi_unlock_context(context);
+}
+
+static void unresolve_plugin_rec(cp_context_t *context, cp_plugin_t *plugin) {
+ lnode_t *node;
+ cpi_plugin_event_t event;
+
+ // Check if already unresolved
+ if (plugin->state < CP_PLUGIN_RESOLVED) {
+ return;
+ }
+ assert(plugin->state == CP_PLUGIN_RESOLVED);
+
+ // Clear the list of imported plug-ins (also breaks dependency loops)
+ while ((node = list_first(plugin->imported)) != NULL) {
+ cp_plugin_t *ip = lnode_get(node);
+
+ cpi_ptrset_remove(ip->importing, plugin);
+ list_delete(plugin->imported, node);
+ lnode_destroy(node);
+ }
+ assert(list_isempty(plugin->imported));
+ list_destroy(plugin->imported);
+ plugin->imported = NULL;
+
+ // Unresolve depending plugins
+ while ((node = list_first(plugin->importing)) != NULL) {
+ unresolve_plugin_rec(context, lnode_get(node));
+ }
+
+ // Unresolve this plug-in
+ unresolve_plugin_runtime(plugin);
+ event.plugin_id = plugin->plugin->identifier;
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_INSTALLED;
+ cpi_deliver_event(context, &event);
+}
+
+/**
+ * Unresolves a plug-in.
+ *
+ * @param context the plug-in context
+ * @param plug-in the plug-in to be unresolved
+ */
+static void unresolve_plugin(cp_context_t *context, cp_plugin_t *plugin) {
+ stop_plugin(context, plugin);
+ unresolve_plugin_rec(context, plugin);
+}
+
+static void free_plugin_import_content(cp_plugin_import_t *import) {
+ assert(import != NULL);
+ free(import->plugin_id);
+ free(import->version);
+}
+
+static void free_ext_point_content(cp_ext_point_t *ext_point) {
+ free(ext_point->name);
+ free(ext_point->local_id);
+ free(ext_point->identifier);
+ free(ext_point->schema_path);
+}
+
+static void free_extension_content(cp_extension_t *extension) {
+ free(extension->name);
+ free(extension->local_id);
+ free(extension->identifier);
+ free(extension->ext_point_id);
+}
+
+static void free_cfg_element_content(cp_cfg_element_t *ce) {
+ int i;
+
+ assert(ce != NULL);
+ free(ce->name);
+ if (ce->atts != NULL) {
+ free(ce->atts[0]);
+ free(ce->atts);
+ }
+ free(ce->value);
+ for (i = 0; i < ce->num_children; i++) {
+ free_cfg_element_content(ce->children + i);
+ }
+ free(ce->children);
+}
+
+CP_HIDDEN void cpi_free_plugin(cp_plugin_info_t *plugin) {
+ int i;
+
+ assert(plugin != NULL);
+ free(plugin->name);
+ free(plugin->identifier);
+ free(plugin->version);
+ free(plugin->provider_name);
+ free(plugin->plugin_path);
+ free(plugin->abi_bw_compatibility);
+ free(plugin->api_bw_compatibility);
+ free(plugin->req_cpluff_version);
+ for (i = 0; i < plugin->num_imports; i++) {
+ free_plugin_import_content(plugin->imports + i);
+ }
+ free(plugin->imports);
+ free(plugin->runtime_lib_name);
+ free(plugin->runtime_funcs_symbol);
+ for (i = 0; i < plugin->num_ext_points; i++) {
+ free_ext_point_content(plugin->ext_points + i);
+ }
+ free(plugin->ext_points);
+ for (i = 0; i < plugin->num_extensions; i++) {
+ free_extension_content(plugin->extensions + i);
+ if (plugin->extensions[i].configuration != NULL) {
+ free_cfg_element_content(plugin->extensions[i].configuration);
+ free(plugin->extensions[i].configuration);
+ }
+ }
+ free(plugin->extensions);
+ free(plugin);
+}
+
+/**
+ * Frees any memory allocated for a registered plug-in.
+ *
+ * @param context the plug-in context
+ * @param plugin the plug-in to be freed
+ */
+static void free_registered_plugin(cp_context_t *context, cp_plugin_t *plugin) {
+ assert(context != NULL);
+ assert(plugin != NULL);
+
+ // Release plug-in information
+ cpi_release_info(context, plugin->plugin);
+
+ // Release data structures
+ if (plugin->importing != NULL) {
+ assert(list_isempty(plugin->importing));
+ list_destroy(plugin->importing);
+ }
+ assert(plugin->imported == NULL);
+
+ free(plugin);
+}
+
+/**
+ * Uninstalls a plug-in associated with the specified hash node.
+ *
+ * @param context the plug-in context
+ * @param node the hash node of the plug-in to be uninstalled
+ */
+static void uninstall_plugin(cp_context_t *context, hnode_t *node) {
+ cp_plugin_t *plugin;
+ cpi_plugin_event_t event;
+
+ // Check if already uninstalled
+ plugin = (cp_plugin_t *) hnode_get(node);
+ if (plugin->state <= CP_PLUGIN_UNINSTALLED) {
+ // TODO: Is this possible state?
+ return;
+ }
+
+ // Make sure the plug-in is not in resolved state
+ unresolve_plugin(context, plugin);
+ assert(plugin->state == CP_PLUGIN_INSTALLED);
+
+ // Plug-in uninstalled
+ event.plugin_id = plugin->plugin->identifier;
+ event.old_state = plugin->state;
+ event.new_state = plugin->state = CP_PLUGIN_UNINSTALLED;
+ cpi_deliver_event(context, &event);
+
+ // Unregister extension objects
+ unregister_extensions(context, plugin->plugin);
+
+ // Unregister the plug-in
+ hash_delete_free(context->env->plugins, node);
+
+ // Free the plug-in data structures
+ free_registered_plugin(context, plugin);
+}
+
+CP_C_API cp_status_t cp_uninstall_plugin(cp_context_t *context, const char *id) {
+ hnode_t *node;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(id);
+
+ // Look up and unload the plug-in
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ node = hash_lookup(context->env->plugins, id);
+ if (node != NULL) {
+ uninstall_plugin(context, node);
+ } else {
+ cpi_warnf(context, N_("Unknown plug-in %s could not be uninstalled."), id);
+ status = CP_ERR_UNKNOWN;
+ }
+ cpi_unlock_context(context);
+
+ return status;
+}
+
+CP_C_API void cp_uninstall_plugins(cp_context_t *context) {
+ hscan_t scan;
+ hnode_t *node;
+
+ CHECK_NOT_NULL(context);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ cp_stop_plugins(context);
+ while (1) {
+ hash_scan_begin(&scan, context->env->plugins);
+ if ((node = hash_scan_next(&scan)) != NULL) {
+ uninstall_plugin(context, node);
+ } else {
+ break;
+ }
+ }
+ cpi_unlock_context(context);
+}
diff --git a/lib/cpluff/libcpluff/pinfo.c b/lib/cpluff/libcpluff/pinfo.c
new file mode 100644
index 0000000000..2bd5a45bac
--- /dev/null
+++ b/lib/cpluff/libcpluff/pinfo.c
@@ -0,0 +1,722 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Plug-in information functions
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "../kazlib/hash.h"
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#include "internal.h"
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+/// Registration of a dynamically allocated information object
+typedef struct info_resource_t {
+
+ /// Pointer to the resource
+ void *resource;
+
+ /// Usage count for the resource
+ int usage_count;
+
+ /// Deallocation function
+ cpi_dealloc_func_t dealloc_func;
+
+} info_resource_t;
+
+/// A plug-in listener registration
+typedef struct el_holder_t {
+
+ /// The plug-in listener
+ cp_plugin_listener_func_t plugin_listener;
+
+ /// The registering plug-in or NULL for the client program
+ cp_plugin_t *plugin;
+
+ /// Associated user data
+ void *user_data;
+
+} el_holder_t;
+
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+// General information object management
+
+CP_HIDDEN cp_status_t cpi_register_info(cp_context_t *context, void *res, cpi_dealloc_func_t df) {
+ cp_status_t status = CP_OK;
+ info_resource_t *ir = NULL;
+
+ assert(context != NULL);
+ assert(res != NULL);
+ assert(df != NULL);
+ assert(cpi_is_context_locked(context));
+ do {
+ if ((ir = malloc(sizeof(info_resource_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ ir->resource = res;
+ ir->usage_count = 1;
+ ir->dealloc_func = df;
+ if (!hash_alloc_insert(context->env->infos, res, ir)) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ } while (0);
+
+ // Report success
+ if (status == CP_OK) {
+ cpi_debugf(context, _("An information object at address %p was registered."), res);
+ }
+
+ // Release resources on failure
+ if (status != CP_OK) {
+ if (ir != NULL) {
+ free(ir);
+ }
+ }
+
+ return status;
+}
+
+CP_HIDDEN void cpi_use_info(cp_context_t *context, void *res) {
+ hnode_t *node;
+
+ assert(context != NULL);
+ assert(res != NULL);
+ assert(cpi_is_context_locked(context));
+ if ((node = hash_lookup(context->env->infos, res)) != NULL) {
+ info_resource_t *ir = hnode_get(node);
+ ir->usage_count++;
+ cpi_debugf(context, _("Reference count of the information object at address %p increased to %d."), res, ir->usage_count);
+ } else {
+ cpi_fatalf(_("Reference count of an unknown information object at address %p could not be increased."), res);
+ }
+}
+
+CP_HIDDEN void cpi_release_info(cp_context_t *context, void *info) {
+ hnode_t *node;
+
+ assert(context != NULL);
+ assert(info != NULL);
+ assert(cpi_is_context_locked(context));
+ if ((node = hash_lookup(context->env->infos, info)) != NULL) {
+ info_resource_t *ir = hnode_get(node);
+ assert(ir != NULL && info == ir->resource);
+ if (--ir->usage_count == 0) {
+ hash_delete_free(context->env->infos, node);
+ ir->dealloc_func(context, info);
+ cpi_debugf(context, _("The information object at address %p was unregistered."), info);
+ free(ir);
+ } else {
+ cpi_debugf(context, _("Reference count of the information object at address %p decreased to %d."), info, ir->usage_count);
+ }
+ } else {
+ cpi_fatalf(_("Could not release an unknown information object at address %p."), info);
+ }
+}
+
+CP_C_API void cp_release_info(cp_context_t *context, void *info) {
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(info);
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ cpi_release_info(context, info);
+ cpi_unlock_context(context);
+}
+
+CP_HIDDEN void cpi_release_infos(cp_context_t *context) {
+ hscan_t scan;
+ hnode_t *node;
+
+ hash_scan_begin(&scan, context->env->infos);
+ while ((node = hash_scan_next(&scan)) != NULL) {
+ info_resource_t *ir = hnode_get(node);
+ cpi_lock_context(context);
+ cpi_errorf(context, _("An unreleased information object was encountered at address %p with reference count %d when destroying the associated plug-in context. Not releasing the object."), ir->resource, ir->usage_count);
+ cpi_unlock_context(context);
+ hash_scan_delfree(context->env->infos, node);
+ free(ir);
+ }
+}
+
+
+// Information acquiring functions
+
+CP_C_API cp_plugin_info_t * cp_get_plugin_info(cp_context_t *context, const char *id, cp_status_t *error) {
+ hnode_t *node;
+ cp_plugin_info_t *plugin = NULL;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+ if (id == NULL && context->plugin == NULL) {
+ cpi_fatalf(_("The plug-in identifier argument to cp_get_plugin_info must not be NULL when the main program calls it."));
+ }
+
+ // Look up the plug-in and return information
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ do {
+
+ // Lookup plug-in information
+ if (id != NULL) {
+ if ((node = hash_lookup(context->env->plugins, id)) == NULL) {
+ cpi_warnf(context, N_("Could not return information about unknown plug-in %s."), id);
+ status = CP_ERR_UNKNOWN;
+ break;
+ }
+ plugin = ((cp_plugin_t *) hnode_get(node))->plugin;
+ } else {
+ plugin = context->plugin->plugin;
+ assert(plugin != NULL);
+ }
+ cpi_use_info(context, plugin);
+ } while (0);
+ cpi_unlock_context(context);
+
+ if (error != NULL) {
+ *error = status;
+ }
+ return plugin;
+}
+
+static void dealloc_plugins_info(cp_context_t *context, cp_plugin_info_t **plugins) {
+ int i;
+
+ assert(context != NULL);
+ assert(plugins != NULL);
+ for (i = 0; plugins[i] != NULL; i++) {
+ cpi_release_info(context, plugins[i]);
+ }
+ free(plugins);
+}
+
+CP_C_API cp_plugin_info_t ** cp_get_plugins_info(cp_context_t *context, cp_status_t *error, int *num) {
+ cp_plugin_info_t **plugins = NULL;
+ int i, n;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ do {
+ hscan_t scan;
+ hnode_t *node;
+
+ // Allocate space for pointer array
+ n = hash_count(context->env->plugins);
+ if ((plugins = malloc(sizeof(cp_plugin_info_t *) * (n + 1))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Get plug-in information structures
+ hash_scan_begin(&scan, context->env->plugins);
+ i = 0;
+ while ((node = hash_scan_next(&scan)) != NULL) {
+ cp_plugin_t *rp = hnode_get(node);
+
+ assert(i < n);
+ cpi_use_info(context, rp->plugin);
+ plugins[i] = rp->plugin;
+ i++;
+ }
+ plugins[i] = NULL;
+
+ // Register the array
+ status = cpi_register_info(context, plugins, (void (*)(cp_context_t *, void *)) dealloc_plugins_info);
+
+ } while (0);
+
+ // Report error
+ if (status != CP_OK) {
+ cpi_error(context, N_("Plug-in information could not be returned due to insufficient memory."));
+ }
+ cpi_unlock_context(context);
+
+ // Release resources on error
+ if (status != CP_OK) {
+ if (plugins != NULL) {
+ dealloc_plugins_info(context, plugins);
+ plugins = NULL;
+ }
+ }
+
+ assert(status != CP_OK || n == 0 || plugins[n - 1] != NULL);
+ if (error != NULL) {
+ *error = status;
+ }
+ if (num != NULL && status == CP_OK) {
+ *num = n;
+ }
+ return plugins;
+}
+
+CP_C_API cp_plugin_state_t cp_get_plugin_state(cp_context_t *context, const char *id) {
+ cp_plugin_state_t state = CP_PLUGIN_UNINSTALLED;
+ hnode_t *hnode;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(id);
+
+ // Look up the plug-in state
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ if ((hnode = hash_lookup(context->env->plugins, id)) != NULL) {
+ cp_plugin_t *rp = hnode_get(hnode);
+ state = rp->state;
+ }
+ cpi_unlock_context(context);
+ return state;
+}
+
+static void dealloc_ext_points_info(cp_context_t *context, cp_ext_point_t **ext_points) {
+ int i;
+
+ assert(context != NULL);
+ assert(ext_points != NULL);
+ for (i = 0; ext_points[i] != NULL; i++) {
+ cpi_release_info(context, ext_points[i]->plugin);
+ }
+ free(ext_points);
+}
+
+CP_C_API cp_ext_point_t ** cp_get_ext_points_info(cp_context_t *context, cp_status_t *error, int *num) {
+ cp_ext_point_t **ext_points = NULL;
+ int i, n;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ do {
+ hscan_t scan;
+ hnode_t *node;
+
+ // Allocate space for pointer array
+ n = hash_count(context->env->ext_points);
+ if ((ext_points = malloc(sizeof(cp_ext_point_t *) * (n + 1))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Get extension point information structures
+ hash_scan_begin(&scan, context->env->ext_points);
+ i = 0;
+ while ((node = hash_scan_next(&scan)) != NULL) {
+ cp_ext_point_t *ep = hnode_get(node);
+
+ assert(i < n);
+ cpi_use_info(context, ep->plugin);
+ ext_points[i] = ep;
+ i++;
+ }
+ ext_points[i] = NULL;
+
+ // Register the array
+ status = cpi_register_info(context, ext_points, (void (*)(cp_context_t *, void *)) dealloc_ext_points_info);
+
+ } while (0);
+
+ // Report error
+ if (status != CP_OK) {
+ cpi_error(context, N_("Extension point information could not be returned due to insufficient memory."));
+ }
+ cpi_unlock_context(context);
+
+ // Release resources on error
+ if (status != CP_OK) {
+ if (ext_points != NULL) {
+ dealloc_ext_points_info(context, ext_points);
+ ext_points = NULL;
+ }
+ }
+
+ assert(status != CP_OK || n == 0 || ext_points[n - 1] != NULL);
+ if (error != NULL) {
+ *error = status;
+ }
+ if (num != NULL && status == CP_OK) {
+ *num = n;
+ }
+ return ext_points;
+}
+
+static void dealloc_extensions_info(cp_context_t *context, cp_extension_t **extensions) {
+ int i;
+
+ assert(context != NULL);
+ assert(extensions != NULL);
+ for (i = 0; extensions[i] != NULL; i++) {
+ cpi_release_info(context, extensions[i]->plugin);
+ }
+ free(extensions);
+}
+
+CP_C_API cp_extension_t ** cp_get_extensions_info(cp_context_t *context, const char *extpt_id, cp_status_t *error, int *num) {
+ cp_extension_t **extensions = NULL;
+ int i, n;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER, __func__);
+ do {
+ hscan_t scan;
+ hnode_t *hnode;
+
+ // Count the number of extensions
+ if (extpt_id != NULL) {
+ if ((hnode = hash_lookup(context->env->extensions, extpt_id)) != NULL) {
+ n = list_count((list_t *) hnode_get(hnode));
+ } else {
+ n = 0;
+ }
+ } else {
+ hscan_t scan;
+
+ n = 0;
+ hash_scan_begin(&scan, context->env->extensions);
+ while ((hnode = hash_scan_next(&scan)) != NULL) {
+ n += list_count((list_t *) hnode_get(hnode));
+ }
+ }
+
+ // Allocate space for pointer array
+ if ((extensions = malloc(sizeof(cp_extension_t *) * (n + 1))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Get extension information structures
+ if (extpt_id != NULL) {
+ i = 0;
+ if ((hnode = hash_lookup(context->env->extensions, extpt_id)) != NULL) {
+ list_t *el = hnode_get(hnode);
+ lnode_t *lnode;
+
+ lnode = list_first(el);
+ while (lnode != NULL) {
+ cp_extension_t *e = lnode_get(lnode);
+
+ assert(i < n);
+ cpi_use_info(context, e->plugin);
+ extensions[i] = e;
+ i++;
+ lnode = list_next(el, lnode);
+ }
+ }
+ extensions[i] = NULL;
+ } else {
+ hash_scan_begin(&scan, context->env->extensions);
+ i = 0;
+ while ((hnode = hash_scan_next(&scan)) != NULL) {
+ list_t *el = hnode_get(hnode);
+ lnode_t *lnode;
+
+ lnode = list_first(el);
+ while (lnode != NULL) {
+ cp_extension_t *e = lnode_get(lnode);
+
+ assert(i < n);
+ cpi_use_info(context, e->plugin);
+ extensions[i] = e;
+ i++;
+ lnode = list_next(el, lnode);
+ }
+ }
+ }
+ extensions[i] = NULL;
+
+ // Register the array
+ status = cpi_register_info(context, extensions, (void (*)(cp_context_t *, void *)) dealloc_extensions_info);
+
+ } while (0);
+
+ // Report error
+ if (status != CP_OK) {
+ cpi_error(context, N_("Extension information could not be returned due to insufficient memory."));
+ }
+ cpi_unlock_context(context);
+
+ // Release resources on error
+ if (status != CP_OK) {
+ if (extensions != NULL) {
+ dealloc_extensions_info(context, extensions);
+ extensions = NULL;
+ }
+ }
+
+ assert(status != CP_OK || n == 0 || extensions[n - 1] != NULL);
+ if (error != NULL) {
+ *error = status;
+ }
+ if (num != NULL && status == CP_OK) {
+ *num = n;
+ }
+ return extensions;
+}
+
+
+// Plug-in listeners
+
+/**
+ * Compares plug-in listener holders.
+ *
+ * @param h1 the first holder to be compared
+ * @param h2 the second holder to be compared
+ * @return zero if the holders point to the same function, otherwise non-zero
+ */
+static int comp_el_holder(const void *h1, const void *h2) {
+ const el_holder_t *plh1 = h1;
+ const el_holder_t *plh2 = h2;
+
+ return (plh1->plugin_listener != plh2->plugin_listener);
+}
+
+/**
+ * Processes a node by delivering the specified event to the associated
+ * plug-in listener.
+ *
+ * @param list the list being processed
+ * @param node the node being processed
+ * @param event the event
+ */
+static void process_event(list_t *list, lnode_t *node, void *event) {
+ el_holder_t *h = lnode_get(node);
+ cpi_plugin_event_t *e = event;
+ h->plugin_listener(e->plugin_id, e->old_state, e->new_state, h->user_data);
+}
+
+/**
+ * Processes a node by unregistering the associated plug-in listener.
+ *
+ * @param list the list being processed
+ * @param node the node being processed
+ * @param plugin plugin whose listeners are to be unregistered or NULL for all
+ */
+static void process_unregister_plistener(list_t *list, lnode_t *node, void *plugin) {
+ el_holder_t *h = lnode_get(node);
+ if (plugin == NULL || h->plugin == plugin) {
+ list_delete(list, node);
+ lnode_destroy(node);
+ free(h);
+ }
+}
+
+CP_HIDDEN void cpi_unregister_plisteners(list_t *listeners, cp_plugin_t *plugin) {
+ list_process(listeners, plugin, process_unregister_plistener);
+}
+
+CP_C_API cp_status_t cp_register_plistener(cp_context_t *context, cp_plugin_listener_func_t listener, void *user_data) {
+ cp_status_t status = CP_ERR_RESOURCE;
+ el_holder_t *holder;
+ lnode_t *node;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(listener);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
+ if ((holder = malloc(sizeof(el_holder_t))) != NULL) {
+ holder->plugin_listener = listener;
+ holder->plugin = context->plugin;
+ holder->user_data = user_data;
+ if ((node = lnode_create(holder)) != NULL) {
+ list_append(context->env->plugin_listeners, node);
+ status = CP_OK;
+ } else {
+ free(holder);
+ }
+ }
+
+ // Report error or success
+ if (status != CP_OK) {
+ cpi_error(context, _("A plug-in listener could not be registered due to insufficient memory."));
+ } else if (cpi_is_logged(context, CP_LOG_DEBUG)) {
+ char owner[64];
+ /* TRANSLATORS: %s is the context owner */
+ cpi_debugf(context, N_("%s registered a plug-in listener."), cpi_context_owner(context, owner, sizeof(owner)));
+ }
+ cpi_unlock_context(context);
+
+ return status;
+}
+
+CP_C_API void cp_unregister_plistener(cp_context_t *context, cp_plugin_listener_func_t listener) {
+ el_holder_t holder;
+ lnode_t *node;
+
+ CHECK_NOT_NULL(context);
+ holder.plugin_listener = listener;
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
+ node = list_find(context->env->plugin_listeners, &holder, comp_el_holder);
+ if (node != NULL) {
+ process_unregister_plistener(context->env->plugin_listeners, node, NULL);
+ }
+ if (cpi_is_logged(context, CP_LOG_DEBUG)) {
+ char owner[64];
+ /* TRANSLATORS: %s is the context owner */
+ cpi_debugf(context, N_("%s unregistered a plug-in listener."), cpi_context_owner(context, owner, sizeof(owner)));
+ }
+ cpi_unlock_context(context);
+}
+
+CP_HIDDEN void cpi_deliver_event(cp_context_t *context, const cpi_plugin_event_t *event) {
+ assert(event != NULL);
+ assert(event->plugin_id != NULL);
+ cpi_lock_context(context);
+ context->env->in_event_listener_invocation++;
+ list_process(context->env->plugin_listeners, (void *) event, process_event);
+ context->env->in_event_listener_invocation--;
+ cpi_unlock_context(context);
+ if (cpi_is_logged(context, CP_LOG_INFO)) {
+ char *str;
+ switch (event->new_state) {
+ case CP_PLUGIN_UNINSTALLED:
+ str = N_("Plug-in %s has been uninstalled.");
+ break;
+ case CP_PLUGIN_INSTALLED:
+ if (event->old_state < CP_PLUGIN_INSTALLED) {
+ str = N_("Plug-in %s has been installed.");
+ } else {
+ str = N_("Plug-in %s runtime library has been unloaded.");
+ }
+ break;
+ case CP_PLUGIN_RESOLVED:
+ if (event->old_state < CP_PLUGIN_RESOLVED) {
+ str = N_("Plug-in %s runtime library has been loaded.");
+ } else {
+ str = N_("Plug-in %s has been stopped.");
+ }
+ break;
+ case CP_PLUGIN_STARTING:
+ str = N_("Plug-in %s is starting.");
+ break;
+ case CP_PLUGIN_STOPPING:
+ str = N_("Plug-in %s is stopping.");
+ break;
+ case CP_PLUGIN_ACTIVE:
+ str = N_("Plug-in %s has been started.");
+ break;
+ default:
+ str = NULL;
+ break;
+ }
+ if (str != NULL) {
+ cpi_infof(context, str, event->plugin_id);
+ }
+ }
+}
+
+
+// Configuration element helpers
+
+static cp_cfg_element_t * lookup_cfg_element(cp_cfg_element_t *base, const char *path, int len) {
+ int start = 0;
+
+ CHECK_NOT_NULL(base);
+ CHECK_NOT_NULL(path);
+
+ // Traverse the path
+ while (base != NULL && path[start] != '\0' && (len == -1 || start < len)) {
+ int end = start;
+ while (path[end] != '\0' && path[end] != '/' && (len == -1 || end < len))
+ end++;
+ if (end - start == 2 && !strncmp(path + start, "..", 2)) {
+ base = base->parent;
+ } else {
+ int i;
+ int found = 0;
+
+ for (i = 0; !found && i < base->num_children; i++) {
+ cp_cfg_element_t *e = base->children + i;
+ if (end - start == strlen(e->name)
+ && !strncmp(path + start, e->name, end - start)) {
+ base = e;
+ found = 1;
+ }
+ }
+ if (!found) {
+ base = NULL;
+ }
+ }
+ start = end;
+ if (path[start] == '/') {
+ start++;
+ }
+ }
+ return base;
+}
+
+CP_C_API cp_cfg_element_t * cp_lookup_cfg_element(cp_cfg_element_t *base, const char *path) {
+ return lookup_cfg_element(base, path, -1);
+}
+
+CP_C_API char * cp_lookup_cfg_value(cp_cfg_element_t *base, const char *path) {
+ cp_cfg_element_t *e;
+ const char *attr;
+
+ CHECK_NOT_NULL(base);
+ CHECK_NOT_NULL(path);
+
+ if ((attr = strrchr(path, '@')) == NULL) {
+ e = lookup_cfg_element(base, path, -1);
+ } else {
+ e = lookup_cfg_element(base, path, attr - path);
+ attr++;
+ }
+ if (e != NULL) {
+ if (attr == NULL) {
+ return e->value;
+ } else {
+ int i;
+
+ for (i = 0; i < e->num_atts; i++) {
+ if (!strcmp(attr, e->atts[2*i])) {
+ return e->atts[2*i + 1];
+ }
+ }
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
diff --git a/lib/cpluff/libcpluff/ploader.c b/lib/cpluff/libcpluff/ploader.c
new file mode 100644
index 0000000000..648c2bb326
--- /dev/null
+++ b/lib/cpluff/libcpluff/ploader.c
@@ -0,0 +1,1186 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Plug-in descriptor loader
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <expat.h>
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#include "internal.h"
+
+// Use XMLCALL if available
+#ifdef XMLCALL
+#define CP_XMLCALL XMLCALL
+#else
+#define CP_XMLCALL
+#endif
+
+
+/* ------------------------------------------------------------------------
+ * Constants
+ * ----------------------------------------------------------------------*/
+
+/// XML parser buffer size (in bytes)
+#define CP_XML_PARSER_BUFFER_SIZE 4096
+
+/// Initial configuration element value size
+#define CP_CFG_ELEMENT_VALUE_INITSIZE 64
+
+/// Plugin descriptor name
+#define CP_PLUGIN_DESCRIPTOR "plugin.xml"
+
+
+/* ------------------------------------------------------------------------
+ * Internal data types
+ * ----------------------------------------------------------------------*/
+
+typedef struct ploader_context_t ploader_context_t;
+
+/// Parser states
+typedef enum parser_state_t {
+ PARSER_BEGIN,
+ PARSER_PLUGIN,
+ PARSER_REQUIRES,
+ PARSER_EXTENSION,
+ PARSER_END,
+ PARSER_UNKNOWN,
+ PARSER_ERROR
+} parser_state_t;
+
+/// Plug-in loader context
+struct ploader_context_t {
+
+ /// The plug-in context, or NULL if none
+ cp_context_t *context;
+
+ /// The XML parser being used
+ XML_Parser parser;
+
+ /// The file being parsed
+ char *file;
+
+ /// The plug-in being constructed
+ cp_plugin_info_t *plugin;
+
+ /// The configuration element being constructed
+ cp_cfg_element_t *configuration;
+
+ /// The current parser state
+ parser_state_t state;
+
+ /// The saved parser state (used in PARSER_UNKNOWN)
+ parser_state_t saved_state;
+
+ /**
+ * The current parser depth (used in PARSER_UNKNOWN and PARSER_EXTENSION)
+ */
+ unsigned int depth;
+
+ /// The number of skipped configuration elements
+ unsigned int skippedCEs;
+
+ /// Size of allocated imports table
+ size_t imports_size;
+
+ /// Size of allocated extension points table
+ size_t ext_points_size;
+
+ /// Size of allocated extensions table
+ size_t extensions_size;
+
+ /// Buffer for a value being read
+ char *value;
+
+ /// Size of allocated value field
+ size_t value_size;
+
+ /// Current length of value string
+ size_t value_length;
+
+ /// The number of parsing errors that have occurred
+ unsigned int error_count;
+
+ /// The number of resource errors that have occurred
+ unsigned int resource_error_count;
+};
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+/**
+ * Reports a descriptor error. Does not set the parser to error state but
+ * increments the error count, unless this is merely a warning.
+ *
+ * @param context the parsing context
+ * @param warn whether this is only a warning
+ * @param error_msg the error message
+ * @param ... parameters for the error message
+ */
+static void descriptor_errorf(ploader_context_t *plcontext, int warn,
+ const char *error_msg, ...) {
+ va_list ap;
+ char message[128];
+
+ va_start(ap, error_msg);
+ vsnprintf(message, sizeof(message), error_msg, ap);
+ va_end(ap);
+ message[127] = '\0';
+ if (warn) {
+ cpi_warnf(plcontext->context,
+ N_("Suspicious plug-in descriptor content in %s, line %d, column %d (%s)."),
+ plcontext->file,
+ XML_GetCurrentLineNumber(plcontext->parser),
+ XML_GetCurrentColumnNumber(plcontext->parser) + 1,
+ message);
+ } else {
+ cpi_errorf(plcontext->context,
+ N_("Invalid plug-in descriptor content in %s, line %d, column %d (%s)."),
+ plcontext->file,
+ XML_GetCurrentLineNumber(plcontext->parser),
+ XML_GetCurrentColumnNumber(plcontext->parser) + 1,
+ message);
+ }
+ if (!warn) {
+ plcontext->error_count++;
+ }
+}
+
+/**
+ * Reports insufficient system resources while parsing and increments the
+ * resource error count.
+ *
+ * @param context the parsing context
+ */
+static void resource_error(ploader_context_t *plcontext) {
+ if (plcontext->resource_error_count == 0) {
+ cpi_errorf(plcontext->context,
+ N_("Insufficient system resources to parse plug-in descriptor content in %s, line %d, column %d."),
+ plcontext->file,
+ XML_GetCurrentLineNumber(plcontext->parser),
+ XML_GetCurrentColumnNumber(plcontext->parser) + 1);
+ }
+ plcontext->resource_error_count++;
+}
+
+/**
+ * Returns whether the specified NULL-terminated list of strings includes
+ * the specified string.
+ *
+ * @param list the NULL-terminated list of strings, or NULL if none
+ * @param str the string
+ * @param step the stepping (1 to check every string or 2 to check every
+ * other string)
+ * @return pointer to the location of the string or NULL if not found
+ */
+static const XML_Char * const *contains_str(const XML_Char * const *list,
+ const XML_Char *str, int step) {
+ if (list != NULL) {
+ while (*list != NULL) {
+ if (!strcmp(*list, str)) {
+ return list;
+ }
+ list += step;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Checks that an element has non-empty values for required attributes.
+ * Increments the error count for each missing attribute.
+ *
+ * @param context the parsing context
+ * @param elem the element being checked
+ * @param atts the attribute list for the element
+ * @param req_atts the required attributes (NULL terminated list, or NULL)
+ * @return whether the required attributes are present
+ */
+static int check_req_attributes(ploader_context_t *plcontext,
+ const XML_Char *elem, const XML_Char * const *atts,
+ const XML_Char * const *req_atts) {
+ const XML_Char * const *a;
+ int error = 0;
+
+ // Check that required attributes have non-empty values
+ for (a = req_atts; a != NULL && *a != NULL; a++) {
+ const XML_Char * const *av;
+
+ if ((av = contains_str(atts, *a, 2)) != NULL) {
+ if ((*(av + 1))[0] == '\0') {
+ descriptor_errorf(plcontext, 0,
+ _("required attribute %s for element %s has an empty value"),
+ *a, elem);
+ error = 1;
+ }
+ } else {
+ descriptor_errorf(plcontext, 0,
+ _("required attribute %s missing for element %s"),
+ *a, elem);
+ error = 1;
+ }
+ }
+
+ return !error;
+}
+
+/**
+ * Checks that an element has non-empty values for required attributes and
+ * warns if there are unknown attributes. Increments the error count for
+ * each missing required attribute.
+ *
+ * @param context the parsing context
+ * @param elem the element being checked
+ * @param atts the attribute list for the element
+ * @param req_atts the required attributes (NULL terminated list, or NULL)
+ * @param opt_atts the optional attributes (NULL terminated list, or NULL)
+ * @return whether the required attributes are present
+ */
+static int check_attributes(ploader_context_t *plcontext,
+ const XML_Char *elem, const XML_Char * const *atts,
+ const XML_Char * const *req_atts, const XML_Char * const *opt_atts) {
+ int error = 0;
+
+ // Check required attributes
+ error = !check_req_attributes(plcontext, elem, atts, req_atts);
+
+ // Warn if there are unknown attributes
+ for (; *atts != NULL; atts += 2) {
+ if (contains_str(req_atts, *atts, 1) == NULL
+ && contains_str(opt_atts, *atts, 1) == NULL) {
+ descriptor_errorf(plcontext, 1,
+ _("ignoring unknown attribute %s for element %s"),
+ *atts, elem);
+ }
+ }
+
+ return !error;
+}
+
+/**
+ * Allocates memory using malloc. Reports a resource error if there is not
+ * enough available memory.
+ *
+ * @param context the parsing context
+ * @param size the number of bytes to allocate
+ * @return pointer to the allocated memory, or NULL if memory allocation failed
+ */
+static void *parser_malloc(ploader_context_t *plcontext, size_t size) {
+ void *ptr;
+
+ if ((ptr = malloc(size)) == NULL) {
+ resource_error(plcontext);
+ }
+ return ptr;
+}
+
+/**
+ * Makes a copy of the specified string. The memory is allocated using malloc.
+ * Reports a resource error if there is not enough available memory.
+ *
+ * @param context the parsing context
+ * @param src the source string to be copied
+ * @return copy of the string, or NULL if memory allocation failed
+ */
+static char *parser_strdup(ploader_context_t *plcontext, const char *src) {
+ char *dup;
+
+ if ((dup = strdup(src)) == NULL) {
+ resource_error(plcontext);
+ }
+ return dup;
+}
+
+/**
+ * Concatenates the specified strings into a new string. The memory for the concatenated
+ * string is allocated using malloc. Reports a resource error if there is not
+ * enough available memory.
+ *
+ * @param context the parsing context
+ * @param ... the strings to be concatenated, terminated by NULL
+ * @return the concatenated string, or NULL if memory allocation failed
+ */
+static char *parser_strscat(ploader_context_t *plcontext, ...) {
+ va_list ap;
+ const char *str;
+ char *dst;
+ size_t len;
+
+ // Calculate the length of the concatenated string
+ va_start(ap, plcontext);
+ len = 0;
+ while ((str = va_arg(ap, const char *)) != NULL) {
+ len += strlen(str);
+ }
+ va_end(ap);
+
+ // Allocate space for the concatenated string
+ if ((dst = parser_malloc(plcontext, sizeof(char) * (len + 1))) == NULL) {
+ return NULL;
+ }
+
+ // Copy the strings
+ len = 0;
+ va_start(ap, plcontext);
+ while ((str = va_arg(ap, const char *)) != NULL) {
+ strcpy(dst + len, str);
+ len += strlen(str);
+ }
+ va_end(ap);
+ dst[len] = '\0';
+ return dst;
+}
+
+/**
+ * Puts the parser to a state in which it skips an unknown element.
+ * Warns error handlers about the unknown element.
+ *
+ * @param context the parsing context
+ * @param elem the element name
+ */
+static void unexpected_element(ploader_context_t *plcontext, const XML_Char *elem) {
+ plcontext->saved_state = plcontext->state;
+ plcontext->state = PARSER_UNKNOWN;
+ plcontext->depth = 0;
+ descriptor_errorf(plcontext, 1, _("ignoring unexpected element %s and its contents"), elem);
+}
+
+/**
+ * Creates a copy of the specified attributes. Reports failed memory
+ * allocation.
+ *
+ * @param context the parser context
+ * @param src the source attributes to be copied
+ * @param num pointer to the location where number of attributes is stored,
+ * or NULL for none
+ * @return the duplicated attribute array, or NULL if empty or failed
+ */
+static char **parser_attsdup(ploader_context_t *plcontext, const XML_Char * const *src,
+ unsigned int *num_atts) {
+ char **atts = NULL, *attr_data = NULL;
+ unsigned int i;
+ unsigned int num;
+ size_t attr_size;
+
+ // Calculate the number of attributes and the amount of space required
+ for (num = 0, attr_size = 0; src[num] != NULL; num++) {
+ attr_size += strlen(src[num]) + 1;
+ }
+ assert((num & 1) == 0);
+
+ // Allocate necessary memory and copy attribute data
+ if (num > 0) {
+ if ((atts = parser_malloc(plcontext, num * sizeof(char *))) != NULL) {
+ if ((attr_data = parser_malloc(plcontext, attr_size * sizeof(char))) != NULL) {
+ size_t offset;
+
+ for (i = 0, offset = 0; i < num; i++) {
+ strcpy(attr_data + offset, src[i]);
+ atts[i] = attr_data + offset;
+ offset += strlen(src[i]) + 1;
+ }
+ }
+ }
+ }
+
+ // If successful then return duplicates, otherwise free any allocations
+ if (num == 0 || (atts != NULL && attr_data != NULL)) {
+ if (num_atts != NULL) {
+ *num_atts = num / 2;
+ }
+ return atts;
+ } else {
+ free(attr_data);
+ free(atts);
+ return NULL;
+ }
+}
+
+/**
+ * Initializes a configuration element. Reports an error if memory allocation fails.
+ *
+ * @param context the parser context
+ * @param ce the configuration element to be initialized
+ * @param name the element name
+ * @param atts the element attributes
+ * @param parent the parent element
+ */
+static void init_cfg_element(ploader_context_t *plcontext, cp_cfg_element_t *ce,
+ const XML_Char *name, const XML_Char * const *atts, cp_cfg_element_t *parent) {
+
+ // Initialize the configuration element
+ memset(ce, 0, sizeof(cp_cfg_element_t));
+ ce->name = parser_strdup(plcontext, name);
+ ce->atts = parser_attsdup(plcontext, atts, &(ce->num_atts));
+ ce->value = NULL;
+ plcontext->value = NULL;
+ plcontext->value_size = 0;
+ plcontext->value_length = 0;
+ ce->parent = parent;
+ ce->children = NULL;
+}
+
+/**
+ * Processes the character data while parsing.
+ *
+ * @param userData the parsing context
+ * @param str the string data
+ * @param len the string length
+ */
+static void CP_XMLCALL character_data_handler(
+ void *userData, const XML_Char *str, int len) {
+ ploader_context_t *plcontext = userData;
+
+ // Ignore leading whitespace
+ if (plcontext->value == NULL) {
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (str[i] != ' ' && str[i] != '\n' && str[i] != '\r' && str[i] != '\t') {
+ break;
+ }
+ }
+ str += i;
+ len -= i;
+ if (len == 0) {
+ return;
+ }
+ }
+
+ // Allocate more memory for the character data if needed
+ if (plcontext->value_length + len >= plcontext->value_size) {
+ size_t ns;
+ char *nv;
+
+ ns = plcontext->value_size;
+ while (plcontext->value_length + len >= ns) {
+ if (ns == 0) {
+ ns = CP_CFG_ELEMENT_VALUE_INITSIZE;
+ } else {
+ ns = 2 * ns;
+ }
+ }
+ if ((nv = realloc(plcontext->value, ns * sizeof(char))) != NULL) {
+ plcontext->value = nv;
+ plcontext->value_size = ns;
+ } else {
+ resource_error(plcontext);
+ return;
+ }
+ }
+
+ // Copy character data
+ strncpy(plcontext->value + plcontext->value_length, str, len * sizeof(char));
+ plcontext->value_length += len;
+}
+
+/**
+ * Processes the start of element events while parsing.
+ *
+ * @param userData the parsing context
+ * @param name the element name
+ * @param atts the element attributes
+ */
+static void CP_XMLCALL start_element_handler(
+ void *userData, const XML_Char *name, const XML_Char **atts) {
+ static const XML_Char * const req_plugin_atts[] = { "id", NULL };
+ static const XML_Char * const opt_plugin_atts[] = { "name", "version", "provider-name", NULL };
+ static const XML_Char * const req_bwcompatibility_atts[] = { NULL };
+ static const XML_Char * const opt_bwcompatibility_atts[] = { "abi", "api", NULL };
+ static const XML_Char * const req_cpluff_atts[] = { "version", NULL };
+ static const XML_Char * const opt_cpluff_atts[] = { NULL };
+ static const XML_Char * const req_import_atts[] = { "plugin", NULL };
+ static const XML_Char * const opt_import_atts[] = { "version", "optional", NULL };
+ static const XML_Char * const req_runtime_atts[] = { "library", NULL };
+ static const XML_Char * const opt_runtime_atts[] = { "funcs", NULL };
+ static const XML_Char * const req_ext_point_atts[] = { "id", NULL };
+ static const XML_Char * const opt_ext_point_atts[] = { "name", "schema", NULL };
+ static const XML_Char * const req_extension_atts[] = { "point", NULL };
+ //static const XML_Char * const opt_extension_atts[] = { "id", "name", NULL };
+ ploader_context_t *plcontext = userData;
+ unsigned int i;
+
+ // Process element start
+ switch (plcontext->state) {
+
+ case PARSER_BEGIN:
+ if (!strcmp(name, "plugin")) {
+ plcontext->state = PARSER_PLUGIN;
+ if (!check_attributes(plcontext, name, atts,
+ req_plugin_atts, opt_plugin_atts)) {
+ break;
+ }
+ for (i = 0; atts[i] != NULL; i += 2) {
+ if (!strcmp(atts[i], "name")) {
+ plcontext->plugin->name
+ = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "id")) {
+ plcontext->plugin->identifier
+ = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "version")) {
+ plcontext->plugin->version
+ = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "provider-name")) {
+ plcontext->plugin->provider_name
+ = parser_strdup(plcontext, atts[i+1]);
+ }
+ }
+ } else {
+ unexpected_element(plcontext, name);
+ }
+ break;
+
+ case PARSER_PLUGIN:
+ if (!strcmp(name, "backwards-compatibility")) {
+ if (check_attributes(plcontext, name, atts,
+ req_bwcompatibility_atts, opt_bwcompatibility_atts)) {
+ for (i = 0; atts[i] != NULL; i += 2) {
+ if (!strcmp(atts[i], "abi")) {
+ plcontext->plugin->abi_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "api")) {
+ plcontext->plugin->api_bw_compatibility = parser_strdup(plcontext, atts[i+1]);
+ }
+ }
+ }
+ } else if (!strcmp(name, "requires")) {
+ plcontext->state = PARSER_REQUIRES;
+ } else if (!strcmp(name, "runtime")) {
+ if (check_attributes(plcontext, name, atts,
+ req_runtime_atts, opt_runtime_atts)) {
+ for (i = 0; atts[i] != NULL; i += 2) {
+ if (!strcmp(atts[i], "library")) {
+ plcontext->plugin->runtime_lib_name
+ = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "funcs")) {
+ plcontext->plugin->runtime_funcs_symbol
+ = parser_strdup(plcontext, atts[i+1]);
+ }
+ }
+ }
+ } else if (!strcmp(name, "extension-point")) {
+ if (check_attributes(plcontext, name, atts,
+ req_ext_point_atts, opt_ext_point_atts)) {
+ cp_ext_point_t *ext_point;
+
+ // Allocate space for extension points, if necessary
+ if (plcontext->plugin->num_ext_points == plcontext->ext_points_size) {
+ cp_ext_point_t *nep;
+ size_t ns;
+
+ if (plcontext->ext_points_size == 0) {
+ ns = 4;
+ } else {
+ ns = plcontext->ext_points_size * 2;
+ }
+ if ((nep = realloc(plcontext->plugin->ext_points,
+ ns * sizeof(cp_ext_point_t))) == NULL) {
+ resource_error(plcontext);
+ break;
+ }
+ plcontext->plugin->ext_points = nep;
+ plcontext->ext_points_size = ns;
+ }
+
+ // Parse extension point specification
+ ext_point = plcontext->plugin->ext_points
+ + plcontext->plugin->num_ext_points;
+ memset(ext_point, 0, sizeof(cp_ext_point_t));
+ ext_point->plugin = plcontext->plugin;
+ ext_point->name = NULL;
+ ext_point->local_id = NULL;
+ ext_point->identifier = NULL;
+ ext_point->schema_path = NULL;
+ for (i = 0; atts[i] != NULL; i += 2) {
+ if (!strcmp(atts[i], "name")) {
+ ext_point->name
+ = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "id")) {
+ ext_point->local_id
+ = parser_strdup(plcontext, atts[i+1]);
+ ext_point->identifier
+ = parser_strscat(plcontext,
+ plcontext->plugin->identifier, ".", atts[i+1], NULL);
+ } else if (!strcmp(atts[i], "schema")) {
+ ext_point->schema_path
+ = parser_strdup(plcontext, atts[i+1]);
+ }
+ }
+ plcontext->plugin->num_ext_points++;
+
+ }
+ } else if (!(strcmp(name, "extension"))) {
+ plcontext->state = PARSER_EXTENSION;
+ plcontext->depth = 0;
+ if (check_req_attributes(
+ plcontext, name, atts, req_extension_atts)) {
+ cp_extension_t *extension;
+
+ // Allocate space for extensions, if necessary
+ if (plcontext->plugin->num_extensions == plcontext->extensions_size) {
+ cp_extension_t *ne;
+ size_t ns;
+
+ if (plcontext->extensions_size == 0) {
+ ns = 16;
+ } else {
+ ns = plcontext->extensions_size * 2;
+ }
+ if ((ne = realloc(plcontext->plugin->extensions,
+ ns * sizeof(cp_extension_t))) == NULL) {
+ resource_error(plcontext);
+ break;
+ }
+ plcontext->plugin->extensions = ne;
+ plcontext->extensions_size = ns;
+ }
+
+ // Parse extension attributes
+ extension = plcontext->plugin->extensions
+ + plcontext->plugin->num_extensions;
+ memset(extension, 0, sizeof(cp_extension_t));
+ extension->plugin = plcontext->plugin;
+ extension->name = NULL;
+ extension->local_id = NULL;
+ extension->identifier = NULL;
+ extension->ext_point_id = NULL;
+ extension->configuration = NULL;
+ for (i = 0; atts[i] != NULL; i += 2) {
+ if (!strcmp(atts[i], "point")) {
+ extension->ext_point_id
+ = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "id")) {
+ extension->local_id
+ = parser_strdup(plcontext, atts[i+1]);
+ extension->identifier
+ = parser_strscat(plcontext,
+ plcontext->plugin->identifier, ".", atts[i+1], NULL);
+ } else if (!strcmp(atts[i], "name")) {
+ extension->name
+ = parser_strdup(plcontext, atts[i+1]);
+ }
+ }
+ plcontext->plugin->num_extensions++;
+
+ // Initialize configuration parsing
+ if ((extension->configuration = plcontext->configuration
+ = parser_malloc(plcontext, sizeof(cp_cfg_element_t))) != NULL) {
+ init_cfg_element(plcontext, plcontext->configuration, name, atts, NULL);
+ }
+ XML_SetCharacterDataHandler(plcontext->parser, character_data_handler);
+ }
+ } else {
+ unexpected_element(plcontext, name);
+ }
+ break;
+
+ case PARSER_REQUIRES:
+ if (!strcmp(name, "c-pluff")) {
+ if (check_attributes(plcontext, name, atts,
+ req_cpluff_atts, opt_cpluff_atts)) {
+ for (i = 0; atts[i] != NULL; i += 2) {
+ if (!strcmp(atts[i], "version")) {
+ plcontext->plugin->req_cpluff_version = parser_strdup(plcontext, atts[i+1]);
+ }
+ }
+ }
+ } else if (!strcmp(name, "import")) {
+ if (check_attributes(plcontext, name, atts,
+ req_import_atts, opt_import_atts)) {
+ cp_plugin_import_t *import = NULL;
+
+ // Allocate space for imports, if necessary
+ if (plcontext->plugin->num_imports == plcontext->imports_size) {
+ cp_plugin_import_t *ni;
+ size_t ns;
+
+ if (plcontext->imports_size == 0) {
+ ns = 16;
+ } else {
+ ns = plcontext->imports_size * 2;
+ }
+ if ((ni = realloc(plcontext->plugin->imports,
+ ns * sizeof(cp_plugin_import_t))) == NULL) {
+ resource_error(plcontext);
+ break;
+ }
+ plcontext->plugin->imports = ni;
+ plcontext->imports_size = ns;
+ }
+
+ // Parse import specification
+ import = plcontext->plugin->imports
+ + plcontext->plugin->num_imports;
+ memset(import, 0, sizeof(cp_plugin_import_t));
+ import->plugin_id = NULL;
+ import->version = NULL;
+ for (i = 0; atts[i] != NULL; i += 2) {
+ if (!strcmp(atts[i], "plugin")) {
+ import->plugin_id
+ = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "version")) {
+ import->version = parser_strdup(plcontext, atts[i+1]);
+ } else if (!strcmp(atts[i], "optional")) {
+ if (!strcmp(atts[i+1], "true")
+ || !strcmp(atts[i+1], "1")) {
+ import->optional = 1;
+ } else if (strcmp(atts[i+1], "false")
+ && strcmp(atts[i+1], "0")) {
+ descriptor_errorf(plcontext, 0, _("unknown boolean value: %s"), atts[i+1]);
+ }
+ }
+ }
+ plcontext->plugin->num_imports++;
+ }
+ } else {
+ unexpected_element(plcontext, name);
+ }
+ break;
+
+ case PARSER_EXTENSION:
+ plcontext->depth++;
+ if (plcontext->configuration != NULL && plcontext->skippedCEs == 0) {
+ cp_cfg_element_t *ce;
+
+ // Allocate more space for children, if necessary
+ if (plcontext->configuration->num_children == plcontext->configuration->index) {
+ cp_cfg_element_t *nce;
+ size_t ns;
+
+ if (plcontext->configuration->index == 0) {
+ ns = 16;
+ } else {
+ ns = plcontext->configuration->index * 2;
+ }
+ if ((nce = realloc(plcontext->configuration->children,
+ ns * sizeof(cp_cfg_element_t))) == NULL) {
+ plcontext->skippedCEs++;
+ resource_error(plcontext);
+ break;
+ }
+ plcontext->configuration->children = nce;
+ plcontext->configuration->index = ns;
+ }
+
+ // Save possible value
+ if (plcontext->value != NULL) {
+ plcontext->value[plcontext->value_length] = '\0';
+ plcontext->configuration->value = plcontext->value;
+ }
+
+ ce = plcontext->configuration->children + plcontext->configuration->num_children;
+ init_cfg_element(plcontext, ce, name, atts, plcontext->configuration);
+ plcontext->configuration->num_children++;
+ plcontext->configuration = ce;
+ }
+ break;
+
+ case PARSER_UNKNOWN:
+ plcontext->depth++;
+ break;
+ default:
+ unexpected_element(plcontext, name);
+ break;
+ }
+}
+
+/**
+ * Processes the end of element events while parsing.
+ *
+ * @param context the parsing context
+ * @param name the element name
+ */
+static void CP_XMLCALL end_element_handler(
+ void *userData, const XML_Char *name) {
+ ploader_context_t *plcontext = userData;
+
+ // Process element end
+ switch (plcontext->state) {
+
+ case PARSER_PLUGIN:
+ if (!strcmp(name, "plugin")) {
+
+ // Readjust memory allocated for extension points, if necessary
+ if (plcontext->ext_points_size != plcontext->plugin->num_ext_points) {
+ cp_ext_point_t *nep;
+
+ if ((nep = realloc(plcontext->plugin->ext_points,
+ plcontext->plugin->num_ext_points *
+ sizeof(cp_ext_point_t))) != NULL
+ || plcontext->plugin->num_ext_points == 0) {
+ plcontext->plugin->ext_points = nep;
+ plcontext->ext_points_size = plcontext->plugin->num_ext_points;
+ }
+ }
+
+ // Readjust memory allocated for extensions, if necessary
+ if (plcontext->extensions_size != plcontext->plugin->num_extensions) {
+ cp_extension_t *ne;
+
+ if ((ne = realloc(plcontext->plugin->extensions,
+ plcontext->plugin->num_extensions *
+ sizeof(cp_extension_t))) != NULL
+ || plcontext->plugin->num_extensions == 0) {
+ plcontext->plugin->extensions = ne;
+ plcontext->extensions_size = plcontext->plugin->num_extensions;
+ }
+ }
+
+ plcontext->state = PARSER_END;
+ }
+ break;
+
+ case PARSER_REQUIRES:
+ if (!strcmp(name, "requires")) {
+
+ // Readjust memory allocated for imports, if necessary
+ if (plcontext->imports_size != plcontext->plugin->num_imports) {
+ cp_plugin_import_t *ni;
+
+ if ((ni = realloc(plcontext->plugin->imports,
+ plcontext->plugin->num_imports *
+ sizeof(cp_plugin_import_t))) != NULL
+ || plcontext->plugin->num_imports == 0) {
+ plcontext->plugin->imports = ni;
+ plcontext->imports_size = plcontext->plugin->num_imports;
+ }
+ }
+
+ plcontext->state = PARSER_PLUGIN;
+ }
+ break;
+
+ case PARSER_UNKNOWN:
+ if (plcontext->depth-- == 0) {
+ plcontext->state = plcontext->saved_state;
+ }
+ break;
+
+ case PARSER_EXTENSION:
+ if (plcontext->skippedCEs > 0) {
+ plcontext->skippedCEs--;
+ } else if (plcontext->configuration != NULL) {
+
+ // Readjust memory allocated for children, if necessary
+ if (plcontext->configuration->index != plcontext->configuration->num_children) {
+ cp_cfg_element_t *nce;
+
+ if ((nce = realloc(plcontext->configuration->children,
+ plcontext->configuration->num_children *
+ sizeof(cp_cfg_element_t))) != NULL
+ || plcontext->configuration->num_children == 0) {
+ plcontext->configuration->children = nce;
+ }
+ }
+
+ if (plcontext->configuration->parent != NULL) {
+ plcontext->configuration->index = plcontext->configuration->parent->num_children - 1;
+ } else {
+ plcontext->configuration->index = 0;
+ }
+ if (plcontext->value != NULL) {
+ char *v = plcontext->value;
+ int i;
+
+ // Ignore trailing whitespace
+ for (i = plcontext->value_length - 1; i >= 0; i--) {
+ if (v[i] != ' ' && v[i] != '\n' && v[i] != '\r' && v[i] != '\t') {
+ break;
+ }
+ }
+ if (i < 0) {
+ free(plcontext->value);
+ plcontext->value = NULL;
+ plcontext->value_length = 0;
+ plcontext->value_size = 0;
+ } else {
+ plcontext->value_length = i + 1;
+ }
+ }
+ if (plcontext->value != NULL) {
+
+ // Readjust memory allocated for value, if necessary
+ if (plcontext->value_size > plcontext->value_length + 1) {
+ char *nv;
+
+ if ((nv = realloc(plcontext->value, (plcontext->value_length + 1) * sizeof(char))) != NULL) {
+ plcontext->value = nv;
+ }
+ }
+
+ plcontext->value[plcontext->value_length] = '\0';
+ plcontext->configuration->value = plcontext->value;
+ plcontext->value = NULL;
+ plcontext->value_size = 0;
+ plcontext->value_length = 0;
+ }
+ plcontext->configuration = plcontext->configuration->parent;
+
+ // Restore possible value
+ if (plcontext->configuration != NULL
+ && plcontext->configuration->value != NULL) {
+ plcontext->value = plcontext->configuration->value;
+ plcontext->value_length = strlen(plcontext->value);
+ plcontext->value_size = CP_CFG_ELEMENT_VALUE_INITSIZE;
+ while (plcontext->value_size < plcontext->value_length + 1) {
+ plcontext->value_size *= 2;
+ }
+ }
+
+ }
+ if (plcontext->depth-- == 0) {
+ assert(!strcmp(name, "extension"));
+ plcontext->state = PARSER_PLUGIN;
+ XML_SetCharacterDataHandler(plcontext->parser, NULL);
+ }
+ break;
+
+ default:
+ descriptor_errorf(plcontext, 0, _("unexpected closing tag for %s"),
+ name);
+ return;
+ }
+}
+
+static void dealloc_plugin_info(cp_context_t *ctx, cp_plugin_info_t *plugin) {
+ cpi_free_plugin(plugin);
+}
+
+CP_C_API cp_plugin_info_t * cp_load_plugin_descriptor(cp_context_t *context, const char *path, cp_status_t *error) {
+ char *file = NULL;
+ cp_status_t status = CP_OK;
+ FILE *fh = NULL;
+ XML_Parser parser = NULL;
+ ploader_context_t *plcontext = NULL;
+ cp_plugin_info_t *plugin = NULL;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(path);
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ do {
+ int path_len;
+
+ // Construct the file name for the plug-in descriptor
+ path_len = strlen(path);
+ if (path_len == 0) {
+ status = CP_ERR_IO;
+ break;
+ }
+ if (path[path_len - 1] == CP_FNAMESEP_CHAR) {
+ path_len--;
+ }
+ file = malloc((path_len + strlen(CP_PLUGIN_DESCRIPTOR) + 2) * sizeof(char));
+ if (file == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ strcpy(file, path);
+ file[path_len] = CP_FNAMESEP_CHAR;
+ strcpy(file + path_len + 1, CP_PLUGIN_DESCRIPTOR);
+
+ // Open the file
+ if ((fh = fopen(file, "rb")) == NULL) {
+ status = CP_ERR_IO;
+ break;
+ }
+
+ // Initialize the XML parsing
+ parser = XML_ParserCreate(NULL);
+ if (parser == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ XML_SetElementHandler(parser,
+ start_element_handler,
+ end_element_handler);
+
+ // Initialize the parsing context
+ if ((plcontext = malloc(sizeof(ploader_context_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ memset(plcontext, 0, sizeof(ploader_context_t));
+ if ((plcontext->plugin = malloc(sizeof(cp_plugin_info_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ plcontext->context = context;
+ plcontext->configuration = NULL;
+ plcontext->value = NULL;
+ plcontext->parser = parser;
+ plcontext->file = file;
+ plcontext->state = PARSER_BEGIN;
+ memset(plcontext->plugin, 0, sizeof(cp_plugin_info_t));
+ plcontext->plugin->name = NULL;
+ plcontext->plugin->identifier = NULL;
+ plcontext->plugin->version = NULL;
+ plcontext->plugin->provider_name = NULL;
+ plcontext->plugin->abi_bw_compatibility = NULL;
+ plcontext->plugin->api_bw_compatibility = NULL;
+ plcontext->plugin->plugin_path = NULL;
+ plcontext->plugin->req_cpluff_version = NULL;
+ plcontext->plugin->imports = NULL;
+ plcontext->plugin->runtime_lib_name = NULL;
+ plcontext->plugin->runtime_funcs_symbol = NULL;
+ plcontext->plugin->ext_points = NULL;
+ plcontext->plugin->extensions = NULL;
+ XML_SetUserData(parser, plcontext);
+
+ // Parse the plug-in descriptor
+ while (1) {
+ int bytes_read;
+ void *xml_buffer;
+ int i;
+
+ // Get buffer from Expat
+ if ((xml_buffer = XML_GetBuffer(parser, CP_XML_PARSER_BUFFER_SIZE))
+ == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Read data into buffer
+ bytes_read = fread(xml_buffer, 1, CP_XML_PARSER_BUFFER_SIZE, fh);
+ if (ferror(fh)) {
+ status = CP_ERR_IO;
+ break;
+ }
+
+ // Parse the data
+ if (!(i = XML_ParseBuffer(parser, bytes_read, bytes_read == 0))
+ && context != NULL) {
+ cpi_lock_context(context);
+ cpi_errorf(context,
+ N_("XML parsing error in %s, line %d, column %d (%s)."),
+ file,
+ XML_GetErrorLineNumber(parser),
+ XML_GetErrorColumnNumber(parser) + 1,
+ XML_ErrorString(XML_GetErrorCode(parser)));
+ cpi_unlock_context(context);
+ }
+ if (!i || plcontext->state == PARSER_ERROR) {
+ status = CP_ERR_MALFORMED;
+ break;
+ }
+
+ if (bytes_read == 0) {
+ break;
+ }
+ }
+ if (status == CP_OK) {
+ if (plcontext->state != PARSER_END || plcontext->error_count > 0) {
+ status = CP_ERR_MALFORMED;
+ }
+ if (plcontext->resource_error_count > 0) {
+ status = CP_ERR_RESOURCE;
+ }
+ }
+ if (status != CP_OK) {
+ break;
+ }
+
+ // Initialize the plug-in path
+ *(file + path_len) = '\0';
+ plcontext->plugin->plugin_path = file;
+ file = NULL;
+
+ // Increase plug-in usage count
+ if ((status = cpi_register_info(context, plcontext->plugin, (void (*)(cp_context_t *, void *)) dealloc_plugin_info)) != CP_OK) {
+ break;
+ }
+
+ } while (0);
+
+ // Report possible errors
+ if (status != CP_OK) {
+ switch (status) {
+ case CP_ERR_MALFORMED:
+ cpi_errorf(context,
+ N_("Plug-in descriptor in %s is invalid."), path);
+ break;
+ case CP_ERR_IO:
+ cpi_errorf(context,
+ N_("An I/O error occurred while loading a plug-in descriptor from %s."), path);
+ break;
+ case CP_ERR_RESOURCE:
+ cpi_errorf(context,
+ N_("Insufficient system resources to load a plug-in descriptor from %s."), path);
+ break;
+ default:
+ cpi_errorf(context,
+ N_("Failed to load a plug-in descriptor from %s."), path);
+ break;
+ }
+ }
+ cpi_unlock_context(context);
+
+ // Release persistently allocated data on failure
+ if (status != CP_OK) {
+ if (file != NULL) {
+ free(file);
+ file = NULL;
+ }
+ if (plcontext != NULL && plcontext->plugin != NULL) {
+ cpi_free_plugin(plcontext->plugin);
+ plcontext->plugin = NULL;
+ }
+ }
+
+ // Otherwise copy the plug-in pointer
+ else {
+ plugin = plcontext->plugin;
+ }
+
+ // Release data allocated for parsing
+ if (parser != NULL) {
+ XML_ParserFree(parser);
+ }
+ if (fh != NULL) {
+ fclose(fh);
+ }
+ if (plcontext != NULL) {
+ if (plcontext->value != NULL) {
+ free(plcontext->value);
+ }
+ free(plcontext);
+ plcontext = NULL;
+ }
+
+ // Return error code
+ if (error != NULL) {
+ *error = status;
+ }
+
+ return plugin;
+}
diff --git a/lib/cpluff/libcpluff/pscan.c b/lib/cpluff/libcpluff/pscan.c
new file mode 100644
index 0000000000..38b32975f8
--- /dev/null
+++ b/lib/cpluff/libcpluff/pscan.c
@@ -0,0 +1,308 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Plug-in scanning functionality
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#include "internal.h"
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+CP_C_API cp_status_t cp_scan_plugins(cp_context_t *context, int flags) {
+ hash_t *avail_plugins = NULL;
+ list_t *started_plugins = NULL;
+ cp_plugin_info_t **plugins = NULL;
+ char *pdir_path = NULL;
+ int pdir_path_size = 0;
+ int plugins_stopped = 0;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_ANY, __func__);
+ cpi_debug(context, N_("Plug-in scan is starting."));
+ do {
+ lnode_t *lnode;
+ hscan_t hscan;
+ hnode_t *hnode;
+
+ // Create a hash for available plug-ins
+ if ((avail_plugins = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Scan plug-in directories for available plug-ins
+ lnode = list_first(context->env->plugin_dirs);
+ while (lnode != NULL) {
+ const char *dir_path;
+ DIR *dir;
+
+ dir_path = lnode_get(lnode);
+ dir = opendir(dir_path);
+ if (dir != NULL) {
+ int dir_path_len;
+ struct dirent *de;
+
+ dir_path_len = strlen(dir_path);
+ if (dir_path[dir_path_len - 1] == CP_FNAMESEP_CHAR) {
+ dir_path_len--;
+ }
+ errno = 0;
+ while ((de = readdir(dir)) != NULL) {
+ if (de->d_name[0] != '\0' && de->d_name[0] != '.') {
+ int pdir_path_len = dir_path_len + 1 + strlen(de->d_name) + 1;
+ cp_plugin_info_t *plugin;
+ cp_status_t s;
+ hnode_t *hnode;
+
+ // Allocate memory for plug-in descriptor path
+ if (pdir_path_size <= pdir_path_len) {
+ char *new_pdir_path;
+
+ if (pdir_path_size == 0) {
+ pdir_path_size = 128;
+ }
+ while (pdir_path_size <= pdir_path_len) {
+ pdir_path_size *= 2;
+ }
+ new_pdir_path = realloc(pdir_path, pdir_path_size * sizeof(char));
+ if (new_pdir_path == NULL) {
+ cpi_errorf(context, N_("Could not check possible plug-in location %s%c%s due to insufficient system resources."), dir_path, CP_FNAMESEP_CHAR, de->d_name);
+ status = CP_ERR_RESOURCE;
+ // continue loading plug-ins from other directories
+ continue;
+ }
+ pdir_path = new_pdir_path;
+ }
+
+ // Construct plug-in descriptor path
+ strcpy(pdir_path, dir_path);
+ pdir_path[dir_path_len] = CP_FNAMESEP_CHAR;
+ strcpy(pdir_path + dir_path_len + 1, de->d_name);
+
+ // Try to load a plug-in
+ plugin = cp_load_plugin_descriptor(context, pdir_path, &s);
+ if (plugin == NULL) {
+ status = s;
+ // continue loading plug-ins from other directories
+ continue;
+ }
+
+ // Insert plug-in to the list of available plug-ins
+ if ((hnode = hash_lookup(avail_plugins, plugin->identifier)) != NULL) {
+ cp_plugin_info_t *plugin2 = hnode_get(hnode);
+ if (cpi_vercmp(plugin->version, plugin2->version) > 0) {
+ hash_delete_free(avail_plugins, hnode);
+ cp_release_info(context, plugin2);
+ hnode = NULL;
+ }
+ }
+ if (hnode == NULL) {
+ if (!hash_alloc_insert(avail_plugins, plugin->identifier, plugin)) {
+ cpi_errorf(context, N_("Plug-in %s version %s could not be loaded due to insufficient system resources."), plugin->identifier, plugin->version);
+ cp_release_info(context, plugin);
+ status = CP_ERR_RESOURCE;
+ // continue loading plug-ins from other directories
+ continue;
+ }
+ }
+
+ }
+ errno = 0;
+ }
+ if (errno) {
+ cpi_errorf(context, N_("Could not read plug-in directory %s: %s"), dir_path, strerror(errno));
+ status = CP_ERR_IO;
+ // continue loading plug-ins from other directories
+ }
+ closedir(dir);
+ } else {
+ cpi_errorf(context, N_("Could not open plug-in directory %s: %s"), dir_path, strerror(errno));
+ status = CP_ERR_IO;
+ // continue loading plug-ins from other directories
+ }
+
+ lnode = list_next(context->env->plugin_dirs, lnode);
+ }
+
+ // Copy the list of started plug-ins, if necessary
+ if ((flags & CP_SP_RESTART_ACTIVE)
+ && (flags & (CP_SP_UPGRADE | CP_SP_STOP_ALL_ON_INSTALL))) {
+ int i;
+ cp_status_t s;
+
+ if ((plugins = cp_get_plugins_info(context, &s, NULL)) == NULL) {
+ status = s;
+ break;
+ }
+ if ((started_plugins = list_create(LISTCOUNT_T_MAX)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ for (i = 0; plugins[i] != NULL; i++) {
+ cp_plugin_state_t state;
+
+ state = cp_get_plugin_state(context, plugins[i]->identifier);
+ if (state == CP_PLUGIN_STARTING || state == CP_PLUGIN_ACTIVE) {
+ char *pid;
+
+ if ((pid = strdup(plugins[i]->identifier)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ if ((lnode = lnode_create(pid)) == NULL) {
+ free(pid);
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ list_append(started_plugins, lnode);
+ }
+ }
+ cpi_release_info(context, plugins);
+ plugins = NULL;
+ }
+
+ // Install/upgrade plug-ins
+ hash_scan_begin(&hscan, avail_plugins);
+ while ((hnode = hash_scan_next(&hscan)) != NULL) {
+ cp_plugin_info_t *plugin;
+ cp_plugin_t *ip = NULL;
+ hnode_t *hn2;
+ int s;
+
+ plugin = hnode_get(hnode);
+ hn2 = hash_lookup(context->env->plugins, plugin->identifier);
+ if (hn2 != NULL) {
+ ip = hnode_get(hn2);
+ }
+
+ // Unload the installed plug-in if it is to be upgraded
+ if (ip != NULL
+ && (flags & CP_SP_UPGRADE)
+ && ((ip->plugin->version == NULL && plugin->version != NULL)
+ || (ip->plugin->version != NULL
+ && plugin->version != NULL
+ && cpi_vercmp(plugin->version, ip->plugin->version) > 0))) {
+ if ((flags & (CP_SP_STOP_ALL_ON_UPGRADE | CP_SP_STOP_ALL_ON_INSTALL))
+ && !plugins_stopped) {
+ plugins_stopped = 1;
+ cp_stop_plugins(context);
+ }
+ s = cp_uninstall_plugin(context, plugin->identifier);
+ assert(s == CP_OK);
+ ip = NULL;
+ }
+
+ // Install the plug-in, if to be installed
+ if (ip == NULL) {
+ if ((flags & CP_SP_STOP_ALL_ON_INSTALL) && !plugins_stopped) {
+ plugins_stopped = 1;
+ cp_stop_plugins(context);
+ }
+ if ((s = cp_install_plugin(context, plugin)) != CP_OK) {
+ status = s;
+ break;
+ }
+ }
+
+ // Remove the plug-in from the hash
+ hash_scan_delfree(avail_plugins, hnode);
+ cp_release_info(context, plugin);
+ }
+
+ // Restart stopped plug-ins if necessary
+ if (started_plugins != NULL) {
+ lnode = list_first(started_plugins);
+ while (lnode != NULL) {
+ char *pid;
+ int s;
+
+ pid = lnode_get(lnode);
+ s = cp_start_plugin(context, pid);
+ if (s != CP_OK) {
+ status = s;
+ }
+ lnode = list_next(started_plugins, lnode);
+ }
+ }
+
+ } while (0);
+
+ // Report error
+ switch (status) {
+ case CP_OK:
+ cpi_debug(context, N_("Plug-in scan has completed successfully."));
+ break;
+ case CP_ERR_RESOURCE:
+ cpi_error(context, N_("Could not scan plug-ins due to insufficient system resources."));
+ break;
+ default:
+ cpi_error(context, N_("Could not scan plug-ins."));
+ break;
+ }
+ cpi_unlock_context(context);
+
+ // Release resources
+ if (pdir_path != NULL) {
+ free(pdir_path);
+ }
+ if (avail_plugins != NULL) {
+ hscan_t hscan;
+ hnode_t *hnode;
+
+ hash_scan_begin(&hscan, avail_plugins);
+ while ((hnode = hash_scan_next(&hscan)) != NULL) {
+ cp_plugin_info_t *p = hnode_get(hnode);
+ hash_scan_delfree(avail_plugins, hnode);
+ cp_release_info(context, p);
+ }
+ hash_destroy(avail_plugins);
+ }
+ if (started_plugins != NULL) {
+ list_process(started_plugins, NULL, cpi_process_free_ptr);
+ list_destroy(started_plugins);
+ }
+ if (plugins != NULL) {
+ cp_release_info(context, plugins);
+ }
+
+ return status;
+}
diff --git a/lib/cpluff/libcpluff/psymbol.c b/lib/cpluff/libcpluff/psymbol.c
new file mode 100644
index 0000000000..ccf9ea697a
--- /dev/null
+++ b/lib/cpluff/libcpluff/psymbol.c
@@ -0,0 +1,336 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Dynamic plug-in symbols
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "../kazlib/hash.h"
+#include "cpluff.h"
+#include "defines.h"
+#include "internal.h"
+#include "util.h"
+
+
+/* ------------------------------------------------------------------------
+ * Data structures
+ * ----------------------------------------------------------------------*/
+
+/// Information about symbol providing plug-in
+typedef struct symbol_provider_info_t {
+
+ // The providing plug-in
+ cp_plugin_t *plugin;
+
+ // Whether there is also an import dependency for the plug-in
+ int imported;
+
+ // Total symbol usage count
+ int usage_count;
+
+} symbol_provider_info_t;
+
+/// Information about used symbol
+typedef struct symbol_info_t {
+
+ // Symbol usage count
+ int usage_count;
+
+ // Information about providing plug-in
+ symbol_provider_info_t *provider_info;
+
+} symbol_info_t;
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+CP_C_API cp_status_t cp_define_symbol(cp_context_t *context, const char *name, void *ptr) {
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(name);
+ CHECK_NOT_NULL(ptr);
+ if (context->plugin == NULL) {
+ cpi_fatalf(_("Only plug-ins can define context specific symbols."));
+ }
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
+ do {
+ char *n;
+
+ // Create a symbol hash if necessary
+ if (context->plugin->defined_symbols == NULL) {
+ if ((context->plugin->defined_symbols = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ }
+
+ // Check for a previously defined symbol
+ if (hash_lookup(context->plugin->defined_symbols, name) != NULL) {
+ status = CP_ERR_CONFLICT;
+ break;
+ }
+
+ // Insert the symbol into the symbol hash
+ n = strdup(name);
+ if (n == NULL || !hash_alloc_insert(context->plugin->defined_symbols, n, ptr)) {
+ free(n);
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ } while (0);
+
+ // Report error
+ if (status != CP_OK) {
+ switch (status) {
+ case CP_ERR_RESOURCE:
+ cpi_errorf(context, N_("Plug-in %s could not define symbol %s due to insufficient memory."), context->plugin->plugin->identifier, name);
+ break;
+ case CP_ERR_CONFLICT:
+ cpi_errorf(context, N_("Plug-in %s tried to redefine symbol %s."), context->plugin->plugin->identifier, name);
+ break;
+ default:
+ break;
+ }
+ }
+ cpi_unlock_context(context);
+
+ return status;
+}
+
+CP_C_API void * cp_resolve_symbol(cp_context_t *context, const char *id, const char *name, cp_status_t *error) {
+ cp_status_t status = CP_OK;
+ int error_reported = 1;
+ hnode_t *node;
+ void *symbol = NULL;
+ symbol_info_t *symbol_info = NULL;
+ symbol_provider_info_t *provider_info = NULL;
+ cp_plugin_t *pp = NULL;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(id);
+ CHECK_NOT_NULL(name);
+
+ // Resolve the symbol
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER | CPI_CF_STOP, __func__);
+ do {
+
+ // Allocate space for symbol hashes, if necessary
+ if (context->resolved_symbols == NULL) {
+ context->resolved_symbols = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
+ }
+ if (context->symbol_providers == NULL) {
+ context->symbol_providers = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
+ }
+ if (context->resolved_symbols == NULL
+ || context->symbol_providers == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Look up the symbol defining plug-in
+ node = hash_lookup(context->env->plugins, id);
+ if (node == NULL) {
+ cpi_warnf(context, N_("Symbol %s in unknown plug-in %s could not be resolved."), name, id);
+ status = CP_ERR_UNKNOWN;
+ break;
+ }
+ pp = hnode_get(node);
+
+ // Make sure the plug-in has been started
+ if ((status = cpi_start_plugin(context, pp)) != CP_OK) {
+ cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved because the plug-in could not be started."), name, id);
+ error_reported = 1;
+ break;
+ }
+
+ // Check for a context specific symbol
+ if (pp->defined_symbols != NULL && (node = hash_lookup(pp->defined_symbols, name)) != NULL) {
+ symbol = hnode_get(node);
+ }
+
+ // Fall back to global symbols, if necessary
+ if (symbol == NULL && pp->runtime_lib != NULL) {
+ symbol = DLSYM(pp->runtime_lib, name);
+ }
+ if (symbol == NULL) {
+ const char *error = DLERROR();
+ if (error == NULL) {
+ error = _("Unspecified error.");
+ }
+ cpi_warnf(context, N_("Symbol %s in plug-in %s could not be resolved: %s"), name, id, error);
+ status = CP_ERR_UNKNOWN;
+ break;
+ }
+
+ // Lookup or initialize symbol provider information
+ if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
+ provider_info = hnode_get(node);
+ } else {
+ if ((provider_info = malloc(sizeof(symbol_provider_info_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ memset(provider_info, 0, sizeof(symbol_provider_info_t));
+ provider_info->plugin = pp;
+ provider_info->imported = (context->plugin == NULL || cpi_ptrset_contains(context->plugin->imported, pp));
+ if (!hash_alloc_insert(context->symbol_providers, pp, provider_info)) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ }
+
+ // Lookup or initialize symbol information
+ if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
+ symbol_info = hnode_get(node);
+ } else {
+ if ((symbol_info = malloc(sizeof(symbol_info_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ memset(symbol_info, 0, sizeof(symbol_info_t));
+ symbol_info->provider_info = provider_info;
+ if (!hash_alloc_insert(context->resolved_symbols, symbol, symbol_info)) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ }
+
+ // Add dependencies (for plug-in)
+ if (provider_info != NULL
+ && !provider_info->imported
+ && provider_info->usage_count == 0) {
+ if (!cpi_ptrset_add(context->plugin->imported, pp)) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ if (!cpi_ptrset_add(pp->importing, context->plugin)) {
+ cpi_ptrset_remove(context->plugin->imported, pp);
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ cpi_debugf(context, "A dynamic dependency was created from plug-in %s to plug-in %s.", context->plugin->plugin->identifier, pp->plugin->identifier);
+ }
+
+ // Increase usage counts
+ symbol_info->usage_count++;
+ provider_info->usage_count++;
+
+ if (cpi_is_logged(context, CP_LOG_DEBUG)) {
+ char owner[64];
+ /* TRANSLATORS: First %s is the context owner */
+ cpi_debugf(context, "%s resolved symbol %s defined by plug-in %s.", cpi_context_owner(context, owner, sizeof(owner)), name, id);
+ }
+ } while (0);
+
+ // Clean up
+ if (symbol_info != NULL && symbol_info->usage_count == 0) {
+ if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
+ hash_delete_free(context->resolved_symbols, node);
+ }
+ free(symbol_info);
+ }
+ if (provider_info != NULL && provider_info->usage_count == 0) {
+ if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
+ hash_delete_free(context->symbol_providers, node);
+ }
+ free(provider_info);
+ }
+
+ // Report insufficient memory error
+ if (status == CP_ERR_RESOURCE && !error_reported) {
+ cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved due to insufficient memory."), name, id);
+ }
+ cpi_unlock_context(context);
+
+ // Return error code
+ if (error != NULL) {
+ *error = status;
+ }
+
+ // Return symbol
+ return symbol;
+}
+
+CP_C_API void cp_release_symbol(cp_context_t *context, const void *ptr) {
+ hnode_t *node;
+ symbol_info_t *symbol_info;
+ symbol_provider_info_t *provider_info;
+
+ CHECK_NOT_NULL(context);
+ CHECK_NOT_NULL(ptr);
+
+ cpi_lock_context(context);
+ cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
+ do {
+
+ // Look up the symbol
+ if ((node = hash_lookup(context->resolved_symbols, ptr)) == NULL) {
+ cpi_errorf(context, N_("Could not release unknown symbol at address %p."), ptr);
+ break;
+ }
+ symbol_info = hnode_get(node);
+ provider_info = symbol_info->provider_info;
+
+ // Decrease usage count
+ assert(symbol_info->usage_count > 0);
+ symbol_info->usage_count--;
+ assert(provider_info->usage_count > 0);
+ provider_info->usage_count--;
+
+ // Check if the symbol is not being used anymore
+ if (symbol_info->usage_count == 0) {
+ hash_delete_free(context->resolved_symbols, node);
+ free(symbol_info);
+ if (cpi_is_logged(context, CP_LOG_DEBUG)) {
+ char owner[64];
+ /* TRANSLATORS: First %s is the context owner */
+ cpi_debugf(context, _("%s released the symbol at address %p defined by plug-in %s."), cpi_context_owner(context, owner, sizeof(owner)), ptr, provider_info->plugin->plugin->identifier);
+ }
+ }
+
+ // Check if the symbol providing plug-in is not being used anymore
+ if (provider_info->usage_count == 0) {
+ node = hash_lookup(context->symbol_providers, provider_info->plugin);
+ assert(node != NULL);
+ hash_delete_free(context->symbol_providers, node);
+ if (!provider_info->imported) {
+ cpi_ptrset_remove(context->plugin->imported, provider_info->plugin);
+ cpi_ptrset_remove(provider_info->plugin->importing, context->plugin);
+ cpi_debugf(context, _("A dynamic dependency from plug-in %s to plug-in %s was removed."), context->plugin->plugin->identifier, provider_info->plugin->plugin->identifier);
+ }
+ free(provider_info);
+ }
+
+ } while (0);
+ cpi_unlock_context(context);
+}
diff --git a/lib/cpluff/libcpluff/serial.c b/lib/cpluff/libcpluff/serial.c
new file mode 100644
index 0000000000..b4b27740e2
--- /dev/null
+++ b/lib/cpluff/libcpluff/serial.c
@@ -0,0 +1,201 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Serial execution implementation
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "cpluff.h"
+#include "internal.h"
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+/// A holder structure for a run function.
+typedef struct run_func_t {
+
+ /// The run function
+ cp_run_func_t runfunc;
+
+ /// The registering plug-in instance
+ cp_plugin_t *plugin;
+
+ /// Whether currently in execution
+ int in_execution;
+
+} run_func_t;
+
+CP_C_API cp_status_t cp_run_function(cp_context_t *ctx, cp_run_func_t runfunc) {
+ lnode_t *node = NULL;
+ run_func_t *rf = NULL;
+ cp_status_t status = CP_OK;
+
+ CHECK_NOT_NULL(ctx);
+ CHECK_NOT_NULL(runfunc);
+ if (ctx->plugin == NULL) {
+ cpi_fatalf(_("Only plug-ins can register run functions."));
+ }
+ if (ctx->plugin->state != CP_PLUGIN_ACTIVE
+ && ctx->plugin->state != CP_PLUGIN_STARTING) {
+ cpi_fatalf(_("Only starting or active plug-ins can register run functions."));
+ }
+
+ cpi_lock_context(ctx);
+ cpi_check_invocation(ctx, CPI_CF_STOP | CPI_CF_LOGGER, __func__);
+ do {
+ int found = 0;
+ lnode_t *n;
+
+ // Check if already registered
+ n = list_first(ctx->env->run_funcs);
+ while (n != NULL && !found) {
+ run_func_t *r = lnode_get(n);
+ if (runfunc == r->runfunc && ctx->plugin == r->plugin) {
+ found = 1;
+ }
+ n = list_next(ctx->env->run_funcs, n);
+ }
+ if (found) {
+ break;
+ }
+
+ // Allocate memory for a new run function entry
+ if ((rf = malloc(sizeof(run_func_t))) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+ if ((node = lnode_create(rf)) == NULL) {
+ status = CP_ERR_RESOURCE;
+ break;
+ }
+
+ // Initialize run function entry
+ memset(rf, 0, sizeof(run_func_t));
+ rf->runfunc = runfunc;
+ rf->plugin = ctx->plugin;
+
+ // Append the run function to queue
+ list_append(ctx->env->run_funcs, node);
+ if (ctx->env->run_wait == NULL) {
+ ctx->env->run_wait = node;
+ }
+
+ } while (0);
+
+ // Log error
+ if (status == CP_ERR_RESOURCE) {
+ cpi_error(ctx, N_("Could not register a run function due to insufficient memory."));
+ }
+ cpi_unlock_context(ctx);
+
+ // Free resources on error
+ if (status != CP_OK) {
+ if (node != NULL) {
+ lnode_destroy(node);
+ }
+ if (rf != NULL) {
+ free(rf);
+ }
+ }
+
+ return status;
+}
+
+CP_C_API void cp_run_plugins(cp_context_t *ctx) {
+ while (cp_run_plugins_step(ctx));
+}
+
+CP_C_API int cp_run_plugins_step(cp_context_t *ctx) {
+ int runnables;
+
+ CHECK_NOT_NULL(ctx);
+ cpi_lock_context(ctx);
+ if (ctx->env->run_wait != NULL) {
+ lnode_t *node = ctx->env->run_wait;
+ run_func_t *rf = lnode_get(node);
+ int rerun;
+
+ ctx->env->run_wait = list_next(ctx->env->run_funcs, node);
+ rf->in_execution = 1;
+ cpi_unlock_context(ctx);
+ rerun = rf->runfunc(rf->plugin->plugin_data);
+ cpi_lock_context(ctx);
+ rf->in_execution = 0;
+ list_delete(ctx->env->run_funcs, node);
+ if (rerun) {
+ list_append(ctx->env->run_funcs, node);
+ if (ctx->env->run_wait == NULL) {
+ ctx->env->run_wait = node;
+ }
+ } else {
+ lnode_destroy(node);
+ free(rf);
+ }
+ cpi_signal_context(ctx);
+ }
+ runnables = (ctx->env->run_wait != NULL);
+ cpi_unlock_context(ctx);
+ return runnables;
+}
+
+CP_HIDDEN void cpi_stop_plugin_run(cp_plugin_t *plugin) {
+ int stopped = 0;
+ cp_context_t *ctx;
+
+ CHECK_NOT_NULL(plugin);
+ ctx = plugin->context;
+ assert(cpi_is_context_locked(ctx));
+ while (!stopped) {
+ lnode_t *node;
+
+ stopped = 1;
+ node = list_first(ctx->env->run_funcs);
+ while (node != NULL) {
+ run_func_t *rf = lnode_get(node);
+ lnode_t *next_node = list_next(ctx->env->run_funcs, node);
+
+ if (rf->plugin == plugin) {
+ if (rf->in_execution) {
+ stopped = 0;
+ } else {
+ if (ctx->env->run_wait == node) {
+ ctx->env->run_wait = list_next(ctx->env->run_funcs, node);
+ }
+ list_delete(ctx->env->run_funcs, node);
+ lnode_destroy(node);
+ free(rf);
+ }
+ }
+ node = next_node;
+ }
+
+ // If some run functions were in execution, wait for them to finish
+ if (!stopped) {
+ cpi_wait_context(ctx);
+ }
+ }
+}
diff --git a/lib/cpluff/libcpluff/thread.h b/lib/cpluff/libcpluff/thread.h
new file mode 100644
index 0000000000..fff9e39b50
--- /dev/null
+++ b/lib/cpluff/libcpluff/thread.h
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Declarations for generic mutex functions and types
+ */
+
+#ifndef THREAD_H_
+#define THREAD_H_
+#ifdef CP_THREADS
+
+#include "defines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif //__cplusplus
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+// A generic mutex implementation
+typedef struct cpi_mutex_t cpi_mutex_t;
+
+
+/* ------------------------------------------------------------------------
+ * Function declarations
+ * ----------------------------------------------------------------------*/
+
+// Mutex functions
+
+/**
+ * Creates a mutex. The mutex is initially available.
+ *
+ * @return the created mutex or NULL if no resources available
+ */
+CP_HIDDEN cpi_mutex_t * cpi_create_mutex(void);
+
+/**
+ * Destroys the specified mutex.
+ *
+ * @param mutex the mutex
+ */
+CP_HIDDEN void cpi_destroy_mutex(cpi_mutex_t *mutex);
+
+/**
+ * Waits for the specified mutex to become available and locks it.
+ * If the calling thread has already locked the mutex then the
+ * lock count of the mutex is increased.
+ *
+ * @param mutex the mutex
+ */
+CP_HIDDEN void cpi_lock_mutex(cpi_mutex_t *mutex);
+
+/**
+ * Unlocks the specified mutex which must have been previously locked
+ * by this thread. If there has been several calls to cpi_lock_mutex
+ * by the same thread then the mutex is unlocked only after corresponding
+ * number of unlock requests.
+ *
+ * @param mutex the mutex
+ */
+CP_HIDDEN void cpi_unlock_mutex(cpi_mutex_t *mutex);
+
+/**
+ * Waits on the specified mutex until it is signaled. The calling thread
+ * must hold the mutex. The mutex is released on call to this function and
+ * it is reacquired before the function returns.
+ *
+ * @param mutex the mutex to wait on
+ */
+CP_HIDDEN void cpi_wait_mutex(cpi_mutex_t *mutex);
+
+/**
+ * Signals the specified mutex waking all the threads currently waiting on
+ * the mutex. The calling thread must hold the mutex. The mutex is not
+ * released.
+ *
+ * @param mutex the mutex to be signaled
+ */
+CP_HIDDEN void cpi_signal_mutex(cpi_mutex_t *mutex);
+
+#if !defined(NDEBUG)
+
+/**
+ * Returns whether the mutex is currently locked. This function
+ * is only intended to be used for assertions. The returned state
+ * reflects the state of the mutex only at the time of inspection.
+ */
+CP_HIDDEN int cpi_is_mutex_locked(cpi_mutex_t *mutex);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif //__cplusplus
+
+#endif //CP_THREADS
+#endif //THREAD_H_
diff --git a/lib/cpluff/libcpluff/thread_posix.c b/lib/cpluff/libcpluff/thread_posix.c
new file mode 100644
index 0000000000..12018f8a69
--- /dev/null
+++ b/lib/cpluff/libcpluff/thread_posix.c
@@ -0,0 +1,228 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Posix implementation for generic mutex functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <pthread.h>
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#include "internal.h"
+#include "thread.h"
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+// A generic recursive mutex implementation
+struct cpi_mutex_t {
+
+ /// The current lock count
+ int lock_count;
+
+ /// The underlying operating system mutex
+ pthread_mutex_t os_mutex;
+
+ /// The condition variable for signaling availability
+ pthread_cond_t os_cond_lock;
+
+ /// The condition variable for broadcasting a wake request
+ pthread_cond_t os_cond_wake;
+
+ /// The locking thread if currently locked
+ pthread_t os_thread;
+
+};
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+CP_HIDDEN cpi_mutex_t * cpi_create_mutex(void) {
+ cpi_mutex_t *mutex;
+
+ if ((mutex = malloc(sizeof(cpi_mutex_t))) == NULL) {
+ return NULL;
+ }
+ memset(mutex, 0, sizeof(cpi_mutex_t));
+ if (pthread_mutex_init(&(mutex->os_mutex), NULL)) {
+ return NULL;
+ } else if (pthread_cond_init(&(mutex->os_cond_lock), NULL)) {
+ int ec;
+
+ ec = pthread_mutex_destroy(&(mutex->os_mutex));
+ assert(!ec);
+ return NULL;
+ } else if (pthread_cond_init(&(mutex->os_cond_wake), NULL)) {
+ int ec;
+
+ ec = pthread_mutex_destroy(&(mutex->os_mutex));
+ assert(!ec);
+ ec = pthread_cond_destroy(&(mutex->os_cond_wake));
+ assert(!ec);
+ return NULL;
+ }
+ return mutex;
+}
+
+CP_HIDDEN void cpi_destroy_mutex(cpi_mutex_t *mutex) {
+ int ec;
+
+ assert(mutex != NULL);
+ assert(mutex->lock_count == 0);
+ ec = pthread_mutex_destroy(&(mutex->os_mutex));
+ assert(!ec);
+ ec = pthread_cond_destroy(&(mutex->os_cond_lock));
+ assert(!ec);
+ ec = pthread_cond_destroy(&(mutex->os_cond_wake));
+ assert(!ec);
+ free(mutex);
+}
+
+static void lock_mutex(pthread_mutex_t *mutex) {
+ int ec;
+
+ if ((ec = pthread_mutex_lock(mutex))) {
+ cpi_fatalf(_("Could not lock a mutex due to error %d."), ec);
+ }
+}
+
+static void unlock_mutex(pthread_mutex_t *mutex) {
+ int ec;
+
+ if ((ec = pthread_mutex_unlock(mutex))) {
+ cpi_fatalf(_("Could not unlock a mutex due to error %d."), ec);
+ }
+}
+
+static void lock_mutex_holding(cpi_mutex_t *mutex) {
+ pthread_t self = pthread_self();
+
+ while (mutex->lock_count != 0
+ && !pthread_equal(self, mutex->os_thread)) {
+ int ec;
+
+ if ((ec = pthread_cond_wait(&(mutex->os_cond_lock), &(mutex->os_mutex)))) {
+ cpi_fatalf(_("Could not wait for a condition variable due to error %d."), ec);
+ }
+ }
+ mutex->os_thread = self;
+ mutex->lock_count++;
+}
+
+CP_HIDDEN void cpi_lock_mutex(cpi_mutex_t *mutex) {
+ assert(mutex != NULL);
+ lock_mutex(&(mutex->os_mutex));
+ lock_mutex_holding(mutex);
+ unlock_mutex(&(mutex->os_mutex));
+}
+
+CP_HIDDEN void cpi_unlock_mutex(cpi_mutex_t *mutex) {
+ pthread_t self = pthread_self();
+
+ assert(mutex != NULL);
+ lock_mutex(&(mutex->os_mutex));
+ if (mutex->lock_count > 0
+ && pthread_equal(self, mutex->os_thread)) {
+ if (--mutex->lock_count == 0) {
+ int ec;
+
+ if ((ec = pthread_cond_signal(&(mutex->os_cond_lock)))) {
+ cpi_fatalf(_("Could not signal a condition variable due to error %d."), ec);
+ }
+ }
+ } else {
+ cpi_fatalf(_("Internal C-Pluff error: Unauthorized attempt at unlocking a mutex."));
+ }
+ unlock_mutex(&(mutex->os_mutex));
+}
+
+CP_HIDDEN void cpi_wait_mutex(cpi_mutex_t *mutex) {
+ pthread_t self = pthread_self();
+
+ assert(mutex != NULL);
+ lock_mutex(&(mutex->os_mutex));
+ if (mutex->lock_count > 0
+ && pthread_equal(self, mutex->os_thread)) {
+ int ec;
+ int lc = mutex->lock_count;
+
+ // Release mutex
+ mutex->lock_count = 0;
+ if ((ec = pthread_cond_signal(&(mutex->os_cond_lock)))) {
+ cpi_fatalf(_("Could not signal a condition variable due to error %d."), ec);
+ }
+
+ // Wait for signal
+ if ((ec = pthread_cond_wait(&(mutex->os_cond_wake), &(mutex->os_mutex)))) {
+ cpi_fatalf(_("Could not wait for a condition variable due to error %d."), ec);
+ }
+
+ // Re-acquire mutex and restore lock count for this thread
+ lock_mutex_holding(mutex);
+ mutex->lock_count = lc;
+
+ } else {
+ cpi_fatalf(_("Internal C-Pluff error: Unauthorized attempt at waiting on a mutex."));
+ }
+ unlock_mutex(&(mutex->os_mutex));
+}
+
+CP_HIDDEN void cpi_signal_mutex(cpi_mutex_t *mutex) {
+ pthread_t self = pthread_self();
+
+ assert(mutex != NULL);
+ lock_mutex(&(mutex->os_mutex));
+ if (mutex->lock_count > 0
+ && pthread_equal(self, mutex->os_thread)) {
+ int ec;
+
+ // Signal the mutex
+ if ((ec = pthread_cond_broadcast(&(mutex->os_cond_wake)))) {
+ cpi_fatalf(_("Could not broadcast a condition variable due to error %d."), ec);
+ }
+
+ } else {
+ cpi_fatalf(_("Internal C-Pluff error: Unauthorized attempt at signaling a mutex."));
+ }
+ unlock_mutex(&(mutex->os_mutex));
+}
+
+#if !defined(NDEBUG)
+CP_HIDDEN int cpi_is_mutex_locked(cpi_mutex_t *mutex) {
+ int locked;
+
+ lock_mutex(&(mutex->os_mutex));
+ locked = (mutex->lock_count != 0);
+ unlock_mutex(&(mutex->os_mutex));
+ return locked;
+}
+#endif
diff --git a/lib/cpluff/libcpluff/thread_windows.c b/lib/cpluff/libcpluff/thread_windows.c
new file mode 100644
index 0000000000..39b416a4b4
--- /dev/null
+++ b/lib/cpluff/libcpluff/thread_windows.c
@@ -0,0 +1,268 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Windows implementation for generic mutex functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <windows.h>
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+#include "internal.h"
+#include "thread.h"
+
+
+/* ------------------------------------------------------------------------
+ * Data types
+ * ----------------------------------------------------------------------*/
+
+// A generic recursive mutex implementation
+struct cpi_mutex_t {
+
+ /// The current lock count
+ int lock_count;
+
+ /// The underlying operating system mutex
+ HANDLE os_mutex;
+
+ /// The condition variable for signaling availability
+ HANDLE os_cond_lock;
+
+ /// The condition variable for signaling a wake request
+ HANDLE os_cond_wake;
+
+ /// Number of threads currently waiting on this mutex
+ int num_wait_threads;
+
+ /// The locking thread if currently locked
+ DWORD os_thread;
+
+};
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+CP_HIDDEN cpi_mutex_t * cpi_create_mutex(void) {
+ cpi_mutex_t *mutex;
+
+ if ((mutex = malloc(sizeof(cpi_mutex_t))) == NULL) {
+ return NULL;
+ }
+ memset(mutex, 0, sizeof(cpi_mutex_t));
+ if ((mutex->os_mutex = CreateMutex(NULL, FALSE, NULL)) == NULL) {
+ return NULL;
+ } else if ((mutex->os_cond_lock = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) {
+ int ec;
+
+ ec = CloseHandle(mutex->os_mutex);
+ assert(ec);
+ return NULL;
+ } else if ((mutex->os_cond_wake = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) {
+ int ec;
+
+ ec = CloseHandle(mutex->os_mutex);
+ assert(ec);
+ ec = CloseHandle(mutex->os_cond_lock);
+ assert(ec);
+ return NULL;
+ }
+ return mutex;
+}
+
+CP_HIDDEN void cpi_destroy_mutex(cpi_mutex_t *mutex) {
+ int ec;
+
+ assert(mutex != NULL);
+ assert(mutex->lock_count == 0);
+ ec = CloseHandle(mutex->os_mutex);
+ assert(ec);
+ ec = CloseHandle(mutex->os_cond_lock);
+ assert(ec);
+ ec = CloseHandle(mutex->os_cond_wake);
+ assert(ec);
+ free(mutex);
+}
+
+static char *get_win_errormsg(DWORD error, char *buffer, size_t size) {
+ if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ error,
+ 0,
+ buffer,
+ size / sizeof(char),
+ NULL)) {
+ strncpy(buffer, _("unknown error"), size);
+ }
+ buffer[size/sizeof(char) - 1] = '\0';
+ return buffer;
+}
+
+static void lock_mutex(HANDLE mutex) {
+ DWORD ec;
+
+ if ((ec = WaitForSingleObject(mutex, INFINITE)) != WAIT_OBJECT_0) {
+ char buffer[256];
+ ec = GetLastError();
+ cpi_fatalf(_("Could not lock a mutex due to error %ld: %s"),
+ (long) ec, get_win_errormsg(ec, buffer, sizeof(buffer)));
+ }
+}
+
+static void unlock_mutex(HANDLE mutex) {
+ if (!ReleaseMutex(mutex)) {
+ char buffer[256];
+ DWORD ec = GetLastError();
+ cpi_fatalf(_("Could not release a mutex due to error %ld: %s"),
+ (long) ec, get_win_errormsg(ec, buffer, sizeof(buffer)));
+ }
+}
+
+static void wait_for_event(HANDLE event) {
+ if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) {
+ char buffer[256];
+ DWORD ec = GetLastError();
+ cpi_fatalf(_("Could not wait for an event due to error %ld: %s"),
+ (long) ec, get_win_errormsg(ec, buffer, sizeof(buffer)));
+ }
+}
+
+static void set_event(HANDLE event) {
+ if (!SetEvent(event)) {
+ char buffer[256];
+ DWORD ec = GetLastError();
+ cpi_fatalf(_("Could not set an event due to error %ld: %s"),
+ (long) ec, get_win_errormsg(ec, buffer, sizeof(buffer)));
+ }
+}
+
+static void reset_event(HANDLE event) {
+ if (!ResetEvent(event)) {
+ char buffer[256];
+ DWORD ec = GetLastError();
+ cpi_fatalf(_("Could not reset an event due to error %ld: %s"),
+ (long) ec, get_win_errormsg(ec, buffer, sizeof(buffer)));
+ }
+}
+
+static void lock_mutex_holding(cpi_mutex_t *mutex) {
+ DWORD self = GetCurrentThreadId();
+
+ while (mutex->lock_count != 0
+ && self != mutex->os_thread) {
+ unlock_mutex(mutex->os_mutex);
+ wait_for_event(mutex->os_cond_lock);
+ lock_mutex(mutex->os_mutex);
+ }
+ mutex->os_thread = self;
+ mutex->lock_count++;
+}
+
+CP_HIDDEN void cpi_lock_mutex(cpi_mutex_t *mutex) {
+ assert(mutex != NULL);
+ lock_mutex(mutex->os_mutex);
+ lock_mutex_holding(mutex);
+ unlock_mutex(mutex->os_mutex);
+}
+
+CP_HIDDEN void cpi_unlock_mutex(cpi_mutex_t *mutex) {
+ DWORD self = GetCurrentThreadId();
+
+ assert(mutex != NULL);
+ lock_mutex(mutex->os_mutex);
+ if (mutex->lock_count > 0
+ && self == mutex->os_thread) {
+ if (--mutex->lock_count == 0) {
+ set_event(mutex->os_cond_lock);
+ }
+ } else {
+ cpi_fatalf(_("Internal C-Pluff error: Unauthorized attempt at unlocking a mutex."));
+ }
+ unlock_mutex(mutex->os_mutex);
+}
+
+CP_HIDDEN void cpi_wait_mutex(cpi_mutex_t *mutex) {
+ DWORD self = GetCurrentThreadId();
+
+ assert(mutex != NULL);
+ lock_mutex(mutex->os_mutex);
+ if (mutex->lock_count > 0
+ && self == mutex->os_thread) {
+ int lc = mutex->lock_count;
+
+ // Release mutex
+ mutex->lock_count = 0;
+ mutex->num_wait_threads++;
+ set_event(mutex->os_cond_lock);
+ unlock_mutex(mutex->os_mutex);
+
+ // Wait for signal
+ wait_for_event(mutex->os_cond_wake);
+
+ // Reset wake signal if last one waking up
+ lock_mutex(mutex->os_mutex);
+ if (--mutex->num_wait_threads == 0) {
+ reset_event(mutex->os_cond_wake);
+ }
+
+ // Re-acquire mutex and restore lock count for this thread
+ lock_mutex_holding(mutex);
+ mutex->lock_count = lc;
+
+ } else {
+ cpi_fatalf(_("Internal C-Pluff error: Unauthorized attempt at waiting on a mutex."));
+ }
+ unlock_mutex(mutex->os_mutex);
+}
+
+CP_HIDDEN void cpi_signal_mutex(cpi_mutex_t *mutex) {
+ DWORD self = GetCurrentThreadId();
+
+ assert(mutex != NULL);
+ lock_mutex(mutex->os_mutex);
+ if (mutex->lock_count > 0
+ && self == mutex->os_thread) {
+ set_event(mutex->os_cond_wake);
+ } else {
+ cpi_fatalf(_("Internal C-Pluff error: Unauthorized attempt at signaling a mutex."));
+ }
+ unlock_mutex(mutex->os_mutex);
+}
+
+#if !defined(NDEBUG)
+CP_HIDDEN int cpi_is_mutex_locked(cpi_mutex_t *mutex) {
+ int locked;
+
+ lock_mutex(mutex->os_mutex);
+ locked = (mutex->lock_count != 0);
+ unlock_mutex(mutex->os_mutex);
+ return locked;
+}
+#endif
diff --git a/lib/cpluff/libcpluff/util.c b/lib/cpluff/libcpluff/util.c
new file mode 100644
index 0000000000..493b3f05a2
--- /dev/null
+++ b/lib/cpluff/libcpluff/util.c
@@ -0,0 +1,211 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Internal utility functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include "../kazlib/list.h"
+#include "cpluff.h"
+#include "defines.h"
+#include "util.h"
+
+
+/* ------------------------------------------------------------------------
+ * Function definitions
+ * ----------------------------------------------------------------------*/
+
+CP_HIDDEN int cpi_comp_ptr(const void *ptr1, const void *ptr2) {
+ return !(ptr1 == ptr2);
+}
+
+CP_HIDDEN hash_val_t cpi_hashfunc_ptr(const void *ptr) {
+ return (hash_val_t) ptr;
+}
+
+CP_HIDDEN int cpi_ptrset_add(list_t *set, void *ptr) {
+
+
+ // Only add the pointer if it is not already included
+ if (cpi_ptrset_contains(set, ptr)) {
+ return 1;
+ } else {
+ lnode_t *node;
+
+ /* Add the pointer to the list */
+ node = lnode_create(ptr);
+ if (node == NULL) {
+ return 0;
+ }
+ list_append(set, node);
+ return 1;
+ }
+
+}
+
+CP_HIDDEN int cpi_ptrset_remove(list_t *set, const void *ptr) {
+ lnode_t *node;
+
+ // Find the pointer if it is in the set
+ node = list_find(set, ptr, cpi_comp_ptr);
+ if (node != NULL) {
+ list_delete(set, node);
+ lnode_destroy(node);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+CP_HIDDEN int cpi_ptrset_contains(list_t *set, const void *ptr) {
+ return list_find(set, ptr, cpi_comp_ptr) != NULL;
+}
+
+CP_HIDDEN void cpi_process_free_ptr(list_t *list, lnode_t *node, void *dummy) {
+ void *ptr = lnode_get(node);
+ list_delete(list, node);
+ lnode_destroy(node);
+ free(ptr);
+}
+
+static const char *vercmp_nondigit_end(const char *v) {
+ while (*v != '\0' && (*v < '0' || *v > '9')) {
+ v++;
+ }
+ return v;
+}
+
+static const char *vercmp_digit_end(const char *v) {
+ while (*v >= '0' && *v <= '9') {
+ v++;
+ }
+ return v;
+}
+
+static int vercmp_char_value(char c) {
+ if (c == '\0') {
+ return 0;
+ } else if (c >= 'A' && c <= 'Z') {
+ return 1 + (c - 'A');
+ } else if (c >= 'a' && c <= 'z') {
+ return 1 + ('Z' - 'A' + 1) + (c - 'a');
+ } else {
+ int i = 1 + ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ((int) c - CHAR_MIN);
+ if (c > 'z') {
+ i -= 'z' - 'a' + 1;
+ }
+ if (c > 'Z') {
+ i -= 'Z' - 'A' + 1;
+ }
+ if (c > '\0') {
+ i--;
+ }
+ return i;
+ }
+}
+
+static int vercmp_num_value(const char *v, const char *vn) {
+
+ // Skip leading zeros
+ while (v < vn && *v == '0') {
+ v++;
+ }
+
+ // Empty string equals to zero
+ if (v == vn) {
+ return 0;
+ }
+
+ // Otherwise return the integer value
+ else {
+ char str[16];
+ strncpy(str, v, vn - v < 16 ? vn - v : 16);
+ str[vn - v < 16 ? vn - v : 15] = '\0';
+ return atoi(str);
+ }
+}
+
+CP_HIDDEN int cpi_vercmp(const char *v1, const char *v2) {
+ const char *v1n;
+ const char *v2n;
+
+ // Check for NULL versions
+ if (v1 == NULL && v2 != NULL) {
+ return -1;
+ } else if (v1 == NULL && v2 == NULL) {
+ return 0;
+ } else if (v1 != NULL && v2 == NULL) {
+ return 1;
+ }
+ assert(v1 != NULL && v2 != NULL);
+
+ // Component comparison loop
+ while (*v1 != '\0' || *v2 != '\0') {
+
+ // Determine longest non-digit prefix
+ v1n = vercmp_nondigit_end(v1);
+ v2n = vercmp_nondigit_end(v2);
+
+ // Compare the non-digit strings
+ while (v1 < v1n || v2 < v2n) {
+ char c1 = '\0';
+ char c2 = '\0';
+
+ if (v1 < v1n) {
+ c1 = *v1++;
+ }
+ if (v2 < v2n) {
+ c2 = *v2++;
+ }
+ int diff = vercmp_char_value(c1) - vercmp_char_value(c2);
+ if (diff != 0) {
+ return diff;
+ }
+ assert(v1 <= v1n && v2 <= v2n);
+ }
+ assert(v1 == v1n && v2 == v2n);
+
+ // Determine the longest digit prefix
+ v1n = vercmp_digit_end(v1);
+ v2n = vercmp_digit_end(v2);
+
+ // Compare the digit strings
+ {
+ int i1 = vercmp_num_value(v1, v1n);
+ int i2 = vercmp_num_value(v2, v2n);
+ int diff = i1 - i2;
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ v1 = v1n;
+ v2 = v2n;
+
+ }
+ return 0;
+}
diff --git a/lib/cpluff/libcpluff/util.h b/lib/cpluff/libcpluff/util.h
new file mode 100644
index 0000000000..837580a4cb
--- /dev/null
+++ b/lib/cpluff/libcpluff/util.h
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------------------------
+ * C-Pluff, a plug-in framework for C
+ * Copyright 2007 Johannes Lehtinen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *-----------------------------------------------------------------------*/
+
+/** @file
+ * Declarations for internal utility functions
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include "../kazlib/list.h"
+#include "../kazlib/hash.h"
+#include "cpluff.h"
+#include "defines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif //__cplusplus
+
+
+/* ------------------------------------------------------------------------
+ * Function declarations
+ * ----------------------------------------------------------------------*/
+
+// For operating on smallish pointer sets implemented as lists
+
+/**
+ * Compares pointers.
+ *
+ * @param ptr1 the first pointer
+ * @param ptr2 the second pointer
+ * @return zero if the pointers are equal, otherwise non-zero
+ */
+CP_HIDDEN int cpi_comp_ptr(const void *ptr1, const void *ptr2) CP_GCC_CONST;
+
+/**
+ * Returns a hash value for a pointer.
+ *
+ * @param ptr the pointer being hashed
+ * @return the corresponding hash value
+ */
+CP_HIDDEN hash_val_t cpi_hashfunc_ptr(const void *ptr) CP_GCC_CONST;
+
+/**
+ * Adds a new pointer to a list if the pointer is not yet included.
+ *
+ * @param set the set being operated on
+ * @param ptr the pointer being added
+ * @return non-zero if the operation was successful, zero if allocation failed
+ */
+CP_HIDDEN int cpi_ptrset_add(list_t *set, void *ptr);
+
+/**
+ * Removes a pointer from a pointer set, if it is included.
+ *
+ * @param set the set being operated on
+ * @param ptr the pointer being removed
+ * @return whether the pointer was contained in the set
+ */
+CP_HIDDEN int cpi_ptrset_remove(list_t *set, const void *ptr);
+
+/**
+ * Returns whether a pointer is included in a pointer set.
+ *
+ * @param set the set being operated on
+ * @param ptr the pointer
+ * @return non-zero if the pointer is included, zero otherwise
+ */
+CP_HIDDEN int cpi_ptrset_contains(list_t *set, const void *ptr) CP_GCC_PURE;
+
+
+// Other list processing utility functions
+
+/**
+ * Processes a node of the list by freeing the associated pointer and
+ * deleting and destroying the node.
+ *
+ * @param list the list being processed
+ * @param node the list node being processed
+ * @param dummy a dummy argument to comply with prototype
+ */
+CP_HIDDEN void cpi_process_free_ptr(list_t *list, lnode_t *node, void *dummy);
+
+
+// Version strings
+
+/**
+ * Compares two version strings. The comparison algorithm is derived from the
+ * way Debian package management system compares package versions. First the
+ * the longest prefix of each string composed entirely of non-digit characters
+ * is determined. These are compared lexically so that all the letters sort
+ * earlier than all the non-letters and otherwise the ordering is based on
+ * ASCII values. If there is a difference it is returned. Otherwise the longest
+ * prefix of remainder of each string composed entirely of digit characters
+ * is determined. These are compared numerically with empty string interpreted
+ * as zero. Again, if there is different it is returned. Otherwise the
+ * comparison continues with a non-digit component and so on. A NULL version
+ * is earlier than any non-NULL version. Two NULL versions are equal.
+ *
+ * @param v1 the first version string to compare or NULL
+ * @param v2 the second version string to compare or NULL
+ * @return less than, equal to or greater than zero when @a v1 < @a v2, @a v1 == @a v2 or @a v1 > @a v2, correspondingly
+ */
+CP_HIDDEN int cpi_vercmp(const char *v1, const char *v2) CP_GCC_PURE;
+
+
+#ifdef __cplusplus
+}
+#endif //__cplusplus
+
+#endif //UTIL_H_