diff options
author | alcoheca <alcoheca@svn> | 2010-03-26 13:35:35 +0000 |
---|---|---|
committer | alcoheca <alcoheca@svn> | 2010-03-26 13:35:35 +0000 |
commit | b632a42d5cacdda7bb07a3fee995a4f0de1deba9 (patch) | |
tree | b174ecd7ce4830785293fa6d18ab65d30c0cc375 /lib/cpluff/libcpluff | |
parent | 5ccef76d00e1dc22d6ae5d1c08172630f5972e58 (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')
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"> + * <<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>> + * <<a class="code" href="#pluginDescPluginBWC">backwards-compatibility</a> abi=<span class="charliteral">"0.3"</span> api=<span class="charliteral">"0.2.8"</span>/> + * <<a class="code" href="#pluginDescPluginRequires">requires</a>> + * <<a class="code" href="#pluginDescPluginReqCP">c-pluff</a> version=<span class="charliteral">"0.1"</span>/> + * <<a class="code" href="#pluginDescPluginReqImport">import</a> plugin=<span class="charliteral">"org.c-pluff.util"</span> version=<span class="charliteral">"0.2"</span>/> + * <<a class="code" href="#pluginDescPluginReqImport">import</a> plugin=<span class="charliteral">"org.c-pluff.extra"</span> optional=<span class="charliteral">"true"</span>/> + * </<a class="code" href="#pluginDescPluginRequires">requires</a>> + * <<a class="code" href="#pluginDescPluginRuntime">runtime</a> library=<span class="charliteral">"libruntime"</span> funcs=<span class="charliteral">"org_cpluff_example_funcs"</span>/> + * <<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>/> + * <<a class="code" href="#pluginDescPluginEP">extension-point</a> id=<span class="charliteral">"url-families"</span>/> + * <<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>> + * <type random-access=<span class="charliteral">"false"</span>/> + * <exec bin=<span class="charliteral">"tar"</span>/> + * </<a class="code" href="#pluginDescPluginE">extension</a>> + * <<a class="code" href="#pluginDescPluginE">extension</a> point=<span class="charliteral">"org.c-pluff.example.editors</span>> + * <editor name=<span class="charliteral">"Text Editor"</span> runtime=<span class="charliteral">"org_cpluff_example_txteditor_runtime"</span>> + * <file-types> + * <file-type mime-type=<span class="charliteral">"text/plain"</span>/> + * </file-types> + * </editor> + * </<a class="code" href="#pluginDescPluginE">extension</a>> + * </<a class="code" href="#pluginDescPlugin">plugin</a>></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"> + * <<a class="code" href="#pluginDescPlugin">plugin</a> id=<span class="charliteral">"org.c-pluff.useless"</span>/></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_ |