diff --git a/.gitignore b/.gitignore index 9ae5f54..bf2ce3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,61 +1,63 @@ *.[oa] *.deps *.so *.la *.lo *.so.* *.3 *.rpm *.swp *.pc *.log Makefile Makefile.in aclocal.m4 autom4te.cache/ config.status configure conftest.* # dotfiles .* # - subsumed: # .dirstamp (since we use subdir-objects as automake option) # .libs # .{,snapshot-,tarball-}version # - sans: !.git* !/.tito !/.travis.yml # ignore "libtoolized" m4 files, but keep our (custom-prefixed) ones /m4/* !/m4/ax_*.m4 # build-aux/release.mk related litter /tag-* /lib/qblog_script.ld # already captured with wildcard: /lib/qblog_script.la libtool libqb.spec libqb-* +/doxygen2man/doxygen2man cov compat_reports abi_dumps TAGS *~ test-driver /tests/**/*.real /tests/**/*.trs +/tests/ipc-test-name /tests/functional/log_callsite_bench.c /tests/functional/**/log_callsite_bench_section*.c /tests/functional/**/log_client /tests/functional/**/log_interlib_client /tests/functional/_pkgs /tests/functional/_results diff --git a/Makefile.am b/Makefile.am index 6d8acdf..cf984c9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,146 +1,146 @@ # Copyright (C) 2010 Red Hat, Inc. # # Authors: Andrew Beekhof # Steven Dake # Angus Salkeld # # This file is part of libqb. # # libqb is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 2.1 of the License, or # (at your option) any later version. # # libqb is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with libqb. If not, see . SPEC = $(PACKAGE_NAME).spec EXTRA_DIST = autogen.sh $(SPEC).in \ build-aux/git-version-gen \ build-aux/gitlog-to-changelog \ build-aux/release.mk \ .version AUTOMAKE_OPTIONS = foreign MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ autoheader automake autoconf libtool libtoolize \ ltmain.sh compile build-aux/test-driver ACLOCAL_AMFLAGS = -I m4 dist_doc_DATA = COPYING INSTALL README.markdown -SUBDIRS = include lib docs tools tests examples +SUBDIRS = include lib docs tools tests examples doxygen2man doxygen: $(MAKE) -C docs doxygen dist-clean-local: rm -f .snapshot-version autoconf automake autoheader # this will also get rid of "libtoolized" m4 files maintainer-clean-local: rm -f .version .tarball-version rm -f $(patsubst $(top_srcdir)/m4/ax_%,,$(wildcard $(top_srcdir)/m4/*.m4)) clean-local: rm -rf $(SPEC) $(DIST_ARCHIVES) ## make rpm/srpm section. $(SPEC): $(SPEC).in @rm -f $@-t $@ @date="$(shell LC_ALL=C date "+%a %b %d %Y")" && \ if [ -s .tarball-version ] || [ -n "$$(echo '${VERSION}' | grep -v UNKNOWN)" ]; then \ ver="$$({ cat .tarball-version 2>/dev/null; echo '${VERSION}'; } | head -n1)" && \ gitver=$$(echo $$ver | sed \ -e 's|^\(v[0-9][0-9]*\.[0-9][0-9]*\)\([^.].*\)\?$$|\1.0\2|') && \ rpmver=$$(echo $$gitver | sed 's/-.*//') && \ alphatag="" && \ dirty=$$(echo $$gitver | sed -n 's/[^-][^-]*-//p') && \ numcomm="0"; \ else \ ver="$(shell git describe --abbrev=4 --match='v*' --tags HEAD 2>/dev/null)" && \ gitver=$$(echo $$ver | sed \ -e 's|^\(v[0-9][0-9]*\.[0-9][0-9]*\)\([^.].*\)\?$$|\1.0\2|') && \ rpmver=$$(echo $$gitver | sed -e "s/^v//" -e "s/-.*//g") && \ alphatag=$$(echo $$gitver | sed -e "s/.*-//" -e "s/^g//") && \ vtag=$$(echo $$ver | sed -e "s/-.*//g") && \ numcomm=$$(git rev-list $$vtag..HEAD | wc -l) && \ git update-index --refresh > /dev/null 2>&1 || true && \ dirty=$$(git diff-index --name-only HEAD 2>/dev/null | sed 's/..*/dirty/'); \ fi && \ if [ "$$numcomm" = "0" ]; then \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#%glo.*alpha.*##g" \ -e "s#%glo.*numcomm.*##g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ else \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#@alphatag@#$$alphatag#g" \ -e "s#@numcomm@#$$numcomm#g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ fi; \ if [ -z "$$dirty" ]; then sed -i -e "s#%glo.*dirty.*##g" $@-t; fi @chmod a-w $@-t $(AM_V_GEN)mv $@-t $@ RPMBUILDOPTS = --define "_sourcedir $(abs_builddir)" \ --define "_specdir $(abs_builddir)" \ --define "_builddir $(abs_builddir)" \ --define "_srcrpmdir $(abs_builddir)" \ --define "_rpmdir $(abs_builddir)" # generates both .tar.[gx]z (backward compatibility) tarball: dist srpm: clean autoreconf -if $(MAKE) $(SPEC) dist-xz rpmbuild $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: clean autoreconf -if $(MAKE) $(SPEC) dist-xz rpmbuild $(RPMBUILDOPTS) -ba $(SPEC) # release/versioning BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ # untaint configure.ac when modified upon obtaining the "yanked" snapshot form dist-hook: gen-ChangeLog echo $(VERSION) | tee $(distdir)/.tarball-version | grep -Eqv '\-yank' \ || sed "s/\(.*git-version-gen[^']*[']\)[^']*/\1\$$Format:%h??%D\$$/" \ $(distdir)/configure.ac > $(builddir)/configure.ac-t if [ -f $(builddir)/configure.ac-t ]; then \ touch -r $(distdir)/configure.ac $(builddir)/configure.ac-t; \ chmod u+w $(builddir)/configure.ac-t; \ mv $(builddir)/configure.ac-t $(distdir)/configure.ac; \ fi gen_start_date = 2000-01-01 .PHONY: gen-ChangeLog gen-ChangeLog: if test -d .git; then \ $(top_srcdir)/build-aux/gitlog-to-changelog \ --since=$(gen_start_date) > $(distdir)/cl-t; \ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi diff --git a/configure.ac b/configure.ac index ec8736c..bd99c1f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,832 +1,851 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) dnl inject zero as a "patch" component of the version if missing in tag; dnl care to bump "X.Y.Z-yank" template below upon each release very desirable AC_INIT([libqb], m4_esyscmd([build-aux/git-version-gen $(echo '$Format:%h??%D$'\ | sed -ne 's/^[$]Format:[^$]*[$]$/.tarball-version/p;tend'\ -e 's/.*tag: v\([^, ][^, ]*\).*/\1-yank/;tv'\ -e 's/^\([[:xdigit:]][[:xdigit:]]*\).*/1.9.0-yank\1/'\ -e ':v' -e 'w .snapshot-version' -e 'i\ .snapshot-version' -e ':end' -e 'q')\ 's/^\(v[0-9][0-9]*\.[0-9][0-9]*\)\([^.].*\)\?$/\1.0\2/']), [developers@clusterlabs.org]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([lib/ringbuffer.c]) AC_CONFIG_HEADERS([include/config.h include/qb/qbconfig.h]) AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([-Wno-portability dist-xz subdir-objects]) dnl automake >= 1.11 offers --enable-silent-rules for suppressing the output from dnl normal compilation. When a failure occurs, it will then display the full dnl command line dnl Wrap in m4_ifdef to avoid breaking on older platforms m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) LT_PREREQ([2.2.6]) # --enable-new-dtags: Use RUNPATH instead of RPATH. # It is necessary to have this done before libtool does linker detection. # See also: https://github.com/kronosnet/kronosnet/issues/107 AX_CHECK_LINK_FLAG([-Wl,--enable-new-dtags], [AM_LDFLAGS=-Wl,--enable-new-dtags], [AC_MSG_ERROR(["Linker support for --enable-new-dtags is required"])]) AC_SUBST([AM_LDFLAGS]) saved_LDFLAGS="$LDFLAGS" LDFLAGS="$AM_LDFLAGS $LDFLAGS" LT_INIT LDFLAGS="$saved_LDFLAGS" AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_HOST AC_PROG_LIBTOOL AC_LANG([C]) dnl Fix default variables - "prefix" variable if not specified if test "$prefix" = "NONE"; then prefix="/usr" if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi if test "$srcdir" = "."; then AC_MSG_NOTICE([building in place srcdir:$srcdir]) AC_DEFINE([BUILDING_IN_PLACE], 1, [building in place]) else AC_MSG_NOTICE([building out of tree srcdir:$srcdir]) fi # Checks for programs. # check stolen from gnulib/m4/gnu-make.m4 if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then AC_MSG_ERROR([you don't seem to have GNU make; it is required]) fi AC_PROG_CXX AM_CONDITIONAL(HAVE_GXX, [test "x$GXX" = xyes]) AC_PROG_AWK AC_PROG_CC AC_PROG_CPP AM_PROG_CC_C_O AC_PROG_CC_C99 if test "x$ac_cv_prog_cc_c99" = "xno"; then AC_MSG_ERROR(["C99 support is required"]) fi AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_CHECK_PROGS([PKGCONFIG], [pkg-config]) AC_CHECK_PROGS([DOXYGEN], [doxygen]) AM_CONDITIONAL(HAVE_DOXYGEN, test -n "${DOXYGEN}") AC_CHECK_TOOLS([NM], [eu-nm nm], [:]) AC_CHECK_TOOLS([READELF], [eu-readelf readelf], [:]) AM_PATH_PYTHON([2.6],, [:]) AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != :]) ## local helper functions # this function checks if CC support options passed as # args. Global CFLAGS are ignored during this test. cc_supports_flag() { BACKUP="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $@ -Werror" AC_MSG_CHECKING([whether $CC supports "$@"]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) CPPFLAGS="$BACKUP" return $RC } ## cleanup AC_MSG_NOTICE(Sanitizing prefix: ${prefix}) case $prefix in NONE) prefix=/usr/local;; esac AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix}) case $exec_prefix in NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac # Checks for libraries. AX_PTHREAD(,[AC_MSG_ERROR([POSIX threads support is required])]) AX_SAVE_FLAGS AC_SEARCH_LIBS([dlopen],[dl],,[AC_MSG_ERROR([cannot find dlopen() function])]) AC_SUBST([dlopen_LIBS],[$LIBS]) AX_RESTORE_FLAGS AX_SAVE_FLAGS AC_SEARCH_LIBS([socket], [socket],,[AC_MSG_ERROR([cannot find socket() function])]) AC_SUBST([socket_LIBS],[$LIBS]) AX_RESTORE_FLAGS AX_SAVE_FLAGS AC_SEARCH_LIBS([gethostbyname], [nsl],,[AC_MSG_ERROR([cannot find gethostbyname() function])]) AC_SUBST([nsl_LIBS],[$LIBS]) AX_RESTORE_FLAGS AX_SAVE_FLAGS AC_SEARCH_LIBS([clock_gettime], [rt],,[AC_MSG_ERROR([cannot find clock_gettime() function])]) AC_SUBST([rt_LIBS],[$LIBS]) AX_RESTORE_FLAGS USE_JOURNAL="no" PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes],[have_systemd=no]) if test "x$enable_systemd_journal" = "xyes" ; then if test "x$have_systemd" = "xyes" ; then AC_DEFINE_UNQUOTED(USE_JOURNAL, 1, [Use systemd journal logging]) USE_JOURNAL="yes" else echo "systemd libraries not found, will just use syslog" fi fi # look for testing harness "check" PKG_CHECK_MODULES([CHECK], [check >= 0.9.4],[with_check=yes],[with_check=no]) AM_CONDITIONAL(HAVE_CHECK, test "${with_check}" = "yes") # look for GLIB (used for testing integration) PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.0, have_glib=yes, have_glib=no) AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) if test x"$have_glib" = xyes; then AC_DEFINE_UNQUOTED([HAVE_GLIB], [1], [We have glib]) fi +# For building doxygen2man and man pages +PKG_CHECK_MODULES([libxml], [libxml-2.0]) + +# if we are not cross-compiling, we can use the locally built +# version of doxygen2man, otherwise we can look for +# a locally installed version. If neither are usable, then +# don“t build the man pages +if test "x$cross_compiling" = "xno"; then + AM_CONDITIONAL([BUILD_MAN], 1) + DOXYGEN2MAN="\$(abs_builddir)/doxygen2man/doxygen2man" +else + AC_CHECK_PROGS([DOXYGEN2MAN], [doxygen2man]) + if test "x$DOXYGEN2MAN" = "x"; then + AM_CONDITIONAL([BUILD_MAN], 0) + fi +fi +AC_SUBST(DOXYGEN2MAN) + # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([arpa/inet.h link.h fcntl.h inttypes.h limits.h netinet/in.h \ stdint.h stddef.h stdlib.h string.h strings.h \ dlfcn.h time.h sys/time.h sys/types.h sys/stat.h \ sys/param.h sys/socket.h sys/time.h sys/poll.h sys/epoll.h \ sys/uio.h sys/event.h sys/sockio.h sys/un.h sys/resource.h \ syslog.h errno.h unistd.h sys/mman.h \ sys/sem.h sys/ipc.h sys/msg.h netdb.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_UID_T AC_C_INLINE AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_INT8_T AC_TYPE_MODE_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT64_T AC_TYPE_UINT32_T AC_TYPE_UINT16_T AC_TYPE_UINT8_T AC_CHECK_MEMBER([struct sockaddr_un.sun_len], [AC_DEFINE([HAVE_STRUCT_SOCKADDR_UN_SUN_LEN], 1, [Define to 1 if struct sockaddr_un has a member sun_len])], [], [#include ]) AC_MSG_CHECKING(looking for union semun in sys/sem.h) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#include #include #include ]], [[union semun arg; semctl(0, 0, 0, arg);]])], [ AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([HAVE_SEMUN], 1, [Define to 1 if you have union semun.]) ], [ AC_MSG_RESULT([no]) ] ) AX_SAVE_FLAGS LIBS="$LIBS $rt_LIBS" AC_MSG_CHECKING(for a working clock_getres(CLOCK_MONOTONIC, &ts)) AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[#include ]], [[struct timespec ts; if(clock_getres(CLOCK_MONOTONIC, &ts)) return -1;]])], [ AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([HAVE_CLOCK_GETRES_MONOTONIC], 1, [Define to 1 if clock_getres(CLOCK_MONOTONIC, &ts) works]) ], [ AC_MSG_RESULT([no]) ] ) AC_CHECK_FUNCS([clock_gettime]) AX_RESTORE_FLAGS # Checks for library functions AC_FUNC_CHOWN AC_FUNC_FORK AC_FUNC_MMAP AC_FUNC_STRERROR_R AC_CHECK_FUNCS([alarm fsync fdatasync ftruncate \ gettimeofday localtime localtime_r \ memset munmap socket \ strchr strrchr strdup strstr strcasecmp \ poll epoll_create epoll_create1 kqueue \ random rand getrlimit sysconf \ getpeerucred getpeereid \ openat unlinkat]) AX_SAVE_FLAGS CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$LIBS $PTHREAD_LIBS" AC_CHECK_FUNCS([pthread_spin_lock pthread_setschedparam \ pthread_mutexattr_setpshared \ pthread_condattr_setpshared \ sem_timedwait semtimedop]) AX_RESTORE_FLAGS # Checks for defined macros AC_MSG_CHECKING(for MSG_NOSIGNAL) AC_TRY_COMPILE([#include ], [ int f = MSG_NOSIGNAL; ], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_MSG_NOSIGNAL, 1, [Define this symbol if you have MSG_NOSIGNAL])], [ AC_MSG_RESULT(no)]) AC_MSG_CHECKING(for SO_NOSIGPIPE ) AC_TRY_COMPILE([#include ], [ int f = SO_NOSIGPIPE; ], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SO_NOSIGPIPE, 1, [Define this symbol if you have SO_NOSIGPIPE]) ], [ AC_MSG_RESULT(no)]) AC_MSG_CHECKING([for RTLD_NEXT]) AC_TRY_COMPILE([#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include ], [ void *h = RTLD_NEXT; ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_FAILURE_INJECTION], 1, [have failure injection]) have_RTLD_NEXT=yes ], [ AC_MSG_RESULT([no])]) AM_CONDITIONAL(HAVE_SEM_TIMEDWAIT, [test "x$ac_cv_func_sem_timedwait" = xyes]) AM_CONDITIONAL(HAVE_EPOLL, [test "x$ac_cv_func_epoll_create" = xyes]) AM_CONDITIONAL(HAVE_POLL, [test "x$ac_cv_func_poll" = xyes]) AM_CONDITIONAL(HAVE_KQUEUE, [test "x$ac_cv_func_kqueue" = xyes]) AM_CONDITIONAL(HAVE_FAILURE_INJECTION, [test "x$have_RTLD_NEXT" = xyes]) AC_CONFIG_LIBOBJ_DIR(lib) AC_REPLACE_FUNCS(strlcpy strlcat strchrnul) ## local defines PACKAGE_FEATURES="" if test x$ac_cv_func_epoll_create = xyes; then PACKAGE_FEATURES="$PACKAGE_FEATURES epoll" fi nongcc_memory_barrier_needed=no arch_force_shmlba=no file_sync=fdatasync AC_MSG_CHECKING([for architecture in ${host_cpu}]) case $host_cpu in sparc*) AC_MSG_RESULT([sparc]) AC_DEFINE_UNQUOTED([QB_ARCH_SPARC], [1], [sparc]) nongcc_memory_barrier_needed=yes arch_force_shmlba=yes ;; alpha*) AC_MSG_RESULT([alpha]) AC_DEFINE_UNQUOTED([QB_ARCH_ALPHA], [1], [alpha]) nongcc_memory_barrier_needed=yes ;; powerpc*) AC_MSG_RESULT([powerpc]) AC_DEFINE_UNQUOTED([QB_ARCH_POWERPC], [1], [powerpc]) nongcc_memory_barrier_needed=yes arch_force_shmlba=yes ;; ia64) AC_MSG_RESULT([ia64]) AC_DEFINE_UNQUOTED([QB_ARCH_IA64], [1], [ia64]) nongcc_memory_barrier_needed=yes ;; arm*) AC_MSG_RESULT([arm]) AC_DEFINE_UNQUOTED([QB_ARCH_ARM], [1], [arm]) arch_force_shmlba=yes ;; hppa*) AC_MSG_RESULT([hppa]) AC_DEFINE_UNQUOTED([QB_ARCH_HPPA], [1], [hppa]) ;; mips*) AC_MSG_RESULT([ia64]) AC_DEFINE_UNQUOTED([QB_ARCH_MIPS], [1], [mips]) arch_force_shmlba=yes ;; *) AC_MSG_RESULT([${host_cpu}]) ;; esac if test $arch_force_shmlba = yes; then AC_DEFINE_UNQUOTED([QB_FORCE_SHM_ALIGN], [1], [shared and fixed mmap must align on 16k]) fi # OS detection # THIS SECTION MUST DIE! CP=cp AC_MSG_CHECKING([for os in ${host_os}]) case "$host_os" in *linux*) AC_DEFINE_UNQUOTED([QB_LINUX], [1], [Compiling for Linux platform]) AC_MSG_RESULT([Linux]) ;; *cygwin*) AC_DEFINE_UNQUOTED([QB_CYGWIN], [1], [Compiling for Cygwin platform]) nongcc_memory_barrier_needed=yes gcc_has_builtin_sync_operations=no AC_MSG_RESULT([Cygwin]) ;; darwin*) AC_DEFINE_UNQUOTED([QB_DARWIN], [1], [Compiling for Darwin platform]) CP=rsync dnl Attribute section appears to work here but fails later with: dnl cc1: error in backend: Global variable 'descriptor.4902' dnl has an invalid section specifier '__verbose': mach-o dnl section specifier requires a segment and section dnl separated by a comma AC_DEFINE_UNQUOTED([DISABLE_POSIX_THREAD_PROCESS_SHARED], [1], [Disable _POSIX_THREAD_PROCESS_SHARED]) AC_MSG_RESULT([Darwin]) ;; *bsd*) AC_DEFINE_UNQUOTED([QB_BSD], [1], [Compiling for BSD platform]) case "$host_os" in *netbsd*) AC_DEFINE_UNQUOTED([UNIX_PATH_MAX], [103], [Unix path length]) ;; *openbsd*) AC_DEFINE_UNQUOTED([UNIX_PATH_MAX], [104], [Unix path length]) ;; esac AC_MSG_RESULT([BSD]) ;; *solaris*) AC_DEFINE_UNQUOTED(DISABLE_IPC_SHM, 1, [Disable shared mem ipc]) AC_DEFINE_UNQUOTED([QB_SOLARIS], [1], [Compiling for Solaris platform]) CP=rsync AC_MSG_RESULT([Solaris]) ;; *gnu*) AC_DEFINE_UNQUOTED([QB_GNU], [1], [Compiling for GNU/Hurd platform]) AC_MSG_RESULT([GNU]) ;; *) AC_MSG_ERROR([Unsupported OS? hmmmm]) ;; esac dnl break on first hit, fallthrough otherwise, until empty or unsupported dnl string reached; output QB_FILE_SYNC macro operates on a file descriptor while : ; do case ${file_sync} in fdatasync) test "x${ac_cv_func_fdatasync}" = "xno" || break file_sync=fsync;; fsync) test "x${ac_cv_func_fsync}" = "xno" || break file_sync=;; "") break;; *) AC_MSG_ERROR([Cannot select file sync method]);; esac done if test "x${file_sync}" != x; then AC_DEFINE_UNQUOTED([QB_FILE_SYNC(fd)], [${file_sync}(fd)], [File sync method]) else AC_MSG_WARN([No file sync method applicable!]) AC_DEFINE([QB_FILE_SYNC(x)], [], [File sync method]) fi AC_MSG_CHECKING([whether GCC supports builtin sync intrinsics]) if test -z "$gcc_has_builtin_sync_operations"; then gcc_has_builtin_sync_operations=no if test x"$GCC" = xyes && test x$have_mingw != xyes; then AC_TRY_LINK([], [int i; __sync_synchronize (); __sync_bool_compare_and_swap (&i, 0, 1); __sync_fetch_and_add (&i, 1); ], [gcc_has_builtin_sync_operations=yes], [gcc_has_builtin_sync_operations=no]) fi fi AC_MSG_RESULT($gcc_has_builtin_sync_operations) AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS, [test "x$gcc_has_builtin_sync_operations" = xyes]) if test "x$gcc_has_builtin_sync_operations" = xyes; then AC_DEFINE_UNQUOTED(HAVE_GCC_BUILTINS_FOR_SYNC_OPERATIONS, 1, [have builtin sync operations]) fi # __atomic_XXX AC_MSG_CHECKING([whether GCC supports builtin atomic intrinsics]) if test -z "$gcc_has_builtin_atomic_operations"; then gcc_has_builtin_atomic_operations=no if test x"$GCC" = xyes && test x$have_mingw != xyes; then AC_TRY_LINK([], [int i; __atomic_load_n(&i, __ATOMIC_ACQUIRE); __atomic_exchange_n(&i, 0, __ATOMIC_RELEASE); ], [gcc_has_builtin_atomic_operations=yes], [gcc_has_builtin_atomic_operations=no]) fi fi AC_MSG_RESULT($gcc_has_builtin_atomic_operations) AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS, [test "x$gcc_has_builtin_atomic_operations" = xyes]) if test "x$gcc_has_builtin_atomic_operations" = xyes; then AC_DEFINE_UNQUOTED(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS, 1, [have builtin atomic operations]) fi AC_MSG_CHECKING([whether atomics need memory barrier]) if test -n "$ac_cv_atomic_need_memory_barrier"; then memory_barrier_needed=$ac_cv_atomic_need_memory_barrier else if test x$gcc_has_builtin_sync_operations = xyes; then memory_barrier_needed=yes PACKAGE_FEATURES="$PACKAGE_FEATURES gcc__sync" else memory_barrier_needed=$nongcc_memory_barrier_needed AC_MSG_WARN([-----------------------------]) AC_MSG_WARN([You have gcc but not __sync_bool_compare_and_swap]) AC_MSG_WARN([try CFLAGS="-march= -mtune=native" ./configure]) AC_MSG_WARN([-----------------------------]) fi fi AC_MSG_RESULT($memory_barrier_needed) if test x"$memory_barrier_needed" != xno; then AC_DEFINE_UNQUOTED(QB_ATOMIC_OP_MEMORY_BARRIER_NEEDED, 1, [need atomic memory barrier]) fi LINT_FLAGS="-weak -D__gnuc_va_list=va_list -D__attribute\(x\)= \ -badflag -fcnuse -syntax -unrecog -sysunrecog -warnposix \ +ignoresigns +matchanyintegral +posixlib \ +showscan +showsummary" # local options AC_ARG_ENABLE([ansi], [AS_HELP_STRING([--enable-ansi],[force to build with ANSI standards])]) AC_ARG_ENABLE([fatal-warnings], [AS_HELP_STRING([--enable-fatal-warnings],[enable fatal warnings])]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug],[enable debug build])]) AC_ARG_WITH([sanitizers], [AS_HELP_STRING([--with-sanitizers=...,...], [enable SANitizer build, do *NOT* use for production. Only ASAN/UBSAN/TSAN are currently supported])], [ SANITIZERS="$withval" ], [ SANITIZERS="" ]) AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage],[coverage analysis of the codebase])]) AC_ARG_ENABLE([interlib-deps], [AS_HELP_STRING([--disable-interlib-deps], [disable inter-library dependencies (might break builds)])]) AC_ARG_ENABLE([slow-tests], [AS_HELP_STRING([--enable-slow-tests],[build and run slow tests])]) AC_ARG_WITH([socket-dir], [AS_HELP_STRING([--with-socket-dir=DIR],[socket directory @<:@LOCALSTATEDIR/run@:>@])], [ SOCKETDIR="$withval" ], [ SOCKETDIR="$localstatedir/run" ]) AC_ARG_ENABLE([systemd-journal], [AS_HELP_STRING([--enable-systemd-journal],[Allow use of systemd journal instead of syslog])]) AC_ARG_WITH([force-sockets-config-file], [AS_HELP_STRING([--with-force-sockets-config-file=FILE],[config file to force IPC to use filesystem sockets (Linux & Cygwin only) @<:@SYSCONFDIR/libqb/force-filesystem-sockets@:>@])], [ FORCESOCKETSFILE="$withval" ], [ FORCESOCKETSFILE="$sysconfdir/libqb/force-filesystem-sockets" ]) AC_ARG_ENABLE([install-tests], [AS_HELP_STRING([--enable-install-tests],[install tests])],, [ enable_install_tests="no" ]) AM_CONDITIONAL([INSTALL_TESTS], [test x$enable_install_tests = xyes]) AC_ARG_WITH([testdir], [AS_HELP_STRING([--with-testdir=DIR],[path to /usr/lib../libqb/tests/ dir where to install the test suite])], [ TESTDIR="$withval" ], [ TESTDIR="$libdir/libqb/tests" ]) AC_SUBST([TESTDIR]) AC_SUBST(CP) # *FLAGS handling goes here ENV_CFLAGS="$CFLAGS" ENV_CPPFLAGS="$CPPFLAGS" ENV_LDFLAGS="$LDFLAGS" # debug build stuff if test "x${enable_debug}" = xyes; then AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code]) OPT_CFLAGS="-O0" if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi PACKAGE_FEATURES="$PACKAGE_FEATURES debug" fi # extra warnings EXTRA_WARNINGS="" WARNLIST=" all extra unused shadow missing-prototypes missing-declarations suggest-attribute=noreturn suggest-attribute=format property-attribute-mismatch strict-prototypes pointer-arith write-strings cast-align bad-function-cast missing-format-attribute float-equal format=2 format-signedness shift-overflow shift-overflow=2 overlength-strings redundent-decls init-self uninitialized unknown-pragmas no-unused-parameter unused-const-variable no-format-nonliteral no-format-truncation no-sign-compare " # at least no-sign-compare assumed temporary! for j in $WARNLIST; do if cc_supports_flag -W$j; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j"; fi done # warnings suppression gcc_format_complaints=no if test x"$GCC" = xyes && cc_supports_flag -Wmissing-format-attribute; then gcc_format_complaints=yes AC_DEFINE([HAVE_GCC_MISSING_FORMAT_ATTRIBUTE], [], [gcc supports -Wmissing-format-attribute]) fi if test x"$GCC" = xyes && cc_supports_flag -Wsuggest-attribute=format; then gcc_format_complaints=yes AC_DEFINE([HAVE_GCC_SUGGEST_ATTRIBUTE_FORMAT], [], [gcc supports -Wsuggest-attribute=format]) fi dnl pretend GCC (<4.6) is not capable of format complaints when it does not dnl support diagnostic push/pop pragmas (cannot track state reliably, then) if test x"$gcc_format_complaints" = xyes; then backup_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #pragma GCC diagnostic push #pragma GCC diagnostic pop ]])], AC_DEFINE([HAVE_GCC_FORMAT_COMPLAINTS], [], [gcc can complain about missing format attribute])) CFLAGS="$backup_CFLAGS" fi # --- coverage --- if test "x${enable_coverage}" = xyes && \ cc_supports_flag -ftest-coverage && \ cc_supports_flag -fprofile-arcs ; then AC_MSG_NOTICE([Enabling Coverage (enable -O0 by default)]) OPT_CFLAGS="-O0" COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" PACKAGE_FEATURES="$PACKAGE_FEATURES coverage" else COVERAGE_CFLAGS="" COVERAGE_LDFLAGS="" fi # --- inter-library dependencies --- # because of debian/ubuntu swimming against the stream # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=702737, # override the libtool variable by force because the current # arrangement relies on transitive dependency propagation AC_MSG_NOTICE([Enabling inter-library dependencies: $enable_interlib_deps]) if test "x${enable_interlib_deps}" = xno; then link_all_deplibs=no else link_all_deplibs=yes fi # --- slow tests --- if test "x${enable_slow_tests}" = xyes ; then AC_DEFINE([HAVE_SLOW_TESTS], 1,[have slow tests]) AC_MSG_NOTICE([Enabling Slow tests]) fi AM_CONDITIONAL(HAVE_SLOW_TESTS, [test "x${enable_slow_tests}" = xyes]) AC_SUBST(HAVE_SLOW_TESTS) # --- ansi --- if test "x${enable_ansi}" = xyes && \ cc_supports_flag -std=iso9899:199409 ; then AC_MSG_NOTICE([Enabling ANSI Compatibility]) ANSI_CPPFLAGS="-ansi -D_GNU_SOURCE -DANSI_ONLY" PACKAGE_FEATURES="$PACKAGE_FEATURES ansi" else ANSI_CPPFLAGS="" fi # --- fatal warnings --- if test "x${enable_fatal_warnings}" = xyes && \ cc_supports_flag -Werror ; then AC_MSG_NOTICE([Enabling Fatal Warnings (-Werror)]) WERROR_CFLAGS="-Werror" PACKAGE_FEATURES="$PACKAGE_FEATURES fatal-warnings" else WERROR_CFLAGS="" fi # --- ASAN/UBSAN/TSAN (see man gcc) --- # when using SANitizers, we need to pass the -fsanitize.. # to both CFLAGS and LDFLAGS. The CFLAGS/LDFLAGS must be # specified as first in the list or there will be runtime # issues (for example user has to LD_PRELOAD asan for it to work # properly). if test -n "${SANITIZERS}"; then SANITIZERS=$(echo $SANITIZERS | sed -e 's/,/ /g') for SANITIZER in $SANITIZERS; do case $SANITIZER in asan|ASAN) SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=address" SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=address -lasan" AC_CHECK_LIB([asan],[main],,AC_MSG_ERROR([Unable to find libasan])) ;; ubsan|UBSAN) SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=undefined" SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=undefined -lubsan" AC_CHECK_LIB([ubsan],[main],,AC_MSG_ERROR([Unable to find libubsan])) ;; tsan|TSAN) SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=thread" SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=thread -ltsan" AC_CHECK_LIB([tsan],[main],,AC_MSG_ERROR([Unable to find libtsan])) ;; esac done fi # final build of *FLAGS CFLAGS="$SANITIZERS_CFLAGS $ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS \ $COVERAGE_CFLAGS $EXTRA_WARNINGS $WERROR_CFLAGS" CPPFLAGS="$ENV_CPPFLAGS $ANSI_CPPFLAGS" LDFLAGS="$SANITIZERS_LDFLAGS $ENV_LDFLAGS $COVERAGE_LDFLAGS" if test -f /usr/share/dict/words ; then HAVE_DICT_WORDS=yes AC_DEFINE([HAVE_DICT_WORDS], 1, "Have /usr/share/dict/words") fi AM_CONDITIONAL([HAVE_DICT_WORDS], [test "x$HAVE_DICT_WORDS" = xyes]) # substitute what we need: AC_SUBST([SOCKETDIR]) AC_SUBST([LINT_FLAGS]) AC_SUBST([FORCESOCKETSFILE]) AC_DEFINE_UNQUOTED([SOCKETDIR], "$(eval echo ${SOCKETDIR})", [Socket directory]) AC_DEFINE_UNQUOTED([LOCALSTATEDIR], "$(eval echo ${localstatedir})", [localstate directory]) AC_DEFINE_UNQUOTED([PACKAGE_FEATURES], "${PACKAGE_FEATURES}", [quarterback built-in features]) AC_DEFINE_UNQUOTED([FORCESOCKETSFILE], "$(eval echo ${FORCESOCKETSFILE})", [for sockets config file]) # version parsing (for qbconfig.h) AC_DEFINE_UNQUOTED([QB_VER_MAJOR], [$(echo "${VERSION}" \ | sed -e 's|^\([[0-9]][[0-9]]*\).*|\1|' \ -e t -e 's|.*|1|')], [libqb major version]) AC_DEFINE_UNQUOTED([QB_VER_MINOR], [$(echo "${VERSION}" \ | sed -e 's|^[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\).*|\1|' \ -e t -e 's|.*|0|')], [libqb minor version]) AC_DEFINE_UNQUOTED([QB_VER_MICRO], [$(echo "${VERSION}" \ | sed -e 's|^[[0-9]][[0-9]]*\.[[0-9]][[0-9]]*\.\([[0-9]][[0-9]]*\).*|\1|' \ -e t -e 's|.*|0|')], [libqb patch version]) AC_DEFINE_UNQUOTED([QB_VER_REST], [$(echo "${VERSION}" \ | sed -e 's|^[[0-9]][[0-9]]*\(\.[[0-9]][[0-9]]*\)\{0,2\}\(.*\)|"\2"|' \ -e t -e 's|.*|""|')], [libqb patch version]) AC_CONFIG_FILES([Makefile include/Makefile include/qb/Makefile lib/Makefile lib/libqb.pc tools/Makefile tests/Makefile tests/test.conf examples/Makefile + doxygen2man/Makefile docs/Makefile docs/common.dox docs/html.dox docs/man.dox lib/qblog_script.la:lib/qblog_script.la.in]) AC_OUTPUT AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${VERSION}]) AC_MSG_RESULT([ Prefix = ${prefix}]) AC_MSG_RESULT([ Executables = ${sbindir}]) AC_MSG_RESULT([ Man pages = ${mandir}]) AC_MSG_RESULT([ Doc dir = ${docdir}]) AC_MSG_RESULT([ Libraries = ${libdir}]) AC_MSG_RESULT([ Header files = ${includedir}]) AC_MSG_RESULT([ Arch-independent files = ${datadir}]) AC_MSG_RESULT([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) AC_MSG_RESULT([ SOCKETDIR = ${SOCKETDIR}]) AC_MSG_RESULT([ Features = ${PACKAGE_FEATURES}]) AC_MSG_RESULT([ Use systemd journal = ${USE_JOURNAL}]) AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE build info:]) AC_MSG_RESULT([ Optimization = ${OPT_CFLAGS}]) AC_MSG_RESULT([ Debug options = ${GDB_CFLAGS}]) AC_MSG_RESULT([ Extra compiler warnings = ${EXTRA_WARNING}]) AC_MSG_RESULT([ Env. defined CFLAG = ${ENV_CFLAGS}]) AC_MSG_RESULT([ Env. defined CPPFLAGS = ${ENV_CPPFLAGS}]) AC_MSG_RESULT([ Env. defined LDFLAGS = ${ENV_LDFLAGS}]) AC_MSG_RESULT([ ANSI defined CPPFLAGS = ${ANSI_CPPFLAGS}]) AC_MSG_RESULT([ Coverage CFLAGS = ${COVERAGE_CFLAGS}]) AC_MSG_RESULT([ Coverage LDFLAGS = ${COVERAGE_LDFLAGS}]) AC_MSG_RESULT([ Fatal War. CFLAGS = ${WERROR_CFLAGS}]) AC_MSG_RESULT([ Final CFLAGS = ${CFLAGS}]) AC_MSG_RESULT([ Final CPPFLAGS = ${CPPFLAGS}]) AC_MSG_RESULT([ Final LDFLAGS = ${LDFLAGS}]) diff --git a/doxygen2man/Doxyfile.example b/doxygen2man/Doxyfile.example new file mode 100644 index 0000000..2ea39d3 --- /dev/null +++ b/doxygen2man/Doxyfile.example @@ -0,0 +1,19 @@ +# +# Copyright (C) 2017-2020 Red Hat, Inc. All rights reserved. +# +# Author: Fabio M. Di Nitto +# Christine Caulfield +# +# This software licensed under GPL-2.0+ +# +# Example file for doxygen when using doxygen2man +# +PROJECT_NAME = libqb +PROJECT_NUMBER = 2.0 +INPUT = ../include/qb/ +XML_OUTPUT = ./xml +GENERATE_XML = YES +XML_PROGRAMLISTING = NO +AUTOLINK_SUPPORT = NO +GENERATE_HTML = NO +GENERATE_LATEX = NO diff --git a/doxygen2man/Makefile.am b/doxygen2man/Makefile.am new file mode 100644 index 0000000..6b0c68a --- /dev/null +++ b/doxygen2man/Makefile.am @@ -0,0 +1,24 @@ +# +# Copyright (C) 2017-2019 Red Hat, Inc. All rights reserved. +# +# Authors: Fabio M. Di Nitto +# Federico Simoncelli +# +# This software licensed under GPL-2.0+ +# + +MAINTAINERCLEANFILES = Makefile.in + +EXTRA_DIST = doxygen2man.1 + +bin_PROGRAMS = doxygen2man + +all: $(PROGRAMS) $(MANS) + +doxygen2man_SOURCES = doxygen2man.c +doxygen2man_CPPFLAGS = -I$(top_srcdir)/include/ +doxygen2man_CFLAGS = $(AM_CFLAGS) $(libxml_CFLAGS) +doxygen2man_LDADD = $(top_builddir)/lib/libqb.la $(libxml_LIBS) + +man1_MANS = doxygen2man.1 + diff --git a/doxygen2man/doxygen2man.1 b/doxygen2man/doxygen2man.1 new file mode 100644 index 0000000..78489b7 --- /dev/null +++ b/doxygen2man/doxygen2man.1 @@ -0,0 +1,67 @@ +.\" +.\" * Copyright (C) 2010-2020 Red Hat, Inc. +.\" * +.\" * All rights reserved. +.\" * +.\" * Author: Christine Caulfield +.\" * + +.TH "DOXYGEN2MAN" "8" "2020-03-09" "" "" +.SH "NAME" +doxygen2man \- A tool to generate man pages from Doxygen XML files +.SH "SYNOPSIS" +.B doxygen2man [OPTIONS] +.SH "DESCRIPTION" +.P +This is a tool to generate API manpages from a doxygen-annotated header file +First run doxygen on the file and then run this program against the main XML file +it created and the directory containing the ancilliary files. It will then +output a lot of *.3 man page files which you can then ship with your library. +.P +You will need to invoke this program once for each .h file in your library, +using the name of the generated .xml file. This file will usually be called +something like _8h.xml, eg qbipcs_8h.xml +.P +If you want HTML output then simpy use nroff on the generated files as you +would do with any other man page. + +.SH "OPTIONS" +.TP +.B -a +Print ASCII dump of man pages to stdout +.TP +.B -m +Write man page files to +.TP +.B -P +Print PARAMS section +.TP +.B -s +Write man pages into section +Use name. default +.TP +.B -H
+Set header (default \"Programmer's Manual\") +.TP +.B -D +Date to print at top of man pages (format not checked, default: today) +.TP +.B -Y +Year to print at end of copyright line (default: today's year) +.TP +.B -o +Write all man pages to (default .) +.TP +.B -d +Directory for XML files (./xml/) +.TP +.B -h +Print usage text + +.SH "SEE ALSO" +.BR doxygen (1) +.SH "AUTHOR" +Christine Caulfield +.PP diff --git a/doxygen2man/doxygen2man.c b/doxygen2man/doxygen2man.c new file mode 100644 index 0000000..4f97f9d --- /dev/null +++ b/doxygen2man/doxygen2man.c @@ -0,0 +1,943 @@ +/* + * Copyright (C) 2018-2020 Red Hat, Inc. All rights reserved. + * + * Author: Christine Caulfield + * + * This software licensed under GPL-2.0+ + */ + + +/* + * NOTE: this code is very rough, it does the bare minimum to parse the + * XML out from doxygen and is probably very fragile to changes in that XML + * schema. It probably leaks memory all over the place too. + * + * In its favour, it *does* generate nice man pages and should only be run very ocasionally + */ + +#define _DEFAULT_SOURCE +#define _BSD_SOURCE +#define _XOPEN_SOURCE +#define _XOPEN_SOURCE_EXTENDED +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This isn't a maximum size, it just defines how long a parameter + * type can get before we decide it's not worth lining everything up. + * It's mainly to stop function pointer types (which can get VERY long because + * of all *their* parameters) making everything else 'line-up' over separate lines + */ +#define LINE_LENGTH 80 + +static int print_ascii = 1; +static int print_man = 0; +static int print_params = 0; +static int num_functions = 0; +static const char *man_section="3"; +static const char *package_name="Package"; +static const char *header="Programmer's Manual"; +static const char *output_dir="./"; +static const char *xml_dir = "./xml/"; +static const char *xml_file; +static const char *manpage_date = NULL; +static long manpage_year = LONG_MIN; +static struct qb_list_head params_list; +static struct qb_list_head retval_list; +static qb_map_t *function_map; +static qb_map_t *structures_map; +static qb_map_t *used_structures_map; + +struct param_info { + char *paramname; + char *paramtype; + char *paramdesc; + struct param_info *next; + struct qb_list_head list; +}; + +struct struct_info { + enum {STRUCTINFO_STRUCT, STRUCTINFO_ENUM} kind; + char *structname; + struct qb_list_head params_list; /* our params */ + struct qb_list_head list; +}; + +static char *get_texttree(int *type, xmlNode *cur_node, char **returntext); +static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg); + +static void free_paraminfo(struct param_info *pi) +{ + free(pi->paramname); + free(pi->paramtype); + free(pi->paramdesc); + free(pi); +} + +static char *get_attr(xmlNode *node, const char *tag) +{ + xmlAttr *this_attr; + + for (this_attr = node->properties; this_attr; this_attr = this_attr->next) { + if (this_attr->type == XML_ATTRIBUTE_NODE && strcmp((char *)this_attr->name, tag) == 0) { + return strdup((char *)this_attr->children->content); + } + } + return NULL; +} + +static char *get_child(xmlNode *node, const char *tag) +{ + xmlNode *this_node; + xmlNode *child; + char buffer[1024] = {'\0'}; + char *refid = NULL; + char *declname = NULL; + + for (this_node = node->children; this_node; this_node = this_node->next) { + if ((strcmp( (char*)this_node->name, "declname") == 0)) { + declname = strdup((char*)this_node->children->content); + } + + if ((this_node->type == XML_ELEMENT_NODE && this_node->children) && ((strcmp((char *)this_node->name, tag) == 0))) { + refid = NULL; + for (child = this_node->children; child; child = child->next) { + if (child->content) { + strcat(buffer, (char *)child->content); + } + + if ((strcmp( (char*)child->name, "ref") == 0)) { + if (child->children->content) { + strcat(buffer,(char *)child->children->content); + } + refid = get_attr(child, "refid"); + } + } + } + if (declname && refid) { + qb_map_put(used_structures_map, refid, declname); + } + } + return strdup(buffer); +} + +static struct param_info *find_param_by_name(struct qb_list_head *list, const char *name) +{ + struct qb_list_head *iter; + struct param_info *pi; + + qb_list_for_each(iter, list) { + pi = qb_list_entry(iter, struct param_info, list); + if (strcmp(pi->paramname, name) == 0) { + return pi; + } + } + return NULL; +} + +static int not_all_whitespace(char *string) +{ + unsigned int i; + + for (i=0; ichildren; this_tag; this_tag = this_tag->next) { + for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) { + if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameternamelist") == 0) { + paramname = (char*)sub_tag->children->next->children->content; + } + if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameterdescription") == 0) { + paramdesc = (char*)sub_tag->children->next->children->content; + + /* Add text to the param_map */ + pi = find_param_by_name(list, paramname); + if (pi) { + pi->paramdesc = paramdesc; + } + else { + pi = malloc(sizeof(struct param_info)); + if (pi) { + pi->paramname = paramname; + pi->paramdesc = paramdesc; + pi->paramtype = NULL; /* retval */ + qb_list_add_tail(&pi->list, list); + } + } + } + } + } +} + +static char *get_text(xmlNode *cur_node, char **returntext) +{ + xmlNode *this_tag; + xmlNode *sub_tag; + char *kind; + char buffer[4096] = {'\0'}; + + for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { + if (this_tag->type == XML_TEXT_NODE && strcmp((char *)this_tag->name, "text") == 0) { + if (not_all_whitespace((char*)this_tag->content)) { + strcat(buffer, (char*)this_tag->content); + strcat(buffer, "\n"); + } + } + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "emphasis") == 0) { + if (print_man) { + strcat(buffer, "\\fB"); + } + strcat(buffer, (char*)this_tag->children->content); + if (print_man) { + strcat(buffer, "\\fR"); + } + } + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "itemizedlist") == 0) { + for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) { + if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "listitem") == 0) { + strcat(buffer, (char*)sub_tag->children->children->content); + strcat(buffer, "\n"); + } + } + } + + /* Look for subsections - return value & params */ + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "simplesect") == 0) { + char *tmp; + + kind = get_attr(this_tag, "kind"); + tmp = get_text(this_tag->children, NULL); + + if (returntext && strcmp(kind, "return") == 0) { + *returntext = tmp; + } + } + + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "parameterlist") == 0) { + kind = get_attr(this_tag, "kind"); + if (strcmp(kind, "param") == 0) { + get_param_info(this_tag, ¶ms_list); + } + if (strcmp(kind, "retval") == 0) { + get_param_info(this_tag, &retval_list); + } + } + } + return strdup(buffer); +} + +static void read_structname(xmlNode *cur_node, void *arg) +{ + struct struct_info *si=arg; + xmlNode *this_tag; + + for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { + if (strcmp((char*)this_tag->name, "compoundname") == 0) { + si->structname = strdup((char*)this_tag->children->content); + } + } +} + +/* Called from traverse_node() */ +static void read_struct(xmlNode *cur_node, void *arg) +{ + xmlNode *this_tag; + struct struct_info *si=arg; + struct param_info *pi; + char fullname[1024]; + char *type = NULL; + char *name = NULL; + const char *args=""; + + for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { + if (strcmp((char*)this_tag->name, "type") == 0) { + type = (char*)this_tag->children->content ; + } + if (strcmp((char*)this_tag->name, "name") == 0) { + name = (char*)this_tag->children->content ; + } + if (this_tag->children && strcmp((char*)this_tag->name, "argsstring") == 0) { + args = (char*)this_tag->children->content; + } + } + + if (name) { + pi = malloc(sizeof(struct param_info)); + if (pi) { + snprintf(fullname, sizeof(fullname), "%s%s", name, args); + pi->paramtype = type?strdup(type):strdup(""); + pi->paramname = strdup(fullname); + pi->paramdesc = NULL; + qb_list_add_tail(&pi->list, &si->params_list); + } + } +} + +static int read_structure_from_xml(char *refid, char *name) +{ + char fname[PATH_MAX]; + xmlNode *rootdoc; + xmlDocPtr doc; + struct struct_info *si; + int ret = -1; + + snprintf(fname, sizeof(fname), "%s/%s.xml", xml_dir, refid); + + doc = xmlParseFile(fname); + if (doc == NULL) { + fprintf(stderr, "Error: unable to open xml file for %s\n", refid); + return -1; + } + + rootdoc = xmlDocGetRootElement(doc); + if (!rootdoc) { + fprintf(stderr, "Can't find \"document root\"\n"); + return -1; + } + + si = malloc(sizeof(struct struct_info)); + if (si) { + si->kind = STRUCTINFO_STRUCT; + qb_list_init(&si->params_list); + traverse_node(rootdoc, "memberdef", read_struct, si); + traverse_node(rootdoc, "compounddef", read_structname, si); + ret = 0; + qb_map_put(structures_map, refid, si); + } + xmlFreeDoc(doc); + + return ret; +} + + +static void print_param(FILE *manfile, struct param_info *pi, int field_width, int bold, const char *delimiter) +{ + const char *asterisks = " "; + char *type = pi->paramtype; + int typelength = strlen(type); + + /* Reformat pointer params so they look nicer */ + if (typelength > 0 && pi->paramtype[typelength-1] == '*') { + asterisks=" *"; + type = strdup(pi->paramtype); + type[typelength-1] = '\0'; + + /* Cope with double pointers */ + if (typelength > 1 && pi->paramtype[typelength-2] == '*') { + asterisks="**"; + type[typelength-2] = '\0'; + } + } + + fprintf(manfile, " %s%-*s%s%s\\fI%s\\fP%s\n", + bold?"\\fB":"", field_width, type, + asterisks, bold?"\\fP":"", pi->paramname, delimiter); + + if (type != pi->paramtype) { + free(type); + } +} + +static void print_structure(FILE *manfile, char *refid, char *name) +{ + struct struct_info *si; + struct param_info *pi; + struct qb_list_head *iter; + unsigned int max_param_length=0; + + /* If it's not been read in - go and look for it */ + si = qb_map_get(structures_map, refid); + if (!si) { + if (!read_structure_from_xml(refid, name)) { + si = qb_map_get(structures_map, refid); + } + } + + if (si) { + qb_list_for_each(iter, &si->params_list) { + pi = qb_list_entry(iter, struct param_info, list); + if (strlen(pi->paramtype) > max_param_length) { + max_param_length = strlen(pi->paramtype); + } + } + + if (si->kind == STRUCTINFO_STRUCT) { + fprintf(manfile, "struct %s {\n", si->structname); + } else if (si->kind == STRUCTINFO_ENUM) { + fprintf(manfile, "enum %s {\n", si->structname); + } else { + fprintf(manfile, "%s {\n", si->structname); + } + + qb_list_for_each(iter, &si->params_list) { + pi = qb_list_entry(iter, struct param_info, list); + print_param(manfile, pi, max_param_length, 0,";"); + } + fprintf(manfile, "};\n"); + } +} + +char *get_texttree(int *type, xmlNode *cur_node, char **returntext) +{ + xmlNode *this_tag; + char *tmp = NULL; + char buffer[4096] = {'\0'}; + + for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { + + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "para") == 0) { + tmp = get_text(this_tag, returntext); + strcat(buffer, tmp); + strcat(buffer, "\n"); + free(tmp); + } + } + + if (buffer[0]) { + tmp = strdup(buffer); + } + + return tmp; +} + +/* The text output is VERY basic and just a check that it's working really */ +static void print_text(char *name, char *def, char *brief, char *args, char *detailed, + struct qb_list_head *param_list, char *returntext) +{ + printf(" ------------------ %s --------------------\n", name); + printf("NAME\n"); + printf(" %s - %s\n", name, brief); + + printf("SYNOPSIS\n"); + printf(" %s %s\n\n", name, args); + + printf("DESCRIPTION\n"); + printf(" %s\n", detailed); + + if (returntext) { + printf("RETURN VALUE\n"); + printf(" %s\n", returntext); + } +} + +/* Print a long string with para marks in it. */ +static void man_print_long_string(FILE *manfile, char *text) +{ + char *next_nl; + char *current = text; + + next_nl = strchr(text, '\n'); + while (next_nl && *next_nl != '\0') { + *next_nl = '\0'; + if (strlen(current)) { + fprintf(manfile, ".PP\n%s\n", current); + } + + *next_nl = '\n'; + current = next_nl+1; + next_nl = strchr(current, '\n'); + } +} + +static void print_manpage(char *name, char *def, char *brief, char *args, char *detailed, + struct qb_list_head *param_map, char *returntext) +{ + char manfilename[PATH_MAX]; + char gendate[64]; + const char *dateptr = gendate; + FILE *manfile; + time_t t; + struct tm *tm; + qb_map_iter_t *map_iter; + struct qb_list_head *iter; + struct qb_list_head *tmp; + const char *p; + void *data; + unsigned int max_param_type_len; + unsigned int max_param_name_len; + unsigned int num_param_descs; + int param_count = 0; + int param_num = 0; + struct param_info *pi; + + t = time(NULL); + tm = localtime(&t); + if (!tm) { + perror("unable to get localtime"); + exit(1); + } + strftime(gendate, sizeof(gendate), "%Y-%m-%d", tm); + + if (manpage_date) { + dateptr = manpage_date; + } + if (manpage_year == LONG_MIN) { + manpage_year = tm->tm_year+1900; + } + + snprintf(manfilename, sizeof(manfilename), "%s/%s.%s", output_dir, name, man_section); + manfile = fopen(manfilename, "w+"); + if (!manfile) { + perror("unable to open output file"); + printf("%s", manfilename); + exit(1); + } + + /* Work out the length of the parameters, so we can line them up */ + max_param_type_len = 0; + max_param_name_len = 0; + num_param_descs = 0; + + qb_list_for_each(iter, ¶ms_list) { + pi = qb_list_entry(iter, struct param_info, list); + + if ((strlen(pi->paramtype) < LINE_LENGTH) && + (strlen(pi->paramtype) > max_param_type_len)) { + max_param_type_len = strlen(pi->paramtype); + } + if (strlen(pi->paramname) > max_param_name_len) { + max_param_name_len = strlen(pi->paramname); + } + if (pi->paramdesc) { + num_param_descs++; + } + param_count++; + } + + /* Off we go */ + + fprintf(manfile, ".\\\" Automatically generated man page, do not edit\n"); + fprintf(manfile, ".TH %s %s %s \"%s\" \"%s\"\n", name, man_section, dateptr, package_name, header); + + fprintf(manfile, ".SH NAME\n"); + fprintf(manfile, "%s \\- %s\n", name, brief); + + fprintf(manfile, ".SH SYNOPSIS\n"); + fprintf(manfile, ".nf\n"); + fprintf(manfile, ".B #include \n"); + fprintf(manfile, ".sp\n"); + fprintf(manfile, "\\fB%s\\fP(\n", def); + + + qb_list_for_each(iter, ¶ms_list) { + pi = qb_list_entry(iter, struct param_info, list); + + print_param(manfile, pi, max_param_type_len, 1, ++param_num < param_count?",":""); + } + + fprintf(manfile, ");\n"); + fprintf(manfile, ".fi\n"); + + if (print_params && num_param_descs) { + fprintf(manfile, ".SH PARAMS\n"); + + qb_list_for_each(iter, ¶ms_list) { + pi = qb_list_entry(iter, struct param_info, list); + fprintf(manfile, "\\fB%-*s \\fP\\fI%s\\fP\n", (int)max_param_name_len, pi->paramname, + pi->paramdesc); + fprintf(manfile, ".PP\n"); + } + } + + fprintf(manfile, ".SH DESCRIPTION\n"); + man_print_long_string(manfile, detailed); + + if (qb_map_count_get(used_structures_map)) { + fprintf(manfile, ".SH STRUCTURES\n"); + + map_iter = qb_map_iter_create(used_structures_map); + for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) { + fprintf(manfile, ".nf\n"); + fprintf(manfile, "\\fB\n"); + + print_structure(manfile, (char*)p, (char *)data); + + fprintf(manfile, "\\fP\n"); + fprintf(manfile, ".fi\n"); + } + qb_map_iter_free(map_iter); + + fprintf(manfile, ".RE\n"); + } + + if (returntext) { + fprintf(manfile, ".SH RETURN VALUE\n"); + man_print_long_string(manfile, returntext); + } + + qb_list_for_each(iter, &retval_list) { + pi = qb_list_entry(iter, struct param_info, list); + + fprintf(manfile, "\\fB%-*s \\fP\\fI%s\\fP\n", 10, pi->paramname, + pi->paramdesc); + fprintf(manfile, ".PP\n"); + } + + fprintf(manfile, ".SH SEE ALSO\n"); + fprintf(manfile, ".PP\n"); + fprintf(manfile, ".nh\n"); + fprintf(manfile, ".ad l\n"); + + param_num = 0; + map_iter = qb_map_iter_create(function_map); + for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) { + + /* Exclude us! */ + if (strcmp(data, name)) { + fprintf(manfile, "\\fI%s(%s)%s", (char *)data, man_section, + param_num < (num_functions - 1)?", ":""); + } + param_num++; + } + qb_map_iter_free(map_iter); + + fprintf(manfile, "\n"); + fprintf(manfile, ".ad\n"); + fprintf(manfile, ".hy\n"); + fprintf(manfile, ".SH \"COPYRIGHT\"\n"); + fprintf(manfile, ".PP\n"); + fprintf(manfile, "Copyright (C) 2010-%4ld Red Hat, Inc. All rights reserved.\n", manpage_year); + fclose(manfile); + + /* Free the params & retval info */ + qb_list_for_each_safe(iter, tmp, ¶ms_list) { + pi = qb_list_entry(iter, struct param_info, list); + qb_list_del(&pi->list); + free_paraminfo(pi); + } + + qb_list_for_each_safe(iter, tmp, &retval_list) { + pi = qb_list_entry(iter, struct param_info, list); + qb_list_del(&pi->list); + free_paraminfo(pi); + } + + /* Free used-structures map */ + map_iter = qb_map_iter_create(used_structures_map); + for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) { + qb_map_rm(used_structures_map, p); + free(data); + } +} + +/* Same as traverse_members, but to collect function names */ +static void collect_functions(xmlNode *cur_node, void *arg) +{ + xmlNode *this_tag; + char *kind; + char *name = NULL; + + if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) { + + kind = get_attr(cur_node, "kind"); + if (kind && strcmp(kind, "function") == 0) { + + for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) { + name = strdup((char *)this_tag->children->content); + } + } + + if (name) { + qb_map_put(function_map, name, name); + num_functions++; + } + } + } +} + +/* Same as traverse_members, but to collect enums. The behave like structures for, + but, for some reason, are in the main XML file rather than their own */ +static void collect_enums(xmlNode *cur_node, void *arg) +{ + xmlNode *this_tag; + struct struct_info *si; + char *kind; + char *refid = NULL; + char *name = NULL; + + if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) { + + kind = get_attr(cur_node, "kind"); + if (kind && strcmp(kind, "enum") == 0) { + refid = get_attr(cur_node, "id"); + + for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) { + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) { + name = strdup((char *)this_tag->children->content); + } + } + + if (name) { + si = malloc(sizeof(struct struct_info)); + if (si) { + si->kind = STRUCTINFO_ENUM; + qb_list_init(&si->params_list); + si->structname = strdup(name); + traverse_node(cur_node, "enumvalue", read_struct, si); + qb_map_put(structures_map, refid, si); + } + } + } + } +} + +static void traverse_members(xmlNode *cur_node, void *arg) +{ + xmlNode *this_tag; + + if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) { + char *kind = NULL; + char *def = NULL; + char *args = NULL; + char *name = NULL; + char *brief = NULL; + char *detailed = NULL; + char *returntext = NULL; + int type; + + kind=def=args=name=NULL; + + kind = get_attr(cur_node, "kind"); + + for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) + { + if (!this_tag->children || !this_tag->children->content) + continue; + + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "definition") == 0) + def = strdup((char *)this_tag->children->content); + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "argsstring") == 0) + args = strdup((char *)this_tag->children->content); + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) + name = strdup((char *)this_tag->children->content); + + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "briefdescription") == 0) { + brief = get_texttree(&type, this_tag, &returntext); + if (brief) { + /* + * apparently brief text contains extra trailing space and 2 \n. + * remove them. + */ + brief[strlen(brief) - 3] = '\0'; + } + } + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "detaileddescription") == 0) { + detailed = get_texttree(&type, this_tag, &returntext); + } + /* Get all the params */ + if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "param") == 0) { + char *param_type = get_child(this_tag, "type"); + char *param_name = get_child(this_tag, "declname"); + struct param_info *pi = malloc(sizeof(struct param_info)); + if (pi) { + pi->paramname = param_name; + pi->paramtype = param_type; + pi->paramdesc = NULL; + qb_list_add_tail(&pi->list, ¶ms_list); + } + } + } + + if (kind && strcmp(kind, "function") == 0) { + + /* Make sure function has a doxygen description */ + if (!detailed) { + fprintf(stderr, "No doxygen description for function '%s' - please fix this\n", name); + exit(1); + } + + if (print_man) { + print_manpage(name, def, brief, args, detailed, ¶ms_list, returntext); + } + else { + print_text(name, def, brief, args, detailed, ¶ms_list, returntext); + } + + } + + free(kind); + free(def); + free(args); + free(name); + } +} + + +static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg) +{ + xmlNode *cur_node; + + for (cur_node = parentnode->children; cur_node; cur_node = cur_node->next) { + + if (cur_node->type == XML_ELEMENT_NODE && cur_node->name + && strcmp((char*)cur_node->name, leafname)==0) { + do_members(cur_node, arg); + continue; + } + if (cur_node->type == XML_ELEMENT_NODE) { + traverse_node(cur_node, leafname, do_members, arg); + } + } +} + + +static void usage(char *name) +{ + printf("Usage:\n"); + printf(" %s [OPTIONS] \n", name); + printf("\n"); + printf(" This is a tool to generate API manpages from a doxygen-annotated header file.\n"); + printf(" First run doxygen on the file and then run this program against the main XML file\n"); + printf(" it created and the directory containing the ancilliary files. It will then\n"); + printf(" output a lot of *.3 man page files which you can then ship with your library.\n"); + printf("\n"); + printf(" You will need to invoke this program once for each .h file in your library,\n"); + printf(" using the name of the generated .xml file. This file will usually be called\n"); + printf(" something like _8h.xml, eg qbipcs_8h.xml\n"); + printf("\n"); + printf(" If you want HTML output then simpy use nroff on the generated files as you\n"); + printf(" would do with any other man page.\n"); + printf("\n"); + printf(" -a Print ASCII dump of man pages to stdout\n"); + printf(" -m Write man page files to \n"); + printf(" -P Print PARAMS section\n"); + printf(" -s Write man pages into section Use name. default \n"); + printf(" -H
Set header (default \"Programmer's Manual\"\n"); + printf(" -D Date to print at top of man pages (format not checked, default: today)\n"); + printf(" -Y Year to print at end of copyright line (default: today's year)\n"); + printf(" -o Write all man pages to (default .)\n"); + printf(" -d Directory for XML files (./xml/)\n"); + printf(" -h Print this usage text\n"); +} + +int main(int argc, char *argv[]) +{ + xmlNode *rootdoc; + xmlDocPtr doc; + int quiet=0; + int opt; + char xml_filename[PATH_MAX]; + + while ( (opt = getopt_long(argc, argv, "H:amPD:Y:s:d:o:p:f:h?", NULL, NULL)) != EOF) + { + switch(opt) + { + case 'a': + print_ascii = 1; + print_man = 0; + break; + case 'm': + print_man = 1; + print_ascii = 0; + break; + case 'P': + print_params = 1; + break; + case 's': + man_section = optarg; + break; + case 'd': + xml_dir = optarg; + break; + case 'D': + manpage_date = optarg; + break; + case 'Y': + manpage_year = strtol(optarg, NULL, 10); + /* + * Don't make too many assumptions about the year. I was on call at the + * 2000 rollover. #experience + */ + if (manpage_year == LONG_MIN || manpage_year == LONG_MAX || + manpage_year < 1900) { + fprintf(stderr, "Value passed to -Y is not a valid year number\n"); + return 1; + } + break; + case 'p': + package_name = optarg; + break; + case 'H': + header = optarg; + break; + case 'o': + output_dir = optarg; + break; + case '?': + case 'h': + usage(argv[0]); + return 0; + } + } + + if (argv[optind]) { + xml_file = argv[optind]; + } + if (!xml_file) { + usage(argv[0]); + exit(1); + } + + if (!quiet) { + fprintf(stderr, "reading xml ... "); + } + + snprintf(xml_filename, sizeof(xml_filename), "%s/%s", xml_dir, xml_file); + doc = xmlParseFile(xml_filename); + if (doc == NULL) { + fprintf(stderr, "Error: unable to read xml file %s\n", xml_filename); + exit(1); + } + + rootdoc = xmlDocGetRootElement(doc); + if (!rootdoc) { + fprintf(stderr, "Can't find \"document root\"\n"); + exit(1); + } + if (!quiet) + fprintf(stderr, "done.\n"); + + qb_list_init(¶ms_list); + qb_list_init(&retval_list); + structures_map = qb_hashtable_create(10); + function_map = qb_hashtable_create(10); + used_structures_map = qb_hashtable_create(10); + + /* Collect functions */ + traverse_node(rootdoc, "memberdef", collect_functions, NULL); + + /* Collect enums */ + traverse_node(rootdoc, "memberdef", collect_enums, NULL); + + /* print pages */ + traverse_node(rootdoc, "memberdef", traverse_members, NULL); + + return 0; +} diff --git a/libqb.spec.in b/libqb.spec.in index d8b1609..ed14a5c 100644 --- a/libqb.spec.in +++ b/libqb.spec.in @@ -1,90 +1,108 @@ %bcond_without check %bcond_without testsrpm +%bcond_without doxygen2man %global alphatag @alphatag@ %global numcomm @numcomm@ %global dirty @dirty@ Name: libqb Version: @version@ Release: 1%{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?dist} Summary: An IPC library for high performance servers Group: System Environment/Libraries License: LGPLv2+ URL: https://github.com/ClusterLabs/libqb Source0: https://fedorahosted.org/releases/q/u/quarterback/%{name}-%{version}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}}.tar.xz BuildRequires: autoconf automake libtool doxygen procps check-devel %description libqb provides high-performance, reusable features for client-server architecture, such as logging, tracing, inter-process communication (IPC), and polling. %prep %setup -q -n %{name}-%{version}%{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}} %build ./autogen.sh %configure \ %if %{with testsrpm} --enable-install-tests \ %endif --disable-static make %{?_smp_mflags} %if 0%{?with_check} %check make V=1 check %endif %install make install DESTDIR=$RPM_BUILD_ROOT find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' rm -rf $RPM_BUILD_ROOT/%{_datadir}/doc/libqb %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %doc COPYING %{_libdir}/libqb.so.* %{_mandir}/man8/qb-blackbox.8.gz %{_sbindir}/qb-blackbox %package devel Summary: Development files for %{name} Group: Development/Libraries Requires: %{name}%{?_isa} = %{version}-%{release} Requires: pkgconfig %description devel The %{name}-devel package contains libraries and header files for developing applications that use %{name}. %files devel %doc COPYING README.markdown %{_includedir}/qb/ %{_libdir}/libqb.so %{_libdir}/pkgconfig/libqb.pc %{_mandir}/man3/qb*3* %if %{with testsrpm} %package tests Summary: Test suite for %{name} Group: Development/Libraries Requires: %{name}%{?_isa} = %{version}-%{release} %files tests %doc COPYING %{_libdir}/libqb/tests/* %description tests The %{name}-tests package contains the %{name} test suite. %endif + +%if %{with doxygen2man} +%package -n doxygen2man +Summary: tool to generate man pages from Doxygen XML files +Group: Development/Libraries +Requires: %{name}%{?_isa} = %{version}-%{release} + +%files -n doxygen2man +%{_bindir}/doxygen2man +%{_mandir}/man1/doxygen2man.1* +%doc COPYING + +%description -n doxygen2man +The doxygen2man package contains the doxygen2man utility. +%endif + + %changelog * @date@ Autotools generated version - @version@-1-@numcomm@.@alphatag@.@dirty@ - Autotools generated version