From e35a763a91cef5b3af0107d341b27ed1c8e60e86 Mon Sep 17 00:00:00 2001 From: Georg Hopp Date: Wed, 13 Dec 2006 14:09:26 +0000 Subject: [PATCH] Initial repository layout --- AUTHORS | 0 ChangeLog | 51 ++ Makefile.am | 27 + NEWS | 65 ++ README | 0 autoclean.sh | 45 ++ autogen.sh | 15 + configure.ac | 168 +++++ design/scot-event-subsystem.dia | Bin 0 -> 6897 bytes event_new.c | 102 +++ include/Makefile.am | 47 ++ include/scot/cmdla.h | 117 +++ include/scot/dir_common.h | 26 + include/scot/event.h | 106 +++ include/scot/event_listener.h | 53 ++ include/scot/excenv_t.h | 52 ++ include/scot/exception.h | 133 ++++ include/scot/exception_t.h | 68 ++ include/scot/fs_watcher.h | 47 ++ include/scot/inotify.h | 31 + include/scot/list.h | 64 ++ include/scot/list_func_proto.h | 214 ++++++ include/scot/list_impl.h | 252 +++++++ include/scot/list_man.h | 382 ++++++++++ include/scot/list_mod.h | 308 ++++++++ include/scot/list_nav.h | 290 ++++++++ include/scot/list_proto.h | 101 +++ include/scot/list_type_proto.h | 64 ++ include/scot/posix/dir.h | 71 ++ include/scot/posix/memory.h | 32 + include/scot/posix/scot_types.h | 9 + include/scot/posix/thread.h | 102 +++ include/scot/queue.h | 69 ++ include/scot/queue_func_proto.h | 89 +++ include/scot/queue_impl.h | 241 ++++++ include/scot/queue_proto.h | 68 ++ include/scot/queue_type_proto.h | 61 ++ include/scot/scot_exceptions.h | 19 + include/scot/socket.h | 85 +++ include/scot/socket_in.h | 15 + include/scot/socket_un.h | 14 + include/scot/stack.h | 69 ++ include/scot/stack_func_proto.h | 89 +++ include/scot/stack_impl.h | 239 ++++++ include/scot/stack_proto.h | 68 ++ include/scot/stack_type_proto.h | 61 ++ include/scot/stream.h | 132 ++++ include/scot/stream_pool.h | 72 ++ include/scot/stream_pool_fraction.h | 53 ++ include/scot/stream_win.h | 8 + include/scot/thread_none.h | 51 ++ include/scot/win32/dir.h | 44 ++ include/scot/win32/memory.h | 40 + include/scot/win32/scot_types.h | 6 + include/scot/win32/thread.h | 79 ++ include/scot_common.h | 98 +++ m4/Makefile.am | 1 + m4/ax_create_stdint_h.m4 | 685 +++++++++++++++++ src/Makefile.am | 61 ++ src/cmdla.c | 408 ++++++++++ src/event.c | 348 +++++++++ src/event_listener.c | 157 ++++ src/event_manager.c | 157 ++++ src/event_subsys/event-subsys.txt | 79 ++ src/event_subsys/scot_dispatch_manager.c | 0 src/event_subsys/scot_dispatcher.c | 0 src/event_subsys/scot_event_sink.c | 3 + src/event_subsys/scot_event_typ.c | 0 src/event_subsys/scot_event_typ_GTK | 0 src/event_subsys/scot_event_typ_OPENGL | 0 src/event_subsys/scot_event_typ_SDL | 0 src/event_subsys/scot_event_typ_manager.c | 0 src/event_subsys/scot_event_typ_mswin.c | 0 src/event_subsys/scot_event_typ_xwin.c | 0 src/exception.c | 868 ++++++++++++++++++++++ src/fs_watcher.c | 416 +++++++++++ src/list.c | 340 +++++++++ src/po/LINGUAS | 1 + src/po/Makevars | 41 + src/po/POTFILES.in | 4 + src/po/de.gmo | Bin 0 -> 3426 bytes src/po/de.po | 171 +++++ src/po/scot.pot | 165 ++++ src/po/stamp-po | 1 + src/posix/dir.c | 136 ++++ src/posix/spf_thread_impl.c | 133 ++++ src/posix/stream_ctl.c | 44 ++ src/posix/thread.c | 169 +++++ src/queue.c | 46 ++ src/scot_common.c | 33 + src/scot_exceptions.c | 60 ++ src/socket.c | 184 +++++ src/socket_in.c | 200 +++++ src/socket_un.c | 149 ++++ src/stack.c | 47 ++ src/stream.c | 189 +++++ src/stream_pool_base.c | 328 ++++++++ src/stream_pool_fraction.c | 154 ++++ src/stream_pool_management.c | 245 ++++++ src/stream_win.c | 16 + src/win32/dir.c | 135 ++++ src/win32/memory.c | 42 ++ src/win32/spf_thread_impl.c | 184 +++++ src/win32/stream_ctl.c | 44 ++ src/win32/thread.c | 38 + src/window-support.txt | 32 + test/Makefile.am | 55 ++ test/exc_test.c | 137 ++++ test/fs_events.c | 125 ++++ test/list_test.c | 94 +++ test/multiserv.c | 172 +++++ test/po/LINGUAS | 1 + test/po/Makevars | 41 + test/po/POTFILES.in | 1 + test/po/de.gmo | Bin 0 -> 3633 bytes test/po/de.po | 132 ++++ test/po/scot_test.pot | 109 +++ test/po/stamp-po | 1 + test/scot_test.c | 176 +++++ test/stream_pool_test.c | 61 ++ test/tcp_socket_clie.c | 71 ++ test/tcp_socket_serv.c | 122 +++ test/unx_socket_clie.c | 67 ++ test/unx_socket_serv.c | 116 +++ 124 files changed, 12807 insertions(+) create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100755 autoclean.sh create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 design/scot-event-subsystem.dia create mode 100644 event_new.c create mode 100644 include/Makefile.am create mode 100644 include/scot/cmdla.h create mode 100644 include/scot/dir_common.h create mode 100644 include/scot/event.h create mode 100644 include/scot/event_listener.h create mode 100644 include/scot/excenv_t.h create mode 100644 include/scot/exception.h create mode 100644 include/scot/exception_t.h create mode 100644 include/scot/fs_watcher.h create mode 100644 include/scot/inotify.h create mode 100644 include/scot/list.h create mode 100644 include/scot/list_func_proto.h create mode 100644 include/scot/list_impl.h create mode 100644 include/scot/list_man.h create mode 100644 include/scot/list_mod.h create mode 100644 include/scot/list_nav.h create mode 100644 include/scot/list_proto.h create mode 100644 include/scot/list_type_proto.h create mode 100644 include/scot/posix/dir.h create mode 100644 include/scot/posix/memory.h create mode 100644 include/scot/posix/scot_types.h create mode 100644 include/scot/posix/thread.h create mode 100644 include/scot/queue.h create mode 100644 include/scot/queue_func_proto.h create mode 100644 include/scot/queue_impl.h create mode 100644 include/scot/queue_proto.h create mode 100644 include/scot/queue_type_proto.h create mode 100644 include/scot/scot_exceptions.h create mode 100644 include/scot/socket.h create mode 100644 include/scot/socket_in.h create mode 100644 include/scot/socket_un.h create mode 100644 include/scot/stack.h create mode 100644 include/scot/stack_func_proto.h create mode 100644 include/scot/stack_impl.h create mode 100644 include/scot/stack_proto.h create mode 100644 include/scot/stack_type_proto.h create mode 100644 include/scot/stream.h create mode 100644 include/scot/stream_pool.h create mode 100644 include/scot/stream_pool_fraction.h create mode 100644 include/scot/stream_win.h create mode 100644 include/scot/thread_none.h create mode 100644 include/scot/win32/dir.h create mode 100644 include/scot/win32/memory.h create mode 100644 include/scot/win32/scot_types.h create mode 100644 include/scot/win32/thread.h create mode 100644 include/scot_common.h create mode 100644 m4/Makefile.am create mode 100644 m4/ax_create_stdint_h.m4 create mode 100644 src/Makefile.am create mode 100644 src/cmdla.c create mode 100644 src/event.c create mode 100644 src/event_listener.c create mode 100644 src/event_manager.c create mode 100644 src/event_subsys/event-subsys.txt create mode 100644 src/event_subsys/scot_dispatch_manager.c create mode 100644 src/event_subsys/scot_dispatcher.c create mode 100644 src/event_subsys/scot_event_sink.c create mode 100644 src/event_subsys/scot_event_typ.c create mode 100644 src/event_subsys/scot_event_typ_GTK create mode 100644 src/event_subsys/scot_event_typ_OPENGL create mode 100644 src/event_subsys/scot_event_typ_SDL create mode 100644 src/event_subsys/scot_event_typ_manager.c create mode 100644 src/event_subsys/scot_event_typ_mswin.c create mode 100644 src/event_subsys/scot_event_typ_xwin.c create mode 100644 src/exception.c create mode 100644 src/fs_watcher.c create mode 100644 src/list.c create mode 100644 src/po/LINGUAS create mode 100644 src/po/Makevars create mode 100644 src/po/POTFILES.in create mode 100644 src/po/de.gmo create mode 100644 src/po/de.po create mode 100644 src/po/scot.pot create mode 100644 src/po/stamp-po create mode 100644 src/posix/dir.c create mode 100644 src/posix/spf_thread_impl.c create mode 100644 src/posix/stream_ctl.c create mode 100644 src/posix/thread.c create mode 100644 src/queue.c create mode 100644 src/scot_common.c create mode 100644 src/scot_exceptions.c create mode 100644 src/socket.c create mode 100644 src/socket_in.c create mode 100644 src/socket_un.c create mode 100644 src/stack.c create mode 100644 src/stream.c create mode 100644 src/stream_pool_base.c create mode 100644 src/stream_pool_fraction.c create mode 100644 src/stream_pool_management.c create mode 100644 src/stream_win.c create mode 100644 src/win32/dir.c create mode 100644 src/win32/memory.c create mode 100644 src/win32/spf_thread_impl.c create mode 100644 src/win32/stream_ctl.c create mode 100644 src/win32/thread.c create mode 100644 src/window-support.txt create mode 100644 test/Makefile.am create mode 100644 test/exc_test.c create mode 100644 test/fs_events.c create mode 100644 test/list_test.c create mode 100644 test/multiserv.c create mode 100644 test/po/LINGUAS create mode 100644 test/po/Makevars create mode 100644 test/po/POTFILES.in create mode 100644 test/po/de.gmo create mode 100644 test/po/de.po create mode 100644 test/po/scot_test.pot create mode 100644 test/po/stamp-po create mode 100644 test/scot_test.c create mode 100644 test/stream_pool_test.c create mode 100644 test/tcp_socket_clie.c create mode 100644 test/tcp_socket_serv.c create mode 100644 test/unx_socket_clie.c create mode 100644 test/unx_socket_serv.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ad68806 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,51 @@ +2006-08-17 Georg Steffers [GST] + + * src/stream_pool.c src/posix/stream_pool.c src/win32/stream_pool.c + include/stream_pool.h include/posix/stream_pool.h + include/win32/stream_pool.h src/Makefile.am include/Makefile.am + and various others (this is a major change): + Sorry, this is in german and much to declarative...this was a + thoughts-list that is now realized completely. I will rewrite it when + i find the time. + + OK, ich fang mal wieder damit an meine Gedanken aufzunehmen. + Das Problem das ich jetzt bekomme ist, das es streams unter + windows nicht gibt...bzw. verschieden arten von streams unterschiedlich + gehandhabt werden müssen. Hinzu kommt, das WaitForMultipleObject + nur max. 64 Objekte werwalten kann. Will man mehr verwalten muß man + das in mehreren streads tun. + + Daher muß ein stream_pool, anders als andere event listener nicht + nur aus einem thread bestehen sondern aus einem pro 64 gleicher + streams und einem Kontoll-Thread. (Hmm, für viele andere event listener + gilt das gleiche, wenn ich unter Windows mehr als 64 Objekte + überwachen will. z.B. der fs_watcher. Gerade beim fs_watcher ist + es aber nun so, daß der unter Windows eh komplett anders + geschrieben werden muß.) + + Startet man einen stream_pool_listener, so wird der Kontroll Thread + gestartet, welcher seinerseits alle weiteren threads startet. + + Added man einen stream, so muß zunächst nachgesehen werden ob es + bereits einen <> für diesen stream_type gibt der außerdem + noch nicht 64 streams enthält, falls es so einen stream pool gibt + kann man den stream zu diesem hinzufügen, anderenfalls muß ein + neuer stream pool angelegt werden. + Nachdem der stream geadded wurde muß nur der stream pool, zu dem + der stream hinzugefügt wurde neu gestartet werden. + + Will man einen stream aus einem stream pool entfernen, so muß dieser + zunächst in allen stream pools gesucht werden, dann muß der thread + zu dem stream pool in dem sich der stream befindet gestoppt werden + woraufhin der stream entfernt werden kann. Danach muß der stream pool + thread wieder gestartet werden. + + Für UNIX brauche ich nur einen einzige thread methode für alle stream + pool typen, nämlich die, welche select nutzt. + Für Windows muß ich je nach dem welcher Art von streams ein stream pool + enthält unterschiedliche thread methoden nutzen: + - select für sockets + - WaitForMultipleObject für andere (Wie stell ich dabei fest was genau + mit dem Objekt passiert ist? OK, für pipes ist das Einfach, da es + eh ein Wege Kommunikation ist.) + - und möglicherweise noch andere. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b80fdcf --- /dev/null +++ b/Makefile.am @@ -0,0 +1,27 @@ +DEFS = -DHAS_CONFIG @DEFS@ + +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = config/config.rpath config/mkinstalldirs \ + include/scot_common.h \ + include/scot/stream_pool_fraction.h \ + include/scot/win32/thread.h \ + include/scot/win32/memory.h \ + include/scot/win32/dir.h \ + include/scot/win32/scot_types.h \ + include/scot/posix/thread.h \ + include/scot/posix/memory.h \ + include/scot/posix/dir.h \ + include/scot/posix/scot_types.h \ + src/win32/thread.c \ + src/win32/dir.c \ + src/win32/memory.c \ + src/win32/spf_thread_impl.c \ + src/win32/stream_ctl.c \ + src/posix/thread.c \ + src/posix/dir.c \ + src/posix/spf_thread_impl.c \ + src/posix/stream_ctl.c \ + design/scot-event-subsystem.dia + +SUBDIRS = m4 include src src/po test test/po diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..6145f9b --- /dev/null +++ b/NEWS @@ -0,0 +1,65 @@ +2006-08-07 +Es gibt noch einen bug im exception handling. Ich kann im Moment noch +nicht genau sagen was passiert, evtl. ist es auch nur ein bug im testprogramm. +Jedenfalls bekomme ich eine Fehlermeldung von wegen THROW kann nur in +einem TRY-CATCH block aufgerufen werden wenn der stream_pool thread mit +exit beendet. +Ich weiß zur Zeit noch nicht ob dieser Fehler im Hauptthread oder im +stream_pool thread passiert, ich vermute aber im Hauptthread, da der +CATCH-Bereich des stream_pool threads ausgeführt wird. + +Möglicherweise ist das aber auch ein genereller Bug im exception system, +der auftritt sobald man exit () in einem catch bereich nutzt. (Was eigentlich +auch nicht wirklich ne gute Idee ist (meistens jedenfalls)...trotzdem sollte +es möglich sein. im zweifelsfall durch eine eigene Funktion exc_exit oder so. + +OK, der richtige excenv stack sollte selectiert werden...daran liegts nicht. +Trotzdem scheint es kein excenv mehr in diesem stack zu geben nachdem ich +in den CATCH Block von scot_stream_pool_main_loop gekommen bin. + +AHA, die event_listener_fini methode throwed evtl. eine exception, und zwar +ganau dann wenn sie von ihrem eigenen main loop aufgerufen wurde. Was +hier passierte...allerdings schon aus dem CATCH Teil der main_loop +methode, wodurch kein excenv mehr da war. +Nachdem ich einen TRY-CATCH Block um den betreffenden Teil in +event_listener_fini gemacht habe war das Problem behoben. + +-------------------- + +Der fs_watcher code funktioniert so nicht. Man kann von dem notify descriptor +leider nicht genau so lesen wie von einem file descriptor. +Ich werde also code in scot stream integrieren, der die besonderheiten +von inotify descriptoren berücksichtigt. +Schließlich soll scot stream genau für sowas eine abstraction liefern. + +-------------------- + +2006-08-23 + +OK, fs-watcher mit inotify functioniert gröstenteils. Das heißt auf einer +single cpu maschine läuft es problemlos, auf meiner alten SMP Maschine kommt +der thread der die filesystem events überwacht ab und zu irgendwie in einen +busy loop oder so was, jedenfalls reagiert er nicht mehr und zieht nahezu +100% CPU time auf einer CPU. + +--------------------- + +Und hier jetzt der Knüller des Jahrhunderts. + ### select von winsock2 ist kein cancellation point ### + ### und (noch besser) kann nicht unterbrochen werden ### +Unerwartete und nervig. Seit Winsock2 ist select nicht mehr unterbrechbar. Das +macht es quasi unbrauchbar für threads wenn diese von Zeit zu Zeit unterbrochen +werden sollen (z.B. damit der select in dem thread neu hinzugekommene + Verbindungen mit überwacht), es sei denn man setzt die threads in einen +asynchronous mode. +Außerdem nuss ich multiple threads für die socket verwaltung verwenden, da +windows select per default nur 64 sockets verwalten kann, was für einen server +geradezu lächerlich wenig ist, also benutze ich pro 64 sockets einen thread. +Im Moment suche ich nach einem Weg wie ich das Problem umgehen kann. Eine gute +Möglichkeit scheint zu sein, den socket an dem ich auf connections warte auch +in jedem select zu überwachen, dann sollte der select zurückkommen sobald eine +neue Verbindung hergestellt wird. +Dann muß nur noch sichergestellt sein das sich nur ein thread um die neu +ankommende Verbindung kümmert und das alle anderen solange warten bis der neue +socket in die zuständige socketliste eingetragen wurde. Das sollte aber mit +einer critical section kein Problem sein. diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/autoclean.sh b/autoclean.sh new file mode 100755 index 0000000..d12ea56 --- /dev/null +++ b/autoclean.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# Script for cleaning all autogenerated files. + +test ! -f Makefile || make distclean + +# Brought in by autopoint. +rm -f ABOUT-NLS +mv m4/ax_create_stdint_h.m4 . +rm -f m4/*.m4 +mv ax_create_stdint_h.m4 m4 +rm -f src/po/Makefile.in.in +rm -f src/po/remove-potcdate.sin +rm -f test/po/Makefile.in.in +rm -f test/po/remove-potcdate.sin + +# Generated by aclocal. +rm -f aclocal.m4 + +# Generated by autoconf. +rm -f configure + +# Generated by autoheader +rm -f config.h.in + +# Generated or brought in by automake. +rm -f Makefile.in +rm -f m4/Makefile.in +rm -f src/Makefile.in +rm -f test/Makefile.in +rm -f include/Makefile.in +rm -f INSTALL +rm -f COPYING + +# Generated by all in config +rm -rf config + +rm -rf autom4te.cache + +# Generated by testruns +find . -name exc_test.fil -exec rm -f {} \; + +# Generated by doxygen +rm -f doxy.wrn +rm -rf doc/full/* +rm -rf doc/usage/* diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..ac9eb66 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Script for regenerating all autogenerated files. + +autopoint -f # was: gettextize -f -c +cp po/Makefile.in.in src/po +cp po/remove-potcdate.sin src/po +cp po/Makefile.in.in test/po +cp po/remove-potcdate.sin test/po +rm -fR po + +aclocal -I m4 +autoconf +autoheader +libtoolize -c -f +automake -a -c diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..6cb4712 --- /dev/null +++ b/configure.ac @@ -0,0 +1,168 @@ +AC_PREREQ(2.59) +AC_INIT(scot, 0.0.3, BUG-REPORT-ADDRESS) +AC_CONFIG_AUX_DIR([config]) +AC_CONFIG_SRCDIR([src/cmdla.c]) +AC_CONFIG_HEADER([config.h]) +AM_INIT_AUTOMAKE + +AC_CANONICAL_HOST + +# Checks for programs. +AC_PROG_CC +AC_PROG_MAKE_SET +AC_LIBTOOL_WIN32_DLL +AC_PROG_LIBTOOL + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([getopt.h libintl.h locale.h stdlib.h string.h unistd.h wchar.h]) +AX_CREATE_STDINT_H([include/scot/scot_int.h]) + + +# Checks for libraries. +AM_GNU_GETTEXT([external]) +AC_MSG_CHECKING([intl]) +AC_MSG_RESULT([$LIBINTL]) +AM_GNU_GETTEXT_VERSION(0.13.1) +localedir=`eval echo $datadir/locale` +AC_DEFINE_UNQUOTED(LOCALEDIR, "$localedir", [Name of gettext locale directory]) +THREAD_LIB= +THREAD_CFLAGS= +AC_MSG_CHECKING([for Win32]) +case "$host" in + *-*-mingw*) + win32="yes, use windows threads" + pthread="pthreadGC2" + SOCK_LIB="-lws2_32" + ;; + *) + win32="no" + pthread="pthread" + SOCK_LIB="" + ;; +esac +AC_MSG_RESULT([$win32]) + +AC_CHECK_LIB($pthread,pthread_create, + THREAD_LIB=-l$pthread + THREAD_CFLAGS="-DUSE_PTHREAD -DREENTRANT" + thread="PTHREAD", + THREAD_CFLAGS="-DUSE_WTHREAD" + thread="WTHREAD",) + +AC_SUBST(SOCK_LIB) +AC_SUBST(THREAD_LIB) +AC_SUBST(THREAD_CFLAGS) + +AM_CONDITIONAL(USE_THREADS, test "x$thread" != "x") +AM_CONDITIONAL(PTHREAD, test "x$thread" == "xPTHREAD") +AM_CONDITIONAL(WTHREAD, test "x$thread" == "xWTHREAD") +AM_CONDITIONAL(WIN32, test "x$win32" != "xno") + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST + +# Checks for library functions. +AC_CHECK_FUNCS([memset setlocale]) + +dnl We need to check if the right inotify version is accessible +use_inotify="yes" +AC_MSG_CHECKING([whether inotify is to be used for filemonitoring]) + +if test "x$win32" == "xno" +then + AC_ARG_ENABLE(inotify, + [ --disable-inotify disable inotify in the ecore_file module], + [ + if test "$enableval" == "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no - but we need it]) + use_inotify="no" + fi + ], [ + AC_MSG_RESULT([yes]) + ] + ) + + dnl It's hard to find a good test on how to check the correct + dnl inotify version. They changed the headers a lot. + dnl in kernel 2.6.13 __NR_inotify_init was added to the defined syscalls + dnl in asm/unistd.h and IN_MOVE_SELF was added to linux/inotify.h + dnl so with this check you need a very new kernel and kernel-headers! + dnl On my gentoo, /usr/include/asm and /usr/include/linux are no symlinks + dnl into the current kernel tree....so i also try to find the includes + dnl under /usr/src/linux or under /usr/include/linux-`uname -r` + linux_rev=`uname -r` + INOTIFY_INCLUDES= + AC_MSG_CHECKING([for sufficient inotify includes]) + if test "x$use_inotify" = "xyes"; then + AC_TRY_COMPILE( + [ + #include + #include + ], + [ int a = __NR_inotify_init; int b = IN_MOVE_SELF; ], + [ + AC_DEFINE(HAVE_INOTIFY, 1, [ File monitoring with Inotify ]) + an_inc=/usr/include + ], + [ + AC_TRY_COMPILE( + [ + #include "/usr/src/linux/include/asm/unistd.h" + #include "/usr/src/linux/include/linux/inotify.h" + ], + [ int a = __NR_inotify_init; int b = IN_MOVE_SELF; ], + [ + AC_DEFINE(HAVE_INOTIFY, 1, [ File monitoring with Inotify ]) + AC_DEFINE(IN_KERNEL, 1, [ blablabla ]) + INOTIFY_INCLUDES=-I/usr/src/linux/include + an_inc=/usr/src/linux/include + ], + [ + AC_TRY_COMPILE( + [ + #include + #include + ], + [ int a = __NR_inotify_init; int b = IN_MOVE_SELF; ], + [ + AC_DEFINE(HAVE_INOTIFY, 1, [ File monitoring with Inotify ]) + AC_DEFINE(IN_KERNEL_UNAME, 1, [ blablabla ]) + INOTIFY_INCLUDES=-I/usr/src/linux-$linux_rev/include + an_inc=/usr/src/linux-$linux_rev/include + ], + [ + use_inotify="no" + an_inc="not found" + ] + ) + ] + ) + ] + ) + AC_MSG_RESULT([$an_inc]) + test "x$an_inc" == "xnot found" && exit + else + AC_MSG_RESULT(["Not used"]) + fi + + AC_SUBST(INOTIFY_INCLUDES) +else + use_inotify="no" + AC_MSG_RESULT([no]) +fi + +AM_CONDITIONAL(USE_INOTIFY, test "x$use_inotify" != "xno") + + +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([src/Makefile]) +AC_CONFIG_FILES([include/Makefile]) +AC_CONFIG_FILES([test/Makefile]) +AC_CONFIG_FILES([m4/Makefile]) +AC_CONFIG_FILES([src/po/Makefile.in]) +AC_CONFIG_FILES([test/po/Makefile.in]) + +AC_OUTPUT diff --git a/design/scot-event-subsystem.dia b/design/scot-event-subsystem.dia new file mode 100644 index 0000000000000000000000000000000000000000..ccecbb4721a14350826ab11bfdae1583cf74da1b GIT binary patch literal 6897 zcmZvfWl$UpueRG#tWeyFyOrXw*g|o);;yB*F78s?-Cc?;zBrTx3KT2u?kuoH7w0_h zbLM>W&6$(yPbM?Dll)00R556;{xzgiqw(Y&NXt5mMfY^Dwxg^M=heC7M-SWNEPAk# zea1?BURreWAKP*U1)54q1ltAc((Q*c)=uvRjKpI{by|jJaUjrbd9R_;>=KE2)9--X zPvCY99%#g#Ye2}f#d?4Ih7JK$p91k=l-%hMi#^>|5MOK~Ot1q07@-{3y_W*ILid2; z0YiUxm%MTqhe?kg;*3Inl|j5CdWv?nP^$aUvtIWt$F(vCM-QdI#2J#6IBDi2qyWW+ zG$KX0DwkQes*atp@5+j@j(_Tew%5*g1Jt5BIN?x#d%PV}eR8n9cf-YjF)4U);?Aj) zqKgceOYpc(fVX;I=?^|67=d-lm*=TTcVMTQ>R-6n6qQX2&0z0cce2 zRy%73xem;2ZxV{_L(isRlegl@IK?I?mu2vGs+$j<)8vk>#%+V%Tich=?;~E_(K7;Q zGm^t2wk>MnsU^n9F;Xq43gvwg2@Mucu1T_4szwDERWGGsDK}m^X4zmB5KA1O3Lo0( zoNi1N5~iMww5g)ntG)Fo1iZKvs$+V&?epU0le!ehrD&D>F3g&r++A^AH`McpRmU25 zpI26~5iT#F_oHzhaSq%Ndj=N_!2^Xqd;mWC3{549)h3@>ec>9+Zd&uhpU`wQ&m@Fb zdQD&7Kr$-U@1OnMO_ZLe^5IG1Y$3vgPE;?%Pji)Clw_^_O9A}`ZcUe@+9`omHXWxM zs)4?+F6RJ+S>8WZH&ap5?3w}($J@fKw>~$*r3Gd0@A@9}>L^i?{?u%7^AC`lu_Jr~ ztN~SOj4A$!x(M3HeGDxWUMKyAjfL!!O$eslA?6Rj!@_JQdYO*xY}6S=0oY-YWutN zt!&LPO0bIvw*PuPMo%kR#C_tc_9s8;N*uq*e@Vw;!01e3V|TcK;V zlpA9^YkyGZyg3ZNdm@(^w?8xcbCj!sy=$=)B>?lV-H=pD-VJ;er7 ztu{_yJ*Hz55wl}Pc#noVY#Z?9e~$`yt)VsUN+N%P&c@)@V7c1wz`~au7b;-fy%OO= z4h;kOU3nE1-*H9!lr}O5{Jr-h&J-n_(%+|w9@WzA1u)T)Z(x9w2W}jTzPxRHkjRPN z7+l#X8{8m_pbhOtx#oV*J6M9#FqR(jFT#8V7OyXDz9W;_bo~6Xg2eMu`E+M8H#t3q zn=t^l!huZ&7U0pg50(Ae#lz2hQRRO@>~q?2zaZ=*Rz@Euk+z+5Z1i}?Iw$;Qe6m`Qy#*!7um=E$qTfB!K26**#MKctdh*^$b$K*iY) zoOux!U@_rnD(FRQ8`3k!D1Owdk2PDjd>Fwx&#(L`PlQU?tAc38E^Q50(23pT@iqaD zieHw45OvplGlniGfkKDK*dXvRVcw`~0San#E zD^&eu3dWE1`Y2NsEU&nIWGrMzXqykkL|!)M{qHw#YV$uZkTc!u!Ul4);O~8>%TXc^ z)g}&BsoNzYlf=_QEJ>+1uI(RMwVv48)2Nf*e=T}ig~a|CngDfgPj&8>llmrp8tub}N-%M?kimS-r?{7j{bx zcbkd>%9aA+kucO9>3AuvNGD~jP!}TAPkD|NX>m$Qk^I^}+Bfu;VY!UFQBSG>F5@=X zI?bwxjN{Vf_g>DJVVhRE8OBuQoZYj!CD{p##3AgnwMyNtnB`lLy0~ZBeW+|XI@J=K z+{b~ZR-YsItH-~zY{Youp?#Z4vle#2y3`yX<4Rs(I&mMUb11k!b_PD|+I-Yy5QbP5 zpkxYXV*I-w%*T0`u7@W^ET-ccctY{ITZ#P$Y+}O`p z#;zNAGjXxy1Gc%YY?w;*W! z@#4ud!znyop~CvF?d6sjD}QHcBT&`KE%mZO8I)o(*}tcsK=j^LpNo7UI0vZ8co%|hs7_mgVzM#fpvQpaA`LhE2K1T&*DK>2snoQRS!=G7=aTnjLtm8Bhc z3zr(4D9b0C0TGHdYlsOh8jfpqwSU1qITsqDraumc@;_64B6=jyV&E#KkLXJ50&gm zEUN%`D{hplP@o&Nz@mGz%iagKEpt*$7#t;^?R^`Yt$J4E5EBEa^VZul^yP+soB7+b0 zCx&YSfj=Z1ALvozr0DsIDinV!M2{b$!}uwWgeK8WRfAbNJKun{^zcQ!pb(-Zi7t z_AZO?{ZOQSU5PfIA_AKJ5;Jwj-7@P zzE5h2{cS<}($OE(XuLjtd_It9<9n=>!}9Q2^jexdkMvYB84(`YH+PuxS(9n5 zbZgiI<->F$!gd*^8B3OI(afVkAkb+7uW#F(Ubq9Se_^s!>$+iAJN!)b#eUKu(fI)r z-*MTyxn9MOdrL+RTORR90?_e|PhA@C=%lTljcOlK?um1THePA>tl?#^}S$t`IU`?#(ckxqf*=031rA{88{Z# zzBpP=WAQvZJ6j~m^)u|QBuJAtm)(XA1sM9AcsI&nvga zWENyd!$W-%l_UriS4&Pyv7rc^N5p*W&~s(lh~H%wdDn1$Ktgs|5fvv4Px*U|Igpt> zqt4QDM48hlHeLE-tf-DWfs?-?KmtY?_|eDHyiH$Bu+{6M=S-P$`PA$AAB4!2o8Azm~UShqms^Z`hylUQHbYPz*E!qU) zq|u#H-S=!tGU8TDmrE|oV#B;j!)Ogb@(%N**Eb|RwPmdN+9rRy$DVgw(pRXM%s?8) z(tLf~*E(~*F=4DyO-~LpXce)TSGea5R~+5gjsRd>8r zaxB#|tzdMsjz$mRtZnVZ{q=mIV?TW^o93EIOQ1jEGhgQ{g|+aieP35hXv@=tC39a| ze#p*|JC4ph$E*Xa<|~Iyt|0f``cDhqDO|eu`lb8g*O zMDe~Ot{WNMS~K#pA~Ww6cJ<&%jwWi%jK`^N9g*j#R#y*JMO@Aod-)7)r8sPx=T;dq z4_Lr(Ka^^T?C3VH*)g8YX~Hu_`nC>I4qvzDwf}9VDAZ#9ttS`sD#6OD19Re%hPeFT z&NK!I-dwQ;6KF+l6%MlN`VAD+TiNw)p=&L`US}i51{6$Sz@-@?T%a|rsb|IluCKm| z6uq3jSMGftS8rQS#6l`WV*ASKVL$B*&UG@*@o8N#*SJrkf4W}u8s4H+g2x}|3YW(! zZZ~9CIJJ1SSji-~gA7Go_4~D`>@5Rj_RM+$E(Bu1+TU`FTC1J+{W@kwbDPZ}#Ey-{ zfQh+?mtPZNX@YTkTmA=Sm2vjm@)GWOSeHn)={eXhBk&i7UVsY(*+Vnc5*ZvP1HxU+ z>URub{tY{$&V&>!G>?A)-H}#C&)Gk6)@Zsy1iSuh?&KL3anatVSo3M@TxsO3){XzO zSm(PC5wyB0<(XJlAV7o~A^r5k1Tj)dbA`PA7-$LXq?;G;F>4ze;90N@^Wx(>TFOQC zArmee@$%KEil=kF@!s-CW69;k?{=@eA-CsbVJ(Cn>JOSR7Ln`tJ9KyV2Rdl_A##^` zS*!5|?t6XW8TN$Ikz&sOV(iJ5&DWC>iu&(O8^wY7vL+&unpnVFC!c63&rz2lWn&ya z#Bf)N(^Uf=+5kfDe!Ky?=ATv0_*VNZiaxDV%B#P86V-7Mtq;Nv{uev9?UX-e)~|_w zOtM}Fx1y|*VlHV&dJvWN;bSe~E243#&{SEcDhDc=Zye0g^aR~PAx^7oKdEs{{Z{1x zaerOBT<*-t|I+=nczJA#3Ze=A7c#EhB$lrQq;a|Ui#Ut*9Q?V}Ml!jpalZ7i39*$f+kOApb6Syu+UIVkznnS3o zJ)!y-IlPKk?fOlX@#i^=A3m?OI0}!EHM4Qw0?CS?(Db52NIpfhGXb#`J0lH zJhEZ>1YOqDM+cwT)98NTQo6Rb?0+S!SlCHH71g>1@A_qC1F{&Yy1&zo+??62)@hNa z%L%Z)tsmMqxQ6pNYShc?oZDJBh39VR4n%x4@x>V;kfti}`!Q!$OQ1vP>V6-_rjWlG z##V<7EUX8k?mmiRGynZ?74+sRsP;NYOyiBu=RZgxlsH#Gg{W_V)Q7LdvC;nj&=Hax z)#v+HKA&|>!}o;$AiZJv5C0}mgWvo!B^##RA0#LmGSKwJ=krb1bu|^F3JW|6Tb1^^zzWboIIvyAU-D z*-Ezu2}qqt`yc-8beq+A!E_j!hzz8TRr`Z@A+KlMp+3p`EM_E`|hpX}UYDw&3Ed`-@1J|0*%NmTawp-xI!V zoaBem9-Z|r3I>guN>I_Kt7lUOUeguvvzS5K4lIYQ<{_~Jg^~^n@Mh(#?Oumsi8m;R z@64G|iwzmX?d1LTN~5?u_~s+F;HsN1w_^{%FDTl6UVj^~*kvt~Zaorw&go%561k-^^?=Kn`-Qlb`*OH7t7T#s+YAuA2^ z8esF>5N(ATTc1{H_`(t)@yfBhA*%{6)=PU3!QPImXdn2M&x_b7hnjD+sg?z!YR}X+=E>X=BQqP`=^-(T6(H7o&-ttKCqQ}2^VG3h6G`^1B25-QJeE7*F8_J} zDSV)xWjy&irPhgARSAv5p*|cIih&at4}iJ<*jDCs3ZEZf^Y2tp9LI$Xk$X${NV4OW zonX!n(+QF$+3(U>fx}`9M<&doeU5B_iXfE!(6^cS6<2*GHj^wpmHGGDK=>Z3J_F3O z3Tq86cI;W}F#U}>J1L86%BobLpzqqTfO$-8NxhoC*6PR|Y$g9Uv6FxBNhVv!GJsKS zPRTO8uXN5fox(@6BD?H!=C!(Wg*KmJh*Sf8T(jo*1#xUq7(;4(7+!jT8@eL4g02Mm zsHmbY|9Q@7YQ2bkpGDb#1AfPXyuIc~W2yAR`(y29+;{r!9ke^S-i z^=WJOl?Ll&))4otEk#>&Pn^f623i}^RF|HF=0UaiqAi~oH24JIfQCy?LRF|WBx-+x zTPZI~U#PdMP4m1qdTMDjbjy*ja6+$fRNJMR^0(%R6fQ@B9WdrMZ^j|rI73ZEyTIhM zAtsj6V;$q1EglavQ zTKs(aT25%%AU?FVl!gaP#mAe=?CX6H}L;2 z=jm>6xh~m~@BO8aG6OQjBN!JjZX1Bn8NuBKtsu3s{K>h^uWBK1s9J3R%5mxJ%zqHX zM96aHy4tREzG$m*WPW!s7Tzm{6PFb0wOF5)t}@Nnon;oY3RfQvPVKa0v&N?9NRxtz zx0)sR-Lc~ygfIRUZYDCEr($)O^I=cpqf&INsU25MtU6BDtPkPE7OuyMSF|y2bzwjp!UFqcpt;3~Q1>$WZKboaE;@@?|eRy_An)qA@dN JO|aLm{tLDFi8TNK literal 0 HcmV?d00001 diff --git a/event_new.c b/event_new.c new file mode 100644 index 0000000..9035b2e --- /dev/null +++ b/event_new.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +#define GEN_LOCAL +#include +#include +#undef GEN_LOCAL + +struct scot_event_manager; + +struct scot_event_source +{ + struct scot_event_manager * manager; + unsigned int group; + THREAD_T sthr; + scot_event_source_entry_fptr event_source_thread_entry; + + int (*event_done_cb) (struct scot_event *); +}; + +/* + * das ist cool, mit folgender Struktur und der Tatsache das die registrierten + * callbacks in einer Instanz dieser Struktur und dann in einer liste + * gespeichert werden bedeutet, das ich mehrere callback zu einem event + * registrieren kann. Im moment würden diese dann in undefinierter + * Reihenfolge aufgerufen, sobald ein event auftaucht. + * Sobald der tree code fertig ist sollte man die callbacks in einem + * balanced b-tree speichern bei dem der key die event-nummer ist. + * Das würde die zugriffe auf die callbacks nochmal erheblich beschleunigen. + * Vorher könnte man sich überlegen callbacks sortiert einzufügen. + * (Warscheinlich würde das auch schon reichen, da callbacks normalerweise + * nur einmal zu beginn des Programms registriert werden und daher die + * insert-zeit nicht so relevant ist.) + */ +struct scot_event_cb +{ + uint32_t event; + int (*cb) (struct scot_event *); /* wenn ein cb 0 zurückliefert werden + keine weiteren registrierten + callbacks zu diesem event ausgeführt, + sonst so lange bis keine weiteren + events vorliegen. */ +}; +typedef struct scot_event_cb scot_event_cb_t; +GEN_LIST (scot_event_cb_t); + +struct scot_event_sink +{ + struct scot_event_manager * manager; + unsigned int group; + uint32_t mask; + + list_scot_event_cb_t_node_t * cb_mappings; +}; + + +typedef struct scot_event scot_event_t; +GEN_QUEUE (scot_event_t); + + +struct scot_event_manager +{ + struct scot_event_source sources [32]; + struct scot_event_sink sinks [32]; + + queue_scot_event_node_t * queue; +}; + + +/* + * Funktionen zum manager + */ +struct scot_event_manager * +scot_event_manager_new (void); + +int +scot_event_manager_register_source ( + struct scot_event_manager *, + struct scot_event_source *); + +int scot_event_manager_register_cb ( + struct scot_event_manager *, + uint32_t event, + int (*cb) (struct scot_event *)); + +/* + * Funktionen zur source + */ +struct scot_event_source * +scot_event_source_init ( + unsigned int group, + int (*event_done_cb) (struct scot_event *)); + +int +scot_event_source_register ( + struct scot_event_source *); + +/* + * Funktionen zur sink + */ diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..b3bce61 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,47 @@ +nobase_include_HEADERS = scot/cmdla.h \ + scot/list.h scot/list_impl.h scot/list_proto.h \ + scot/list_man.h scot/list_mod.h scot/list_nav.h \ + scot/list_type_proto.h scot/list_func_proto.h \ + scot/stack.h scot/stack_impl.h scot/stack_proto.h \ + scot/stack_type_proto.h scot/stack_func_proto.h \ + scot/queue.h scot/queue_impl.h scot/queue_proto.h \ + scot/queue_type_proto.h scot/queue_func_proto.h \ + scot/exception.h scot/exception_t.h scot/excenv_t.h \ + scot/thread.h scot/memory.h scot/dir.h scot/dir_common.h \ + scot/scot_exceptions.h scot/scot_types.h \ + scot/socket.h scot/socket_in.h \ + scot/event.h scot/event_listener.h \ + scot/fs_watcher.h \ + scot/stream.h scot/stream_pool.h + +BUILT_SOURCES = scot/thread.h scot/memory.h scot/dir.h +CLEANFILES = scot/thread.h scot/memory.h scot/dir.h + +if PTHREAD +scot/thread.h: Makefile scot/posix/thread.h + cp scot/posix/thread.h scot/thread.h +else +scot/thread.h: Makefile scot/win32/thread.h + cp scot/win32/thread.h scot/thread.h +endif + +if WIN32 +nobase_include_HEADERS += scot/stream_win.h + +scot/scot_types.h: Makefile scot/win32/scot_types.h + cp scot/win32/scot_types.h scot/scot_types.h +scot/memory.h: Makefile scot/win32/memory.h + cp scot/win32/memory.h scot/memory.h +scot/dir.h: Makefile scot/win32/dir.h + cp scot/win32/dir.h scot/dir.h +else +nobase_include_HEADERS += scot/socket_un.h scot/inotify.h + +scot/scot_types.h: Makefile scot/posix/scot_types.h + cp scot/posix/scot_types.h scot/scot_types.h +scot/memory.h: Makefile scot/posix/memory.h + cp scot/posix/memory.h scot/memory.h +scot/dir.h: Makefile scot/posix/dir.h + cp scot/posix/dir.h scot/dir.h +endif + diff --git a/include/scot/cmdla.h b/include/scot/cmdla.h new file mode 100644 index 0000000..424b00f --- /dev/null +++ b/include/scot/cmdla.h @@ -0,0 +1,117 @@ +/* + * cmdla.h: Prototypes, defines, etc. for cmdla + * + * Copyright (C) 2006 Georg Steffers. All rights reserved. + * + * This software is licensed under the terms of the GNU Genral Public + * License (GPL). Please see gpl.txt for details or refer to + * http://www.gnu.org/licenses/gpl.html + * + * Author: Georg Steffers + * + * 01/14/2006: Georg Steffers - First implemented + * 01/15/2006: Georg Steffers - V0.1 ready. Modified this and that + * now long arguments are supported as + * well as arguments with am optional + * parameter. + * 01/23/2006: Georg Steffers - add support for gettext. + */ + +#ifndef CMDLA_H +#define CMDLA_H + +/* Datatypes of given commandline values */ +#define CMDLA_TYPE_STRING 0x00 +#define CMDLA_TYPE_INT 0x01 +#define CMDLA_TYPE_FLOAT 0x02 + +/* Indicates wether an arg requires an parameter or not */ +#define CMDLA_REQ_ARG 0x01 +#define CMDLA_OPT_ARG 0x02 + +#define CMDLAP_CBT_END { 0, NULL, 0, NULL, 0, NULL, NULL } +#define CMDLAS_CBT_END { 0, NULL, NULL, NULL, NULL } + +#define _GNU_SOURCE /* for getopt */ + +#include +#include +#include + +#include + +/* + * This struct holds all neccesary infomation for the default + * usage callback. It is filled within process_cmd_line and used + * with any usage function that did not specify explicitly different + * behaviour. + */ +struct du_infot +{ + const struct cmdlas_cbt *switches; + const struct cmdlap_cbt *arguments; + const char *about; + const char *copyright; + const char *usage_aa; + const char *program_name; +}; + + +typedef int (*cmdlap_cb) (const char *, const int, void *); +typedef int (*cmdlas_cb) (void *); + +/* + * CoMmanDLine Argument Parameter CallBack Type. + * This holds the definition of an argument that takes a parameter. + */ +struct cmdlap_cbt { + char carg; /* The single letter argument e.g. -a */ + char *sarg; /* The string argument e.g. --arg */ + int cmdla_argt; /* Datatype the arg take e.g. CMDLA_TYPE_INT */ + cmdlap_cb cb; /* pointer to the callback funtion */ + int cmdla_type; /* requires parameter? e.g. CMDLA_REQ_ARG */ + void *var; /* this will be given to the callback */ + const char *info; /* a short description for the help */ +}; + +/* + * CoMmanDLine Argument Switch CallBack Type. + * This holds the definition of an argument that is a switch. + */ +struct cmdlas_cbt { + char carg; /* The single letter argument e.g. -a */ + char *sarg; /* The string argument e.g. --arg */ + cmdlas_cb cb; /* pointer to the callback funtion */ + void *var; /* this will be given to the callback */ + const char *info; /* a short description for the help */ +}; + + +/* + * The defaul usage method....this was previously static within + * cmdla but i realised that it is useful to make it callable + * within the calling code to do special usage in special cases + * and call this if no special case occured. + */ +int default_usage_cb (void *); + +/* + * a default callback for struct cmdlap_cbt. It simply parses its first + * argument into its last according to the type specified by its sec. arg. + */ +int get_cmdlap_cb (const char *, const int, void *); + +/* + * this does all the work. + * Arguments are: (argc, argv, a program description, a copyright string, + * additional text for the usage information, + * a list of arguments with parameter, a list of switches). + */ +int process_cmd_line ( + int, char *[], const char*, const char*, const char*, + const struct cmdlap_cbt *, const struct cmdlas_cbt *); + +int switch_cb (void *); +int inc_cb (void *); + +#endif /* CMDLA_H */ diff --git a/include/scot/dir_common.h b/include/scot/dir_common.h new file mode 100644 index 0000000..3dfcd03 --- /dev/null +++ b/include/scot/dir_common.h @@ -0,0 +1,26 @@ +/* + * dir_common.h: definitions and prototypes common for all plattforms with + * dir.c + * + * Copyright (C) 2006 Georg Steffers + * + * Author: Georg Steffers [GST] + * Developer: + * + * Changes (for this file only): + * (2006-06-12) [GST] Started this changelog...well the program is + * ready since some weeks right now. + */ +#ifndef DIR_COMMON_H +#define DIR_COMMON_H + +/* return values for get_dir_next */ +#define GET_DIRENT_OK 0x00 +#define NO_FILES_LEFT 0x01 +#define GET_DIRENT_ERR 0x02 + +/* flags for symlink following */ +#define FOLLOW_SYM 0x00 +#define DONT_FOLLOW_SYM 0x01 + +#endif /* DIR_COMMON_H */ diff --git a/include/scot/event.h b/include/scot/event.h new file mode 100644 index 0000000..e8473d1 --- /dev/null +++ b/include/scot/event.h @@ -0,0 +1,106 @@ +#ifndef SCOT_EVENT_H +#define SCOT_EVENT_H + +#include + +#include +#include + +#define SCOT_EVENT_NO uint16_t + +/* + * basis event struktur. Ein Typ und die wirkliche größe. + */ +struct scot_event +{ + SCOT_EVENT_NO event; + uint32_t size; /* is used with serialization. If an event + was serialized and send over a datalink, + it is possible that not all data is read + at once. So i can first read + sizeof (struct scot_event) and then i + know exactly how much has to be read for + the whole event. */ + uint32_t ed_size; /* This reflects the size of the extra_data + buffer...when serializing is done it is + neccessary to know where extra_data ends, + and as it could be any arbitrary data this + could not be guessed by the code. */ + void * extra_data; /* maybe this should hold the object that + an callback function to this event belongs + to. */ +}; + + +struct scot_event * scot_event_new (struct scot_event *, + SCOT_EVENT_NO , void *, SIZE_T); +SIZE_T scot_event_serialize (struct scot_event *, char **); +struct scot_event * scot_event_deserialize (struct scot_event *, const char *); +void scot_event_free (struct scot_event *); + +/* + * event definitionen für stream pool events + * Die stream pool event gruppe hat nur eine erweiterte event struktur, + * es ist aber auch denkbar das eine event gruppe mehrere solche strukturen + * einschließt. (Alle events die ein stream pool erzeugt sind read + * write oder exception auf einem stream und fuer alle diese events + * reicht es den stream handle im event mitzugeben). + */ +struct scot_stream_pool_event +{ + struct scot_event event; + struct scot_stream * st; +}; + +struct scot_stream_pool_event * scot_stream_pool_event_new ( + struct scot_stream_pool_event *, + SCOT_EVENT_NO, void *, SIZE_T, struct scot_stream * ); + +/* + * event definitionen für filesystem watcher events + */ +struct scot_fs_watcher_event +{ + struct scot_event event; + char * path; + char * name; + char * oldname; /* used by rename events */ +}; + +struct scot_fs_watcher_event * scot_fs_watcher_event_new ( + struct scot_fs_watcher_event *, + SCOT_EVENT_NO, void *, SIZE_T, + const char *, const char *, const char *); + +#define GEN_SCOT_EVENT_NO (group, mask, no) ((group<<24)&(mask<<18)&no) +#define SCOT_EVENT_GEN_MASK(exp) 1<<(exp) + +#define SCOT_EVENT_CHK_GROUP(e, g) (((e)&((SCOT_EVENT_NO)(g)<<8)) == ((g)<<8)) + +/* EG := EVENT_GROUP */ +#define SCOT_EG_INTERNAL ((SCOT_EVENT_NO)0) +#define SCOT_EG_STREAM_POOL ((SCOT_EVENT_NO)1) +#define SCOT_EG_FS_WATCHER ((SCOT_EVENT_NO)2) + +#define SCOT_EVENT_STREAM_POOL_READ \ + ((SCOT_EVENT_NO)(SCOT_EG_STREAM_POOL << 8) | 0) +#define SCOT_EVENT_STREAM_POOL_WRITE \ + ((SCOT_EVENT_NO)(SCOT_EG_STREAM_POOL << 8) | 1) +#define SCOT_EVENT_STREAM_POOL_EXCEP \ + ((SCOT_EVENT_NO)(SCOT_EG_STREAM_POOL << 8) | 2) + +#define SCOT_EVENT_FS_WATCHER_CREATE \ + ((SCOT_EVENT_NO)(SCOT_EG_FS_WATCHER << 8) | 0) +#define SCOT_EVENT_FS_WATCHER_DELETE \ + ((SCOT_EVENT_NO)(SCOT_EG_FS_WATCHER << 8) | 1) +#define SCOT_EVENT_FS_WATCHER_RENAME \ + ((SCOT_EVENT_NO)(SCOT_EG_FS_WATCHER << 8) | 2) +#define SCOT_EVENT_FS_WATCHER_WRITTEN \ + ((SCOT_EVENT_NO)(SCOT_EG_FS_WATCHER << 8) | 3) +#define SCOT_EVENT_FS_WATCHER_ATTRCHG \ + ((SCOT_EVENT_NO)(SCOT_EG_FS_WATCHER << 8) | 4) +#define SCOT_EVENT_FS_WATCHER_SIZECHG \ + ((SCOT_EVENT_NO)(SCOT_EG_FS_WATCHER << 8) | 5) + + +#endif /* SCOT_EVENT_H */ diff --git a/include/scot/event_listener.h b/include/scot/event_listener.h new file mode 100644 index 0000000..41f37b5 --- /dev/null +++ b/include/scot/event_listener.h @@ -0,0 +1,53 @@ +#ifndef _SCOT_EVENT_LISTENER_H +#define _SCOT_EVENT_LISTENER_H + +#include + +#include +#include +#include + +#include + +/* valid return codes for the callbacks */ +#define SCOT_EVENT_END 0x00 +#define SCOT_EVENT_CONT 0x01 +#define SCOT_EVENT_CONT_DATA_DONE 0x02 + +#define SCOT_EL_WAIT_THREAD_MAX INFINITE + + +typedef unsigned short (*scot_event_cb_fptr) (struct scot_event *); + +GEN_STACK_TYPE_PROTO (scot_event_cb_fptr); + +struct scot_event_listener +{ + unsigned char group; + + stack_scot_event_cb_fptr_node_t * cb_mappings[UCHAR_MAX]; + void * cb_extra_data[UCHAR_MAX]; + + THREAD_T thread_handle; + THREAD_ID_T thread_id; + int thread_run_flg; + + scot_thread_entry_fptr el_entry_func; +}; +typedef struct scot_event_listener scot_event_listener; + +void scot_event_listener_init (struct scot_event_listener *, + const unsigned char , + const scot_thread_entry_fptr); +void scot_event_listener_fini (struct scot_event_listener *); +void scot_event_listener_start (struct scot_event_listener *); +void scot_event_listener_stop (struct scot_event_listener *); +int scot_event_listener_is_running (struct scot_event_listener *); +void scot_event_listener_register_cb (struct scot_event_listener *, + SCOT_EVENT_NO , + scot_event_cb_fptr , + void *); +void scot_event_listener_call_cb (struct scot_event_listener *, + struct scot_event *); + +#endif /* _SCOT_EVENT_LISTENER_H */ diff --git a/include/scot/excenv_t.h b/include/scot/excenv_t.h new file mode 100644 index 0000000..30f57ae --- /dev/null +++ b/include/scot/excenv_t.h @@ -0,0 +1,52 @@ +/** + * \file scot/excenv_t.h + * \author Georg Steffers + * \brief The datatypes for exception environments. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef STACK_EXCENV_T_H +#define STACK_EXCENV_T_H + +#include + +#include + +struct excenv_t; +typedef struct excenv_t excenv_t; + +#ifndef USE_SCOT_STRUCT_EXCENV_T +struct excenv_t +{ + const char _ [sizeof (struct { + jmp_buf env; + void *e_queue; + })]; +}; +#else +/** + * \internal + * \brief holds an exception environment. + */ +struct excenv_t +{ + jmp_buf env; /**< jump here on error. */ + queue_exception_t_node_t *e_queue; /**< holds the exceptions. */ +}; +#endif + +#endif /* STACK_EXCENV_T_H */ diff --git a/include/scot/exception.h b/include/scot/exception.h new file mode 100644 index 0000000..e198fbc --- /dev/null +++ b/include/scot/exception.h @@ -0,0 +1,133 @@ +/** + * \file scot/exception.h + * \author Georg Steffers + * \brief The user interface to exception handling. + * + * This describes the macros TRY, CATCH, THROW and EXC. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include + +#include "excenv_t.h" +#include "exception_t.h" +#include + + +#ifdef USE_THREADS +# define EXC_INIT threaded_exc_init +#else +# define EXC_INIT exc_init +#endif /* USE_THREADS */ + +/** + * \pre None + * \return Nothing + * \post a current exception environment exists. + * + * \brief start exception handled code. + * + * This starts a block of exception handled code. This is done by + * creating a current exception environment. + */ +#define TRY \ + { \ + excenv_new (EXC_INIT ()); \ + if (setjmp (* excenv_jmp_buf (EXC_INIT ())) == 0) + +/** + * \param ee will hold the exception environment actually handled. + * \pre a current exception environment must exists. + * \return Nothing + * \post ee holds the current exception environment and it is removed + * from the stack of exception environments. + * + * \brief start exception handling. + * + * This starts a block of exception handling. This is done by + * retrieving the actual exception environment into \a ee. + */ +#define CATCH(ee) \ + ee = excenv_catch (EXC_INIT ()); \ + } \ + if (excenv_has_exception(ee) == 0) \ + free_catched (ee); \ + else + +/** + * \param e the exception to be thrown. + * \pre a current exception environment must exist. + * \return Nothing + * \post \a e is put into the current exception environment. + * + * \brief Throws an exception into the actual exception environment. + */ +#define THROW(e) \ + exc_throw (EXC_INIT (), (e)) + +/** + * \brief this is just a wrapper around exc_new(). + * + * This is just a wrapper around exc_new() that fills in automatically + * file and line. + */ +#define EXC(lvl, errnum, err_msg) \ + exc_new (lvl, __FILE__, __LINE__, errnum, err_msg) + +/** + * \brief a wrapper for exc_in_this_try() + */ +#define EXC_IN_THIS_TRY(e) \ + exc_in_this_try (e) + +void * exc_init (); +void * threaded_exc_init (); + +void excenv_new (void *); +jmp_buf * excenv_jmp_buf (void *); +void exc_throw (void *, const exception_t *); +excenv_t * excenv_catch (void *); +int excenv_has_exception (const excenv_t *); + +exception_t * exc_new ( + const enum exclvl_t, + const char *, + const int, + const int, + const char *); + +exception_t * retrive_exception (const excenv_t *); + +void free_catched (excenv_t *); +void free_exception (exception_t *); +void thread_exc_end (THREAD_T); +void exc_end (void); + +void print_exception (exception_t *); +void print_all_exceptions (excenv_t *); +void forward_all_exceptions (excenv_t *); + +int exc_in_this_try (exception_t *); +enum exclvl_t exc_lvl_get (exception_t *); +char * exc_file_get (exception_t *); +int exc_line_get (exception_t *); +int exc_errnum_get (exception_t *); +char * exc_err_msg_get (exception_t *); +#endif /* EXCEPTION_H */ diff --git a/include/scot/exception_t.h b/include/scot/exception_t.h new file mode 100644 index 0000000..1f34b9a --- /dev/null +++ b/include/scot/exception_t.h @@ -0,0 +1,68 @@ +/** + * \file scot/exception_t.h + * \author Georg Steffers + * \brief The datatypes for exceptions. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef STACK_EXCEPTION_T_H +#define STACK_EXCEPTION_T_H + +/** + * \brief give the two states a speaking name. + * + * There are two exceptions possible EXC_ERRORS, that immediatly abort the + * current operation and EXC_WARNINGS, that will just be mentioned within + * the exception stack. + */ +enum exclvl_t {EXC_WARNING, EXC_ERROR}; + +struct exception_t; +typedef struct exception_t exception_t; + +#ifndef USE_SCOT_STRUCT_EXCEPTION_T +struct exception_t +{ + const char _ [sizeof (struct { + int was_catched; + const enum exclvl_t lvl; + const char *file; + const int line; + const int errnum; + const char *err_msg; + })]; +}; +#else +/** + * \internal + * \brief holds an exception. + */ +struct exception_t +{ + int was_catched; /**< how often was it catched */ + const enum exclvl_t lvl; /**< EXC_ERROR or EXC_WARNING */ + const char *file; /**< file where it was created */ + const int line; /**< line of that file */ + const int errnum; /**< number of the error */ + const char *err_msg; /**< message of the error */ +}; + +#include +GEN_QUEUE_TYPE_PROTO (exception_t); +#endif + +#endif /* STACK_EXCEPTION_T_H */ diff --git a/include/scot/fs_watcher.h b/include/scot/fs_watcher.h new file mode 100644 index 0000000..c1d7e78 --- /dev/null +++ b/include/scot/fs_watcher.h @@ -0,0 +1,47 @@ +#ifndef SCOT_FS_WATCHER_H +#define SCOT_FS_WATCHER_H + +#include +#include +#include + +#include + +struct scot_fsw_info +{ + int watch_d; + char * path; + uint32_t mask; + uint32_t got_events; /* also a mask, that shows, which events already + occured (will be 0 at init and after a callback was + called.) */ + char * old_name; /* This is used within rename events. With inotify + a rename is build up from 2 inotify_events. + IN_MOVE_FROM and IN_MOVE_TO. The move from + event holds the old name. This old name is saved + here if an IN_MOVE_FORM occured. */ +}; +typedef struct scot_fsw_info scot_fsw_info; +GEN_LIST_TYPE_PROTO (scot_fsw_info); + +struct scot_fs_watcher +{ + struct scot_event_listener el; + fd_set rfds; + struct scot_stream notify_d; + + list_scot_fsw_info_node_t * w_list; + THREAD_MUTEX_T mutex; +}; + + +struct scot_fs_watcher * scot_fs_watcher_new (void); +void scot_fs_watcher_free (struct scot_fs_watcher *); + +void scot_fs_watcher_add (struct scot_fs_watcher *, const char *, uint32_t); +void scot_fs_watcher_remove (struct scot_fs_watcher *, const char *, uint32_t); + +int scot_fs_watcher_get_mask (struct scot_fs_watcher *, int); +void scot_fs_watcher_main_loop (struct scot_fs_watcher *); + +#endif /* SCOT_FS_WATCHER_H */ diff --git a/include/scot/inotify.h b/include/scot/inotify.h new file mode 100644 index 0000000..249f606 --- /dev/null +++ b/include/scot/inotify.h @@ -0,0 +1,31 @@ +#ifndef INOTIFY_H +#define INOTIFY_H + +#include +#include +#include +#include + +#define IN_NEXT_EVENT(ev) \ + (struct inotify_event *) \ + ((char *)(ev) + sizeof (struct inotify_event) + (ev)->len) + +#define IN_NO_EVENT(ev) ((ev)->mask|IN_ALL_EVENTS) == IN_ALL_EVENTS + + +static inline int inotify_init (void) +{ + return syscall (__NR_inotify_init); +} + +static inline int inotify_add_watch (int fd, const char *name, __u32 mask) +{ + return syscall (__NR_inotify_add_watch, fd, name, mask); +} + +static inline int inotify_rm_watch (int fd, __u32 wd) +{ + return syscall (__NR_inotify_rm_watch, fd, wd); +} + +#endif /* INOTIFY_H */ diff --git a/include/scot/list.h b/include/scot/list.h new file mode 100644 index 0000000..cc8e491 --- /dev/null +++ b/include/scot/list.h @@ -0,0 +1,64 @@ +/** + * \file scot/list.h + * \author Georg Steffers + * \brief Macro which produce all prototypes and implementations + * for handling linked lists from Templates. + * + * This is the toplevel template file for typesafe lists. It is quite + * simple. It just defines one MACRO which in turn calls two other macros + * to generate all that is needed for listhandling of a list to a given type. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_H +#define LIST_H + +#include +#include + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The complete framework of functions, types, globals + * prototypes and definitions to handle typesave + * lists for the given datatype are generated within the + * calling build file. + * + * \brief create complete framework to handle typesafe lists. + * + * This macro first colls GEN_LIST_PROTO() to create all prototypes + * neccesary for handling lists of the given \a type. And then it calls + * GEN_LIST_IMPL() to also create the neccesary definitions and + * implementations.\ + * Normally this macro is only called if one only wants to have lists + * of the given type in a single c source file and nowhere else. Normally + * this is then used in conjunction with a previous define of GEN_LOCAL, + * that tells GEN_LIST() and the subsequent macros to generate the functions + * as static.\n + * If one wants to used lists of the given type in several places of the + * project one would normally call GEN_LIST_PROTO() in a h file and + * GEN_LIST_IMPL() in the corresponding c file (without GEN_LOCAL), + */ +#define GEN_LIST(type) \ + GEN_LIST_PROTO (type) \ + GEN_LIST_IMPL (type) + +#endif /* LIST_H */ diff --git a/include/scot/list_func_proto.h b/include/scot/list_func_proto.h new file mode 100644 index 0000000..9ea96e9 --- /dev/null +++ b/include/scot/list_func_proto.h @@ -0,0 +1,214 @@ +/** + * \file scot/list_func_proto.h + * \author Georg Steffers + * \brief Macro that produce function prototypes for handling + * linked lists. + * + * The macros here create all function prototypes to + * the implementations created by the macros in + * \link scot/list_impl.h scot/list_impl.h\endlink. + * These macros are normally not called directly within your code but + * through \link scot/list_proto.h::GEN_LIST_PROTO GEN_LIST_PROTO\endlink. + * This is because to use the interface declaration + * provided here one will also need the typedefs and datatype prototypes. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_FUNC_PROTO_H +#define LIST_FUNC_PROTO_H + +#ifdef GEN_LOCAL +# define STATIC static +#else +# define STATIC +#endif + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The function prototypes for manage lists of the given + * datatype are created. + * + * \brief Function prototypes for management. + * + * This creates the prototypes to the functions that are created by + * \link scot/list_man.h::GEN_LIST_MANAGEMENT GEN_LIST_MANAGEMENT\endlink + * in \link scot/list_man.h scot/list_man.h\endlink. + * + * Normally this is not called directly, but by + * \link scot/list_proto.h::GEN_LIST_PROTO GEN_LIST_PROTO()\endlink + * because this defined just a subset of all function prototypes neccesarry + * to handle typesafe lists. + */ +#define GEN_LIST_MAN_PROTO(type) \ + STATIC \ + void \ + list_ ## type ## _set_cmp ( \ + list_ ## type ## _cmp_fptr); \ + STATIC \ + void \ + list_ ## type ## _set_elem_free ( \ + list_ ## type ## _elem_free_fptr); \ + STATIC \ + long \ + list_ ## type ## _elem_free_is_set (void); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _new ( \ + list_ ## type ## _node_t *); \ + STATIC \ + void \ + list_ ## type ## _free ( \ + list_ ## type ## _node_t *); \ + STATIC \ + int \ + list_ ## type ## _count ( \ + list_ ## type ## _node_t *); \ + STATIC \ + int \ + list_ ## type ## _bol ( \ + list_ ## type ## _node_t *, \ + list_ ## type ## _node_t *); \ + STATIC \ + int \ + list_ ## type ## _eol ( \ + list_ ## type ## _node_t *anchor, \ + list_ ## type ## _node_t *node); + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The function prototypes to navigate lists of the given + * datatype are created. + * + * \brief Function prototypes for navigation. + * + * This creates the prototypes to the functions that are created by + * \link scot/list_nav.h::GEN_LIST_NAVIGATION GEN_LIST_NAVIGATION\endlink + * in \link scot/list_nav.h scot/list_nav.h\endlink. + * + * Normally this is not called directly, but by + * \link scot/list_proto.h::GEN_LIST_PROTO GEN_LIST_PROTO()\endlink + * because this defined just a subset of all function prototypes neccesarry + * to handle typesafe lists. + */ +#define GEN_LIST_NAV_PROTO(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _front ( \ + list_ ## type ## _node_t *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _last ( \ + list_ ## type ## _node_t *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _next ( \ + list_ ## type ## _node_t *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _prev ( \ + list_ ## type ## _node_t *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _find ( \ + list_ ## type ## _node_t *, \ + const type *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _find_anchor ( \ + list_ ## type ## _node_t *); + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The function prototypes to modify lists of the given + * datatype are created. + * + * \brief Function prototypes for modification. + * + * This creates the prototypes to the functions that are created by + * \link scot/list_mod.h::GEN_LIST_MODIFY GEN_LIST_MODIFY\endlink + * in \link scot/list_mod.h scot/list_mod.h\endlink. + * + * Normally this is not called directly, but by + * \link scot/list_proto.h::GEN_LIST_PROTO GEN_LIST_PROTO()\endlink + * because this defined just a subset of all function prototypes neccesarry + * to handle typesafe lists. + */ +#define GEN_LIST_MOD_PROTO(type) \ + STATIC \ + type * \ + list_ ## type ## _retrive ( \ + list_ ## type ## _node_t *); \ + STATIC \ + void \ + list_ ## type ## _set ( \ + list_ ## type ## _node_t *, \ + const type *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _insert ( \ + list_ ## type ## _node_t *, \ + const type *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _delete ( \ + list_ ## type ## _node_t *); \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _concat ( \ + list_ ## type ## _node_t *, \ + list_ ## type ## _node_t *); + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post All function prototypes for a lists of the given + * datatype are created. + * + * \brief Calls GEN_LIST_MAN_PROTO, GEN_LIST_NAV_PROTO and GEN_LIST_MOD_PROTO. + * + * This creates all the prototypes to the functions that are created by + * \link scot/list_impl.h::GEN_LIST_IMPL GEN_LIST_IMPL\endlink in + * \link scot/list_impl.h scot/list_impl.h\endlink. + * This provides one with the complete interface to the list of the given + * datatype. + */ +#define GEN_LIST_FUNC_PROTO(type) \ + GEN_LIST_MAN_PROTO (type); \ + GEN_LIST_NAV_PROTO (type); \ + GEN_LIST_MOD_PROTO (type); + +#endif /* LIST_FUNC_PROTO_H */ diff --git a/include/scot/list_impl.h b/include/scot/list_impl.h new file mode 100644 index 0000000..eba42de --- /dev/null +++ b/include/scot/list_impl.h @@ -0,0 +1,252 @@ +/** + * \file scot/list_impl.h + * \author Georg Steffers + * \brief Macro which creates functions for typesafe lists by templates. + * + * This combines the macro definitions in \link scot/list_man.h scot/list_man.h + * \endlink, \link scot/list_mod.h scot/list_mod.h \endlink and \link + * scot/list_nav.h scot/list_nav.h\endlink into one macro that is + * normally called within a c file that wants to use lists, as it provides + * you with all functions neccesary for list handling.\n + * Additionally the whole errorhandling code for lists is here. For that + * there are some function prototypes of functions implemented in + * \link list.c\endlink here. These functions do error output or will + * throw exceptions. Which function is called depends on if + * \link exception.c::USE_NO_EXCEPTIONS USE_NO_EXCEPTIONS\endlink is set at + * include time of this file, or not. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_IMPL_H +#define LIST_IMPL_H + + +#include +#include + +/** + * \internal + * \param type the datatype that this queue code should handle. + * \param a a variable holding a pointer to queue_[type]_node_t. + * + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * a must be a valid pointer to a queue_[type]_node_t. + * \returns \a a cast to a pointer of list_[type]_node_t. + * \post None + * + * \brief Cast a pointer to list_[type]_node_t. + * + * FIXME: I should check better the validity of \a a. + */ +#define LIST(type, a) (list_ ## type ## _node_t *) (a) + +void list_error_print (const char *, int, int); +void list_warning_print (const char *, int, int); +void list_error_throw (const char *, int, int); +void list_warning_throw (const char *, int, int); +void * list_malloc_print (SIZE_T, const char *, int); +void list_check_null_print (const void *, const char *, int); +void * list_malloc_throw (SIZE_T, const char *, int); +void list_check_null_throw (const void *, const char *, int); + +#ifdef USE_NO_EXCEPTION +# define LIST_ERROR(file, line, id) \ + list_error_print ((file), (line), (id)) +# define LIST_WARNING(file, line, id) \ + list_warning_print ((file), (line), (id)) +# define LIST_MALLOC(size, file, line) \ + list_malloc_print ((size), (file), (line)) +# define LIST_CHECK_NULL(val, file, line) \ + list_check_null_print ((void *) (val), (file), (line)) +# define LIST_EXC_START +# define LIST_EXC_END(file, line, id) +#else +/** + * \internal + * \param file filename of the file where the error occured. + * \param line line in the file where the error occured. + * \param id list error id. + * + * \brief print or thow an error + */ +# define LIST_ERROR(file, line, id) \ + list_error_throw ((file), (line), (id)) +/** + * \internal + * \param file filename of the file where the error occured. + * \param line line in the file where the error occured. + * \param id list error id. + * + * \brief print or throw a warning + */ +# define LIST_WARNING(file, line, id) \ + list_warning_throw ((file), (line), (id)) +/** + * \internal + * \param size the amount of memory that should be reserved. + * \param file filename of the file where this is called. + * \param line line in the file where this is called. + * + * \brief list malloc wrapper + * + * A malloc wrapper which either print an error or throw + * an exception. + */ +# define LIST_MALLOC(size, file, line) \ + list_malloc_throw ((size), (file), (line)) +/** + * \internal + * \param val variable that should be check for NULL. + * \param file filename of the file where this is called. + * \param line line in the file where this is called. + * + * \brief this checks if val is null + */ +# define LIST_CHECK_NULL(val, file, line) \ + list_check_null_throw ((void *) (val), (file), (line)) +/** + * \internal + * \brief start exception environment in generated list function. + */ +# define LIST_EXC_START \ + { \ + excenv_t *ee; \ + TRY { +/** + * \internal + * \brief end the exception environment of the generated list function. + * + * This ends the exception environment in the generated list + * function, that was started with LIST_EXC_START. + * Any exception that had occured will be forwarded in the upper + * exception environment and if exceptions had occured a new one + * will be thrown to the exception environment indicating that the + * function fails. + */ +# define LIST_EXC_END(file, line, id) \ + } \ + CATCH (ee) \ + { \ + forward_all_exceptions (ee); \ + list_error_throw ((file), (line), (id)); \ + } \ + } +#endif /* USE_NO_EXCEPTION */ + +extern const char *list_err_msg[]; +extern const char *list_wrn_msg[]; + +struct list_node_t +{ + const char _ [sizeof (struct { + const void *e; + const void *prev; + const void *next; + })]; +}; + +#ifdef GEN_LOCAL +/** + * \internal + * \brief make functions static or not dependig on if GEN_LOCAL was + * defined or not. + */ +# define STATIC static +#else +# define STATIC +#endif + +#include "list_man.h" +#include "list_nav.h" +#include "list_mod.h" + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The functions, struct and globals to handle typesave + * lists for the given datatype are generated within the + * calling build file. + * FIXME: This seems not threadsafe to me, as the globals + * are used within all threads. Actually one could work + * around this because normally it is enough to set the + * comparison functions once and dont touch them anymore + * but it might be neccesary to compare elements within + * the list differently in different threads. + * + * \brief this creates all functions, structs and globals needed for a + * typesafe list of a given \a type. + * + * In detail the following is created by this macro:\n + * \li struct list_[type]_node_t: This is the structure a list + * is constructed of. A representation of a double linked list node with + * a pointer to the element it contains, a pointer to the next and a pointer + * to the previous element in the list. This list implementation uses a kind + * of a ringlist, that is the next pointer of the last node in the list + * and the prev pointer in the first element of the list points to the anchor. + * Thus in an empty list prev and next of the anchor both points to the + * anchor. + * \li int list_[type]_default_cmp (): This is the default + * comparison function for lists. It simply compares the adresses of two + * elements. At list initialization list_[type]_compare() is set to this. + * \li list_[type]_cmp_fptr list_[type]_compare: This pointer + * to a function that compares list elements is used within the generated + * list functions. + * \li list_[type]_elem_free_fptr list_[type]_elem_free: This pointer + * , if set to non NULL, is used in list_[type]_delete() to free an + * element within a node before deleting the node. At initial time of + * the list code this is set to NULL, thus elements are not freed at all. + * (This is ok when stack variables are used, else one should at least + * set list_[type]_elem_free to free().) + * \li all functions defined in \link scot/list_man.h list_man.h\endlink, + * \link scot/list_mod.h list_mod.h\endlink and + * \link scot/list_nav.h list_nav.h\endlink. + */ +#define GEN_LIST_IMPL(type) \ +struct list_ ## type ## _node_t \ +{ \ + const type *e; \ + struct list_ ## type ## _node_t *prev; \ + struct list_ ## type ## _node_t *next; \ +}; \ + \ +STATIC \ +int list_ ## type ## _default_cmp ( \ + const type * a, \ + const type * b) \ +{ \ + return (a==b)?0:(a + * \brief Templates of functions to manage typesafe lists. + * + * Here are macro definitions that create functions to manage typesafe + * lists. That is create new list, free list, check nodes a.s.f + * + * Normally the macros defined here will be never called directly but only + * via MACROS that group them in a sensefull way in scot/list_impl.h.\n + * \anchor onlyfunc_man + * \attention + * All documentation here does document the functions that are created by + * the macros, as the macros themself are pretty easy and all used the same. + * They are called with a type, that MUST be one word (use typedef if needed) + * and generates the function defined with their value. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_MAN_H +#define LIST_MAN_H + +#include +#include +#include /* because we use functions from there */ + + +/** + * \internal + * \param node a list_[type]_node_t* that should be checked for + * beeing an anchor. + * \param line the line where this MACRO is called. + * \pre a variable of type list_[type]_node_t* must exist. + * \return the code fragment + * \post None + * + * \brief check for anchor. + * + * This checks if the given \a node is an anchor. If not it calls + * LIST_ERROR, which either raises an exception if exceptions are user + * or otherwise prints out an error message to stderr and aborts the + * program. + */ +#define MAN_NODE_NO_ANCHOR_ERROR(node, line) \ + if ((node->e) != NULL) \ + LIST_ERROR ("list_man.h", (line), NODE_NO_ANCHOR_ERR); + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre None + * \return NULL on error, but only if no exceptions are used. + * If exceptions are used an exception is thrown on error. + * On success a pointer to the newly created list_anchor + * will be returned. + * \retval NULL if an error occurs and no exceptions are used. + * \retval !=NULL the pointer to the newly created list anchor. + * \post enough memory on the heap. + * + * \brief template for list constructor. + * + * The function creates a new list anchor on the heap and returns it. + * This can be used with following list_[type]_*() functions. + * The memory reserved for this anchor must be freed by the context + * that uses this function if it did not need the list anymore.\n\n + */ +#define GEN_LIST_NEW(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _new (list_ ## type ## _node_t *anchor) \ + { \ + LIST_EXC_START \ + { \ + anchor = (list_ ## type ## _node_t *) \ + LIST_MALLOC (sizeof (list_ ## type ## _node_t), \ + "list_man.h", 93); \ + \ + anchor->e = NULL; \ + anchor->prev = anchor; \ + anchor->next = anchor; \ + } \ + LIST_EXC_END ("list_man.h", 99, LIST_NEW_ERR); \ + \ + return anchor; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \pre anchor must point to a valid list anchor previously + * assigned by list_[type]_new(). + * \return Nothing + * \post The list is completely removed from the heap. + * + * \brief template for list destructor. + * + * The function frees a list given by its anchor. It uses list_[type]_delete() + * to delete every node one by one and finally it calls free on the anchor. + * list_[type]_delete does by default only delete the node and not the + * stored element. Read here to learn more. + */ +#define GEN_LIST_FREE(type) \ + STATIC \ + void \ + list_ ## type ## _free (list_ ## type ## _node_t *anchor) \ + { \ + list_ ## type ## _node_t *next; \ + \ + LIST_EXC_START \ + { \ + LIST_CHECK_NULL (anchor, "list_man.h", 48); \ + MAN_NODE_NO_ANCHOR_ERROR (anchor, 49); \ + \ + next = list_ ## type ## _next (anchor); \ + while (anchor != next) \ + next = list_ ## type ## _delete (next); \ + \ + SCOT_MEM_FREE (anchor); \ + } \ + LIST_EXC_END ("list_man.h", 58, LIST_FREE_ERR); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \param e A pointer to a variable of the \a type, the + * list was created for. + * \pre The \a anchor has to be not NULL and a valid + * anchor.\a e must not be NULL. + * \return The node that contains \a e. + * \post None + * + * \brief Find the node in the list that contains \a e. + */ +#define GEN_LIST_COUNT(type) \ + STATIC \ + int \ + list_ ## type ## _count (list_ ## type ## _node_t * anchor) \ + { \ + int ret = 0; \ + \ + LIST_EXC_START \ + { \ + list_ ## type ## _node_t *node; \ + \ + LIST_CHECK_NULL (anchor, "list_nav.h", 170); \ + NAV_NODE_NO_ANCHOR_ERROR (anchor, 171); \ + \ + node = anchor; \ + while (! list_ ## type ## _eol (anchor, node)) \ + { \ + node = node->next; \ + \ + if (node->e != NULL) \ + ret ++; \ + } \ + } \ + LIST_EXC_END ("list_man.h", 182, LIST_COUNT_ERR); \ + \ + return ret; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \param node A pointer to a node in the list. + * \pre The \a anchor has to be not NULL and a valid + * anchor, the node has to be not NULL too. + * \return A boolean indicating if the first non-anchor + * node in the list is node. Tested by the address + * of the node, so it really must be the same + * address. + * \retval TRUE The node is the first node in the list. + * \retval FALSE The node is not the first node in the list. + * \post None + * + * \brief Checks if node is at the beginning of the list. + */ +#define GEN_LIST_BOL(type) \ + STATIC \ + int \ + list_ ## type ## _bol ( \ + list_ ## type ## _node_t *anchor, \ + list_ ## type ## _node_t *node) \ + { \ + LIST_EXC_START \ + { \ + LIST_CHECK_NULL (anchor, "list_man.h", 70); \ + LIST_CHECK_NULL (node, "list_man.h", 71); \ + MAN_NODE_NO_ANCHOR_ERROR (anchor, 72); \ + } \ + LIST_EXC_END ("list_man.h", 74, LIST_BOL_ERR); \ + \ + return anchor->next==node; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \param node A pointer to a node in the list. + * \pre The \a anchor has to be not NULL and a valid + * anchor, the node has to be not NULL too. + * \return A boolean indicating if the last non-anchor + * node in the list is node. Tested by the address + * of the node, so it really must be the same + * address. + * \retval TRUE The node is the last node in the list. + * \retval FALSE The node is not the last node in the list. + * \post None + * + * \brief Checks if node is at the end of the list. + */ +#define GEN_LIST_EOL(type) \ + STATIC \ + int \ + list_ ## type ## _eol ( \ + list_ ## type ## _node_t *anchor, \ + list_ ## type ## _node_t *node) \ + { \ + LIST_EXC_START \ + { \ + LIST_CHECK_NULL (anchor, "list_man.h", 88); \ + LIST_CHECK_NULL (node, "list_man.h", 89); \ + MAN_NODE_NO_ANCHOR_ERROR (anchor, 90); \ + } \ + LIST_EXC_END ("list_man.h", 92, LIST_EOL_ERR); \ + \ + if (list_ ## type ## _isempty (anchor)) return -1; \ + return node->next==anchor; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \pre The \a anchor has to be not NULL and a valid + * anchor. + * \return A boolean indicating if the last non-anchor + * node in the list is node. Tested by the address + * of the node, so it really must be the same + * address. + * \retval TRUE The node is the last node in the list. + * \retval FALSE The node is not the last node in the list. + * \post None + * + * \brief Checks if node is at the end of the list. + */ +#define GEN_LIST_ISEMPTY(type) \ + STATIC \ + int \ + list_ ## type ## _isempty (list_ ## type ## _node_t *anchor) \ + { \ + LIST_EXC_START \ + LIST_CHECK_NULL (anchor, "list_man.h", 103); \ + LIST_EXC_END ("list_man.h", 104, LIST_ISEMPTY_ERR); \ + \ + return (anchor->prev==anchor && anchor->next==anchor); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param cmp A pointer to a function that compares two + * variables of \a type. + * \pre None + * \return Nothing + * \post None + * + * \brief Set the compare function to \a cmp. + * + * This sets the compare function used by some functions generated + * for a list of the \a type datatype. This compare function has + * to work similar to strcmp. It should return either <0, ==0 or >0 + * depending on what comparator is lesser, greater or equal. + */ +#define GEN_LIST_SET_CMP(type) \ + STATIC \ + void \ + list_ ## type ## _set_cmp (list_ ## type ## _cmp_fptr cmp) \ + { \ + list_ ## type ## _compare = cmp; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param cmp A pointer to a function that compares two + * variables of \a type. + * \pre None + * \return Nothing + * \post None + * + * \brief Set the compare function to \a cmp. + * + * This sets the compare function used by some functions generated + * for a list of the \a type datatype. This compare function has + * to work similar to strcmp. It should return either <0, ==0 or >0 + * depending on what comparator is lesser, greater or equal. + */ +#define GEN_LIST_SET_ELEM_FREE(type) \ + STATIC \ + void \ + list_ ## type ## _set_elem_free ( \ + list_ ## type ## _elem_free_fptr efree) \ + { \ + list_ ## type ## _elem_free = efree; \ + } + + + +#define GEN_LIST_ELEM_FREE_IS_SET(type) \ + STATIC \ + long \ + list_ ## type ## _elem_free_is_set (void) \ + { \ + return (long) list_ ## type ## _elem_free; \ + } + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The functions for the given datatype that are described + * here are generated within the calling build file. + * + * \brief create functions neccesary to manage lists of the given \a type. + * + * Normally this is not called directly, but by GEN_LIST_IMPL() because this + * defined just a subset of all functions neccesarry to handle typesafe lists. + */ +#define GEN_LIST_MANAGEMENT(type) \ + GEN_LIST_SET_CMP (type); \ + GEN_LIST_SET_ELEM_FREE (type); \ + GEN_LIST_ELEM_FREE_IS_SET (type) \ + GEN_LIST_NEW (type); \ + GEN_LIST_FREE (type); \ + GEN_LIST_COUNT (type); \ + GEN_LIST_BOL (type); \ + GEN_LIST_ISEMPTY (type); \ + GEN_LIST_EOL (type); + + +#endif /* LIST_MAN_H */ diff --git a/include/scot/list_mod.h b/include/scot/list_mod.h new file mode 100644 index 0000000..c72e677 --- /dev/null +++ b/include/scot/list_mod.h @@ -0,0 +1,308 @@ +/** + * \file scot/list_mod.h + * \author Georg Steffers + * \brief Templates of functions to modify typesafe lists. + * + * Here are macro definitions that create functions to modify typesafe + * lists. That is read, write, insert, delete a.s.f. + * + * Normally the macros defined here will be never called directly but only + * via MACROS that group them in a sensefull way in scot/list_impl.h.\n + * \anchor onlyfunc_mod + * \attention + * All documentation here does document the functions that are created by + * the macros, as the macros themself are pretty easy and all used the same. + * They are called with a type, that MUST be one word (use typedef if needed) + * and generates the function defined with their value. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_MOD_H +#define LIST_MOD_H + +#include +#include +#include /* because we use functions from there */ + + +/** + * \internal + * \param node a list_[type]_node_t* that should be checked for + * beeing an anchor. + * \param line the line where this MACRO is called. + * \pre a variable of type list__node_t* must exist. + * \return the code fragment + * \post None + * + * \brief check for anchor. + * + * This checks if the given \a node is an anchor. If not it calls + * LIST_ERROR, which either raises an exception if exceptions are user + * or otherwise prints out an error message to stderr and aborts the + * program. + */ +#define MOD_NODE_NO_ANCHOR_ERROR(node, line) \ + if ((node->e) != NULL) \ + LIST_ERROR ("list_mod.h", (line), NODE_NO_ANCHOR_ERR); + + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_mod "here". + * + * \param node A pointer to a node in the list. + * \pre The \a node must be part of a correctly + * initialized list. + * \return The element saved in the node. + * \post None + * + * \brief Retrive element from node. + * + * This function retrieves the element from a node of a list. + */ +#define GEN_LIST_RETRIVE(type) \ + STATIC \ + type * \ + list_ ## type ## _retrive (list_ ## type ## _node_t *node) \ + { \ + LIST_EXC_START \ + LIST_CHECK_NULL (node, "list_mod.h", 18); \ + LIST_EXC_END ("list_mod.h", 19, LIST_RETR_ERR); \ + \ + return (type *) node->e; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_mod "here". + * + * \param node A pointer to a node in the list. + * \param e A pointer to a variable of the \a type, the + * list was created for. + * \pre The \a node must be part of a correctly + * initialized list. \a e must not be NULL. + * \return Nothing + * \post The element of \a node is \a e. + * + * \brief Set element od node. + * + * This function sets the element from a node of a list. + */ +#define GEN_LIST_SET(type) \ + STATIC \ + void \ + list_ ## type ## _set ( \ + list_ ## type ## _node_t *node, \ + const type *e) \ + { \ + LIST_EXC_START \ + { \ + LIST_CHECK_NULL (node, "list_mod.h", 33); \ + LIST_CHECK_NULL (e, "list_mod.h", 34); \ + } \ + LIST_EXC_END ("list_mod.h", 36, LIST_SET_ERR); \ + \ + node->e = e; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_mod "here". + * + * \param node A pointer to a node in the list. + * \param e A pointer to a variable of the \a type, the + * list was created for. + * \pre The \a node must be part of a correctly + * initialized list. + * \a e must not be NULL. + * \return The node of the inserted element. + * \post List has one new node containing \a e. + * + * \brief Inserts a new node with element \a e into the list. + * + * This function creates a new list node and initializes it with + * \a e. Then this new node will be inserted behind \a node. + */ +#define GEN_LIST_INSERT(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _insert ( \ + list_ ## type ## _node_t *node, \ + const type *e) \ + { \ + list_ ## type ## _node_t *ret; \ + \ + LIST_EXC_START \ + { \ + list_ ## type ## _node_t *new_node; \ + \ + LIST_CHECK_NULL (node, "list_mod.h", 54); \ + LIST_CHECK_NULL (e, "list_mod.h", 55); \ + \ + new_node = (list_ ## type ## _node_t *) \ + LIST_MALLOC (sizeof (list_ ## type ## _node_t), \ + "list_mod.h", 58); \ + \ + new_node->e = e; \ + new_node->prev = node; \ + new_node->next = node->next; \ + node->next->prev = new_node; \ + node->next = new_node; \ + \ + ret = new_node; \ + } \ + LIST_EXC_END ("list_mod.h", 69, LIST_INSERT_ERR); \ + \ + return ret; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_mod "here". + * + * \param node A pointer to a node in the list. + * \pre The \a node must be part of a correctly + * initialized list. + * \return The next node behind the deleted one. + * \post The \a node is removed from the list and + * freed. + * + * \brief Deletes a new node with element \a e into the list. + * + * This function deletes a node from the list it is in. The node + * will be freed but by default NOT the element. Anyway, one can set + * a free function for elements of the list. This should free any + * resource the element has reserved. + * This function can be set with + * \link list_man.h::GEN_LIST_SET_ELEM_FREE + * list_[type]_set_elem_free ()\endlink + * and if set, is called by list_[type]_delete(). If such a function + * was set and one wants to reset to default behaviour (not to delete any + * element) one can pass NULL to \link list_man.h::GEN_LIST_SET_ELEM_FREE + * list_[type]_set_elem_free ()\endlink. + */ +#define GEN_LIST_DELETE(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _delete (list_ ## type ## _node_t *node) \ + { \ + type *e; \ + list_ ## type ## _node_t *prev, \ + *next; \ + \ + LIST_EXC_START \ + { \ + if (list_ ## type ## _isempty (node)) \ + LIST_WARNING ("list_mod.h", 86, DEL_ON_EMPTY_LIST_WRN); \ + \ + e = (type *) node->e; \ + \ + prev = node->prev; \ + next = node->next; \ + prev->next = next; \ + next->prev = prev; \ + \ + if (list_ ## type ## _elem_free != NULL && e != NULL) \ + list_ ## type ## _elem_free (e); \ + \ + SCOT_MEM_FREE (node); \ + } \ + LIST_EXC_END ("list_mod.h", 101, LIST_DELETE_ERR); \ + \ + return next; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_mod "here". + * + * \param anchor1 A pointer to the first list anchor. + * \param anchor2 A pointer to the second list anchor. + * \pre Both, \a anchor1 and \a anchor2 must be + * valid list_anchors. + * \return The anchor of the new concatenated list. + * \post A new list was created that contains both + * given lists. The anchor of at least one of the + * given lists is no longer valid and should no + * longer be used. In fact only the returned + * anchor is garantied to point to a valid list. + * + * \brief Concatenates two lists. + * + * This function joins the given two list to one. This will be done + * partly destructive...that is, at least one of the old anchors + * will be deleted. + */ +#define GEN_LIST_CONCAT(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _concat ( \ + list_ ## type ## _node_t *anchor1, \ + list_ ## type ## _node_t *anchor2) \ + { \ + LIST_EXC_START \ + { \ + LIST_CHECK_NULL (anchor1, "list_mod.h", 115); \ + MOD_NODE_NO_ANCHOR_ERROR (anchor1, 116); \ + LIST_CHECK_NULL (anchor2, "list_mod.h", 117); \ + MOD_NODE_NO_ANCHOR_ERROR (anchor2, 118); \ + } \ + LIST_EXC_END ("list_mod.h", 120, LIST_CONCAT_ERR); \ + \ + if (list_ ## type ## _isempty (anchor1)) \ + return anchor2; \ + \ + if (list_ ## type ## _isempty (anchor2)) \ + return anchor1; \ + \ + anchor2->next->prev = anchor1->prev; \ + anchor2->prev->next = anchor1; \ + anchor1->prev->next = anchor2->next; \ + anchor1->prev = anchor2->prev; \ + } + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The functions for the given datatype that are described + * here are generated within the calling build file. + * + * \brief create functions neccesary to modify lists of the given \a type. + * + * Normally this is not called directly, but by GEN_LIST_IMPL() because this + * defines just a subset of all functions neccesarry to handle typesafe lists. + */ +#define GEN_LIST_MODIFY(type) \ + GEN_LIST_RETRIVE (type); \ + GEN_LIST_SET (type); \ + GEN_LIST_INSERT (type); \ + GEN_LIST_DELETE (type); \ + GEN_LIST_CONCAT (type); + + + +#endif /* LIST_MOD_H */ diff --git a/include/scot/list_nav.h b/include/scot/list_nav.h new file mode 100644 index 0000000..81714a1 --- /dev/null +++ b/include/scot/list_nav.h @@ -0,0 +1,290 @@ +/** + * \file scot/list_nav.h + * \author Georg Steffers + * \brief Templates of functions to navigate within typesafe lists. + * + * Here are macro definitions that create functions to navigate within + * typesafe lists. That is get next node, get first node a.s.f + * + * Normally the macros defined here will be never called directly but only + * via MACROS that group them in a sensefull way in scot/list_impl.h.\n + * \anchor onlyfunc_nav + * \attention + * All documentation here does document the functions that are created by + * the macros, as the macros themself are pretty easy and all used the same. + * They are called with a type, that MUST be one word (use typedef if needed) + * and generates the function defined with their value. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_NAV_H +#define LIST_NAV_H + +#include +#include + + + +/** + * \internal + * \param node a list_[type]_node_t* that should be checked for + * beeing an anchor. + * \param line the line where this MACRO is called. + * \pre a variable of type list_[type]_node_t* must exist. + * \return the code fragment + * \post None + * + * \brief check for anchor. + * + * This checks if the given \a node is an anchor. If not it calls + * LIST_ERROR, which either raises an exception if exceptions are user + * or otherwise prints out an error message to stderr and aborts the + * program. + */ +#define NAV_NODE_NO_ANCHOR_ERROR(node, line) \ + if ((node->e) != NULL) \ + LIST_ERROR ("list_nav.h", (line), NODE_NO_ANCHOR_ERR); + + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \pre The \a anchor has to be not NULL and a valid + * anchor. + * \return The first node in the list, that is normally + * the anchor, thus this is pretty useless. + * \post None + * + * \brief Returns the first element in the list, that is the anchor... + * pathetic... + */ +#define GEN_LIST_FRONT(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _front (list_ ## type ## _node_t *anchor) \ + { \ + LIST_EXC_START \ + { \ + LIST_CHECK_NULL (anchor, "list_nav.h", 20); \ + NAV_NODE_NO_ANCHOR_ERROR (anchor, 21); \ + } \ + LIST_EXC_END ("list_nav.h", 23, LIST_FRONT_ERR); \ + \ + return anchor; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \pre The \a anchor has to be not NULL and a valid + * anchor. + * \return The last node in the list. + * \post None + * + * \brief Get the last node in the list. + */ +#define GEN_LIST_LAST(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _last (list_ ## type ## _node_t *anchor) \ + { \ + LIST_EXC_START \ + { \ + LIST_CHECK_NULL (anchor, "list_nav.h", 35); \ + NAV_NODE_NO_ANCHOR_ERROR (anchor, 36); \ + } \ + LIST_EXC_END ("list_nav.h", 38, LIST_LAST_ERR); \ + \ + return anchor->prev; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param node A pointer to a node in the list. + * \pre The node has to be not NULL and should be in + * a linked list. + * \return The next node after the given node. + * \post None + * + * \brief Get the next node in the list. + */ +#define GEN_LIST_NEXT(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _next (list_ ## type ## _node_t *node) \ + { \ + LIST_EXC_START \ + LIST_CHECK_NULL (node, "list_nav.h", 49); \ + LIST_EXC_END ("list_nav.h", 50, LIST_NEXT_ERR); \ + \ + return node->next; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param node A pointer to a node in the list. + * \pre The node has to be not NULL and should be in + * a linked list. + * \return The previous node after the given node. + * \post None + * + * \brief Get the previous node in the list. + */ +#define GEN_LIST_PREV(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _prev (list_ ## type ## _node_t *node) \ + { \ + LIST_EXC_START \ + LIST_CHECK_NULL (node, "list_nav.h", 61); \ + LIST_EXC_END ("list_nav.h", 62, LIST_PREV_ERR); \ + \ + return node->prev; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param anchor A pointer to the list anchor. + * \param e A pointer to a variable of the \a type, the + * list was created for. + * \pre The \a anchor has to be not NULL and a valid + * anchor.\a e must not be NULL. + * \return The node that contains \a e. + * \post None + * + * \brief Find the node in the list that contains \a e. + */ +#define GEN_LIST_FIND(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _find ( \ + list_ ## type ## _node_t *anchor, \ + const type *e) \ + { \ + list_ ## type ## _node_t *ret = NULL; \ + \ + LIST_EXC_START \ + { \ + list_ ## type ## _node_t *node; \ + \ + LIST_CHECK_NULL (anchor, "list_nav.h", 80); \ + NAV_NODE_NO_ANCHOR_ERROR (anchor, 81); \ + \ + node = anchor; \ + while (! list_ ## type ## _eol (anchor, node)) \ + { \ + node = node->next; \ + \ + if (node->e != NULL) \ + if (list_ ## type ## _compare (e, node->e) == 0) \ + ret = node; \ + } \ + } \ + LIST_EXC_END ("list_nav.h", 93, LIST_FIND_ERR); \ + \ + return ret; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_man "here". + * + * \param node A pointer to a node in the list. + * \pre The node has to be not NULL and should be in + * a linked list. + * \return The anchor of the list, the \a node is in. + * \post None + * + * \brief Get the anchor of the list. + */ +#define GEN_LIST_FIND_ANCHOR(type) \ + STATIC \ + list_ ## type ## _node_t * \ + list_ ## type ## _find_anchor ( \ + list_ ## type ## _node_t *entry) \ + { \ + list_ ## type ## _node_t *ret = NULL; \ + \ + LIST_EXC_START \ + { \ + list_ ## type ## _node_t *node; \ + \ + LIST_CHECK_NULL (entry, "list_nav.h", 110); \ + \ + node = entry; \ + if (node->e == NULL) \ + ret = node; \ + \ + while (! list_ ## type ## _eol (entry, node)) \ + { \ + node = node->next; \ + \ + if (node->e == NULL) \ + ret = node; \ + } \ + \ + if (ret == NULL) \ + LIST_ERROR ("list_nav.h", 125, MALFORMED_LIST_ERR); \ + } \ + LIST_EXC_END ("list_nav.h", 127, LIST_FIND_ANCHOR_ERR); \ + \ + return NULL; \ + } + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The functions for the given datatype that are described + * here are generated within the calling build file. + * + * \brief create functions neccesary to modify lists of the given \a type. + * + * Normally this is not called directly, but by GEN_LIST_IMPL() because this + * defines just a subset of all functions neccesarry to handle typesafe lists. + */ +#define GEN_LIST_NAVIGATION(type) \ + GEN_LIST_FRONT (type); \ + GEN_LIST_LAST (type); \ + GEN_LIST_NEXT (type); \ + GEN_LIST_PREV (type); \ + GEN_LIST_FIND (type); \ + GEN_LIST_FIND_ANCHOR (type); + + + +#endif /* LIST_NAV_H */ diff --git a/include/scot/list_proto.h b/include/scot/list_proto.h new file mode 100644 index 0000000..2da5e44 --- /dev/null +++ b/include/scot/list_proto.h @@ -0,0 +1,101 @@ +/** + * \file scot/list_proto.h + * \author Georg Steffers + * \brief This generates all prototypes needed for typesafe lists. + * + * This combines the macro definitions in \link scot/list_type_proto.h + * scot/list_type_proto.h \endlink and \link scot/list_func_proto.h + * scot/list_func_proto.h \endlink. + * Additionally defines for all errornumbers that list will create can be + * found here. + * GEN_LIST_PROTO is normally called in a header file that describes a + * new Datatype and also wants lists of it. + * By doing this the complete interface to lists of that datatype is + * produced. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_PROTO_H +#define LIST_PROTO_H + +#include +#include + +/** \brief errnum if list_[type]_new() failes*/ +#define LIST_NEW_ERR 0x00 +/** \brief errnum if list_[type]_free() failes*/ +#define LIST_FREE_ERR 0x01 +/** \brief errnum if list_[type]_bol() failes*/ +#define LIST_BOL_ERR 0x02 +/** \brief errnum if list_[type]_eol() failes*/ +#define LIST_EOL_ERR 0x03 +/** \brief errnum if list_[type]_isempty() failes*/ +#define LIST_ISEMPTY_ERR 0x04 +/** \brief errnum if list_[type]_front() failes*/ +#define LIST_FRONT_ERR 0x05 +/** \brief errnum if list_[type]_last() failes*/ +#define LIST_LAST_ERR 0x06 +/** \brief errnum if list_[type]_next() failes*/ +#define LIST_NEXT_ERR 0x07 +/** \brief errnum if list_[type]_prev() failes*/ +#define LIST_PREV_ERR 0x08 +/** \brief errnum if list_[type]_find() failes*/ +#define LIST_FIND_ERR 0x09 +/** \brief errnum if list_[type]_find_anchor() failes*/ +#define LIST_FIND_ANCHOR_ERR 0x0A +/** \brief errnum if list_[type]_retrive() failes*/ +#define LIST_RETR_ERR 0x0B +/** \brief errnum if list_[type]_set() failes*/ +#define LIST_SET_ERR 0x0C +/** \brief errnum if list_[type]_insert() failes*/ +#define LIST_INSERT_ERR 0x0D +/** \brief errnum if list_[type]_delete() failes*/ +#define LIST_DELETE_ERR 0x0E +/** \brief errnum if list_[type]_concat() failes*/ +#define LIST_CONCAT_ERR 0x0F +/** \brief errnum if list_[type]_count() failes*/ +#define LIST_COUNT_ERR 0x10 + +/** \brief errnum if node is no anchor*/ +#define NODE_NO_ANCHOR_ERR 0x11 +/** \brief errnum if list has no anchor*/ +#define MALFORMED_LIST_ERR 0x12 + +/** \brief warning if delete on empty list*/ +#define DEL_ON_EMPTY_LIST_WRN 0x01 + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The typedefs, struct declaration and function prototypes + * for functions to lists for the given datatype are + * generated within the calling build file. + * + * \brief this creates all typdefs, declarations and prototypes needed for a + * typesafe list of a given \a type. This is normally calles within + * a header file. + */ +#define GEN_LIST_PROTO(type) \ + GEN_LIST_TYPE_PROTO (type) \ + GEN_LIST_FUNC_PROTO (type) + +#endif /* LIST_PROTO_H */ diff --git a/include/scot/list_type_proto.h b/include/scot/list_type_proto.h new file mode 100644 index 0000000..7f78450 --- /dev/null +++ b/include/scot/list_type_proto.h @@ -0,0 +1,64 @@ +/** + * \file scot/list_type_proto.h + * \author Georg Steffers + * \brief Macro that creates the datatype for handling linked lists. + * + * The macros here create all datatype prototypes typedefs declarations to + * the implementations created by the macros in + * \link scot/list_impl.h scot/list_impl.h\endlink. + * These macros are normally not called directly within your code but + * through \link scot/list_proto.h::GEN_LIST_PROTO GEN_LIST_PROTO\endlink. + * This is because normally one did not only + * want the datatypes but also the interface declaration to them. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef LIST_TYPE_PROTO_H +#define LIST_TYPE_PROTO_H + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * \return Nothing + * \post The datatype prototypes for lists of the given + * datatype are created. + * + * \brief Datatype prototypes to lists. + * + * This creates the prototypes of the datatypes needed to use the + * list interface. + * + * Normally this is not called directly, but by + * \link scot/list_proto.h::GEN_LIST_PROTO GEN_LIST_PROTO()\endlink + * because it is also neccesary to create the interface to this + * datatypes for working lists. + */ +#define GEN_LIST_TYPE_PROTO(type) \ + struct list_ ## type ## _node_t; \ + typedef struct list_ ## type ## _node_t \ + list_ ## type ## _node_t; \ + \ + typedef int (*list_ ## type ## _cmp_fptr) ( \ + const type *, \ + const type *); \ + \ + typedef void (*list_ ## type ## _elem_free_fptr) (type *); + +#endif /* LIST_TYPE_PROTO_H */ diff --git a/include/scot/posix/dir.h b/include/scot/posix/dir.h new file mode 100644 index 0000000..caa906a --- /dev/null +++ b/include/scot/posix/dir.h @@ -0,0 +1,71 @@ +/* + * dir.h: as one might suggest here are definitions and prototypes for + * posix/dir.c + * + * Copyright (C) 2006 Georg Steffers + * + * Author: Georg Steffers [GST] + * Developer: + * + * Changes (for this file only): + * (2006-06-12) [GST] Started this changelog...well the program is + * ready since some weeks right now. + */ +#ifndef DIR_H +#define DIR_H + +#include +#include + +#include +#include + +struct scot_dir +{ + struct scot_event_listener el; + + DIR * handle; + const char * path; + + int notify_handle; +}; + +/* abstraction for file information and an directory handle */ +#define FILE_DATA struct dirent_stat +#define DIR_HANDLE DIR * +/* #define DIR_HANDLE struct scot_dir * */ + +/* abstraction for checking if a file is a directory */ +#define IS_DIRECTORY(file_data) (S_ISDIR ((file_data).stat.st_mode)) + +/* abstraction for getting the filename of a directory entry. */ +#define DIRENT_FNAME(file_data) ((file_data).file->d_name) + +/* !!!FIXME!!! is there any place where this is already defined in posix? */ +#ifndef MAX_PATH +# define MAX_PATH 2000 +#endif /* MAX_PATH */ + +/* structure for all available information about the directory entry. */ +struct dirent_stat +{ + char path [MAX_PATH+1]; + struct dirent * file; + struct stat stat; +}; + + +/* the last parameter is posix specific. It defines if stat should + * follow symlinks or not. Under Windows it is ignored. */ +DIR_HANDLE get_dir_first (const char *, FILE_DATA *, int); +int get_dir_next (DIR_HANDLE, const char *, FILE_DATA *, int); + +/* +DIR_HANDLE scot_dir_new (const char *); +void scot_dir_free (DIR_HANDLE); + +int get_dir_first (DIR_HANDLE, FILE_DATA *, int); +int get_dir_next (DIR_HANDLE, FILE_DATA *, int); +*/ + +#endif /* DIR_H */ diff --git a/include/scot/posix/memory.h b/include/scot/posix/memory.h new file mode 100644 index 0000000..be0b7dd --- /dev/null +++ b/include/scot/posix/memory.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * memory.h: Prototypes for default memory operations like memcpy and thus * + * on a win32 system nearly all CRT functions (CRT is the * + * windows C RunTime, that is the normal libC) aren't thread- * + * safe. As i build up tests this causes problems for example * + * with strcpy. So i decided to write wrapper that use non CRT * + * functions on Windows and normal libC ones on a posix system. * + * * + * Author: Georg Steffers * + * Date: 03/06/2006 * + ***************************************************************************/ +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include + +#include + +#define SCOT_MEM_GET malloc +#define SCOT_MEM_FREE(p) if ((p)) {free((p)); (p)=NULL;} +#define SCOT_MEM_COPY memcpy +#define SCOT_MEM_MOVE memmove +#define SCOT_MEM_FILL memset +#define SCOT_MEM_ZERO(p,s) memset ((p), 0, (s)) +#define SCOT_STR_LENGTH strlen +#define SCOT_STR_COPY strcpy +#define SCOT_STRN_COPY strncpy +#define SCOT_STR_CHAR strchr +#define SCOT_STRR_CHAR strrchr + +#endif /* MEMORY_H */ diff --git a/include/scot/posix/scot_types.h b/include/scot/posix/scot_types.h new file mode 100644 index 0000000..b65c507 --- /dev/null +++ b/include/scot/posix/scot_types.h @@ -0,0 +1,9 @@ +#ifndef SCOT_TYPES_H +#define SCOT_TYPES_H + +#include + +typedef ssize_t SSIZE_T; +typedef size_t SIZE_T; + +#endif /* SCOT_TYPES_H */ diff --git a/include/scot/posix/thread.h b/include/scot/posix/thread.h new file mode 100644 index 0000000..4038a10 --- /dev/null +++ b/include/scot/posix/thread.h @@ -0,0 +1,102 @@ +/* + * used for threadsafety within exception + * actually only pthread is supported + */ +#ifndef THREAD_H +#define THREAD_H + +#include +#include + +#include + +#ifndef INFINITE +# define INFINITE 0 +#endif /* INFINITE */ + + +#define JOIN_OK 0x00 +#define JOIN_TIMEOUT 0x01 +#define JOIN_ERROR 0x02 + +/* + * i guess it is possible to write mutex code that uses cond + * elements to timeout on lock, but right now i have no clear + * idea of how to do this. The following thoughts i had: + * - create a struct that holds a pthread_mutex_t and a pthread_cond_t + * for every mutex. + * - lock mutexes only by calling pthread_cond_timedwait + * And here starts the problem...to do a cond_timedwait i need to ensure + * that the mutex is already locked...well and then a question i have not + * clearyfied, if a thread ends, will all locks on mutexes be removed? + * I guess so, but i did not test it. And an even more important question, + * is it possible to write cleanup-code that guarantees that a + * pthread_cond_signal is called for every mutex the thread holds... + * well, we will end up with the requirement that the programmer has to + * call a release mutex function before ending a thread and if the programmer + * forgets that the behaviour of out code is unspecified + * Right now i will not support timeouts on mutex-locks and to be consistent + * it is not supported on win32 either, also it would be much easier to + * implement there. + */ +#define MUTEX_LOCK_OK 0x00 +#define MUTEX_LOCK_ERROR 0x02 + +#define THREAD_T pthread_t +#define THREAD_ID_T pthread_t +#define THREAD_ID pthread_self () +#define SELF_THREAD pthread_self () +#define THREAD_EQUAL pthread_equal +#define NEW_THREAD(th,f, a) thread_new ((th), (f), (a)) +#define END_THREAD thread_end +#define DETACH_THREAD(thread) pthread_detach ((thread)) +#define CANCEL_THREAD(thread) pthread_cancel ((thread)) +#define EXIT_THREAD(retval) pthread_exit ((void *) (retval)) +#define JOIN_THREAD thread_join +#define THREAD_CANCEL_ENABLE pthread_setcancelstate \ + (PTHREAD_CANCEL_ENABLE, NULL) +#define THREAD_CANCEL_DISABLE pthread_setcancelstate \ + (PTHREAD_CANCEL_DISABLE, NULL) +#define THREAD_CANCEL_ASYNC pthread_setcanceltype \ + (PTHREAD_CANCEL_ASYNCHRONOUS, NULL) +#define THREAD_CANCEL_DEFER pthread_setcanceltype \ + (PTHREAD_CANCEL_DEFERRED, NULL) +#define THREAD_TESTCANCEL pthread_testcancel () +#define T_PROC_RET void * + +#define THREAD_MUTEX_T pthread_mutex_t +#define NEW_THREAD_MUTEX thread_mutex_new +#define FREE_THREAD_MUTEX pthread_mutex_destroy +#define LOCK_THREAD_MUTEX thread_mutex_lock +#define UNLOCK_THREAD_MUTEX pthread_mutex_unlock + +#define THREAD_COND_T pthread_cond_t +#define THREAD_COND_CS_T pthread_mutex_t +#define GET_THREAD_COND(c) (*(c) = PTHREAD_COND_INITIALIZER) +#define GET_THREAD_COND_CS(c) (*(c) = PTHREAD_MUTEX_INITIALIZER) +#define NEW_THREAD_COND thread_cond_new +#define NEW_THREAD_COND_CS thread_mutex_new +#define FREE_THREAD_COND pthread_cond_destroy +#define FREE_THREAD_COND_CS pthread_mutex_destroy +#define THREAD_COND_ENTER_CS(cs) pthread_mutex_lock ((cs)) +#define THREAD_COND_LEAVE_CS(cs) pthread_mutex_unlock ((cs)) +#define SIGNAL_THREAD_COND(cond) pthread_cond_signal ((cond)) +#define BCAST_THREAD_COND(cond) pthread_cond_broadcast ((cond)) +#define WAIT_THREAD_COND(cond, cs, t) thread_cond_wait ((cond), (cs), t) + +#define THREAD_ATEXIT_BEGIN pthread_cleanup_push +#define THREAD_ATEXIT_END pthread_cleanup_pop + +typedef T_PROC_RET (*scot_thread_entry_fptr) (void *); + +void thread_new (THREAD_T *, T_PROC_RET (*)(void *), void*); +int thread_join (THREAD_T, uint32_t); +void thread_end (THREAD_T, uint32_t); +void thread_mutex_new (THREAD_MUTEX_T *); +void thread_cond_new (THREAD_COND_T *); +int thread_mutex_lock (THREAD_MUTEX_T *); + +int thread_cond_wait (THREAD_COND_T *, + THREAD_COND_CS_T *, uint32_t); + +#endif /* THREAD_H */ diff --git a/include/scot/queue.h b/include/scot/queue.h new file mode 100644 index 0000000..524cc98 --- /dev/null +++ b/include/scot/queue.h @@ -0,0 +1,69 @@ +/** + * \file scot/queue.h + * \author Georg Steffers + * \brief Macro which produce all prototypes and implementations for + * handling queues based on linked lists by Templates. + * + * This is the toplevel template file for queues based on typesafe lists. + * It is quite simple. It just defines one MACRO which in turn calls two + * other macros to generate all that is needed for queue-handling of a queue + * to a given type. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef QUEUE_H +#define QUEUE_H + +#include +#include + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST(type) has to be called for the list functions, + * as queues depend on these. + * \return Nothing + * \post The complete framework of functions, types, globals + * prototypes and definitions to handle queues based on + * typesave + * lists for the given datatype are generated within the + * calling build file. + * + * \brief create complete framework to handle queues based on typesafe lists. + * + * This macro first colls GEN_QUEUE_PROTO() to create all prototypes + * neccesary for handling queues of the given \a type. And then it calls + * GEN_QUEUE_IMPL() to also create the neccesary definitions and + * implementations.\ + * Normally this macro is only called if one only wants to have queues + * of the given type in a single c source file and nowhere else. Normally + * this is then used in conjunction with a previous define of GEN_LOCAL, + * that tells GEN_QUEUE() and the subsequent macros to generate the functions + * as static.\n + * If one wants to used queues of the given type in several places of the + * project one would normally call GEN_QUEUE_PROTO() in a h file and + * GEN_QUEUE_IMPL() in the c file that does the implementation of that + * h file (without GEN_LOCAL). + */ +#define GEN_QUEUE(type) \ + GEN_QUEUE_PROTO (type) \ + GEN_QUEUE_IMPL (type) + +#endif /* QUEUE_H */ diff --git a/include/scot/queue_func_proto.h b/include/scot/queue_func_proto.h new file mode 100644 index 0000000..af38b62 --- /dev/null +++ b/include/scot/queue_func_proto.h @@ -0,0 +1,89 @@ +/** + * \file scot/queue_func_proto.h + * \author Georg Steffers + * \brief Macro that produces function prototypes for handling queues + * based on linked lists. + * + * The macros here create all function prototypes to + * the implementations created by the macros in + * \link scot/queue_impl.h scot/queue_impl.h\endlink. + * These macros are normally not called directly within your code but + * through \link scot/queue_proto.h::GEN_QUEUE_PROTO GEN_QUEUE_PROTO\endlink. + * This is because to use the interface declaration + * provided here one will also need the typedefs and datatype prototypes. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef QUEUE_FUNC_PROTO_H +#define QUEUE_FUNC_PROTO_H + + +#ifdef GEN_LOCAL +# define STATIC static +#else +# define STATIC +#endif + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_FUNC_PROTO should have bin called previously + * in one or the other way. + * \return Nothing + * \post All function prototypes for a queue of the given + * datatype are created. + * + * \brief This creates the prototypes to all queue functions + * + * This creates all the prototypes to the functions that are created by + * \link scot/queue_impl.h::GEN_QUEUE_IMPL GEN_QUEUE_IMPL\endlink in + * \link scot/queue_impl.h scot/queue_impl.h\endlink. + * This provides one with the complete interface to the queue of the given + * datatype. + */ +#define GEN_QUEUE_FUNC_PROTO(type) \ + STATIC \ + queue_ ## type ## _node_t * \ + queue_ ## type ## _new ( \ + queue_ ## type ## _node_t *); \ + \ + STATIC \ + void \ + queue_ ## type ## _free ( \ + queue_ ## type ## _node_t *); \ + \ + STATIC \ + queue_ ## type ## _node_t * \ + queue_ ## type ## _enqueue ( \ + queue_ ## type ## _node_t *, \ + const type *); \ + \ + STATIC \ + type * \ + queue_ ## type ## _dequeue ( \ + queue_ ## type ## _node_t *); \ + \ + STATIC \ + type * \ + queue_ ## type ## _top ( \ + queue_ ## type ## _node_t *); + +#endif /* QUEUE_FUNC_PROTO_H */ diff --git a/include/scot/queue_impl.h b/include/scot/queue_impl.h new file mode 100644 index 0000000..b94183c --- /dev/null +++ b/include/scot/queue_impl.h @@ -0,0 +1,241 @@ +/** + * \file scot/queue_impl.h + * \author Georg Steffers + * \brief Templates of functions for handling queues based on typesafe lists. + * + * The Macros here generate the implementation for the interface to queues + * based on typesafe linked lists. + * \anchor onlyfunc_queue + * \attention + * All documentation here does document the functions that are created by + * the macros, as the macros themself are pretty easy and all used the same. + * They are called with a type, that MUST be one word (use typedef if needed) + * and generates the function defined with their value. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef QUEUE_IMPL_H +#define QUEUE_IMPL_H + +#include + + +/** + * \internal + * \param type the datatype that this queue code should handle. + * \param a a variable holding a pointer to list_[type]_node_t. + * + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * a must be a valid pointer to a list_[type]_node_t. + * \returns \a a cast to a pointer of queue_[type]_node_t. + * \post None + * + * \brief Cast a pointer to queue_[type]_node_t. + * + * FIXME: I should check better the validity of \a a. + */ +#define QUEUE(type, a) (queue_ ## type ## _node_t *) (a) + +extern const char *queue_err_msg[]; + + +#ifdef GEN_LOCAL +# define STATIC static +#else +# define STATIC +#endif + + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_queue "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_NEW(type) has to be called previously. + * \return NULL on error, but only if no exceptions are used. + * If exceptions are used an exception is thrown on error. + * On success a pointer to the newly created list_anchor + * will be returned. + * \retval NULL if an error occurs and no exceptions are used. + * \retval !=NULL the pointer to the newly created list anchor. + * \post enough memory on the heap. + * + * \brief template for queue constructor. + * + * this simply calls list_[type]_new(), that must be generated previously + * by a call of GEN_LIST_NEW(type). + */ +#define GEN_QUEUE_NEW(type) \ + STATIC \ + queue_ ## type ## _node_t * \ + queue_ ## type ## _new (queue_ ## type ## _node_t *anchor) \ + { \ + return anchor = QUEUE (type, \ + list_ ## type ## _new (LIST (type, anchor))); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_queue "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_FREE(type) has to be called previously. + * \return Nothing + * \post The list is completely removed from the heap. + * + * \brief template for queue destructor. + * + * this simply calls list_[type]_free(), that must be generated previously + * by a call of GEN_LIST_FREE(type). + */ +#define GEN_QUEUE_FREE(type) \ + STATIC \ + void \ + queue_ ## type ## _free (queue_ ## type ## _node_t *anchor) \ + { \ + list_ ## type ## _free (LIST (type, anchor)); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_queue "here". + * + * \param anchor a pointer that should contain the list anchor. + * \param e A pointer to a variable of the \a type, the + * list was created for. + * \pre GEN_LIST_LAST(type) and GEN_LIST_INSERT(type) + * has to be called previously. + * \return The inserted element \a e. + * \post Element \a e is enqueued in the queue pointed by + * \a anchor. + * + * \brief template for a function that enqueues a value into a queue. + * + * To add a value into a queue meens to put it at the end of the queue. + * That is done by getting the last node of the list and insert a value + * behind it. + */ +#define GEN_QUEUE_ENQUEUE(type) \ + queue_ ## type ## _node_t * \ + queue_ ## type ## _enqueue ( \ + queue_ ## type ## _node_t *anchor, \ + const type *e) \ + { \ + return QUEUE (type, list_ ## type ## _insert ( \ + list_ ## type ## _last (LIST (type, anchor)), e)); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_queue "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_NEXT(type), GEN_LIST_RETRIVE(type) + * GEN_LIST_ISEMPTY(type) and GEN_LIST_DELETE(type) + * has to be called previously. + * \return The dequeued element. + * \post One Element is dequeued from the queue pointed to + * by \a anchor. + * + * \brief template for a function that dequeues a value from a queue. + * + * Dequeue means get and remove the first element from the queue. + * This is done here. + */ +#define GEN_QUEUE_DEQUEUE(type) \ + STATIC \ + type * \ + queue_ ## type ## _dequeue (queue_ ## type ## _node_t *anchor) \ + { \ + list_ ## type ## _node_t *next; \ + const type *e; \ + \ + next = list_ ## type ## _next (LIST (type, anchor)); \ + e = list_ ## type ## _retrive (next); \ + \ + if (! list_ ## type ## _isempty (next)) \ + list_ ## type ## _delete (next); \ + \ + return (type *) e; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_queue "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_NEXT(type) and GEN_LIST_RETRIVE(type) + * has to be called previously. + * \return The next element in the queue. + * \post None + * + * \brief get next element in the queue without dequeueing it. + */ +#define GEN_QUEUE_TOP(type) \ + STATIC \ + type * \ + queue_ ## type ## _top ( \ + queue_ ## type ## _node_t *anchor) \ + { \ + return list_ ## type ## _retrive ( \ + list_ ## type ## _next (LIST (type, anchor))); \ + } + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_IMPL(type) should be called before this. + * \return Nothing + * \post The functions, struct and globals to handle queues + * based on typesave + * lists for the given datatype are generated within the + * calling build file. + * + * \brief this creates all functions, structs and globals needed for a + * queue based on typesafe list of a given \a type. + * + * In detail the following is created by this macro:\n + * \li struct queue_[type]_node_t: This is the structure a queue + * is constructed of. Practically this just contains an instance of a + * list_[type]_node_t, as queues are completely based on lists and does + * not add further information. + * \li all functions created by the macros defined within this file. + */ +#define GEN_QUEUE_IMPL(type) \ + struct queue_ ## type ## _node_t \ + { \ + struct list_ ## type ## _node_t list; \ + }; \ + \ + GEN_QUEUE_NEW (type) \ + GEN_QUEUE_FREE (type) \ + GEN_QUEUE_ENQUEUE (type) \ + GEN_QUEUE_DEQUEUE (type) \ + GEN_QUEUE_TOP (type) + +#endif /* QUEUE_IMPL_H */ diff --git a/include/scot/queue_proto.h b/include/scot/queue_proto.h new file mode 100644 index 0000000..212f946 --- /dev/null +++ b/include/scot/queue_proto.h @@ -0,0 +1,68 @@ +/** + * \file scot/queue_proto.h + * \author Georg Steffers + * \brief This generates all prototypes needed for queues based on + * typesafe lists. + * + * This combines the macro definitions in \link scot/queue_type_proto.h + * scot/queue_type_proto.h \endlink and \link scot/queue_func_proto.h + * scot/queue_func_proto.h \endlink. + * Additionally defines for all errornumbers that queue will create can be + * found here. + * GEN_QUEUE_PROTO is normally called in a header file that describes a + * new Datatype and also wants lists of it. + * By doing this the complete interface to lists of that datatype is + * produced. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef QUEUE_PROTO_H +#define QUEUE_PROTO_H + +#include +#include + +/** + * \brief errnum if queue is empty + * \attention This is actually not use and will probably never be used + * in this form. + */ +#define QUEUE_EMPTY 0 + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_PROTO(type) should have been called previous + * to this call. + * \return Nothing + * \post The typedefs, struct declaration and function prototypes + * for functions to queues based on lists for the given + * datatype are generated within the calling build file. + * + * \brief this creates all typdefs, declarations and prototypes needed for a + * queue based on typesafe list of a given \a type. + * This is normally calles within a header file. + */ +#define GEN_QUEUE_PROTO(type) \ + GEN_QUEUE_TYPE_PROTO (type) \ + GEN_QUEUE_FUNC_PROTO (type) + +#endif /* QUEUE_PROTO_H */ diff --git a/include/scot/queue_type_proto.h b/include/scot/queue_type_proto.h new file mode 100644 index 0000000..3a1cf93 --- /dev/null +++ b/include/scot/queue_type_proto.h @@ -0,0 +1,61 @@ +/** + * \file scot/queue_type_proto.h + * \author Georg Steffers + * \brief Macro that creates the datatype for handling queues based on + * linked lists. + * + * The macros here create all datatype prototypes and typedefs declarations to + * the implementations created by the macros in + * \link scot/queue_impl.h scot/queue_impl.h\endlink. + * These macros are normally not called directly within your code but + * through \link scot/queue_proto.h::GEN_QUEUE_PROTO GEN_QUEUE_PROTO\endlink. + * This is because normally one did not only + * want the datatypes but also the interface declaration to them. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef QUEUE_TYPE_PROTO_H +#define QUEUE_TYPE_PROTO_H + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_TYPE_PROTO(type) should have been called + * previous to this. + * \return Nothing + * \post The datatype prototypes for queues based on lists + * of the given datatype are created. + * + * \brief Datatype prototypes to queues. + * + * This creates the prototypes of the datatypes needed to use the + * queue interface. + * + * Normally this is not called directly, but by + * \link scot/queue_proto.h::GEN_QUEUE_PROTO GEN_QUEUE_PROTO()\endlink + * because it is also neccesary to create the interface to this + * datatypes for working queues. + */ +#define GEN_QUEUE_TYPE_PROTO(type) \ + struct queue_ ## type ## _node_t; \ + typedef struct queue_ ## type ## _node_t \ + queue_ ## type ## _node_t; + +#endif /* QUEUE_TYPE_PROTO_H */ diff --git a/include/scot/scot_exceptions.h b/include/scot/scot_exceptions.h new file mode 100644 index 0000000..5f78a99 --- /dev/null +++ b/include/scot/scot_exceptions.h @@ -0,0 +1,19 @@ +#ifndef SCOT_EXCEPTIONS_H +#define SCOT_EXCEPTIONS_H + +#include + +#include + + +#define MALLOC_ERR 0x00 +#define NULL_PTR_ERR 0x01 + +extern const char *scot_err_msg[]; + +void * exc_malloc (SIZE_T); +void * exc_malloc_fl (SIZE_T, const char *, int); +void check_null (const void *); +void check_null_fl (const void *, const char *, int); + +#endif /* SCOT_EXCEPTIONS_H */ diff --git a/include/scot/socket.h b/include/scot/socket.h new file mode 100644 index 0000000..b79b08c --- /dev/null +++ b/include/scot/socket.h @@ -0,0 +1,85 @@ +#ifndef SCOT_SOCKET_H +#define SCOT_SOCKET_H + +#include +#include + +#ifndef WIN32 +# include +# include +# include +# include +# include +# include +# include + +# define INVALID_SOCKET -1 +# define SCOT_ERRNO errno +# define SCOT_H_ERRNO h_errno +# define SOCKET int +# define SCOT_SOCK_CLOSE close +#else +# include + +# define SCOT_ERRNO WSAGetLastError() +# define SCOT_H_ERRNO WSAGetLastError() +# define SCOT_SOCK_CLOSE closesocket +#endif /* WIN32 */ + +#include + +/* + * prevent direct access to the structure + * from other methods then the ones that are made for this. + * Should especially encourage the use of getter and setter + * functions + */ +#ifndef USE_STRUCT_SCOT_SOCKET +struct scot_socket +{ + const char _ [sizeof (struct { + struct scot_stream socket; + struct sockaddr * sa; + SIZE_T addr_len; + })]; +}; +#else +struct scot_socket +{ + struct scot_stream socket; + struct sockaddr * sa; + SIZE_T addr_len; +}; +#endif /* USE_STRUCT_SCOT_SOCKET */ + +/* socket errors */ +#define SCOT_SOCKET_NEW_FAIL 0 +#define SCOT_SOCKET_LISTEN_FAIL 1 +#define SCOT_SOCKET_ACCEPT_FAIL 2 +#define SCOT_SOCKET_CONNECT_FAIL 3 +#define SCOT_SOCKET_NO_VALID_HOST 4 +#define SCOT_SOCKET_AF_NOT_IMPLEMENTED 5 +/* socket warnings */ +#define SCOT_SOCKET_UN_FILE_EXISTS 0 +extern const char * scot_socket_errmsg[]; +extern const char * scot_socket_wrnmsg[]; + +/* + * This function initializes scot sockets. This primary means it does + * nothing under a posix system an WSASartup on a Windows system. + */ +void scot_socket_init (uint16_t, uint16_t); +/* + * This finalizes the scot sockets system. Again does noting on posix + * and WSACleanup on Windows. + */ +void scot_socket_fini (void); + +void scot_socket_listen (const struct scot_socket*); +struct scot_socket * scot_socket_accept (const struct scot_socket*); +void scot_socket_connect (const struct scot_socket*, + const char *); + +void scot_socket_free (struct scot_socket*); + +#endif /* SCOT_SOCKET_H */ diff --git a/include/scot/socket_in.h b/include/scot/socket_in.h new file mode 100644 index 0000000..7b5f0d8 --- /dev/null +++ b/include/scot/socket_in.h @@ -0,0 +1,15 @@ +#ifndef SCOT_SOCKET_IN_H +#define SCOT_SOCKET_IN_H + +extern const char * scot_socket_errmsg[]; + + +struct scot_socket * scot_socket_in_new (const char*, const char*); +struct scot_socket * scot_socket_in_accept (const struct scot_socket*); +void scot_socket_in_prep_con (const struct scot_socket *, + const char *); + +const char * scot_socket_in_get_host (struct scot_socket *); +const char * scot_socket_in_get_ddc (struct scot_socket *); + +#endif /* SCOT_SOCKET_IN_H */ diff --git a/include/scot/socket_un.h b/include/scot/socket_un.h new file mode 100644 index 0000000..614d235 --- /dev/null +++ b/include/scot/socket_un.h @@ -0,0 +1,14 @@ +#ifndef SCOT_SOCKET_UN_H +#define SCOT_SOCKET_UN_H + +extern const char * scot_socket_errmsg[]; + + +struct scot_socket * scot_socket_un_new (const char*); +struct scot_socket * scot_socket_un_accept (const struct scot_socket*); +void scot_socket_un_prep_con (const struct scot_socket *, + const char *); + +const char * scot_socket_un_get_path (struct scot_socket*); + +#endif /* SCOT_SOCKET_UN_H */ diff --git a/include/scot/stack.h b/include/scot/stack.h new file mode 100644 index 0000000..b3016b1 --- /dev/null +++ b/include/scot/stack.h @@ -0,0 +1,69 @@ +/** + * \file scot/stack.h + * \author Georg Steffers + * \brief Macro which produce all prototypes and implementations for + * handling stacks based on linked lists by Templates. + * + * This is the toplevel template file for stacks based on typesafe lists. + * It is quite simple. It just defines one MACRO which in turn calls two + * other macros to generate all that is needed for queue-handling of a queue + * to a given type. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef STACK_H +#define STACK_H + +#include +#include + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST(type) has to be called for the list functions, + * as queues depend on these. + * \return Nothing + * \post The complete framework of functions, types, globals + * prototypes and definitions to handle stacks based on + * typesafe + * lists for the given datatype are generated within the + * calling build file. + * + * \brief create complete framework to handle stacks based on typesafe lists. + * + * This macro first colls GEN_STACK_PROTO() to create all prototypes + * neccesary for handling stacks of the given \a type. And then it calls + * GEN_STACK_IMPL() to also create the neccesary definitions and + * implementations.\ + * Normally this macro is only called if one only wants to have stacks + * of the given type in a single c source file and nowhere else. Normally + * this is then used in conjunction with a previous define of GEN_LOCAL, + * that tells GEN_STACK() and the subsequent macros to generate the functions + * as static.\n + * If one wants to used queues of the given type in several places of the + * project one would normally call GEN_STACK_PROTO() in a h file and + * GEN_STACK_IMPL() in the c file that does the implementation of that + * h file (without GEN_LOCAL). + */ +#define GEN_STACK(type) \ + GEN_STACK_PROTO (type) \ + GEN_STACK_IMPL (type) + +#endif /* STACK_H */ diff --git a/include/scot/stack_func_proto.h b/include/scot/stack_func_proto.h new file mode 100644 index 0000000..b436091 --- /dev/null +++ b/include/scot/stack_func_proto.h @@ -0,0 +1,89 @@ +/** + * \file scot/stack_func_proto.h + * \author Georg Steffers + * \brief Macro that produces function prototypes for handling stacks + * based on linked lists. + * + * The macros here create all function prototypes to + * the implementations created by the macros in + * \link scot/stack_impl.h scot/stack_impl.h\endlink. + * These macros are normally not called directly within your code but + * through \link scot/stack_proto.h::GEN_STACK_PROTO GEN_STACK_PROTO\endlink. + * This is because to use the interface declaration + * provided here one will also need the typedefs and datatype prototypes. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef STACK_FUNC_PROTO_H +#define STACK_FUNC_PROTO_H + + +#ifdef GEN_LOCAL +# define STATIC static +#else +# define STATIC +#endif + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_FUNC_PROTO should have bin called previously + * in one or the other way. + * \return Nothing + * \post All function prototypes for a stack of the given + * datatype are created. + * + * \brief This creates the prototypes to all stack functions + * + * This creates all the prototypes to the functions that are created by + * \link scot/stack_impl.h::GEN_STACK_IMPL GEN_STACK_IMPL\endlink in + * \link scot/stack_impl.h scot/stack_impl.h\endlink. + * This provides one with the complete interface to the stack of the given + * datatype. + */ +#define GEN_STACK_FUNC_PROTO(type) \ + STATIC \ + stack_ ## type ## _node_t * \ + stack_ ## type ## _new ( \ + stack_ ## type ## _node_t *); \ + \ + STATIC \ + void \ + stack_ ## type ## _free ( \ + stack_ ## type ## _node_t *); \ + \ + STATIC \ + stack_ ## type ## _node_t * \ + stack_ ## type ## _push ( \ + stack_ ## type ## _node_t *, \ + const type *); \ + \ + STATIC \ + type * \ + stack_ ## type ## _pop ( \ + stack_ ## type ## _node_t *); \ + \ + STATIC \ + type * \ + stack_ ## type ## _top ( \ + stack_ ## type ## _node_t *); + +#endif /* STACK_FUNC_PROTO_H */ diff --git a/include/scot/stack_impl.h b/include/scot/stack_impl.h new file mode 100644 index 0000000..f468315 --- /dev/null +++ b/include/scot/stack_impl.h @@ -0,0 +1,239 @@ +/** + * \file scot/stack_impl.h + * \author Georg Steffers + * \brief Templates of functions for handling stacks based on typesafe lists. + * + * The Macros here generate the implementation for the interface to stacks + * based on typesafe linked lists. + * \anchor onlyfunc_stack + * \attention + * All documentation here does document the functions that are created by + * the macros, as the macros themself are pretty easy and all used the same. + * They are called with a type, that MUST be one word (use typedef if needed) + * and generates the function defined with their value. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef STACK_IMPL_H +#define STACK_IMPL_H + +#include + + +/** + * \internal + * \param type the datatype that this queue code should handle. + * \param a a variable holding a pointer to list_[type]_node_t. + * + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * a must be a valid pointer to a list_[type]_node_t. + * \returns \a a cast to a pointer of stack_[type]_node_t. + * \post None + * + * \brief Cast a pointer to stack_[type]_node_t. + * + * FIXME: I should check better the validity of \a a. + */ +#define STACK(type, a) (stack_ ## type ## _node_t *) (a) + +extern const char *stack_err_msg[]; + + +#ifdef GEN_LOCAL +# define STATIC static +#else +# define STATIC +#endif + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_stack "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_NEW(type) has to be called previously. + * \return NULL on error, but only if no exceptions are used. + * If exceptions are used an exception is thrown on error. + * On success a pointer to the newly created list_anchor + * will be returned. + * \retval NULL if an error occurs and no exceptions are used. + * \retval !=NULL the pointer to the newly created list anchor. + * \post enough memory on the heap. + * + * \brief template for stack constructor. + * + * this simply calls list_[type]_new(), that must be generated previously + * by a call of GEN_LIST_NEW(type). + */ +#define GEN_STACK_NEW(type) \ + STATIC \ + stack_ ## type ## _node_t * \ + stack_ ## type ## _new (stack_ ## type ## _node_t *anchor) \ + { \ + return anchor = STACK (type, \ + list_ ## type ## _new (LIST (type, anchor))); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_stack "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_FREE(type) has to be called previously. + * \return Nothing + * \post The list is completely removed from the heap. + * + * \brief template for stack destructor. + * + * this simply calls list_[type]_free(), that must be generated previously + * by a call of GEN_LIST_FREE(type). + */ +#define GEN_STACK_FREE(type) \ + STATIC \ + void \ + stack_ ## type ## _free (stack_ ## type ## _node_t *anchor) \ + { \ + list_ ## type ## _free (LIST (type, anchor)); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_stack "here". + * + * \param anchor a pointer that should contain the list anchor. + * \param e A pointer to a variable of the \a type, the + * list was created for. + * \pre GEN_LIST_INSERT(type) has to be called previously. + * \return The pushed element \a e. + * \post Element \a e is pushed in the stack pointed by + * \a anchor. + * + * \brief template for a function that pushes a value into a stack. + * + * To add a value into a stack meens to put it at the beginning of the stack. + * That is simply a call to list_[type]_insert(). + */ +#define GEN_STACK_PUSH(type) \ + STATIC \ + stack_ ## type ## _node_t * \ + stack_ ## type ## _push ( \ + stack_ ## type ## _node_t *anchor, \ + const type *e) \ + { \ + return STACK (type, list_ ## type ## _insert ( \ + LIST (type, anchor), e)); \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_stack "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_NEXT(type), GEN_LIST_RETRIVE(type) + * GEN_LIST_ISEMPTY(type) and GEN_LIST_DELETE(type) + * has to be called previously. + * \return The poped element. + * \post One Element is poped from the stack pointed to + * by \a anchor. + * + * \brief template for a function that pops a value from a stack. + * + * Pop means get and remove the first element from the stack. + * This is done here. + */ +#define GEN_STACK_POP(type) \ + STATIC \ + type * \ + stack_ ## type ## _pop (stack_ ## type ## _node_t *anchor) \ + { \ + list_ ## type ## _node_t *next; \ + const type *e; \ + \ + next = list_ ## type ## _next (LIST (type, anchor)); \ + e = list_ ## type ## _retrive (next); \ + \ + if (! list_ ## type ## _isempty (next)) \ + list_ ## type ## _delete (next); \ + \ + return (type *) e; \ + } + +/** + * \attention + * Only the generated function is explained here, for the reason look + * \ref onlyfunc_stack "here". + * + * \param anchor a pointer that should contain the list anchor. + * \pre GEN_LIST_NEXT(type) and GEN_LIST_RETRIVE(type) + * has to be called previously. + * \return The next element in the stack. + * \post None + * + * \brief get next element in the stack without poping it. + */ +#define GEN_STACK_TOP(type) \ + STATIC \ + type * \ + stack_ ## type ## _top ( \ + stack_ ## type ## _node_t *anchor) \ + { \ + return list_ ## type ## _retrive ( \ + list_ ## type ## _next (LIST (type, anchor))); \ + } + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_IMPL(type) should be called before this. + * \return Nothing + * \post The functions, struct and globals to handle stacks + * based on typesave + * lists for the given datatype are generated within the + * calling build file. + * + * \brief this creates all functions, structs and globals needed for a + * stack based on typesafe list of a given \a type. + * + * In detail the following is created by this macro:\n + * \li struct stack_[type]_node_t: This is the structure a stack + * is constructed of. Practically this just contains an instance of a + * list_[type]_node_t, as stacks are completely based on lists and does + * not add further information. + * \li all functions created by the macros defined within this file. + */ +#define GEN_STACK_IMPL(type) \ + struct stack_ ## type ## _node_t \ + { \ + struct list_node_t list; \ + }; \ + \ + GEN_STACK_NEW (type) \ + GEN_STACK_FREE (type) \ + GEN_STACK_PUSH (type) \ + GEN_STACK_POP (type) \ + GEN_STACK_TOP (type) + +#endif /* STACK_IMPL_H */ diff --git a/include/scot/stack_proto.h b/include/scot/stack_proto.h new file mode 100644 index 0000000..44135e3 --- /dev/null +++ b/include/scot/stack_proto.h @@ -0,0 +1,68 @@ +/** + * \file scot/stack_proto.h + * \author Georg Steffers + * \brief This generates all prototypes needed for stacks based on + * typesafe lists. + * + * This combines the macro definitions in \link scot/stack_type_proto.h + * scot/stack_type_proto.h \endlink and \link scot/stack_func_proto.h + * scot/stack_func_proto.h \endlink. + * Additionally defines for all errornumbers that stack will create can be + * found here. + * GEN_STACK_PROTO is normally called in a header file that describes a + * new Datatype and also wants lists of it. + * By doing this the complete interface to lists of that datatype is + * produced. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef STACK_PROTO_H +#define STACK_PROTO_H + +#include +#include + +/** + * \brief errnum if stack is empty + * \attention This is actually not use and will probably never be used + * in this form. + */ +#define STACK_EMPTY 0 + + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_PROTO(type) should have been called previous + * to this call. + * \return Nothing + * \post The typedefs, struct declaration and function prototypes + * for functions to stacks based on lists for the given + * datatype are generated within the calling build file. + * + * \brief this creates all typdefs, declarations and prototypes needed for a + * stack based on typesafe list of a given \a type. + * This is normally calles within a header file. + */ +#define GEN_STACK_PROTO(type) \ + GEN_STACK_TYPE_PROTO (type) \ + GEN_STACK_FUNC_PROTO (type) + +#endif /* STACK_PROTO_H */ diff --git a/include/scot/stack_type_proto.h b/include/scot/stack_type_proto.h new file mode 100644 index 0000000..46382c5 --- /dev/null +++ b/include/scot/stack_type_proto.h @@ -0,0 +1,61 @@ +/** + * \file scot/stack_type_proto.h + * \author Georg Steffers + * \brief Macro that creates the datatype for handling stacks based on + * linked lists. + * + * The macros here create all datatype prototypes and typedefs declarations to + * the implementations created by the macros in + * \link scot/stack_impl.h scot/stack_impl.h\endlink. + * These macros are normally not called directly within your code but + * through \link scot/stack_proto.h::GEN_STACK_PROTO GEN_STACK_PROTO\endlink. + * This is because normally one did not only + * want the datatypes but also the interface declaration to them. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef STACK_TYPE_PROTO_H +#define STACK_TYPE_PROTO_H + +/** + * \param type the datatype that this list code should handle. + * \pre Type must be a single word typename. If one wants + * to use e.g. lists of structs one has to use typedef + * to create a single word type name like this: + * typedef struct mystruct_t mystruct_t; + * GEN_LIST_TYPE_PROTO(type) should have been called + * previous to this. + * \return Nothing + * \post The datatype prototypes for stacks based on lists + * of the given datatype are created. + * + * \brief Datatype prototypes to stacks. + * + * This creates the prototypes of the datatypes needed to use the + * stack interface. + * + * Normally this is not called directly, but by + * \link scot/stack_proto.h::GEN_STACK_PROTO GEN_STACK_PROTO()\endlink + * because it is also neccesary to create the interface to this + * datatypes for working stacks. + */ +#define GEN_STACK_TYPE_PROTO(type) \ + struct stack_ ## type ## _node_t; \ + typedef struct stack_ ## type ## _node_t \ + stack_ ## type ## _node_t; \ + +#endif /* STACK_TYPE_PROTO_H */ diff --git a/include/scot/stream.h b/include/scot/stream.h new file mode 100644 index 0000000..a436a7e --- /dev/null +++ b/include/scot/stream.h @@ -0,0 +1,132 @@ +/* + * Zunächst ein paar allgemeine Bemerkungen zu scot_stream: + * + * 1. scot-stream ist eine auf andere Klassen aufbauende Klasse. Es gibt + * kein scot_stream_new. Mann kann natürlich über malloc ein leeres + * stream-objekt anlegen, das macht für gewöhnlich aber keinen Sinn. + * Stattdessen gibt es in Klassen, die stream-io Endpunkte darstellen + * Funktionen der Form scot__get_new_stream + * (z.B. scot_socket_get_new_stream). Diese Funktionen geben ein + * gültiges Stream Objekt zu einem Objekt der übergeordneten Klasse + * zurück. Das Objekt der übergeordnete Klasse muss sich alle auf diese + * Art angelegten Stream-Objekte merken. + * 2. Wenn ein Objekt der übergeordneten Klasse geschlossen wird, so werden + * auch alle Stream Objekte die von diesem erzeugt wurden freigegeben und + * auf NULL gesetzt. (Auf Sie währe eh keine Sinnvolle operation mehr + * möglich.) + * 3. Wird ein Stream-Objekt freigegeben, so bleibt das übergeordnete + * Objekt erhalten (und somit auch der handle geöffnet). + * 4. Ein close auf ein Stream Objekt schließt auch das übergeordnete + * Objekt und gibt es frei. Somit werden auch alle weiteren Stream + * Objekte geschlossen. + * + * !!!Zusammenfassend bleibt nochmal hervorzuheben das close gegenüber free + * die stärkere operation ist und zu einem free aller stream objekte und + * des übergeordneten Objekts führt.!!! + */ +#ifndef SCOT_STREAM_H +#define SCOT_STREAM_H + +#include +#include + +#define SCOT_STREAM_BUF_SIZE 100 + +/* scot stream types (scot_stream:s_type) . */ +/* Die ersten bis zum --- Kommentar sind stream_pool fähig */ +#define SCOT_STREAM_TYPE_SOCKET 0 +#define SCOT_STREAM_TYPE_PIPE 1 +#define SCOT_STREAM_TYPE_TERM 2 +/* --- */ +#define SCOT_STREAM_TYPE_FILE 3 +#define SCOT_STREAM_TYPE_INOTIFY 4 + +#ifdef WIN32 +# include +# include +# define SCOT_FILE_READ win_file_read +#else +# define SCOT_FILE_READ read +#endif /* WIN32 */ + +/* s must me a pointer to a stream...this is normally the case! */ +#define SCOT_STREAM_TO_SOCKET(s) ((struct scot_socket *)(s)) +#define SCOT_STREAM_TO_PIPE(s) ((struct scot_pipe *)(s)) +#define SCOT_STREAM_TO_TERM(s) ((struct scot_term *)(s)) +#define SCOT_STREAM_TO_FILE(s) ((struct scot_file *)(s)) +#define SCOT_STREAM_TO_INOTIFY(s) ((struct scot_inotify *)(s)) + +/* + * get the base object of the given stream with the correct type + * Those base types has to be specified here at the moment... maybe + * sometimes there will be some kind of registration method for + * base types + */ +#define SCOT_STREAM_PROV(st) \ + ((st)->s_type == SCOT_STREAM_TYPE_SOCKET)? SCOT_STREAM_TO_SOCKET((st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_PIPE)? SCOT_STREAM_TO_PIPE((st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_TERM)? SCOT_STREAM_TO_TERM((st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_FILE)? SCOT_STREAM_TO_FILE((st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_INOTIFY)?SCOT_STREAM_TO_INOTIFY((st)):(st) + +#define SCOT_STREAM_FREE(st) \ + ((st)->s_type == SCOT_STREAM_TYPE_SOCKET)?\ + scot_socket_free (SCOT_STREAM_TO_SOCKET (st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_PIPE)? scot_stream_free ((st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_TERM)? scot_stream_free ((st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_FILE)? scot_stream_free ((st)):\ + ((st)->s_type == SCOT_STREAM_TYPE_INOTIFY)?scot_stream_free ((st)):\ + scot_stream_free ((st)) + +/* + * connections via ssl must be handled separately, because communication + * over them has some very distinc behaviour then with other streams. + * First, read and write are not done on a handle but on an ssl object. + * Next, ssl has an all or nothing approach. If a write could not send + * all data it sould be assumed as failed. This is neccessary for the + * encryption. + * Next, ssl is designed to sit on top of most stream types. Thus it + * is a separate object, that should be createable by a stream object. + */ + +struct scot_stream +{ +#ifndef WIN32 + union + { + int32_t file; + int32_t sock; + } handle; +#else + union + { + HANDLE file; + SOCKET sock; + } handle; +#endif + char rbuf [SCOT_STREAM_BUF_SIZE]; + char wbuf [SCOT_STREAM_BUF_SIZE]; + uint32_t rbuf_idx; + uint32_t wbuf_idx; + int32_t s_type; /* an identifier of this stream (file, socket, pipe...) */ + int16_t s_blk; +}; + +int scot_stream_eof (struct scot_stream *); +SSIZE_T scot_stream_read (struct scot_stream *, char *, SIZE_T); +SSIZE_T scot_stream_write (struct scot_stream *, char *, SIZE_T); +void scot_stream_flush (struct scot_stream *); + +SSIZE_T scot_stream_read_pending (struct scot_stream *); +SSIZE_T scot_stream_write_pending (struct scot_stream *); + +int scot_stream_get_blocking (struct scot_stream *); +void scot_stream_set_block (struct scot_stream *); +void scot_stream_set_nonblock (struct scot_stream *); + +void scot_stream_close (struct scot_stream *); +int scot_stream_is_closed (struct scot_stream *); + +void scot_stream_free (struct scot_stream *); + +#endif /* SCOT_STREAM_H */ diff --git a/include/scot/stream_pool.h b/include/scot/stream_pool.h new file mode 100644 index 0000000..0f72e76 --- /dev/null +++ b/include/scot/stream_pool.h @@ -0,0 +1,72 @@ +#ifndef SCOT_STREAM_POOL_H +#define SCOT_STREAM_POOL_H + +#include +#include +#include + +#include +#include +#include +#include + +#define GEN_LOCAL +# include +#undef GEN_LOCAL + +/* + * folgende masken dienen dazu zu bestimmen in welches fd_set + * (read, write, exception) ein filehandle eingetragen werden soll. + * D.h. für welche operation der select diesen handle überwachen soll. + */ +#define SCOT_STREAM_POOL_FD_ADD_RMASK 0x1 +#define SCOT_STREAM_POOL_FD_ADD_WMASK 0x2 +#define SCOT_STREAM_POOL_FD_ADD_EMASK 0x4 + +#define SCOT_STREAM_POOL_CLEANUP 0x00 +#define SCOT_STREAM_POOL_RESTART_FRACTION 0x01 +#define SCOT_STREAM_POOL_START_FRACTION 0x02 +#define SCOT_STREAM_POOL_STOP_FRACTION 0x03 +#define SCOT_STREAM_POOL_RESTART_ALL_FRAC 0x04 +#define SCOT_STREAM_POOL_START_ALL_FRAC 0x05 +#define SCOT_STREAM_POOL_STOP_ALL_FRAC 0x06 + +#define SCOT_STREAM_POOL_KEEP_STREAMS 0x0 +#define SCOT_STREAM_POOL_FREE_STREAMS 0x1 + + +typedef struct scot_sp_fraction scot_sp_fraction; +GEN_LIST_TYPE_PROTO (scot_sp_fraction); + +struct scot_stream_pool +{ + struct scot_event_listener el; + + uint16_t cmd; + void * cmd_arg; + THREAD_COND_T cmd_cond; + THREAD_COND_CS_T cmd_cs; + + list_scot_sp_fraction_node_t * pool; + THREAD_MUTEX_T mutex; +}; + + +struct scot_stream_pool * scot_stream_pool_new (struct scot_stream_pool *); +void scot_stream_pool_free (struct scot_stream_pool *); +void scot_stream_pool_free_s (struct scot_stream_pool *, + int); +void scot_stream_pool_add (struct scot_stream_pool *, + struct scot_stream *, + int); +void scot_stream_pool_remove (struct scot_stream_pool *, + struct scot_stream *, + int); +int scot_stream_pool_get_rwe (struct scot_stream_pool *, + struct scot_stream *); +void scot_stream_pool_cmd (struct scot_stream_pool *, + uint16_t, + void *); +struct scot_stream * scot_sp_get_all_streams (struct scot_stream_pool *); + +#endif /* SCOT_STREAM_POOL_H */ diff --git a/include/scot/stream_pool_fraction.h b/include/scot/stream_pool_fraction.h new file mode 100644 index 0000000..6577f67 --- /dev/null +++ b/include/scot/stream_pool_fraction.h @@ -0,0 +1,53 @@ +#ifndef SCOT_STREAM_POOL_FRACTION_H +#define SCOT_STREAM_POOL_FRACTION_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SCOT_MAX_FRACTION 64 /* Windows kann max. 64 Objekte pro thread + überwachen. */ + +struct scot_sp_fraction +{ + struct scot_event_listener * el; + + int stream_type; /* what streams to accept */ + + THREAD_T thread_handle; + THREAD_ID_T thread_id; + int thread_run_flg; + + scot_thread_entry_fptr spf_entry_func; + + uint16_t s_count; + struct scot_stream * s_list [SCOT_MAX_FRACTION]; + int free_s; + + /* for select */ + fd_set rfds, wfds, efds; + int max_fd; +}; + +extern scot_thread_entry_fptr scot_spf_thread_func[]; + +struct scot_sp_fraction * scot_spf_new (struct scot_sp_fraction *, + struct scot_event_listener *, + int); +void scot_spf_free (struct scot_sp_fraction *); +void scot_spf_free_s (struct scot_sp_fraction *, + int); +void scot_spf_add (struct scot_sp_fraction *, + struct scot_stream *, + int); +void scot_spf_remove (struct scot_sp_fraction *, + struct scot_stream *, + int); + +#endif /* SCOT_STREAM_POOL_FRACTION_H */ diff --git a/include/scot/stream_win.h b/include/scot/stream_win.h new file mode 100644 index 0000000..59c70d6 --- /dev/null +++ b/include/scot/stream_win.h @@ -0,0 +1,8 @@ +#ifndef SCOT_STREAM_WIN_H +#define SCOT_STREAM_WIN_H + +#include + +SSIZE_T win_file_read (HANDLE, void *, SIZE_T); + +#endif /* SCOT_STREAM_WIN_H */ diff --git a/include/scot/thread_none.h b/include/scot/thread_none.h new file mode 100644 index 0000000..f8ecbc4 --- /dev/null +++ b/include/scot/thread_none.h @@ -0,0 +1,51 @@ +/* + * used for threadsafety within exception + * actually only pthread is supported + */ +#ifndef THREAD_H +#define THREAD_H + +#include + +#define JOIN_OK 0x00 +#define JOIN_TIMEOUT 0x01 +#define JOIN_ERROR 0x02 + +/* + * i guess it is possible to write mutex code that uses cond + * elements to timeout on lock, but right now i have no clear + * idea of how to do this. The following thoughts i had: + * - create a struct that holds a pthread_mutex_t and a pthread_cond_t + * for every mutex. + * - lock mutexes only by calling pthread_cond_timedwait + * And here starts the problem...to do a cond_timedwait i need to ensure + * that the mutex is already locked...well and then a question i have not + * clearyfied, if a thread ends, will all locks on mutexes be removed? + * I guess so, but i did not test it. And an even more important question, + * is it possible to write cleanup-code that guarantees that a + * pthread_cond_signal is called for every mutex the thread holds... + * well, we will end up with the requirement that the programmer has to + * call a release mutex function before ending a thread and if the programmer + * forgets that the behaviour of out code is unspecified + * Right now i will not support timeouts on mutex-locks and to be consistent + * it is not supported on win32 either, also it would be much easier to + * implement there. + */ +#define MUTEX_LOCK_OK 0x00 +#define MUTEX_LOCK_ERROR 0x02 + +#define THREAD_T int +#define THREAD_ID_T int +#define THREAD_ID 0 +#define SELF_THREAD 0 +#define NEW_THREAD perror ("No supported thread system used!\n") +#define CANCEL_THREAD(t) NEW_THREAD +#define EXIT_THREAD(r) NEW_THREAD +#define JOIN_THREAD(t,i) NEW_THREAD +#define THREAD_CANCEL_ENABLE NEW_THREAD +#define THREAD_CANCEL_DISABLE NEW_THREAD +#define THREAD_CANCEL_ASYNC NEW_THREAD +#define THREAD_CANCEL_DEFER NEW_THREAD +#define T_PROC_RET int + +#endif /* THREAD_H */ diff --git a/include/scot/win32/dir.h b/include/scot/win32/dir.h new file mode 100644 index 0000000..5b70cf0 --- /dev/null +++ b/include/scot/win32/dir.h @@ -0,0 +1,44 @@ +/* + * dir.h: as one might suggest here are definitions and prototypes for + * win32/dir.c + * + * Copyright (C) 2006 Georg Steffers + * + * Author: Georg Steffers [GST] + * Developer: + * + * Changes (for this file only): + * (2006-06-12) [GST] Started this changelog...well the program is + * ready since some weeks right now. + */ +#ifndef DIR_H +#define DIR_H + +#include + +#include + +/* abstraction for file information and an directory handle */ +#define FILE_DATA struct dir_file +#define DIR_HANDLE HANDLE + +/* abstraction for checking if a file is a directory */ +#define IS_DIRECTORY(file_data) \ + ((file_data).file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + +/* abstraction for getting the filename of a directory entry. */ +#define DIRENT_FNAME(file_data) ((file_data).file.cFileName) + +/* structure for all available information about the directory entry. */ +struct dir_file +{ + char path [MAX_PATH+1]; + WIN32_FIND_DATA file; +}; + +/* the last in parameter is posix specific. It defines if stat should + * follow symlinks or not. Under Windows it is ignored. */ +DIR_HANDLE get_dir_first (const char *, FILE_DATA *, int); +int get_dir_next (DIR_HANDLE, const char *, FILE_DATA *, int); + +#endif /* DIR_H */ diff --git a/include/scot/win32/memory.h b/include/scot/win32/memory.h new file mode 100644 index 0000000..4b3d4c5 --- /dev/null +++ b/include/scot/win32/memory.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * memory.h: Prototypes for default memory operations like memcpy and thus * + * on a win32 system nearly all CRT functions (CRT is the * + * windows C RunTime, that is the normal libC) aren't thread- * + * safe. As i build up tests this causes problems for example * + * with strcpy. So i decided to write wrapper that use non CRT * + * functions on Windows and normal libC ones on a posix system. * + * * + * Author: Georg Steffers * + * Date: 03/06/2006 * + ***************************************************************************/ +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include + +#include +#include + +#define SCOT_MEM_GET(s) HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, (s)); +#define SCOT_MEM_FREE(p) \ + if ((p)) {HeapFree(GetProcessHeap(), 0, (p)); (p)=NULL;} +#define SCOT_MEM_COPY(p,q,s) (CopyMemory ((p), (q), (s)), (p)) +#define SCOT_MEM_MOVE(p,q,s) (MoveMemory ((p), (q), (s)), (p)) +#define SCOT_MEM_FILL(p,v,s) (FillMemory ((p), (s), (v)), (p)) +#define SCOT_MEM_ZERO ZeroMemory +#define SCOT_STR_LENGTH str_length +#define SCOT_STR_COPY str_copy +#define SCOT_STRN_COPY strn_copy +#define SCOT_STR_CHAR str_char +#define SCOT_STRR_CHAR strr_char + +SIZE_T str_length (const char *); +char * str_copy (char *, const char *); +char * strn_copy (char *, const char *, SIZE_T); +char * str_char (const char *, int); +char * strr_char (const char *, int); + +#endif /* MEMORY_H */ diff --git a/include/scot/win32/scot_types.h b/include/scot/win32/scot_types.h new file mode 100644 index 0000000..4530747 --- /dev/null +++ b/include/scot/win32/scot_types.h @@ -0,0 +1,6 @@ +#ifndef SCOT_TYPES_H +#define SCOT_TYPES_H + +#include + +#endif /* SCOT_TYPES_H */ diff --git a/include/scot/win32/thread.h b/include/scot/win32/thread.h new file mode 100644 index 0000000..d10a5f7 --- /dev/null +++ b/include/scot/win32/thread.h @@ -0,0 +1,79 @@ +/* + * used for threadsafety within exception + * actually only pthread is supported + */ +#ifndef THREAD_H +#define THREAD_H + +#include +#include + + +#define JOIN_OK 0x00 +#define JOIN_TIMEOUT 0x01 +#define JOIN_ERROR 0x02 + +/* + * i guess it is possible to write mutex code that uses cond + * elements to timeout on lock, but right now i have no clear + * idea of how to do this. The following thoughts i had: + * - create a struct that holds a pthread_mutex_t and a pthread_cond_t + * for every mutex. + * - lock mutexes only by calling pthread_cond_timedwait + * And here starts the problem...to do a cond_timedwait i need to ensure + * that the mutex is already locked...well and then a question i have not + * clearyfied, if a thread ends, will all locks on mutexes be removed? + * I guess so, but i did not test it. And an even more important question, + * is it possible to write cleanup-code that guarantees that a + * pthread_cond_signal is called for every mutex the thread holds... + * well, we will end up with the requirement that the programmer has to + * call a release mutex function before ending a thread and if the programmer + * forgets that the behaviour of out code is unspecified + * Right now i will not support timeouts on mutex-locks and to be consistent + * it is not supported on win32 either, also it would be much easier to + * implement there. + */ +#define MUTEX_LOCK_OK 0x00 +#define MUTEX_LOCK_ERROR 0x02 + +#define THREAD_T HANDLE +#define THREAD_ID_T DWORD +#define THREAD_ID GetCurrentThreadId () +#define SELF_THREAD GetCurrentThread () +#define NEW_THREAD thread_new +#define CANCEL_THREAD(thread) (! TerminateThread ((thread), -1)) +#define EXIT_THREAD(retval) _endthreadex ((DWORD) (retval)) +#define JOIN_THREAD thread_join +#define THREAD_CANCEL_ENABLE +#define THREAD_CANCEL_DISABLE +#define THREAD_CANCEL_ASYNC +#define THREAD_CANCEL_DEFER +#define T_PROC_RET DWORD WINAPI + +#define THREAD_MUTEX_T HANDLE +#define NEW_THREAD_MUTEX CreateMutex (NULL, FALSE, NULL) +#define LOCK_THREAD_MUTEX thread_mutex_lock +#define UNLOCK_THREAD_MUTEX(mutex) (! ReleaseMutex ((mutex))) + +#define THREAD_COND_T CONDITION_VARIABLE +#define THREAD_COND_CS_T CRITICAL_SECTION +#define GET_THREAD_COND(c) +#define GET_THREAD_COND_CS(c) +#define THREAD_COND_ENTER_CS(cs) EnterCriticalSection ((cs)) +#define THREAD_COND_LEAVE_CS(cs) LeaveCriticalSection ((cs)) +#define SIGNAL_THREAD_COND(cond) WakeConditionVariable ((cond)) +#define BCAST_THREAD_COND(cond) WakeAllConditionVariable ((cond)) +#define WAIT_THREAD_COND(cond, cs, t) \ + SleepConditionVariableCS((cond), (cs), t) + +#define THREAD_ATEXIT_BEGIN +#define THREAD_ATEXIT_END + +typedef T_PROC_RET (*scot_thread_entry_fptr) (void *); + +THREAD_T thread_new (T_PROC_RET (*)(void *), void*); +int thread_join (THREAD_T, unsigned int); +THREAD_MUTEX_T thread_mutex_new (void); +int thread_mutex_lock (THREAD_MUTEX_T *); + +#endif /* THREAD_H */ diff --git a/include/scot_common.h b/include/scot_common.h new file mode 100644 index 0000000..f17ddad --- /dev/null +++ b/include/scot_common.h @@ -0,0 +1,98 @@ +/* + * scot_common.h: commen difinitions for scot. + * scot is a c obliging toolbox. + * + * Copyright (C) 2006 Georg Steffers. All rights reserved. + * + * This software is licensed under the terms of the GNU Genral Public + * License (GPL). Please see gpl.txt for details or refer to + * http://www.gnu.org/licenses/gpl.html + * + * Author: Georg Steffers + * + * 01/22/2006: Georg Steffers - introduced to give support for gettext + * to all code used in san. + * 01/24/2006: Georg Steffers - introduce some new macros: + * SANTEXTDOMAIN -> textdomain for messages + * given by libsan functions. + * LOCALDIR -> directory where to find that + * textdomain. + * D_(s) -> translates with the + * SANTEXTDOMAIN. This should be + * called for strings defined + * within san. But remember to + * call + * bindtextdomain ( + * SANTEXTDOMAIN, LOCALEDIR); + * first. + */ +#ifndef SCOT_COMMOM_H +#define SCOT_COMMON_H + +#include +#include +#include +#include + +/* for PACKAGE and LOCALEDIR */ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#else +#define PACKAGE "scot" +#define LOCALEDIR "/usr/share/locale" +#endif /* HAVE_CONFIG_H */ + +#define _(string) gettext(string) /* our mark for xgettext */ +#define D_(s) dgettext(PACKAGE, s) + /* a mark dgettext calls */ +#define N_(string) (string) /* noop mark for gettext */ + +/* inline for C++, GCC, C99. For C89 these macros are empty. */ +#if defined(__cplusplus) +# define STATIC_INLINE static inline +# define INLINE inline +scot/# define EXTERN_INLINE inline +#elif defined(__GNUC__) +# define STATIC_INLINE static __inline__ +# define INLINE static __inline__ +# define EXTERN_INLINE extern __inline__ +#elif __STDC_VERSION__ >= 199901 +# define STATIC_INLINE static inline +# define INLINE static inline +# define EXTERN_INLINE inline +#else /* C89 */ +# define STATIC_INLINE static +# define INLINE static +# define EXTERN_INLINE +#endif /* inline definitions */ + +#define SCOT_SOCKET_BACKLOG 5 + +/*int scot_strisdigit (const char *);*/ +#ifndef SCOT_STRISDIGIT +#define SCOT_STRISDIGIT scot_strisdigit +static +inline +int scot_strisdigit (const char * str) +{ + while (*str) + if (!isdigit (*(str++))) return 0; + return 1; +} +#endif + +#ifndef UNIX_PATH_MAX +/* + * this should be done better....i got this val from linux/un.h but as + * this should be portable i should think about a more portable way to get + * this...maybe with configure.... + */ +#define UNIX_PATH_MAX 108 +#endif /* UNIX_PATH_MAX */ + +#define SCOT_UNX_PATH_TO_LONG 0 +extern const char * scot_common_errmsg[]; + +int base2exp (uint32_t); + +#endif /* SCOT_COMMON_H */ diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 0000000..f971283 --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 intmax.m4 inttypes.m4 inttypes_h.m4 inttypes-pri.m4 isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 longdouble.m4 longlong.m4 nls.m4 po.m4 printf-posix.m4 progtest.m4 signed.m4 size_max.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4 wchar_t.m4 wint_t.m4 xsize.m4 diff --git a/m4/ax_create_stdint_h.m4 b/m4/ax_create_stdint_h.m4 new file mode 100644 index 0000000..78fa2ab --- /dev/null +++ b/m4/ax_create_stdint_h.m4 @@ -0,0 +1,685 @@ +dnl @synopsis AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])] +dnl +dnl the "ISO C9X: 7.18 Integer types " section requires the +dnl existence of an include file that defines a set of +dnl typedefs, especially uint8_t,int32_t,uintptr_t. Many older +dnl installations will not provide this file, but some will have the +dnl very same definitions in . In other enviroments we can +dnl use the inet-types in which would define the typedefs +dnl int8_t and u_int8_t respectivly. +dnl +dnl This macros will create a local "_stdint.h" or the headerfile given +dnl as an argument. In many cases that file will just "#include +dnl " or "#include ", while in other environments +dnl it will provide the set of basic 'stdint's definitions/typedefs: +dnl +dnl int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t +dnl int_least32_t.. int_fast32_t.. intmax_t +dnl +dnl which may or may not rely on the definitions of other files, or +dnl using the AC_CHECK_SIZEOF macro to determine the actual sizeof each +dnl type. +dnl +dnl if your header files require the stdint-types you will want to +dnl create an installable file mylib-int.h that all your other +dnl installable header may include. So if you have a library package +dnl named "mylib", just use +dnl +dnl AX_CREATE_STDINT_H(mylib-int.h) +dnl +dnl in configure.ac and go to install that very header file in +dnl Makefile.am along with the other headers (mylib.h) - and the +dnl mylib-specific headers can simply use "#include " to +dnl obtain the stdint-types. +dnl +dnl Remember, if the system already had a valid , the +dnl generated file will include it directly. No need for fuzzy +dnl HAVE_STDINT_H things... +dnl +dnl @category C +dnl @author Guido Draheim +dnl @version 2003-12-07 +dnl @license GPLWithACException + +AC_DEFUN([AX_CHECK_DATA_MODEL],[ + AC_CHECK_SIZEOF(char) + AC_CHECK_SIZEOF(short) + AC_CHECK_SIZEOF(int) + AC_CHECK_SIZEOF(long) + AC_CHECK_SIZEOF(void*) + ac_cv_char_data_model="" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short" + ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long" + ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp" + AC_MSG_CHECKING([data model]) + case "$ac_cv_char_data_model/$ac_cv_long_data_model" in + 122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;; + 122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;; + 122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;; + 124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;; + 124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;; + 124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;; + 124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;; + 128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;; + 128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;; + 222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;; + 333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;; + 444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;; + 666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;; + 888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;; + 222/*|333/*|444/*|666/*|888/*) : + ac_cv_data_model="iDSP" ; n="unusual dsptype" ;; + *) ac_cv_data_model="none" ; n="very unusual model" ;; + esac + AC_MSG_RESULT([$ac_cv_data_model ($ac_cv_long_data_model, $n)]) +]) + +dnl AX_CHECK_HEADER_STDINT_X([HEADERLIST][,ACTION-IF]) +AC_DEFUN([AX_CHECK_HEADER_STDINT_X],[ +AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[ + ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[stdint.h inttypes.h sys/inttypes.h]) ; do + unset ac_cv_type_uintptr_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uintptr_t,[ac_cv_header_stdint_x=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + done + AC_MSG_CHECKING([for stdint uintptr_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_O],[ +AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[ + ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[inttypes.h sys/inttypes.h stdint.h]) ; do + unset ac_cv_type_uint32_t + unset ac_cv_type_uint64_t + AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + break; + done + AC_MSG_CHECKING([for stdint uint32_t]) + ]) +]) + +AC_DEFUN([AX_CHECK_HEADER_STDINT_U],[ +AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[ + ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h) + AC_MSG_RESULT([(..)]) + for i in m4_ifval([$1],[$1],[sys/types.h inttypes.h sys/inttypes.h]) ; do + unset ac_cv_type_u_int32_t + unset ac_cv_type_u_int64_t + AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],continue,[#include <$i>]) + AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>]) + m4_ifvaln([$1],[$1]) break + break; + done + AC_MSG_CHECKING([for stdint u_int32_t]) + ]) +]) + +AC_DEFUN([AX_CREATE_STDINT_H], +[# ------ AX CREATE STDINT H ------------------------------------- +AC_MSG_CHECKING([for stdint types]) +ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)` +# try to shortcircuit - if the default include path of the compiler +# can find a "stdint.h" header then we assume that all compilers can. +AC_CACHE_VAL([ac_cv_header_stdint_t],[ +old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS="" +old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS="" +old_CFLAGS="$CFLAGS" ; CFLAGS="" +AC_TRY_COMPILE([#include ],[int_least32_t v = 0;], +[ac_cv_stdint_result="(assuming C99 compatible system)" + ac_cv_header_stdint_t="stdint.h"; ], +[ac_cv_header_stdint_t=""]) +CXXFLAGS="$old_CXXFLAGS" +CPPFLAGS="$old_CPPFLAGS" +CFLAGS="$old_CFLAGS" ]) + +v="... $ac_cv_header_stdint_h" +if test "$ac_stdint_h" = "stdint.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)]) +elif test "$ac_stdint_h" = "inttypes.h" ; then + AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)]) +elif test "_$ac_cv_header_stdint_t" = "_" ; then + AC_MSG_RESULT([(putting them into $ac_stdint_h)$v]) +else + ac_cv_header_stdint="$ac_cv_header_stdint_t" + AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)]) +fi + +if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit.. + +dnl .....intro message done, now do a few system checks..... +dnl btw, all old CHECK_TYPE macros do automatically "DEFINE" a type, +dnl therefore we use the autoconf implementation detail CHECK_TYPE_NEW +dnl instead that is triggered with 3 or more arguments (see types.m4) + +inttype_headers=`echo $2 | sed -e 's/,/ /g'` + +ac_cv_stdint_result="(no helpful system typedefs seen)" +AX_CHECK_HEADER_STDINT_X(dnl + stdint.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen uintptr_t$and64 in $i)") + +if test "_$ac_cv_header_stdint_x" = "_" ; then +AX_CHECK_HEADER_STDINT_O(dnl, + inttypes.h sys/inttypes.h stdint.h $inttype_headers, + ac_cv_stdint_result="(seen uint32_t$and64 in $i)") +fi + +if test "_$ac_cv_header_stdint_x" = "_" ; then +if test "_$ac_cv_header_stdint_o" = "_" ; then +AX_CHECK_HEADER_STDINT_U(dnl, + sys/types.h inttypes.h sys/inttypes.h $inttype_headers, + ac_cv_stdint_result="(seen u_int32_t$and64 in $i)") +fi fi + +dnl if there was no good C99 header file, do some typedef checks... +if test "_$ac_cv_header_stdint_x" = "_" ; then + AC_MSG_CHECKING([for stdint datatype model]) + AC_MSG_RESULT([(..)]) + AX_CHECK_DATA_MODEL +fi + +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_x" +elif test "_$ac_cv_header_stdint_o" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_o" +elif test "_$ac_cv_header_stdint_u" != "_" ; then + ac_cv_header_stdint="$ac_cv_header_stdint_u" +else + ac_cv_header_stdint="stddef.h" +fi + +AC_MSG_CHECKING([for extra inttypes in chosen header]) +AC_MSG_RESULT([($ac_cv_header_stdint)]) +dnl see if int_least and int_fast types are present in _this_ header. +unset ac_cv_type_int_least32_t +unset ac_cv_type_int_fast32_t +AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>]) +AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>]) +AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>]) + +fi # shortcircut to system "stdint.h" +# ------------------ PREPARE VARIABLES ------------------------------ +if test "$GCC" = "yes" ; then +ac_cv_stdint_message="using gnu compiler "`$CC --version | head -n 1` +else +ac_cv_stdint_message="using $CC" +fi + +AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl +$ac_cv_stdint_result]) + +dnl ----------------------------------------------------------------- +# ----------------- DONE inttypes.h checks START header ------------- +AC_CONFIG_COMMANDS([$ac_stdint_h],[ +AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h) +ac_stdint=$tmp/_stdint.h + +echo "#ifndef" $_ac_stdint_h >$ac_stdint +echo "#define" $_ac_stdint_h "1" >>$ac_stdint +echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint +echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint +echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint +if test "_$ac_cv_header_stdint_t" != "_" ; then +echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint +echo "#include " >>$ac_stdint +echo "#endif" >>$ac_stdint +echo "#endif" >>$ac_stdint +else + +cat >>$ac_stdint < +#else +#include + +/* .................... configured part ............................ */ + +STDINT_EOF + +echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_x" != "_" ; then + ac_header="$ac_cv_header_stdint_x" + echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint +fi + +echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint +if test "_$ac_cv_header_stdint_o" != "_" ; then + ac_header="$ac_cv_header_stdint_o" + echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint +fi + +echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint +if test "_$ac_cv_header_stdint_u" != "_" ; then + ac_header="$ac_cv_header_stdint_u" + echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint +else + echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint +fi + +echo "" >>$ac_stdint + +if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then + echo "#include <$ac_header>" >>$ac_stdint + echo "" >>$ac_stdint +fi fi + +echo "/* which 64bit typedef has been found */" >>$ac_stdint +if test "$ac_cv_type_uint64_t" = "yes" ; then +echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint +fi +if test "$ac_cv_type_u_int64_t" = "yes" ; then +echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* which type model has been detected */" >>$ac_stdint +if test "_$ac_cv_char_data_model" != "_" ; then +echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint +echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint +else +echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint +echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint +fi +echo "" >>$ac_stdint + +echo "/* whether int_least types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_least32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint +fi +echo "/* whether int_fast types were detected */" >>$ac_stdint +if test "$ac_cv_type_int_fast32_t" = "yes"; then +echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint +fi +echo "/* whether intmax_t type was detected */" >>$ac_stdint +if test "$ac_cv_type_intmax_t" = "yes"; then +echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint +else +echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint +fi +echo "" >>$ac_stdint + + cat >>$ac_stdint <= 199901L +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#elif !defined __STRICT_ANSI__ +#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__ +#define _HAVE_UINT64_T +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__ +/* note: all ELF-systems seem to have loff-support which needs 64-bit */ +#if !defined _NO_LONGLONG +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef long long int64_t; +typedef unsigned long long uint64_t; +#endif + +#elif defined __alpha || (defined __mips && defined _ABIN32) +#if !defined _NO_LONGLONG +typedef long int64_t; +typedef unsigned long uint64_t; +#endif + /* compiler/cpu type to define int64_t */ +#endif +#endif +#endif + +#if defined _STDINT_HAVE_U_INT_TYPES +/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */ +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; + +/* glibc compatibility */ +#ifndef __int8_t_defined +#define __int8_t_defined +#endif +#endif + +#ifdef _STDINT_NEED_INT_MODEL_T +/* we must guess all the basic types. Apart from byte-adressable system, */ +/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */ +/* (btw, those nibble-addressable systems are way off, or so we assume) */ + +dnl /* have a look at "64bit and data size neutrality" at */ +dnl /* http://unix.org/version2/whatsnew/login_64bit.html */ +dnl /* (the shorthand "ILP" types always have a "P" part) */ + +#if defined _STDINT_BYTE_MODEL +#if _STDINT_LONG_MODEL+0 == 242 +/* 2:4:2 = IP16 = a normal 16-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444 +/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */ +/* 4:4:4 = ILP32 = a normal 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488 +/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */ +/* 4:8:8 = LP64 = a normal 64-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* this system has a "long" of 64bit */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +typedef unsigned long uint64_t; +typedef long int64_t; +#endif +#elif _STDINT_LONG_MODEL+0 == 448 +/* LLP64 a 64-bit system derived from a 32-bit system */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +#endif +/* assuming the system has a "long long" */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#define _HAVE_LONGLONG_UINT64_T +typedef unsigned long long uint64_t; +typedef long long int64_t; +#endif +#else +#define _STDINT_NO_INT32_T +#endif +#else +#define _STDINT_NO_INT8_T +#define _STDINT_NO_INT32_T +#endif +#endif + +/* + * quote from SunOS-5.8 sys/inttypes.h: + * Use at your own risk. As of February 1996, the committee is squarely + * behind the fixed sized types; the "least" and "fast" types are still being + * discussed. The probability that the "fast" types may be removed before + * the standard is finalized is high enough that they are not currently + * implemented. + */ + +#if defined _STDINT_NEED_INT_LEAST_T +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_least64_t; +#endif + +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_least64_t; +#endif + /* least types */ +#endif + +#if defined _STDINT_NEED_INT_FAST_T +typedef int8_t int_fast8_t; +typedef int int_fast16_t; +typedef int32_t int_fast32_t; +#ifdef _HAVE_UINT64_T +typedef int64_t int_fast64_t; +#endif + +typedef uint8_t uint_fast8_t; +typedef unsigned uint_fast16_t; +typedef uint32_t uint_fast32_t; +#ifdef _HAVE_UINT64_T +typedef uint64_t uint_fast64_t; +#endif + /* fast types */ +#endif + +#ifdef _STDINT_NEED_INTMAX_T +#ifdef _HAVE_UINT64_T +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#else +typedef long intmax_t; +typedef unsigned long uintmax_t; +#endif +#endif + +#ifdef _STDINT_NEED_INTPTR_T +#ifndef __intptr_t_defined +#define __intptr_t_defined +/* we encourage using "long" to store pointer values, never use "int" ! */ +#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484 +typedef unsinged int uintptr_t; +typedef int intptr_t; +#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444 +typedef unsigned long uintptr_t; +typedef long intptr_t; +#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T +typedef uint64_t uintptr_t; +typedef int64_t intptr_t; +#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */ +typedef unsigned long uintptr_t; +typedef long intptr_t; +#endif +#endif +#endif + +/* The ISO C99 standard specifies that in C++ implementations these + should only be defined if explicitly requested. */ +#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS +#ifndef UINT32_C + +/* Signed. */ +# define INT8_C(c) c +# define INT16_C(c) c +# define INT32_C(c) c +# ifdef _HAVE_LONGLONG_UINT64_T +# define INT64_C(c) c ## L +# else +# define INT64_C(c) c ## LL +# endif + +/* Unsigned. */ +# define UINT8_C(c) c ## U +# define UINT16_C(c) c ## U +# define UINT32_C(c) c ## U +# ifdef _HAVE_LONGLONG_UINT64_T +# define UINT64_C(c) c ## UL +# else +# define UINT64_C(c) c ## ULL +# endif + +/* Maximal type. */ +# ifdef _HAVE_LONGLONG_UINT64_T +# define INTMAX_C(c) c ## L +# define UINTMAX_C(c) c ## UL +# else +# define INTMAX_C(c) c ## LL +# define UINTMAX_C(c) c ## ULL +# endif + + /* literalnumbers */ +#endif +#endif + +/* These limits are merily those of a two complement byte-oriented system */ + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types. */ +# define UINT8_MAX (255) +# define UINT16_MAX (65535) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (__UINT64_C(18446744073709551615)) + +/* Minimum of signed integral types having a minimum size. */ +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST64_MIN INT64_MIN +/* Maximum of signed integral types having a minimum size. */ +# define INT_LEAST8_MAX INT8_MAX +# define INT_LEAST16_MAX INT16_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST64_MAX INT64_MAX + +/* Maximum of unsigned integral types having a minimum size. */ +# define UINT_LEAST8_MAX UINT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define UINT_LEAST64_MAX UINT64_MAX + + /* shortcircuit*/ +#endif + /* once */ +#endif +#endif +STDINT_EOF +fi + if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then + AC_MSG_NOTICE([$ac_stdint_h is unchanged]) + else + ac_dir=`AS_DIRNAME(["$ac_stdint_h"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f $ac_stdint_h + mv $ac_stdint $ac_stdint_h + fi +],[# variables for create stdint.h replacement +PACKAGE="$PACKAGE" +VERSION="$VERSION" +ac_stdint_h="$ac_stdint_h" +_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h) +ac_cv_stdint_message="$ac_cv_stdint_message" +ac_cv_header_stdint_t="$ac_cv_header_stdint_t" +ac_cv_header_stdint_x="$ac_cv_header_stdint_x" +ac_cv_header_stdint_o="$ac_cv_header_stdint_o" +ac_cv_header_stdint_u="$ac_cv_header_stdint_u" +ac_cv_type_uint64_t="$ac_cv_type_uint64_t" +ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t" +ac_cv_char_data_model="$ac_cv_char_data_model" +ac_cv_long_data_model="$ac_cv_long_data_model" +ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t" +ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t" +ac_cv_type_intmax_t="$ac_cv_type_intmax_t" +]) +]) diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..a2985c6 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,61 @@ +LTLIBS = $(INTLLIBS) +LIBS = $(INTLLIBS) + +LTLIBS += @THREAD_LIB@ @SOCK_LIB@ +LIBS += @THREAD_LIB@ @SOCK_LIB@ + +INCLUDES = -I../include @INOTIFY_INCLUDES@ + +libscot_source = scot_common.c cmdla.c \ + list.c stack.c queue.c \ + thread.c dir.c \ + exception.c scot_exceptions.c \ + stream.c stream_ctl.c socket.c socket_in.c \ + event.c event_listener.c \ + stream_pool_base.c stream_pool_management.c \ + stream_pool_fraction.c spf_thread_impl.c + +BUILT_SOURCES = thread.c memory.c dir.c spf_thread_impl.c stream_ctl.c +CLEANFILES = thread.c memory.c dir.c spf_thread_impl.c stream_ctl.c + +if PTHREAD +thread.c: Makefile posix/thread.c + cp posix/thread.c ./thread.c +else +thread.c: Makefile win32/thread.c + cp win32/thread.c ./thread.c +endif + +if WIN32 +libscot_source += memory.c stream_win.c + +stream_ctl.c: Makefile win32/stream_ctl.c + cp win32/stream_ctl.c ./stream_ctl.c +spf_thread_impl.c: Makefile win32/spf_thread_impl.c + cp win32/spf_thread_impl.c ./spf_thread_impl.c +memory.c: Makefile win32/memory.c + cp win32/memory.c ./memory.c +dir.c: Makefile win32/dir.c + cp win32/dir.c ./dir.c +else +libscot_source += socket_un.c fs_watcher.c + +stream_ctl.c: Makefile posix/stream_ctl.c + cp posix/stream_ctl.c ./stream_ctl.c +spf_thread_impl.c: Makefile posix/spf_thread_impl.c + cp posix/spf_thread_impl.c ./spf_thread_impl.c +memory.c: Makefile + touch memory.c +dir.c: Makefile posix/dir.c + cp posix/dir.c ./dir.c +endif + +lib_LTLIBRARIES = libscot.la + +libscot_la_SOURCES = $(libscot_source) +libscot_la_LDFLAGS = -version-info 0:3:0 -no-undefined +libscot_la_CFLAGS = @THREAD_CFLAGS@ + +# install: install-am +# strip -x $(DESTDIR)$(libdir)/libscot.so.0.0.2 +# strip -x $(DESTDIR)$(libdir)/libscot.a diff --git a/src/cmdla.c b/src/cmdla.c new file mode 100644 index 0000000..3c640a3 --- /dev/null +++ b/src/cmdla.c @@ -0,0 +1,408 @@ +/* + * cmdla.c: Some small function to make command line argument parsing + * more convinient. Actually this is only intended for single + * character arguments. + * COMMENT: This is only appropriate for default application that do not + * want to do special things like assining same parameters to + * different files. For an example of such an application see sox. + * But this behavier can be, at least to some level achieved by + * using an appropriate callback, that saves all paramters in a + * special manner, associated to the given filename that in turn + * needs to be an option too. + * + * Copyright (C) 2006 Georg Steffers. All rights reserved. + * + * This software is licensed under the terms of the GNU Genral Public + * License (GPL). Please see gpl.txt for details or refer to + * http://www.gnu.org/licenses/gpl.html + * + * Author: Georg Steffers + * + * 01/14/2006: Georg Steffers - First implemented + * 01/15/2006: Georg Steffers - V0.1 ready. Modified this and that. + * now long arguments are supported as + * well as arguments with am optional + * parameter. + * 01/23/2006: Georg Steffers - add support for gettext. + * 01/24/2006: Georg Steffers - changes so that cmdla uses the textdomain + * of san (the lib cmdla belongs to) for the + * few strings that introduces itself, namely + * "help", "gives this help", "switches", + * "arguments" and the formatstring + * "usage: %s [%s] [%s] %s". All other + * strings given to process_cmd_line from + * within a program are translated with + * the textdomain of that program, e.g. + * the function should always be called with + * the untranslated strings. This is done to make + * it possible to call it with constant strings + * declared before any call of bindtextdomain + * or textdomain. + */ + +#define MAX_ARGS 200 + + +#include +#include +#include +#include + +#include +#include + +/* + * search for an appropriate cb to @name in @pcbs and @scbs and call + * it. If none is found call the help cb. + */ +static +int +call_cb ( + const struct cmdlap_cbt *pcbs, + const struct cmdlas_cbt *scbs, + const char *optarg, + const char *name, + const char val) +{ + int cbs_idx; + + bindtextdomain (PACKAGE, LOCALEDIR); + + cbs_idx = 0; + while (pcbs[cbs_idx].cb != NULL) + { + if ((name != NULL && pcbs[cbs_idx].sarg == name) || + (val != 0 && pcbs[cbs_idx].carg == val)) + { + switch (pcbs[cbs_idx].cmdla_argt) { + case CMDLA_REQ_ARG: + if (optarg == NULL) + return call_cb (pcbs, scbs, optarg, D_("help"), '?'); + + /* we got an arg, so fallthrough CMDLA_OPT_ARG to get + * it processed */ + case CMDLA_OPT_ARG: + return + pcbs[cbs_idx].cb ( + optarg, + pcbs[cbs_idx].cmdla_type, + pcbs[cbs_idx].var); + } + } + + cbs_idx++; + } + + cbs_idx = 0; + while (scbs[cbs_idx].cb != NULL) + { + if ((name != NULL && scbs[cbs_idx].sarg == name) || + (val != 0 && scbs[cbs_idx].carg == val)) + return scbs[cbs_idx].cb (scbs[cbs_idx].var); + + cbs_idx++; + } + + return call_cb (pcbs, scbs, optarg, D_("help"), '?'); +} + +/* + * If no usage_cb is given to cmdla, then create a basic default usage. + */ +int +default_usage_cb (void *info) +{ + struct du_infot *du_infop; + const struct cmdlas_cbt *sw; + const struct cmdlap_cbt *ar; + + bindtextdomain (PACKAGE, LOCALEDIR); + + du_infop = (struct du_infot *) info; + sw = du_infop->switches; + ar = du_infop->arguments; + + if (du_infop->about != NULL) + fprintf (stderr, du_infop->about); + + if (du_infop->copyright != NULL) + { + fprintf (stderr, "\n\n"); + fprintf (stderr, du_infop->copyright); + } + + fprintf (stderr, "\n\n"); + fprintf ( + stderr, + D_("Usage: %s [%s] [%s] %s"), + du_infop->program_name, + D_("switches"), D_("arguments"), + du_infop->usage_aa != NULL ? du_infop->usage_aa : ""); + fprintf (stderr, "\n\n"); + + fprintf (stderr, "\t%s: \n", D_("switches")); + + while (sw != NULL && sw->cb != NULL) + { + fprintf ( + stderr, + "\t %c%c%s%-7s : %-45s\n", + sw->carg != 0 ? '-' : ' ', + sw->carg != 0 ? sw->carg : ' ', + (sw->sarg != NULL && + sw->carg != 0) ? " / --" : + (sw->sarg != NULL && + sw->carg == 0) ? " --" : " ", + sw->sarg != NULL ? sw->sarg : "", + sw->info != NULL ? sw->info : ""); + + sw++; + } + + fprintf (stderr, "\t%s: \n", D_("arguments")); + + while (ar != NULL && ar->cb != NULL) + { + fprintf ( + stderr, + "\t %c%c%-8s%s%s%-10s : %-20s\n", + ar->carg != 0 ? '-' : ' ', + ar->carg != 0 ? ar->carg : ' ', + (ar->cmdla_argt != CMDLA_OPT_ARG && + ar->carg != 0) ? "[ ]" : + (ar->carg == 0) ? "" : "[val]", + (ar->sarg != NULL && + ar->carg != 0) ? " | --" : + (ar->sarg != NULL && + ar->carg == 0) ? " --" : " ", + ar->sarg != NULL ? ar->sarg : "", + (ar->cmdla_argt != CMDLA_OPT_ARG && + ar->sarg != NULL) ? "[=| ]" : + (ar->sarg == NULL) ? "" : "[=val]", + ar->info != NULL ? ar->info : ""); + + ar++; + } + + exit(-1); +} + +int +switch_cb (void *arg) +{ + int * sw = (int *) arg; + + *sw = (*sw == 0)?1:0; +} + +int +inc_cb (void *arg) +{ + int * sw = (int *) arg; + + *sw = *sw + 1; +} + + +/* + * The default callback for process_cmd_line to get a parameter saved to + * a variable of the correct type. + * Converts @param to the Type specified in the @type and saves it into + * the pointer to a variable delivered in @arg + */ +int +get_cmdlap_cb ( + const char *param, + const int cmdla_type, + void *arg) +{ + if (param != NULL) + switch (cmdla_type) + { + case CMDLA_TYPE_STRING: + * (const char **) arg = param; + break; + case CMDLA_TYPE_INT: + sscanf (param, "%d", (int *) arg); + break; + case CMDLA_TYPE_FLOAT: + sscanf (param, "%f", (float *) arg); + } + + return 0; +} + +/* + * Processes command line patameters + */ +int +process_cmd_line ( + int argc, + char *argv[], + const char *about, + const char *copyright, + const char *usage_aa, /* usage additional args */ + const struct cmdlap_cbt *pcbs, + const struct cmdlas_cbt *scbs) +{ + char optstring[MAX_ARGS]; + int option_index = 0; + struct option long_opts[MAX_ARGS]; + + int cbs_idx, optstring_idx, long_opts_idx; + int has_u = 0; + int ch; + + struct cmdlas_cbt switches[MAX_ARGS]; + struct cmdlap_cbt arguments[MAX_ARGS]; + + struct du_infot du_info = { + switches, arguments, _(about), _(copyright), _(usage_aa), argv[0] }; + + bindtextdomain (PACKAGE, LOCALEDIR); + + /* + * Every command line argument, either with or without an additional + * parameter is associated with a callback function, which in turn + * does the neccessaty initialization. Callbacks for switches get an + * void* as parameter and callbacks for parameter arguments get the + * parameter as a const char*, the datatype the parameter has and + * a pointer to the variable that should hold the parameter throughout + * the program. + */ + + /* + * create optstring and long_opts for getopt_long(). + * As this should be called with the untranslated messages + * we need to call _() to put the translated ones into optstring + * and long_opts. Also we need to update the values in switches and + * arguments must be converted to the translated strings. + */ + + /* Zero out optsting and long_opts */ + SCOT_MEM_FILL (optstring, 0, MAX_ARGS); + SCOT_MEM_FILL (long_opts, 0, MAX_ARGS * sizeof (struct option)); + + /* zero switches and copy scbs to switches */ + SCOT_MEM_FILL (switches, 0, MAX_ARGS * sizeof (struct cmdlas_cbt)); + cbs_idx = 0; + while (scbs != NULL && scbs[cbs_idx].cb != NULL) + { + SCOT_MEM_COPY ( + &(switches[cbs_idx]), + &(scbs[cbs_idx]), + sizeof (struct cmdlas_cbt)); + switches[cbs_idx].sarg = _(scbs[cbs_idx].sarg); + switches[cbs_idx].info = _(scbs[cbs_idx].info); + + cbs_idx++; + } + + /* zero arguments and copy scbs to switches */ + SCOT_MEM_FILL (arguments, 0, MAX_ARGS * sizeof (struct cmdlap_cbt)); + cbs_idx = 0; + while (pcbs != NULL && pcbs[cbs_idx].cb != NULL) + { + SCOT_MEM_COPY ( + &(arguments[cbs_idx]), + &(pcbs[cbs_idx]), + sizeof (struct cmdlap_cbt)); + arguments[cbs_idx].sarg = _(pcbs[cbs_idx].sarg); + arguments[cbs_idx].info = _(pcbs[cbs_idx].info); + + cbs_idx++; + } + + /* walk through arguments */ + cbs_idx = optstring_idx = long_opts_idx = 0; + while (arguments != NULL && arguments[cbs_idx].cb != NULL) { + if (arguments[cbs_idx].carg != 0) + { + optstring[optstring_idx++] = arguments[cbs_idx].carg; + + if (arguments[cbs_idx].carg == '?') + { + has_u = 1; + if (switches[cbs_idx].var == NULL) + { + switches[cbs_idx].var = (void *) &du_info; + } + } + + switch (arguments[cbs_idx].cmdla_argt) + { + case CMDLA_OPT_ARG: optstring[optstring_idx++] = ':'; + case CMDLA_REQ_ARG: optstring[optstring_idx++] = ':'; + } + } + + if (pcbs[cbs_idx].sarg != NULL) + { + long_opts[long_opts_idx].name = arguments[cbs_idx].sarg; + long_opts[long_opts_idx].has_arg = arguments[cbs_idx].cmdla_argt; + long_opts[long_opts_idx].val = arguments[cbs_idx].carg; + long_opts_idx++; + } + + cbs_idx++; + } + + /* and now through switches */ + cbs_idx = 0; + while (switches != NULL && switches[cbs_idx].cb != NULL) { + if (switches[cbs_idx].carg != 0) + optstring[optstring_idx++] = switches[cbs_idx].carg; + + if (switches[cbs_idx].carg == '?') + has_u = 1; + + if (switches[cbs_idx].sarg != NULL) + { + long_opts[long_opts_idx].name = switches[cbs_idx].sarg; + long_opts[long_opts_idx].val = switches[cbs_idx].carg; + long_opts_idx++; + } + + cbs_idx++; + } + + /* if no usage callback is give use our default usage */ + if (has_u == 0) + { + switches[cbs_idx].carg = '?'; + switches[cbs_idx].sarg = D_("help"); + switches[cbs_idx].cb = default_usage_cb; + switches[cbs_idx].var = (void *) &du_info; + switches[cbs_idx].info = D_("gives this help"); + cbs_idx++; + SCOT_MEM_FILL (&(switches[cbs_idx]), 0, sizeof (struct cmdlas_cbt)); + + optstring[optstring_idx++] = '?'; + long_opts[long_opts_idx].name = D_("help"); + long_opts[long_opts_idx].val = '?'; + long_opts_idx++; + } + + /* + * Now parse the command line arguments. + * Check if the argument appears in switch_cids, or in param_cids, or + * of the don't if there is a '?'-switch defined and call the + * appropriate cb if possible. + */ + ch = getopt_long (argc, argv, optstring, long_opts, &option_index); + while (ch != EOF) + { + const char *name; + + if (ch == 0) + name = long_opts[option_index].name; + else + name = NULL; + + call_cb (arguments, switches, optarg, name, ch); + + ch = getopt_long (argc, argv, optstring, long_opts, &option_index); + } + + return optind; +} diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..6083fdd --- /dev/null +++ b/src/event.c @@ -0,0 +1,348 @@ +#include +#include +#include +#include +#include + +/* + * !!! REALLY IMPORTANT !!! + * If there is extra data to an event, and the event sould be serializable, + * one has to ensure that ed points to already network byteorder + * converted data. Else there might occur problems when sending the data + * to another machine with a different byteorder. + */ +struct scot_event * +scot_event_new (struct scot_event * e, SCOT_EVENT_NO no, void * ed, SIZE_T eds) +{ + if (e == NULL) + { + e = SCOT_MEM_GET (sizeof (struct scot_event)); + SCOT_MEM_ZERO (e, sizeof (struct scot_event)); + } + + e->event = no; + + e->size = sizeof (SCOT_EVENT_NO); + e->size += sizeof (SIZE_T); + + if (ed != NULL) + { + e->extra_data = SCOT_MEM_GET (eds); + SCOT_MEM_COPY (e->extra_data, ed, eds); + e->ed_size = eds; + e->size += eds; + } + + e->size += sizeof (SIZE_T); + + return e; +} + +struct scot_stream_pool_event * +scot_stream_pool_event_new ( + struct scot_stream_pool_event * e, + SCOT_EVENT_NO no, + void * ed, + SIZE_T eds, + struct scot_stream * st) +{ + if (e == NULL) + { + e = SCOT_MEM_GET (sizeof (struct scot_stream_pool_event)); + SCOT_MEM_ZERO (e, sizeof (struct scot_stream_pool_event)); + } + + e = (struct scot_stream_pool_event *) scot_event_new ( + (struct scot_event *) e, no, ed, eds); + + e->st = st; + + e->event.size += sizeof (struct scot_stream); + + return e; +} + +struct scot_fs_watcher_event * +scot_fs_watcher_event_new ( + struct scot_fs_watcher_event * e, + SCOT_EVENT_NO no, + void * ed, + SIZE_T eds, + const char * path, + const char * name, + const char * oldname) +{ + if (e == NULL) + { + e = SCOT_MEM_GET (sizeof (struct scot_fs_watcher_event)); + SCOT_MEM_ZERO (e, sizeof (struct scot_fs_watcher_event)); + } + + e = (struct scot_fs_watcher_event *) scot_event_new ( + (struct scot_event *) e, no, ed, eds); + + e->path = SCOT_MEM_GET (SCOT_STR_LENGTH (path) + 1); + SCOT_STR_COPY (e->path, path); + + e->event.size += SCOT_STR_LENGTH (path) + 1; + + e->name = SCOT_MEM_GET (SCOT_STR_LENGTH (name) + 1); + SCOT_STR_COPY (e->name, name); + + e->event.size += SCOT_STR_LENGTH (name) + 1; + + if (oldname != NULL) + { + e->oldname = SCOT_MEM_GET (SCOT_STR_LENGTH (oldname) + 1); + SCOT_STR_COPY (e->oldname, oldname); + + e->event.size += SCOT_STR_LENGTH (oldname) + 1; + } + else + e->event.size += 1; + + return e; +} + +static +void +scot_stream_pool_event_free (struct scot_stream_pool_event * e) +{} + +static +void +scot_fs_watcher_event_free (struct scot_fs_watcher_event * e) +{ + if (e->path != NULL) + SCOT_MEM_FREE (e->path); + + if (e->name != NULL) + SCOT_MEM_FREE (e->name); + + if (e->oldname != NULL) + SCOT_MEM_FREE (e->oldname); +} + +/* + * scot_event_free identifies the given event and call appropriate + * free methods for it. Thus only scot_event_free is exported to + * free any type of event. + * The same is true for serialize and deserialize. + */ +void +scot_event_free (struct scot_event * e) +{ + if (e != NULL) + { + if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_STREAM_POOL)) + scot_stream_pool_event_free ((struct scot_stream_pool_event *) e); + if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_FS_WATCHER)) + scot_fs_watcher_event_free ((struct scot_fs_watcher_event *) e); + + if (e->extra_data != NULL) + SCOT_MEM_FREE (e->extra_data); + + SCOT_MEM_FREE (e); + } +} + +/* + * Eine Sache die man unbedingt beachten sollte ist, das die serialisierten + * Daten unter umständen auf einer Maschine mit unterschiedlicher + * Byte-Order landen. Daher muessen alle Daten in Network Byteorder + * umgewandelt werden und beim deserialisieren wieder zurück. + */ +static +void +scot_stream_pool_event_serialize (struct scot_stream_pool_event * e, char ** s) +{ + char * fill = *s; + + /* + * serialize the struct scot_stream item of a stream_pool_event. + * well, normally we don't send stream_pool_event to other machines, + * so it might look like bloat to convert every part of it to + * network byte order. But as we do it we are ready to send the if + * the need araises in future. + * But at least to send this information to other machines is kind of + * senseless, because handle is something completely different on + * windows and linux. + * + * Well, the htonl approch does only work with sockets on windows. + * Under linux it is unimportant what part of the union is used, as + * all are uint32_t. Serialization of the stream handle makes no + * sense at all, as it couldn't be used by the receiver anyway. + */ + * (int32_t *)fill = htonl (e->st->handle.sock); + fill += 4; + SCOT_MEM_COPY (fill, e->st->rbuf, SCOT_STREAM_BUF_SIZE); + fill += SCOT_STREAM_BUF_SIZE; + SCOT_MEM_COPY (fill, e->st->wbuf, SCOT_STREAM_BUF_SIZE); + fill += SCOT_STREAM_BUF_SIZE; + * (uint32_t *)fill = htonl (e->st->rbuf_idx); + fill += 4; + * (uint32_t *)fill = htonl (e->st->wbuf_idx); + fill += 4; + * (uint32_t *)fill = htonl (e->st->s_type); + fill += 4; + + *s = fill; +} + +static +void +scot_fs_watcher_event_serialize (struct scot_fs_watcher_event * e, char ** s) +{ + char * fill = *s; + + /* + * zuerst fswi serialisieren.... + */ + SCOT_STR_COPY (fill, e->path); + fill += SCOT_STR_LENGTH (e->path) + 1; + SCOT_STR_COPY (fill, e->name); + fill += SCOT_STR_LENGTH (e->name) + 1; + if (e->oldname != NULL) + SCOT_STR_COPY (fill, e->oldname); + else + SCOT_STR_COPY (fill, ""); + fill += SCOT_STR_LENGTH (fill) + 1; + + *s = fill; +} + +static +struct scot_event * +scot_stream_pool_event_deserialize ( + struct scot_stream_pool_event * e, const char * s) +{ + e->st = SCOT_MEM_GET (sizeof (struct scot_stream)); + + e->st->handle.sock = ntohl (* (int32_t *)s); + s += 4; + SCOT_MEM_COPY (e->st->rbuf, s, SCOT_STREAM_BUF_SIZE); + s += SCOT_STREAM_BUF_SIZE; + SCOT_MEM_COPY (e->st->wbuf, s, SCOT_STREAM_BUF_SIZE); + s += SCOT_STREAM_BUF_SIZE; + e->st->rbuf_idx = ntohl (* (uint32_t *)s); + s += 4; + e->st->wbuf_idx = ntohl (* (uint32_t *)s); + s += 4; + e->st->s_type = ntohl (* (uint32_t *)s); + + SCOT_MEM_COPY (e->st, s, sizeof (struct scot_stream)); + + return (struct scot_event *) e; +} + +static +struct scot_event * +scot_fs_watcher_event_deserialize ( + struct scot_fs_watcher_event * e, const char * s) +{ + e->path = SCOT_MEM_GET (SCOT_STR_LENGTH (s) + 1); + SCOT_STR_COPY (e->path, s); + s += SCOT_STR_LENGTH (s) + 1; + + e->name = SCOT_MEM_GET (SCOT_STR_LENGTH (s) + 1); + SCOT_STR_COPY (e->name, s); + s += SCOT_STR_LENGTH (s) + 1; + + e->oldname = SCOT_MEM_GET (SCOT_STR_LENGTH (s) + 1); + SCOT_STR_COPY (e->oldname, s); + + return (struct scot_event *) e; +} + +SIZE_T +scot_event_serialize (struct scot_event * e, char ** s) +{ + char * fill; + + *s = fill = SCOT_MEM_GET (e->size); + SCOT_MEM_ZERO (fill, e->size); + + * (SCOT_EVENT_NO *)fill = htons (e->event); + fill += 2; + * (uint32_t *)fill = htonl (e->size); + fill += 4; + * (uint32_t *)fill = htonl (e->ed_size); + fill += 4; + + /* + * every extra data must be available in Network Byteorder if the + * event should be serializable. + * This is important at an ..._event_new function, as the void * for + * extra_data should always point to data that is already converted + * to network byteorder. + */ + if (e->extra_data != NULL) + { + SCOT_MEM_COPY (fill, &e->extra_data, e->ed_size); + fill += e->ed_size; + } + + if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_STREAM_POOL)) + scot_stream_pool_event_serialize ( + (struct scot_stream_pool_event *) e, &fill); + if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_FS_WATCHER)) + scot_fs_watcher_event_serialize ( + (struct scot_fs_watcher_event *) e, &fill); + + return e->size; +} + +/* + * GANZ WICHTIG....für diese Funktion muß sichergestellt sein, daß + * s mindestens sizeof (SCOT_EVENT_NO) + sizeof (SIZE_T) bytes + * vom anfang einer gültigen event-serialisierung enthält. + * Sonst kann die größe logischerweise nicht ermittelt werden. + */ +SIZE_T +scot_event_size_serial (struct scot_event * e, const char * s) +{ + return ntohl (* (uint32_t *)(s + sizeof (SCOT_EVENT_NO))); +} + +SCOT_EVENT_NO +scot_event_no_serial (struct scot_event * e, const char * s) +{ + return ntohs (* (SCOT_EVENT_NO *)s); +} + +/* + * Hier sollte sichergestellt sein, das s ein komplettes serialisiertes + * event enthält. + */ +struct scot_event * +scot_event_deserialize (struct scot_event * e, const char * s) +{ + SIZE_T size; + + size = scot_event_size_serial (e, s); + + e = SCOT_MEM_GET (size); + SCOT_MEM_ZERO (e, size); + + e->event = ntohs (* (SCOT_EVENT_NO *)s); + s += 2; + e->size = ntohl (* (uint32_t *)s); + s += 4; + e->ed_size = ntohl (* (uint32_t *)s); + s += 4; + + if (e->ed_size != 0) + { + SCOT_MEM_COPY (&e->extra_data, s, e->ed_size); + s += e->ed_size; + } + + if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_STREAM_POOL)) + e = scot_stream_pool_event_deserialize ( + (struct scot_stream_pool_event *) e, s); + if (SCOT_EVENT_CHK_GROUP (e->event, SCOT_EG_FS_WATCHER)) + e = scot_fs_watcher_event_deserialize ( + (struct scot_fs_watcher_event *) e, s); + + return e; +} diff --git a/src/event_listener.c b/src/event_listener.c new file mode 100644 index 0000000..1471c2d --- /dev/null +++ b/src/event_listener.c @@ -0,0 +1,157 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#define GEN_LOCAL +#include +#include +#undef GEN_LOCAL + +#define EVENT_SOURCE_THREAD_CANCEL_WAIT_MAX INFINITE + +GEN_LIST (scot_event_cb_fptr); +GEN_STACK_FUNC_PROTO (scot_event_cb_fptr); +GEN_STACK_IMPL (scot_event_cb_fptr); + +void +scot_event_listener_init ( + struct scot_event_listener * l, + const unsigned char group, + const scot_thread_entry_fptr entry_func) +{ + l->group = group; + l->el_entry_func = entry_func; + + SCOT_MEM_ZERO (l->cb_mappings, + sizeof (stack_scot_event_cb_fptr_node_t *) * UCHAR_MAX); +} + +void +scot_event_listener_fini ( + struct scot_event_listener * l) +{ + int i; + excenv_t *ee; + + TRY + { + if (THREAD_EQUAL (l->thread_id, THREAD_ID)) + THROW (EXC (EXC_ERROR, 102, "a event listener can't be destroyed from " + "its main thread.")); + + if (scot_event_listener_is_running (l)) + scot_event_listener_stop (l); + + for (i=0; icb_mappings[i] != NULL) + stack_scot_event_cb_fptr_free (l->cb_mappings[i]); + } + CATCH (ee) + { + printf ("Exception in %s (%s, %d)\n", __FUNCTION__, __FILE__, __LINE__); + forward_all_exceptions (ee); + } +} + +void +scot_event_listener_start (struct scot_event_listener * l) +{ + NEW_THREAD (&l->thread_handle, l->el_entry_func, l); +} + +void +scot_event_listener_stop (struct scot_event_listener * l) +{ + if (! scot_event_listener_is_running (l)) + return; + + if (l->thread_run_flg != 0) + { + END_THREAD (l->thread_handle, SCOT_EL_WAIT_THREAD_MAX); + l->thread_run_flg = 0; + } +} + +void +scot_event_listener_restart (struct scot_event_listener * l) +{ + scot_event_listener_stop (l); + scot_event_listener_start (l); +} + +int +scot_event_listener_is_running (struct scot_event_listener * l) +{ + return l->thread_run_flg; +} + +void +scot_event_listener_register_cb ( + struct scot_event_listener * l, + SCOT_EVENT_NO e, + scot_event_cb_fptr cb, + void * extra_data) +{ + excenv_t *ee; + + TRY + { + if ((e >> 8) != l->group) + THROW (EXC (EXC_ERROR, 101, + "[EVENT_SOURCE_REG_CB]Event not supported by listener")); + + if (l->cb_mappings[e&0x00FF] == NULL) + l->cb_mappings[e&0x00FF] = stack_scot_event_cb_fptr_new ( + l->cb_mappings[e&0x00FF]); + + stack_scot_event_cb_fptr_push ( + l->cb_mappings[e&0x00FF], + (scot_event_cb_fptr *)cb); + + l->cb_extra_data[e&0x00FF] = extra_data; + + /* restart listener after adding a callback. */ + if (scot_event_listener_is_running (l) != 0) + { + scot_event_listener_stop (l); + scot_event_listener_start (l); + } + } + CATCH (ee) + { + printf ("Exception in %s (%s, %d)\n", __FUNCTION__, __FILE__, __LINE__); + forward_all_exceptions (ee); + } +} + +void +scot_event_listener_call_cb ( + struct scot_event_listener * l, + struct scot_event * e) +{ + SCOT_EVENT_NO eno = e->event; + + if (l->cb_mappings[e->event&0x00FF] != NULL) + { + list_scot_event_cb_fptr_node_t * anchor = + LIST (scot_event_cb_fptr, l->cb_mappings[e->event&0x00FF]); + list_scot_event_cb_fptr_node_t * node = anchor; + + while (! list_scot_event_cb_fptr_eol (anchor, node)) + { + scot_event_cb_fptr cb; + + node = list_scot_event_cb_fptr_next (node); + cb = (scot_event_cb_fptr) list_scot_event_cb_fptr_retrive (node); + + if (cb (e) == SCOT_EVENT_END) + break; + } + } +} diff --git a/src/event_manager.c b/src/event_manager.c new file mode 100644 index 0000000..e5e5c73 --- /dev/null +++ b/src/event_manager.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include + +#define GEN_LOCAL +#include +#include +#undef GEN_LOCAL + +/* +struct scot_event_sink_event_wrapper +{ + struct scot_event_source es; + union scot_event_u e; +}; + +typedef struct scot_event_sink_event_wrapper scot_esew_t; + +GEN_QUEUE (scot_esew_t); +*/ + +typedef struct scot_event_sink scot_event_sink_t; +GEN_LIST (scot_event_sink_t); + +typedef struct scot_event_source scot_event_source_t; +GEN_LIST (scot_event_source_t); + +struct scot_event_manager +{ + list_scot_event_sink_t_node_t * sinks; + list_scot_event_source_t_node_t * sources; +}; + + +static +int +scot_event_source_compare_by_group_no ( + const struct scot_event_source * a, + const struct scot_event_source * b) +{ + return (a->group_no==b->group_no)?0:(a->group_nogroup_no)?-1:1; +}; + +void +scot_event_manager_register_source ( + struct scot_event_manager * m, + struct scot_event_source * es) +{ + struct scot_event_manager_src_cb_map * s_cb_map; + + if (scot_event_source_set_manager (es, m) != 0) + THROW (EXC (EXC_ERROR, 1, "source already registered within other " + "manager.")); + + if (list_scot_event_source_t_find (m->sources, es)) + /* well, this source is already registered within this manager */ + { + THROW (EXC (EXC_WARNING, 1, "event source already registered within " + "this manager.")); + return 0; + } + + list_scot_event_source_t_node_t_set_cmp ( + scot_event_source_compare_by_group_no); + if (list_scot_event_source_t_find (m->sources, es)) + /* an event source of the same group is already registered within + * this manager */ + { + THROW (EXC (EXC_WARNING, 1, "another event source of the same group " + "is already registered within this " + "manager.")); + return 0; + } + list_scot_event_source_t_node_t_set_cmp ( + list_scot_event_source_t_default_cmp); + + list_scot_event_source_t_add (m->sources, es); +} + +void +scot_event_manager_register_sink ( + struct scot_event_manager * m, + struct scot_event_sink * es) +{ + if (scot_event_sink_set_manager (es, m) != 0) + THROW (EXC (EXC_ERROR, 2, "Sink already registered within other " + "manager.")); + list_scot_event_sink_t_add (m->sinks, s_cb_map); +} + +void +scot_event_manager_call_cb ( + struct scot_event_manager * m, + struct scot_event_source * es, + struct scot_event * e) +{ + list_scot_emscm_t_node_t * emscm_node = m->src_cb; + + while (! list_scot_emscm_t_eol (m->src_cb, emscm_node)) + { + struct scot_event_manager_src_cb_map * scbm; + + emscm_node = list_scot_emscm_t_next (emscm_node); + scbm = list_scot_emscm_t_retrive (emscm_node); + + if (scbm->source == es) + { + scbm->cb (e); + return; + } + } +} + +void +scot_event_manager_dispatch_event ( + struct scot_event_manager * m, + struct scot_event * e, + struct scot_event_source * src, + int mask) +{ + list_event_sink_t_node_t * es_node = m->sinks; + int dipatched = 0; + + while (! list_scot_event_sink_t_node_t_eol (m->sinks, es_node)) + { + struct scot_event_sink * es; + int i; + + es_node = list_scot_event_sink_t_next (es_node); + es = list_scot_event_sink_t_retrive (es_node); + + for (i=0; isrc[i]!=0; i++) + { + if (es->src[i] == scot_event_get_src (e) && + es->mask[i] & scot_event_get_mask (e) != 0) + { + /* Im folgenden sollte das nicht e sondern halt irgend eine + * art wrapper sein aus dem auch die gruppe und maske + * des events hervorgeht. + * sonst wird es unmöglich eine brauchbare Notice zu schicken + * wenn das event abgearbeitet wurde. */ + scot_event_sink_put_event (es, e); + dispatched = 1; + } + } + } + + /* wenn sich keine sink für das event interessiert hat. */ + if (dispatched == 0) + { + /* einen etwaigen callback aufrufen... */ + scot_event_manager_call_bc (m, get_source from event(e), e); + /* resourcen des events freigeben... */ + scot_event_free (e); + } +} diff --git a/src/event_subsys/event-subsys.txt b/src/event_subsys/event-subsys.txt new file mode 100644 index 0000000..de2a25c --- /dev/null +++ b/src/event_subsys/event-subsys.txt @@ -0,0 +1,79 @@ +Das scot event subsystem versucht ein Abstraction layer auf beliebige +event systeme (auch selbst definierte) zu sein. + +Ich trenn Objeke (Strukture oder was auch immer) von der Event verarbeitung. +Das soll heißen, wenn ich ein Modul (struktur & funktionen) habe das +Xwindows erzeugt, anzeigt und maniputliert, so kann dieses Modul aber noch +nicht arbeiten, das es von sich aus nicht auf Events reagiert. Um es zum +arbeiten zu bringen muß man von einem Konkreten Xwindow (struktur gefüllt,etc) +eine X11_event_sink erzeugen. Diese kümmert sich um die Verarbeitung +von events (masken erzeugen, events eintragen, callbacks aufrufen, etc.). +Die event_sink muß noch in einen dispatcher eingetragen werden, der dann die +event_sink event Verarbeitungen auf eine bestimmte art und weise aufruft. +Dieser wird dann noch in einen dispatcher_manager eingetragen, der die +dispatcher ein oder ausschalten kann...evtl. brauch man dafür keinen +Manager sondern bietet zu dispatchern eben eine dispatcher_on, dispatcher_off +methode an. +Ein Objekt muß selbst die Methoden bieten, die eine passende event_sink +zu diesem objekt erzeugen. Dadurch kann ich z.B. ein erweiterte XWindow +Modul anlegen, dessen Struktur zum einen eine Kopie der Xwindow Struktur aber +auch noch weitere Informationen incl. Informationen für andere event typen +enthält. Diese Struktur kann dann mit den XWindow Methoden einen +X11_event_sink anlegen, aber auch einen neue Methode bauen die eine +andere event_sink erzeugt. Dadurch kann das neue Objekt dann auf beide +Arten Events reagieren. + + +scot_event_typ.c: +----------------- +Stellt die Struktur scot_event_typ bereit, welche voraussichtlich so aussieht: + +typedef struct scot_event_typ_st +{ + int id; /* diese wird von scot_event_typ_register gefüllt. */ + char * name; /* this is a unique name for the event_typ */ + + /* eine Struktur die für jeden event_typ anders aussieht, z.B. muß + für X events mindestens das Display bekannt sein. */ + void * event_typ_info; + + /* + * folgende Funktionen sind allen event Typen gemein, sie müssen nur + * für jeden event_typ unterschiedlich implementiert werden. + * Diese Implementierungen stehen dann in scot_event_type_*.[ch] + */ + void (process_events*) (scot_dispatcher_st * dispatcher); + void (trigger_event*) (int event, + const void* event_sink, + const void* event_data); +}; + +außerdem wird eine globale statische Liste von registrierten event Typen +angelegt. (evtl. aber auch nicht, nämlich dann wenn ich einen Manger für +die event_types baue...dann enthält natürlich der manager die Liste.) + +GEN_LIST (scot_event_typ_st); +list_scot_event_typ_st_node_t * scot_registered_event_types; + +und folgende Funktionen: +void scot_event_typ_register (const scot_event_typ_st *event_typ); +void scot_event_typ_unregister (const scot_event_typ_st *event_typ); +scot_event_typ_st * scot_get_event_typ_by_name (const char * name); + + +scot_event_sink.c: +------------------ +Stellt die Struktur scot_event_sink bereit. Diese sieht voraussichtlich +irgendwie so aus: + +struct scot_event_sink +{ + struct scot_event_typ * event_typ; + /* + void (process_events*) (void); + void (trigger_event*) (int event, + const void* event_sink, + const void* data); + */ + void * objekt; +}; diff --git a/src/event_subsys/scot_dispatch_manager.c b/src/event_subsys/scot_dispatch_manager.c new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_dispatcher.c b/src/event_subsys/scot_dispatcher.c new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_event_sink.c b/src/event_subsys/scot_event_sink.c new file mode 100644 index 0000000..5703ee4 --- /dev/null +++ b/src/event_subsys/scot_event_sink.c @@ -0,0 +1,3 @@ +#include + +s diff --git a/src/event_subsys/scot_event_typ.c b/src/event_subsys/scot_event_typ.c new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_event_typ_GTK b/src/event_subsys/scot_event_typ_GTK new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_event_typ_OPENGL b/src/event_subsys/scot_event_typ_OPENGL new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_event_typ_SDL b/src/event_subsys/scot_event_typ_SDL new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_event_typ_manager.c b/src/event_subsys/scot_event_typ_manager.c new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_event_typ_mswin.c b/src/event_subsys/scot_event_typ_mswin.c new file mode 100644 index 0000000..e69de29 diff --git a/src/event_subsys/scot_event_typ_xwin.c b/src/event_subsys/scot_event_typ_xwin.c new file mode 100644 index 0000000..e69de29 diff --git a/src/exception.c b/src/exception.c new file mode 100644 index 0000000..45e457b --- /dev/null +++ b/src/exception.c @@ -0,0 +1,868 @@ +/** + * \file exception.c + * \author Georg Steffers + * \brief The core of the exception system implemented within libscot. + * + * This file has all implementations for the exception system of scot. + * It defines the exception_t structure which holds basic information + * about an exception. Instances of this structure are organized + * in a queue that is part of excenv_t. That stucture describes the environment + * in which exceptions may occure, that is a TRY-CATCH block. + * Every TRY creates a new excenv_t instance and puts them in a stack, + * every CATCH get one excenv_t from that stack. + * To achive threadsaveness those excenv_t stack is organized within + * thread_excenv_t which associates a thread-id to an excenv_t stack. + * Thus every stack has its own exception stack. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +/** + * \internal + * \brief We need direct access to elements of struct excenv_t. + */ +#define USE_SCOT_STRUCT_EXCENV_T +/** + * \internal + * \brief We need direct access to elements of struct exception_t. + */ +#define USE_SCOT_STRUCT_EXCEPTION_T +#include +#undef USE_SCOT_STRUCT_EXCENV_T +#undef USE_SCOT_STRUCT_EXCEPTION_T + +#include /* for SELF_THREAD and THREAD_T macro */ +#include /* for windows threadsafe memory functions */ + +/** + * \internal + * \brief Don't use exceptions in the list code. + * + * as the code here provides the exception system it is not very wise to + * use exceptions in this code, Thus this define deactivates exception + * support within the code generation macros defined in \link scot/list.h + * scot/list.h\endlink. + */ +#define USE_NO_EXCEPTION +/** + * \internal + * \brief make list functions static for this file. + * + * All list, stack and queue stuff for exception structures is only needed + * here and not very likely to be needed anywhere else in the code. + * Thus generate them static, (said with GEN_LOCAL) + */ +#define GEN_LOCAL +#include +#include +#include +#undef GEN_LOCAL +#undef USE_NO_EXCEPTION + +#include + +/************************************************************************ + * Generation of typesafe functions to handle lists of the types * + ************************************************************************/ +GEN_LIST (exception_t); +GEN_QUEUE_FUNC_PROTO (exception_t); +GEN_QUEUE_IMPL (exception_t); +GEN_LIST (excenv_t); +GEN_STACK (excenv_t); + +/** + * \internal + * Structure to assign every thread an own exception environment stack. + */ +struct thread_excenv_t +{ + THREAD_T thread_handle; /**< Current thread-id */ + stack_excenv_t_node_t *ee_stack; /**< The exception environment stack */ +}; +typedef struct thread_excenv_t thread_excenv_t; +GEN_LIST (thread_excenv_t); + +/** + * \internal + * \brief If we use threads this is the root of all exception environments. + */ +list_thread_excenv_t_node_t *tee_list = NULL; +/** + * \internal + * \brief If we don't use threads this is the root of all exception + * environments. + */ +stack_excenv_t_node_t *ee_stack = NULL; + +/************************************************************************ + * static helper * + ************************************************************************/ +/** + * \internal + * \param a instance to be compared + * \param b instance to be compared + * \pre None + * \return A value greater than, equal or less than 0 as with strcmp. + * \retval <0 if a->thread_handle is less than b->thread_handle + * \retval >0 if a->thread_handle is greater than b->thread_handle + * \retval ==0 if a->thread_handle is equal to b->thread_handle + * \post None + * + * \brief Comparison for instances of thread_excenv_t. + * + * This is a comparison function for instances of thread_excenv_t. They are + * assumed equal if both thread_excenv_t::thread_handle are equal. + * The function follows the same scheme as for example strcmp. + */ +static +int +compare_thread_excenv_t (const thread_excenv_t *a , const thread_excenv_t *b) +{ + return ! THREAD_EQUAL (*(THREAD_T *) a, *(THREAD_T *) b); +} + +/** + * \internal + * \param e an instance of thread_excenv_t + * \pre \a e has to be initialize correctly previous to this call. + * \return Nothing + * \post The memory needed by \a e is correctly freed. + * + * \brief Destructor for thread_excenv_t instances. + * + * This cleanly frees an instance of thread_excenv_t. + */ +static +void +free_thread_excenv_t (thread_excenv_t *e) +{ + stack_excenv_t_free (e->ee_stack); + SCOT_MEM_FREE (e); +} + +/** + * \internal + * \param e an instance of excenv_t + * \pre \a e has to be initialize correctly previous to this call. + * \return Nothing + * \post The memory needed by \a e is correctly freed. + * + * \brief Destructor for excenv_t instances. + * + * This cleanly frees an instance of excenv_t. + */ +static +void +free_excenv_t (excenv_t *e) +{ + queue_exception_t_free (e->e_queue); + SCOT_MEM_FREE (e); +} + +/** + * \internal + * \param e an instance of exception_t + * \pre \a e has to be initialize correctly previous to this call. + * \return Nothing + * \post The memory needed by \a e is correctly freed. + * + * \brief Destructor for exception_t instances. + * + * This cleanly frees an instance of exception_t. + */ +static +void +free_exception_t (exception_t * e) +{ + SCOT_MEM_FREE (e); +} + + + +/************************************************************************ + * interface implementtaion * + ************************************************************************/ +/** + * \internal + * \pre No threads are used. + * \return Nothing + * \post None + * + * \brief initializes the root of all exception environments for non + * threaded code. + * + * This is called within the TRY, CATCH macros to ensure that the correct + * exception environment stack for the current thread is used. + * \nWell, we don't have threads when this is used, so it just returns + * ee_stack if it has initialized it previously. + */ +void * +exc_init (void) +{ + /* + * If threads are used already, use them further on. + * (If there is a threaded_exception list tee_list). + */ + if (tee_list != NULL) + return threaded_exc_init (); + + if (ee_stack != NULL) + return (void *) ee_stack; + + ee_stack = stack_excenv_t_new (ee_stack); + + bindtextdomain (PACKAGE, LOCALEDIR); + + if (ee_stack == NULL) + /* FIXME: make output to syslog or something... */ + { + char buf[1024]; + + sprintf ( + buf, + "[Fatal(exception.c:97)]\n%s", + D_("exc_init failed")); + perror (buf); + abort (); + } + + return (void *) ee_stack; +} + +/** + * \internal + * \pre Threads are used. + * \return Nothing + * \post None + * + * \brief initializes the root of all exception environments for non + * threaded code. + * + * This is called within the TRY, CATCH macros to ensure that the correct + * exception environment stack for the current thread is used. + */ +void * +threaded_exc_init (void) +{ + list_thread_excenv_t_node_t *tee_l; + thread_excenv_t *tee; + THREAD_T t; + + t = SELF_THREAD; + + bindtextdomain (PACKAGE, LOCALEDIR); + + /* + * First create the list if it does not exist at all and + * set the comparison function. + */ + if (tee_list == NULL) + { + tee_list = list_thread_excenv_t_new (tee_list); + list_thread_excenv_t_set_cmp (compare_thread_excenv_t); + } + + /* + * Now a little trick: As i compare thread_excenv_t by comparing its + * first element thread_excenv_t->thread_handle and don't use any other part of + * this struct with a compare i can cast THREAD_T * to thread_excenv_t * + * to check for the existance of an entry in the list. + */ + tee_l = list_thread_excenv_t_find (tee_list, (thread_excenv_t *) &t); + + /* + * if tee_l != NULL we have found a previosly initialized stack, good! + * Return the excenv_t we have to use. + */ + if (tee_l != NULL) + return (void *) list_thread_excenv_t_retrive (tee_l)->ee_stack; + + /* + * else we must create a new entry. + */ + tee = (thread_excenv_t *) SCOT_MEM_GET (sizeof (struct thread_excenv_t)); + if (tee == NULL) + { + char buf[1024]; + + sprintf ( + buf, + "[Fatal(exception.c:97)]\n%s", + D_("exc_init failed, can't get memory for new list element.")); + perror (buf); + abort (); + } + + tee->thread_handle = SELF_THREAD; + tee->ee_stack = stack_excenv_t_new (tee->ee_stack); + + if (tee->ee_stack == NULL) + /* FIXME: make output to syslog or something... */ + { + char buf[1024]; + + sprintf ( + buf, + "[Fatal(exception.c:97)]\n%s", + D_("exc_init failed")); + perror (buf); + abort (); + } + + list_thread_excenv_t_insert (tee_list, tee); + + return (void *) tee->ee_stack; +} + +/** + * \internal + * \param ee_s the stack of exception environments for the actual + * thread. + * \pre The root of the exception environments must be initialized + * either by exc_init() (if no threads are used) or by + * threaded_exc_init() (if threads are used). + * \return Nothing + * \post A new exception environment is pushed int the stack of exception + * environment (previously called root sometimes) and thus became + * current. + * + * \brief creates a new exception environment and pushes it into the root. + */ +void +excenv_new (void *ee_s) +{ + stack_excenv_t_node_t *ee_stack = (stack_excenv_t_node_t *) ee_s; + excenv_t *ee; + + bindtextdomain (PACKAGE, LOCALEDIR); + + ee = (excenv_t *) SCOT_MEM_GET (sizeof (excenv_t)); + if (ee == NULL) + /* FIXME: make output to syslog or something... */ + { + char buf[1024]; + + sprintf ( + buf, + "[Fatal(exception.c:113)]\n%s", + D_("excenv_new failed")); + perror (buf); + abort (); + } + + ee->e_queue = queue_exception_t_new (ee->e_queue); + if (ee->e_queue == NULL) + /* FIXME: make output to syslog or something... */ + { + char buf[1024]; + + sprintf ( + buf, + "[Fatal(exception.c:121)]\n%s", + D_("excenv_new failed")); + perror (buf); + abort (); + } + + if (stack_excenv_t_push (ee_stack, ee) == NULL) + { + char *errmsg; + char buf[1024]; + + errmsg = strerror (errno); + + stack_excenv_t_free (ee_stack); + SCOT_MEM_FREE (ee); + + sprintf ( + buf, + "[FATAL(exception.c:101)]\n%s%s", + D_("could not push the new excenv_t " + "into ee_stack:\n"), + errmsg); + + fprintf (stderr, buf); + + abort (); + } +} + +/** + * \internal + * \param ee_s the stack of exception environments for the actual + * thread. + * \pre The root of the exception environments must be initialized + * either by exc_init() (if no threads are used) or by + * threaded_exc_init() (if threads are used). + * There must be an actual exception environment into the stack + * of exception environments. + * \return a pointer to the jmp_buf variable of the current exception + * environment. + * \post None + * + * \brief This returns a pointer to the jmp_buf variable of the current + * exception environment. + * + * A pointer to the jmp_buf variable of the current exception environment is + * returned for use by the setjmp call in TRY. I tried to call setjmp directly + * here, but that causes confusion when TRY-CATCH blocks are nested. + */ +jmp_buf * +excenv_jmp_buf (void *ee_s) +{ + stack_excenv_t_node_t *ee_stack = (stack_excenv_t_node_t *) ee_s; + excenv_t *ee; + + ee = stack_excenv_t_top (ee_stack); + if (ee == NULL) + { + fprintf ( + stderr, + D_("invalid exception environment\n")); + + abort (); + } + + return & (ee->env); +} + +/** + * \internal + * \param ee_s the stack of exception environments for the actual + * thread. + * \param e the exception to be thrown. + * \pre The root of the exception environments must be initialized + * either by exc_init() (if no threads are used) or by + * threaded_exc_init() (if threads are used). + * There must be an actual exception environment into the stack + * of exception environments. + * \return Nothing + * \post A new exception is stored within the actual exception environment. + * + * \brief The implementation of the THROW macro. + * + * This stores a new exception within the actual exception environment. + */ +void +exc_throw (void *ee_s, const exception_t *e) +{ + stack_excenv_t_node_t *ee_stack = (stack_excenv_t_node_t *) ee_s; + excenv_t *ee; + + bindtextdomain (PACKAGE, LOCALEDIR); + + /* get the actual excenv */ + ee = stack_excenv_t_top (ee_stack); + if (ee == NULL) + { + char buf[1024]; + + sprintf ( + buf, + D_("any code that THROWs exeptions has to be\n" + "%s within a TRY block\n"), + " "); + + fprintf ( + stderr, + "[Fatal(exception.c:200)]\n%s", + buf); + + abort (); + } + + queue_exception_t_enqueue (ee->e_queue, e); + + if (e->lvl == EXC_ERROR && e->was_catched == 0) + longjmp (ee->env, 1); +} + +/** + * \internal + * \param ee_s the stack of exception environments for the actual + * thread. + * \pre The root of the exception environments must be initialized + * either by exc_init() (if no threads are used) or by + * threaded_exc_init() (if threads are used). + * There must be an actual exception environment into the stack + * of exception environments. + * \return The current exception environment. + * \post The current exception environment is removed from the stack, thus + * making the next one on the stack to the current exception + * environment. + * + * \brief Get the current exception environment. + * + * The is the core of the CATCH() macro. It retrieves the actual exception + * environment from the stack of exception environments and returns it. The + * actual exception environment is removed from the stack. + */ +excenv_t * +excenv_catch (void *ee_s) +{ + stack_excenv_t_node_t *ee_stack = (stack_excenv_t_node_t *) ee_s; + excenv_t *ee; + + bindtextdomain (PACKAGE, LOCALEDIR); + + ee = stack_excenv_t_pop (ee_stack); + if (ee == NULL) + { + char buf[1024]; + + sprintf ( + buf, + "[Fatal(exception.c:177)]\n%s", + D_("use of CATCH with no previous TRY")); + perror (buf); + abort (); + } + + return ee; +} + +/** + * \param ee The exception environment to be checked. + * \pre \a ee must be a valid exception environment. + * \return Boolean wether \a ee has exceptions or not. + * \retval ==0 there are no exceptions + * \retval !=0 there are exceptions + * \post None + * + * \brief Check for existent exceptions. + * + * This checks the given exception environment \a ee for existent + * exceptions. + */ +int +excenv_has_exception (const excenv_t *ee) +{ + return ! list_exception_t_isempty ( + (list_exception_t_node_t *)ee->e_queue); +} + +/** + * \param lvl either ERROR or WARNING + * \param file the file where the exception was created. + * \param line the line in that file. + * \param errnum an identifier of the exception. This might be + * dublicated as it is normally just used as an + * index for the error message. So rely only on this + * if you check only a special kind of error. + * \param err_msg a string describing what happens. + * \pre None + * \return The newly created exception. + * \post None + * + * \brief Creates a new exception. + * + * This creates a new exception. Normally this function is called + * through the EXC() macro which fills in file and line automatically + * by using __FILE__ and __LINE__ but sometimes it is desirable to + * have direct control over those to values too. (For example with + * the templates of list functions.) + */ +exception_t * +exc_new ( + const enum exclvl_t lvl, + const char *file, + const int line, + const int errnum, + const char *err_msg) +{ + exception_t *e; + + bindtextdomain (PACKAGE, LOCALEDIR); + + e = (exception_t *) SCOT_MEM_GET (sizeof (exception_t)); + if (e == NULL) + /* FIXME: make output to syslog or something... */ + { + char buf[1024]; + + sprintf ( + buf, + "[Fatal(exception.c:220)]\n%s", + D_("exc_new failed")); + perror (buf); + abort (); + } + + /* This one time we write to lvl and id */ + e->was_catched = 0; + * (enum exclvl_t *) & e->lvl = lvl; + e->file = file; + * (int *) & e->line = line; + * (int *) & e->errnum = errnum; + e->err_msg = err_msg; + + return e; +} + +/** + * \param ee The exception environment to be used. + * \pre \a ee must be a valid exception environment. + * \return The dequeued exception. + * \post The exception is removed from \a ee. + * + * \brief Get the actual exception. + * + * This dequeues the the next exception from the exception environment and + * returns it. + */ +exception_t * +retrive_exception (const excenv_t *ee) +{ + exception_t *e; + + e = queue_exception_t_dequeue (ee->e_queue); + if (e != NULL) + e->was_catched += 1; + + return e; +} + +/** + * \param ee The exception environment to be used. + * \pre Must be in a CATCH Block. So the exception environment is already + * removed from the stack of exception environments and only + * accessed by \a ee. + * \return Nothing + * \post All memory accessed over \a ee is freed, this includes all + * exceptions within it. + * + * \brief Frees the actually catched exception environment. + */ +void free_catched (excenv_t *ee) +{ + free_excenv_t (ee); +} + +/** + * \param e The exception to be used. + * \pre It is desirable to remove the exception first from the + * exception stack it is in, to keep from problems. + * \return Nothing + * \post All memory accessed over \a e is freed. + * + * \brief Frees an exception. + */ +void +free_exception (exception_t *e) +{ + free_exception_t (e); +} + +/** + * \pre None + * \return Nothing + * \post all memory allocated for any exception handling in the current + * thread is feed. + * + * \brief Ends exception handling for the actual thread. + * + * This should be called before ending a thread to avoid memory leaks. + */ +void +exc_end (void) +{ + excenv_t *ee; + + list_exception_t_set_elem_free (free_exception_t); + list_excenv_t_set_elem_free (free_excenv_t); + + if (tee_list == NULL) + { + stack_excenv_t_free (ee_stack); + } + else + { + thread_exc_end (SELF_THREAD); + } + + list_exception_t_set_elem_free (NULL); + list_excenv_t_set_elem_free (NULL); +} + +void +thread_exc_end (THREAD_T t) +{ + list_thread_excenv_t_node_t *tee_l; + + list_exception_t_set_elem_free (free_exception_t); + list_excenv_t_set_elem_free (free_excenv_t); + list_thread_excenv_t_set_elem_free (free_thread_excenv_t); + + tee_l = list_thread_excenv_t_find (tee_list, (thread_excenv_t *) &t); + + list_thread_excenv_t_delete (tee_l); + + /* + * well, maybe this has to have some syncronization with + * threaded_exc_init or i make a function exc_fini, that + * does this final step and should only be called in the + * main thread. + */ + if (list_thread_excenv_t_isempty (tee_list)) + list_thread_excenv_t_free (tee_list); + + list_thread_excenv_t_set_elem_free (NULL); + list_exception_t_set_elem_free (NULL); + list_excenv_t_set_elem_free (NULL); +} + +/** + * \pre \a e must be a valid exception. + * \return Nothing + * \post \a e is freed. + * + * \brief Prints out the exception and frees it after that. + */ +void +print_exception (exception_t * e) +{ + fprintf ( + stderr, + "[%s(%s:%d)]\n(%d) %s\n", + (e->lvl==EXC_ERROR)?D_("ERROR"):D_("WARNING"), + e->file, + e->line, + e->errnum, + e->err_msg); + + /* no more free at print...this is not intuitive usable. */ + /* free_exception (e); */ +} + +/** + * \pre \a ee must be a valid exception environment. + * \return Nothing + * \post \a ee is freed. + * + * \brief Prints out all exceptions and frees them after that. + * + * This prints out all exception one by one using print_exception() + * and after that frees also the exception environment \a ee. + */ +void +print_all_exceptions (excenv_t *ee) +{ + exception_t *e; + + e = retrive_exception (ee); + while (e != NULL) + { + print_exception (e); + e = retrive_exception (ee); + } + + free_catched (ee); +} + +/** + * \param ee the exception environment to be used. + * \pre \a ee must be a valid exception environment.\n + * There must be at least one exception environment left in the + * stack of exception environments. + * \return Nothing + * \post All exception within \a ee are in the actual exception environment. + * + * \brief Forwards all exceptions in \a ee to the actual + * exception environment. + * + * This did not free any exception but throw the from \a ee into the + * actual exception environment. + */ +void +forward_all_exceptions (excenv_t *ee) +{ + exception_t *e; + excenv_t *_ee; + + if (tee_list != NULL) + /* we use threads */ + _ee = threaded_exc_init (); + else + /* we don't use threads */ + _ee = exc_init (); + + e = retrive_exception (ee); + while (e != NULL) + { + exc_throw (_ee, e); + e = retrive_exception (ee); + } + + free_catched (ee); +} + +/** + * \param e the exception to be checked. + * \pre \a e must be a valid exception. + * \return Boolean indicating if the exception was thrown within the + * actual exception environment or just be forwarded from another one. + * \retval ==0 the exception was forwarded. + * \retval !=0 the exception was thrown. + * \post None + * + * \brief Checks wether \a e was thrown or forwarded. + */ +int +exc_in_this_try (exception_t *e) +{ + return e->was_catched == 1; +} + + +/************************************************************************ + * exception getter * + ************************************************************************/ + enum exclvl_t +exc_lvl_get (exception_t *e) +{ + return e->lvl; +} + + int +exc_errnum_get (exception_t *e) +{ + return e->errnum; +} + + char * +exc_err_msg_get (exception_t *e) +{ + return (char *) e->err_msg; +} + + char * +exc_file_get (exception_t *e) +{ + return (char *) e->file; +} + + int +exc_line_get (exception_t *e) +{ + return e->line; +} diff --git a/src/fs_watcher.c b/src/fs_watcher.c new file mode 100644 index 0000000..e52c19a --- /dev/null +++ b/src/fs_watcher.c @@ -0,0 +1,416 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GEN_LOCAL +# include +#undef GEN_LOCAL + +GEN_LIST_FUNC_PROTO (scot_fsw_info); +GEN_LIST_IMPL (scot_fsw_info); + +static +void +scot_fsw_info_free (scot_fsw_info * e) +{ + if (e != NULL) + { + if (e->path != NULL) + SCOT_MEM_FREE (e->path); + if (e->old_name != NULL) + SCOT_MEM_FREE (e->old_name); + + SCOT_MEM_FREE (e); + } +} + +static +int +scot_fsw_info_cmp_path (const scot_fsw_info * a, const scot_fsw_info * b) +{ + return strcmp (a->path, b->path); +} + +static +int +scot_fsw_info_cmp_watch_d (const scot_fsw_info * a, const scot_fsw_info * b) +{ + return (a->watch_d == b->watch_d)?0:(a->watch_d < b->watch_d)?-1:1; +} + + +static +void +thread_fini (void * arg) +{ + exc_end (); +} + +static +unsigned short +default_cb (struct scot_event * _e) +{ + SSIZE_T n; + char puffer[1024]; + struct scot_fs_watcher_event * e = (struct scot_fs_watcher_event *) _e; + + switch (_e->event) + { + case (SCOT_EVENT_FS_WATCHER_CREATE): + printf ("%s/%s created\n", e->path, e->name); + break; + case (SCOT_EVENT_FS_WATCHER_DELETE): + printf ("%s/%s deleted\n", e->path, e->name); + break; + case (SCOT_EVENT_FS_WATCHER_RENAME): + printf ("%s/%s renamed to %s/%s\n", + e->path, e->oldname, + e->path, e->name); + break; + + /* more to come .... */ + + default: break; + } + + fflush (stdout); + + return SCOT_EVENT_END; +} + + +struct scot_fs_watcher * +scot_fs_watcher_new (void) +{ + struct scot_fs_watcher * fsw; + + fsw = SCOT_MEM_GET (sizeof (struct scot_fs_watcher)); + SCOT_MEM_ZERO (fsw, sizeof (struct scot_fs_watcher)); + + fsw->w_list = list_scot_fsw_info_new (fsw->w_list); + + list_scot_fsw_info_set_elem_free (scot_fsw_info_free); + + SCOT_MEM_ZERO (&fsw->notify_d, sizeof (struct scot_stream)); + + fsw->notify_d.handle.file = inotify_init (); + if (fsw->notify_d.handle.file == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + + fsw->notify_d.s_type = SCOT_STREAM_TYPE_INOTIFY; + scot_stream_set_nonblock (&fsw->notify_d); + + FD_SET (fsw->notify_d.handle.file, &fsw->rfds); + + scot_event_listener_init ( + (struct scot_event_listener *) fsw, + SCOT_EG_FS_WATCHER, + (scot_thread_entry_fptr) scot_fs_watcher_main_loop); + + scot_event_listener_register_cb ( + (struct scot_event_listener *) fsw, + SCOT_EVENT_FS_WATCHER_CREATE, + default_cb, NULL); + scot_event_listener_register_cb ( + (struct scot_event_listener *) fsw, + SCOT_EVENT_FS_WATCHER_DELETE, + default_cb, NULL); + scot_event_listener_register_cb ( + (struct scot_event_listener *) fsw, + SCOT_EVENT_FS_WATCHER_RENAME, + default_cb, NULL); + + NEW_THREAD_MUTEX (&fsw->mutex); + + return fsw; +} + +void +scot_fs_watcher_free (struct scot_fs_watcher * fsw) +{ + scot_event_listener_fini ((struct scot_event_listener *) fsw); + + list_scot_fsw_info_free (fsw->w_list); + + FREE_THREAD_MUTEX (&fsw->mutex); + + SCOT_MEM_FREE (fsw); +} + +void +scot_fs_watcher_add ( + struct scot_fs_watcher * fsw, + const char * path, + uint32_t mask) +{ + struct scot_event_listener * l = (struct scot_event_listener *) fsw; + struct scot_fsw_info * fswi; + + /* + * soweit ich das in erinnerung habe sind meine listen nicht per se + * thread save. Daher sollte hier unbedingt noch ein mutex her, + * damit nicht gleichzeitig in die liste geschriebe (hier) + * und gelöscht wird (in remove) + */ + LOCK_THREAD_MUTEX (&fsw->mutex); + + fswi = SCOT_MEM_GET (sizeof (struct scot_fsw_info)); + SCOT_MEM_ZERO (fswi, sizeof (struct scot_fsw_info)); + fswi->path = SCOT_MEM_GET (SCOT_STR_LENGTH (path) + 1); + SCOT_STR_COPY (fswi->path, path); + + if ((mask & SCOT_EVENT_FS_WATCHER_CREATE) == SCOT_EVENT_FS_WATCHER_CREATE) + fswi->mask |= IN_CREATE; + if ((mask & SCOT_EVENT_FS_WATCHER_DELETE) == SCOT_EVENT_FS_WATCHER_DELETE) + fswi->mask |= IN_DELETE; + if ((mask & SCOT_EVENT_FS_WATCHER_RENAME) == SCOT_EVENT_FS_WATCHER_RENAME) + fswi->mask |= IN_MOVED_FROM | IN_MOVED_TO; + + fswi->watch_d = inotify_add_watch (fsw->notify_d.handle.file, path, fswi->mask); + if (fswi->watch_d == -1) + { + SCOT_MEM_FREE (fswi->path); + SCOT_MEM_FREE (fswi); + + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + } + + list_scot_fsw_info_set_cmp (scot_fsw_info_cmp_path); + + if (list_scot_fsw_info_find (fsw->w_list, fswi) == NULL) + list_scot_fsw_info_insert (fsw->w_list, fswi); + + list_scot_fsw_info_set_cmp (list_scot_fsw_info_default_cmp); + + /* restart thread if running. */ + if (scot_event_listener_is_running (l) != 0 && l->thread_handle != THREAD_ID) + { + scot_event_listener_stop (l); + scot_event_listener_start (l); + } + + UNLOCK_THREAD_MUTEX (&fsw->mutex); +} + +void +scot_fs_watcher_remove ( + struct scot_fs_watcher * fsw, + const char * path, + uint32_t mask) +{ + struct scot_event_listener * l = (struct scot_event_listener *) fsw; + int was_running = scot_event_listener_is_running (l); + list_scot_fsw_info_node_t * node; + struct scot_fsw_info search_fswi = {0, (char *)path, 0, 0, NULL}; + struct scot_fsw_info * fswi; + + LOCK_THREAD_MUTEX (&fsw->mutex); + + if (was_running != 0 && l->thread_handle != THREAD_ID) + scot_event_listener_stop (l); + + list_scot_fsw_info_set_cmp (scot_fsw_info_cmp_path); + + node = list_scot_fsw_info_find (fsw->w_list, &search_fswi); + fswi = list_scot_fsw_info_retrive (node); + + list_scot_fsw_info_set_cmp (list_scot_fsw_info_default_cmp); + + if ((mask & SCOT_EVENT_FS_WATCHER_CREATE) == SCOT_EVENT_FS_WATCHER_CREATE) + fswi->mask &= ~IN_CREATE; + if ((mask & SCOT_EVENT_FS_WATCHER_DELETE) == SCOT_EVENT_FS_WATCHER_DELETE) + fswi->mask &= ~IN_DELETE; + if ((mask & SCOT_EVENT_FS_WATCHER_RENAME) == SCOT_EVENT_FS_WATCHER_RENAME) + fswi->mask &= ~IN_MOVED_FROM & ~IN_MOVED_TO; + + if (fswi->mask == 0) + { + inotify_rm_watch (fsw->notify_d.handle.file, fswi->watch_d); + node = list_scot_fsw_info_delete (node); + } + else + { + inotify_add_watch (fsw->notify_d.handle.file, fswi->path, fswi->mask); + node = list_scot_fsw_info_next (node); + } + + if (was_running != 0 && l->thread_handle != THREAD_ID) + scot_event_listener_start (l); + + UNLOCK_THREAD_MUTEX (&fsw->mutex); +} + + +static +struct inotify_event * +scot_fs_watcher_get_event (struct scot_fs_watcher * fsw) +{ + static int size = 0; + static int got = 0; + static char * buffer = NULL; + struct inotify_event * e = NULL; + + if (size == 0) + { + got = 0; + size = 16; + size = sizeof (struct inotify_event); + buffer = SCOT_MEM_GET (size); + } + + got += scot_stream_read (&fsw->notify_d, buffer+got, size-got); + + if (size == got) + { + if (size == sizeof (struct inotify_event)) + { + char * tmp_buf = buffer; + + size += ((struct inotify_event *)buffer)->len; + buffer = SCOT_MEM_GET (size); + SCOT_MEM_COPY (buffer, tmp_buf, sizeof (struct inotify_event)); + SCOT_MEM_FREE (tmp_buf); + got += scot_stream_read (&fsw->notify_d, buffer+got, size-got); + } + + if (size == got) + { + e = SCOT_MEM_GET (size); + SCOT_MEM_COPY (e, buffer, size); + SCOT_MEM_FREE (buffer); + size = 0; + } + } + + return e; +} + +/* + * entry point für einen stream-pool event-source thread. + */ +void +scot_fs_watcher_main_loop (struct scot_fs_watcher * fsw) +{ + fd_set run_rfds; + excenv_t * ee; + + THREAD_CANCEL_ENABLE; + THREAD_CANCEL_DEFER; + + + TRY + { + THREAD_ATEXIT_BEGIN (thread_fini, NULL); + + while (1) + { + struct inotify_event * ie = NULL; + + run_rfds = fsw->rfds; + + /* evtl. sollte man hier noch timeout support einbauen, sprich + * das letzte NULL mit einer time austauschen. */ + if (select (fsw->notify_d.handle.file+1, &run_rfds, NULL, NULL, NULL) == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + + while ((ie = scot_fs_watcher_get_event (fsw)) != NULL) + { + struct scot_fsw_info sfswi = {ie->wd, NULL, 0, 0, NULL}; + struct scot_fsw_info * fswi; + struct scot_fs_watcher_event * e = NULL; + list_scot_fsw_info_node_t * w_node; + + SCOT_MEM_ZERO (&e, sizeof (struct scot_fs_watcher_event)); + + list_scot_fsw_info_set_cmp (scot_fsw_info_cmp_watch_d); + + w_node = list_scot_fsw_info_find (fsw->w_list, &sfswi); + fswi = list_scot_fsw_info_retrive (w_node); + + list_scot_fsw_info_set_cmp (list_scot_fsw_info_default_cmp); + + /* + * Ok, jetzt muß code folgen, der die got_events maske + * analysiert und je nachdem wie diese gesetzt ist + * ein scot event erzeugt oder nicht...soll heißen einen + * callback aufruft oder nicht. + */ + if (ie->mask == IN_MOVED_FROM) + { + struct inotify_event * _ie = scot_fs_watcher_get_event (fsw); + + if (_ie != NULL && + _ie->mask == IN_MOVED_TO && + ie->wd == _ie->wd) + { + e = scot_fs_watcher_event_new ( + e, SCOT_EVENT_FS_WATCHER_RENAME, NULL, 0, + fswi->path, _ie->name, ie->name); + + if (_ie != NULL) + SCOT_MEM_FREE (_ie); + } + else + { + e = scot_fs_watcher_event_new ( + e, SCOT_EVENT_FS_WATCHER_DELETE, NULL, 0, + fswi->path, ie->name, NULL); + + scot_event_listener_call_cb ( + (struct scot_event_listener *) fsw, + (struct scot_event *) e); + + scot_event_free ((struct scot_event *) e); + e = NULL; + + SCOT_MEM_FREE (ie); + ie = _ie; + } + } + + if (ie != NULL && (ie->mask & (IN_MOVED_TO | IN_CREATE)) != 0) + e = scot_fs_watcher_event_new ( + e, SCOT_EVENT_FS_WATCHER_CREATE, NULL, 0, + fswi->path, ie->name, NULL); + + if (ie != NULL && ie->mask ==IN_DELETE) + e = scot_fs_watcher_event_new ( + e, SCOT_EVENT_FS_WATCHER_DELETE, NULL, 0, + fswi->path, ie->name, NULL); + + if (e != NULL) + { + /* call callback... */ + scot_event_listener_call_cb ( + (struct scot_event_listener *) fsw, + (struct scot_event *) e); + + /* free e after callback */ + scot_event_free ((struct scot_event *) e); + e = NULL; + } + + if (ie != NULL) + SCOT_MEM_FREE (ie); + } + } + + THREAD_ATEXIT_END (1); + } + CATCH (ee) + { + print_all_exceptions (ee); + exit (EXIT_FAILURE); + } +} diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..9b930b1 --- /dev/null +++ b/src/list.c @@ -0,0 +1,340 @@ +/** + * \file list.c + * \author Georg Steffers + * \brief Internal implementations for list handling. + * + * This implements the internals for the typesafe linked list + * implementation provided by \link scot/list.h list.h\endlink, + * \link scot/list_proto.h list_proto.h\endlink and + * \link scot/list_impl.h list_impl.h\endlink. + * The only thing one has to remember when one only wants to + * use linked lists within the own code is to link the code agains + * an object of this. This is normally done by linking agains + * libscot with an -lscot or similar compiler switch. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * \internal + * \brief Error messages given. + * + * This holds all error messages that go either to the exception system + * or will be printed on stderr if an error occurs within a list function. + */ +const char *list_err_msg[] = +{ + N_("[LIST]Failed to create new list."), + N_("[LIST]Failed to free list."), + N_("[LIST]Failed to check for begin of list."), + N_("[LIST]Failed to check for end of list."), + N_("[LIST]Failed to check for list emptiness."), + N_("[LIST]Failed to get first list node."), + N_("[LIST]Failed to get last list node."), + N_("[LIST]Failed to get next list node."), + N_("[LIST]Failed to get previous list node."), + N_("[LIST]Failed to find list node."), + N_("[LIST]Failed to find list anchor."), + N_("[LIST]Failed to retrive the node's value."), + N_("[LIST]Failed to set the node's value."), + N_("[LIST]Failed to insert node."), + N_("[LIST]Failed to delete node."), + N_("[LIST]Failed to concatanate the lists."), + N_("[LIST]Failed to count list nodes."), + N_("[LIST]Node is no anchor."), + N_("[LIST]Malformed list."), + NULL +}; + +/** + * \internal + * \brief Warning messages given. + * + * This holds all warning messages that go either to the exception system + * or will be printed on stderr if an error occurs within a list function. + */ +const char *list_wrn_msg[] = +{ + N_("[LIST]tried to delete on an empyt list"), + NULL +}; + +/** + * \internal + * \param lvl either ERROR or WARNING + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * \param id the error or warning id. (Used to pick the correct message) + * + * \pre None + * \returns Nothing + * \post An error or warning message printed to stderr. In case of an + * ERROR the program is aborted. + * + * \brief Print error or warning to stderr. + */ +static +void +list_ew_print ( + const enum exclvl_t lvl, + const char *file, + const int line, + const int id) +{ + bindtextdomain (PACKAGE, LOCALEDIR); + fprintf ( + stderr, + "[%s(%s:%d)]\n(%d) %s\n", + (lvl == EXC_ERROR)?D_("ERROR"):D_("WARNING"), + file, + line, + id, + (lvl == EXC_ERROR)?D_(list_err_msg [id]):D_(list_wrn_msg [id])); + + if (lvl == EXC_ERROR) + { + abort (); + } +} + +/** + * \internal + * \param lvl either ERROR or WARNING + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * \param id the error or warning id. (Used to pick the correct message) + * + * \pre An exception environment created by a previous call of TRY. + * \returns Nothing + * \post An exception thrown into the exception environment. + * + * \brief Throw error or warning in an exception environment. + */ +static +void +list_ew_throw ( + enum exclvl_t lvl, + const char *file, + int line, + int id) +{ + THROW (exc_new (lvl, file, line, id, D_(list_err_msg [id]))); +} + +/** + * \internal + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * \param id the error id. (Used to pick the correct message) + * + * \pre None + * \returns Nothing + * \post An error message printed to stderr. The program is aborted. + * + * \brief Print error to stderr. + */ +void +list_error_print (const char *file, int line, const int id) +{ + list_ew_print (EXC_ERROR, file, line, id); +} +/** + * \internal + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * \param id the warning id. (Used to pick the correct message) + * + * \pre None + * \returns Nothing + * \post An warning message printed to stderr. + * + * \brief Print warning to stderr. + */ +void +list_warning_print (const char *file, int line, int id) +{ + list_ew_print (EXC_WARNING, file, line, id); +} +/** + * \internal + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * \param id the error id. (Used to pick the correct message) + * + * \pre An exception environment created by a previous call of TRY. + * \returns Nothing + * \post An exception thrown into the exception environment. + * + * \brief Throw error. + */ +void +list_error_throw (const char *file, int line, int id) +{ + list_ew_throw (EXC_ERROR, file, line, id); +} +void +/** + * \internal + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * \param id the warning id. (Used to pick the correct message) + * + * \pre An exception environment created by a previous call of TRY. + * \returns Nothing + * \post An exception thrown into the exception environment. + * + * \brief Throw warning. + */ +list_warning_throw (const char *file, int line, int id) +{ + list_ew_throw (EXC_WARNING, file, line, id); +} + +/** + * \internal + * \param size SIZE_T size of memory to be allocates (see man malloc). + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * + * \pre None + * \returns A pointer to the memory location reserved by malloc. + * \post Either memory is allocated on the heap or an error messege is + * printed to stderr and the program aborted. + * + * \brief Malloc wrapper with error handling. + * + * This calls malloc and if it fails gives an error message to stderr. + * If this malloc fails it is assumed a serious error and thus the program + * is aborted in this case. + */ +void * +list_malloc_print (SIZE_T size, const char *file, int line) +{ + void *a; + + bindtextdomain (PACKAGE, LOCALEDIR); + a = SCOT_MEM_GET (size); + + if (a == NULL) + { + fprintf ( + stderr, + "[ERROR(%s:%d)]\n(%d) %s\n", + file, + line, + MALLOC_ERR, + D_(scot_err_msg [MALLOC_ERR])); + + abort (); + } +} + +/** + * \internal + * \param size SIZE_T size of memory to be allocates (see man malloc). + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * + * \pre An exception environment created by a previous call of TRY. + * \returns A pointer to the memory location reserved by malloc. + * \post Either memory is allocated on the heap or an exception thrown + * into the exception environment. + * + * \brief Malloc wrapper with error handling. + * + * This calls malloc and if it fails throws an exception. + * This malloc fails it is assumed a serious error and thus an ERROR + * is thrown into the exception system causing the calling function to + * be aborted and goes to exception handling CATCH of the current + * exception environment. + */ +void * +list_malloc_throw (SIZE_T size, const char *file, int line) +{ + return exc_malloc_fl (size, file, line); +} + +/** + * \internal + * \param val Pointer to be checked. + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * + * \pre None + * \returns Nothing. + * \post If val is NULL an error messege is + * printed to stderr and the program aborted. + * + * \brief Checks the given value for beeing NULL. + * + * This checks the given \a val for beeing NULL and if is gives an + * error message to stderr. If this check fails within the list + * code it is a serious error and thus the program is aborted. + */ +void +list_check_null_print (const void *val, const char *file, int line) +{ + bindtextdomain (PACKAGE, LOCALEDIR); + + if (val == NULL) + { + fprintf ( + stderr, + "[ERROR(%s:%d)]\n(%d) %s\n", + file, + line, + NULL_PTR_ERR, + D_(scot_err_msg [NULL_PTR_ERR])); + + abort (); + } +} + +/** + * \internal + * \param val Pointer to be checked. + * \param file the filename of the source file from where this is called + * \param line the line in the source file from where this is called + * + * \pre An exception environment created by a previous call of TRY. + * \returns Nothing. + * \post If val is NULL an exception thrown + * into the exception environment. + * + * \brief Checks the given value for beeing NULL. + * + * This checks the given \a val for beeing NULL and if is throws + * an exception. If this check fails within the list code it is a + * serious error and thus an ERROR is throw into the exception system, + * causing the calling function to abort and go to exception handling + * CATCH of the actual exception environment. + */ +void +list_check_null_throw (const void *val, const char *file, int line) +{ + check_null_fl (val, file, line); +} + diff --git a/src/po/LINGUAS b/src/po/LINGUAS new file mode 100644 index 0000000..7673daa --- /dev/null +++ b/src/po/LINGUAS @@ -0,0 +1 @@ +de diff --git a/src/po/Makevars b/src/po/Makevars new file mode 100644 index 0000000..446fe25 --- /dev/null +++ b/src/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = src/po +top_builddir = ../.. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=D_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Georg Steffers + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = georg@steffers.org + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/src/po/POTFILES.in b/src/po/POTFILES.in new file mode 100644 index 0000000..db04a09 --- /dev/null +++ b/src/po/POTFILES.in @@ -0,0 +1,4 @@ +src/cmdla.c +src/list.c +src/stack.c +src/exception.c diff --git a/src/po/de.gmo b/src/po/de.gmo new file mode 100644 index 0000000000000000000000000000000000000000..f1e041ef7d0080ce6ddf8b27761e9df61bf0e0b3 GIT binary patch literal 3426 zcma);OKcle6ozk0d6{=9v^;wYP2b}>QA;AXDNTd(ph;RePNS++VeC2f+{E6SOOCNnHf8A>ZG}n{ds22J+FJt z8UMa>$L9iTKR&PGb9$Q)C&Bl&;|nYIkPr`pgWz6p7<>_21Rn)8cnEBP2f&-)Vep6a z`Y%x8diRb*{|LAb*9#!)TLxbSzX11xUxROgzoz^LJc#SPaDx4t1X;fW4uGG6FM!{G zd%zz-_Tw)2Hkf%th{wTq!Q)^Fdphv|{uz+v-v^I@pMVzlU3z^FWc$0GNb)rdvb`m6C-^bQ`TG()0)7jQfpAk+Ql`)e&g7?i{s)NLu%p}*e_n}C!a0Hf%1&= z!!>}=;u_#M`AlEKhtG!Vs=Ers;)SA^^JT*iNRmel(xH(aRYR$?G@)IoXT^fCYm}D* zMqU{(SCg*+BNoSsbJKH^;>yhQeCg_htvqsM=*x;rmAb6@fh9@H&Rt_Hul8U71s|o zlh{B*K#6bnZ>>=%t13X4v4#HoS;(^=ETifA1NAfmYAHX$nR9OY+f&+5u+hVS!a%i> zeDn5$Mz(A(>XSGFxA|21NzK|G3Z{VqVm~^0nS(y540g!gT*G8iYw1b(8UkDyD@nk@ z)+@5YyF=FwWofE-VbMssPADB;8|m7HE454+6!o$SU6iRUOU28w?D>^?M%Y0uihDs+ z{K#|QK`5J%agzX0ICQ;2y0ye@0!2hiaj)bv0uPpyRyfAEA=Y0XlNDPZ3?-jogV1&> zai?@@vD1Uucpu+gRm1ky+^He!h{8pFT=JSC{!$vrS5E9+9IM1G75JKi$q0j3(Vj*` zhPdIz$4cW1isC$B zs>x+iAqAvmAsNObTYli$+9914IEZD8%81SMo5l;>Xtvg?6at`vho~$iN~y>>s0KGsC)ETagDg3;?d4O3K+xbOpk}WBO6isCQo%} z;f89&Q~exIb!l)TBgWFvPNME>bUP|u$cW$v@kH=BxvR$ZQluEqb9AR^uL!32=CgK;t{as9nhnaO?BS|y-#YY z97;z)hn!Mgm4q9Aqutwgyb$HGnlQQ0K!M{)M$BWjd6+sPc^&vJ<`7RE;sP_tYmsz> HN*4bEQk=}% literal 0 HcmV?d00001 diff --git a/src/po/de.po b/src/po/de.po new file mode 100644 index 0000000..87832f2 --- /dev/null +++ b/src/po/de.po @@ -0,0 +1,171 @@ +# German translations for PACKAGE package +# German messages for PACKAGE. +# Copyright (C) 2006 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Georg Steffers , 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: scot 0.0.2\n" +"Report-Msgid-Bugs-To: georg@steffers.org\n" +"POT-Creation-Date: 2006-08-24 07:17+0200\n" +"PO-Revision-Date: 2006-02-22 15:08+0100\n" +"Last-Translator: Georg Steffers \n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/cmdla.c:80 src/cmdla.c:106 src/cmdla.c:373 src/cmdla.c:381 +msgid "help" +msgstr "hilfe" + +#: src/cmdla.c:137 +#, c-format +msgid "Usage: %s [%s] [%s] %s" +msgstr "Verwendung: %s [%s] [%s] %s" + +#: src/cmdla.c:139 src/cmdla.c:143 +msgid "switches" +msgstr "Schalter" + +#: src/cmdla.c:139 src/cmdla.c:162 +msgid "arguments" +msgstr "Argumente" + +#: src/cmdla.c:376 +msgid "gives this help" +msgstr "gibt diese Hilfe" + +#: src/list.c:50 +msgid "[LIST]Failed to create new list." +msgstr "[LIST]Neue Liste anlegen mißlungen." + +#: src/list.c:51 +msgid "[LIST]Failed to free list." +msgstr "[LIST]Liste freigeben mißlungen." + +#: src/list.c:52 +msgid "[LIST]Failed to check for begin of list." +msgstr "[LIST]Test auf Anfang der Liste mißlungen." + +#: src/list.c:53 +msgid "[LIST]Failed to check for end of list." +msgstr "[LIST]Test auf Ende der Liste mißlungen" + +#: src/list.c:54 +msgid "[LIST]Failed to check for list emptiness." +msgstr "[LIST]Test auf leere Liste mißlungen." + +#: src/list.c:55 +msgid "[LIST]Failed to get first list node." +msgstr "[LIST]Ersten Listenknoten holen mißlungen." + +#: src/list.c:56 +msgid "[LIST]Failed to get last list node." +msgstr "[LIST]Letzten Listenknoten holen mißlungen." + +#: src/list.c:57 +msgid "[LIST]Failed to get next list node." +msgstr "[LIST]Nächten Listenknoten holen mißlungen." + +#: src/list.c:58 +msgid "[LIST]Failed to get previous list node." +msgstr "[LIST]Vorigen Listenknoten holen mißlungen." + +#: src/list.c:59 +msgid "[LIST]Failed to find list node." +msgstr "[LIST]Listenknoten finden mißlungen." + +#: src/list.c:60 +msgid "[LIST]Failed to find list anchor." +msgstr "[LIST]Listenanker finden mißlungen." + +#: src/list.c:61 +msgid "[LIST]Failed to retrive the node's value." +msgstr "[LIST]Wert von Knoten holen mißlungen." + +#: src/list.c:62 +msgid "[LIST]Failed to set the node's value." +msgstr "[LIST]Wert von Knoten setzen mißlungen." + +#: src/list.c:63 +msgid "[LIST]Failed to insert node." +msgstr "[LIST]Knoten einfügen mißlungen." + +#: src/list.c:64 +msgid "[LIST]Failed to delete node." +msgstr "[LIST]Knoten löschen mißlungen." + +#: src/list.c:65 +msgid "[LIST]Failed to concatanate the lists." +msgstr "[LIST]Listen aneinander hängen mißlungen." + +#: src/list.c:66 +#, fuzzy +msgid "[LIST]Failed to count list nodes." +msgstr "[LIST]Listenknoten finden mißlungen." + +#: src/list.c:67 +msgid "[LIST]Node is no anchor." +msgstr "[LIST]Knoten ist kein Anker." + +#: src/list.c:68 +msgid "[LIST]Malformed list." +msgstr "Mißbildete Liste." + +#: src/list.c:81 +msgid "[LIST]tried to delete on an empyt list" +msgstr "[LIST]versuch in leerer Liste zu löschen." + +#: src/list.c:111 src/exception.c:747 +msgid "ERROR" +msgstr "FEHLER" + +#: src/list.c:111 src/exception.c:747 +msgid "WARNING" +msgstr "WARNUNG" + +#: src/stack.c:45 +msgid "No more elements left in stack." +msgstr "Keine weiteren Elemente vorhanden." + +#: src/exception.c:244 src/exception.c:327 +msgid "exc_init failed" +msgstr "exc_init fehlgeschlagen" + +#: src/exception.c:311 +msgid "exc_init failed, can't get memory for new list element." +msgstr "" +"exc_init fehlgeschlagen, kann keinen Speicher für neues Listenelement " +"reservieren." + +#: src/exception.c:368 src/exception.c:382 +msgid "excenv_new failed" +msgstr "excenv_new fehlgeschlagen" + +#: src/exception.c:400 +msgid "could not push the new excenv_t into ee_stack:\n" +msgstr "konnte den neuen excenv_t nicht in ee_stack pushen:\n" + +#: src/exception.c:441 +msgid "invalid exception environment\n" +msgstr "ungültige Exception Umgebung\n" + +#: src/exception.c:482 +#, c-format +msgid "" +"any code that THROWs exeptions has to be\n" +"%s within a TRY block\n" +msgstr "" +"jeder code der Exceptions THROWd muß\n" +"%s in einem TRY Block sein\n" + +#: src/exception.c:536 +msgid "use of CATCH with no previous TRY" +msgstr "CATCH ohne vorheriges TRY benutzt" + +#: src/exception.c:606 +msgid "exc_new failed" +msgstr "exc_new fehlgeschlagen" diff --git a/src/po/scot.pot b/src/po/scot.pot new file mode 100644 index 0000000..210587e --- /dev/null +++ b/src/po/scot.pot @@ -0,0 +1,165 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Georg Steffers +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: georg@steffers.org\n" +"POT-Creation-Date: 2006-08-24 07:17+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/cmdla.c:80 src/cmdla.c:106 src/cmdla.c:373 src/cmdla.c:381 +msgid "help" +msgstr "" + +#: src/cmdla.c:137 +#, c-format +msgid "Usage: %s [%s] [%s] %s" +msgstr "" + +#: src/cmdla.c:139 src/cmdla.c:143 +msgid "switches" +msgstr "" + +#: src/cmdla.c:139 src/cmdla.c:162 +msgid "arguments" +msgstr "" + +#: src/cmdla.c:376 +msgid "gives this help" +msgstr "" + +#: src/list.c:50 +msgid "[LIST]Failed to create new list." +msgstr "" + +#: src/list.c:51 +msgid "[LIST]Failed to free list." +msgstr "" + +#: src/list.c:52 +msgid "[LIST]Failed to check for begin of list." +msgstr "" + +#: src/list.c:53 +msgid "[LIST]Failed to check for end of list." +msgstr "" + +#: src/list.c:54 +msgid "[LIST]Failed to check for list emptiness." +msgstr "" + +#: src/list.c:55 +msgid "[LIST]Failed to get first list node." +msgstr "" + +#: src/list.c:56 +msgid "[LIST]Failed to get last list node." +msgstr "" + +#: src/list.c:57 +msgid "[LIST]Failed to get next list node." +msgstr "" + +#: src/list.c:58 +msgid "[LIST]Failed to get previous list node." +msgstr "" + +#: src/list.c:59 +msgid "[LIST]Failed to find list node." +msgstr "" + +#: src/list.c:60 +msgid "[LIST]Failed to find list anchor." +msgstr "" + +#: src/list.c:61 +msgid "[LIST]Failed to retrive the node's value." +msgstr "" + +#: src/list.c:62 +msgid "[LIST]Failed to set the node's value." +msgstr "" + +#: src/list.c:63 +msgid "[LIST]Failed to insert node." +msgstr "" + +#: src/list.c:64 +msgid "[LIST]Failed to delete node." +msgstr "" + +#: src/list.c:65 +msgid "[LIST]Failed to concatanate the lists." +msgstr "" + +#: src/list.c:66 +msgid "[LIST]Failed to count list nodes." +msgstr "" + +#: src/list.c:67 +msgid "[LIST]Node is no anchor." +msgstr "" + +#: src/list.c:68 +msgid "[LIST]Malformed list." +msgstr "" + +#: src/list.c:81 +msgid "[LIST]tried to delete on an empyt list" +msgstr "" + +#: src/list.c:111 src/exception.c:747 +msgid "ERROR" +msgstr "" + +#: src/list.c:111 src/exception.c:747 +msgid "WARNING" +msgstr "" + +#: src/stack.c:45 +msgid "No more elements left in stack." +msgstr "" + +#: src/exception.c:244 src/exception.c:327 +msgid "exc_init failed" +msgstr "" + +#: src/exception.c:311 +msgid "exc_init failed, can't get memory for new list element." +msgstr "" + +#: src/exception.c:368 src/exception.c:382 +msgid "excenv_new failed" +msgstr "" + +#: src/exception.c:400 +msgid "could not push the new excenv_t into ee_stack:\n" +msgstr "" + +#: src/exception.c:441 +msgid "invalid exception environment\n" +msgstr "" + +#: src/exception.c:482 +#, c-format +msgid "" +"any code that THROWs exeptions has to be\n" +"%s within a TRY block\n" +msgstr "" + +#: src/exception.c:536 +msgid "use of CATCH with no previous TRY" +msgstr "" + +#: src/exception.c:606 +msgid "exc_new failed" +msgstr "" diff --git a/src/po/stamp-po b/src/po/stamp-po new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/src/po/stamp-po @@ -0,0 +1 @@ +timestamp diff --git a/src/posix/dir.c b/src/posix/dir.c new file mode 100644 index 0000000..e18a38b --- /dev/null +++ b/src/posix/dir.c @@ -0,0 +1,136 @@ +/* + * dir.c: This is the home of the posix-style get_dir_first, get_dir_next. + * Right now, those function only return non-directory filenames. + * (This is because thats what i wanted for checkout_files.) + * + * Copyright (C) 2006 Georg Steffers + * + * Author: Georg Steffers [GST] + * Developer: + * + * Changes (for this file only): + * (2006-06-12) [GST] Started this changelog...well the program is + * ready since some weeks right now. + */ +#include +#include +#include + +#include +#include + +/* + * within the get_dir_... functions one can decide if one wants to follow + * symlinks or not. To achive this i use different stat functions to + * identify files. A variable of this type is capable of holding those + * stat functions. + */ +typedef int (*stat_func_t) (const char *, struct stat *); + +/* + * This functions tries an opendir on the given path and uses the + * get_dir_next function to retrieve the first file within that directory. + * + * parameters: + * path: The path to the directory that should be read. + * file: A pointer to a structure that holds further information to the + * found file. (e.g. the path to the file) + * fsym: fsym==0 => don't follow symlinks, fsym!=0 => follow symlinks + * + * returns: + * NULL if an error occures or an handle to the opened directory on + * success. This handle should be closed later in the program. + * There should be a function that does this, but as this program + * actually exits anyway after directory reading i have not done it + * right now. + */ +DIR_HANDLE +get_dir_first (const char * path, FILE_DATA * file, int fsym) +{ + DIR * dir; + + dir = opendir (path); /* !!!FIXME!!! Do error handling here */ + + /* opendir could fail for various reasons, most likely are two. + * The given string does not reflect any file, or is no directory */ + if (NULL == dir) + /* errno is set by opendir */ + return NULL; + + if (get_dir_next (dir, path, file, fsym) != GET_DIRENT_OK) + return NULL; + + return dir; +} + +/* + * This functions retrieves the next file within the given dir. + * + * parameters: + * dir: Handle to a directory opened with get_dir_first previously. + * path: The path to the directory that is read. + * (hmmm, this seems not that ideal to me...why should this + * function construct the full path to the found file. + * The filename is more that enough i think, as the caller knows + * the path already.) + * file: A pointer to a structure that holds further information to the + * found file. (e.g. the path to the file) + * fsym: fsym==0 => don't follow symlinks, fsym!=0 => follow symlinks + * + * returns: + * This function returns always one of the following: + * GET_DIRENT_ERR: if an error occured...errno might or might not be set. + * NO_FILES_LEFT: if the last entry of the directory was read. + * GET_DIRENT_OK: if the next directory entry was read successfully. + */ +int +get_dir_next (DIR_HANDLE dir, const char * path, FILE_DATA * file, int fsym) +{ + stat_func_t my_stat_f; + + /* first look if we should use a symlink following stat */ + switch (fsym) + { + case DONT_FOLLOW_SYM: my_stat_f = lstat; break; + case FOLLOW_SYM: + default: my_stat_f = stat;; + } + + strncpy (file->path, path, MAX_PATH); + + /* add a / to path but don't write more than MAX_PATH */ + if (strlen (file->path) < MAX_PATH && + file->path [strlen (file->path)-1] != '/') + { + file->path [strlen (file->path) + 1] = '\0'; + file->path [strlen (file->path)] = '/'; + } + + /* now fill the given struct dirent_stat */ + errno = 0; + file->file = readdir (dir); + + if (NULL == file->file) + { + if (NULL != dir) + closedir (dir); + + if (errno != EBADF) + return NO_FILES_LEFT; + + return GET_DIRENT_ERR; + } + + strncpy ( + file->path+strlen (file->path), + file->file->d_name, + MAX_PATH-strlen (file->path)); + + if (my_stat_f (file->path, &(file->stat)) == -1) + { + /* errno is filled by the stat function */ + return GET_DIRENT_ERR; + } + + return GET_DIRENT_OK; +} diff --git a/src/posix/spf_thread_impl.c b/src/posix/spf_thread_impl.c new file mode 100644 index 0000000..ab0625b --- /dev/null +++ b/src/posix/spf_thread_impl.c @@ -0,0 +1,133 @@ +#ifndef WIN32 +# include +# include +# include +# include +#else +# include +#endif + +#include +#include + +#include +#include +#include +#include + +#define GEN_LOCAL +# include +#undef GEN_LOCAL + +GEN_LIST_FUNC_PROTO (scot_sp_fraction); +GEN_LIST_IMPL (scot_sp_fraction); + + +static +T_PROC_RET +scot_spf_select_entry_func (void * _sp) +{ + struct scot_sp_fraction * sp = (struct scot_sp_fraction *)_sp; + fd_set run_rfds, run_wfds, run_efds; + excenv_t * ee; + + THREAD_CANCEL_ENABLE; + THREAD_CANCEL_ASYNC; + + sp->thread_id = THREAD_ID; + + TRY + { + FD_ZERO (&run_rfds); + FD_ZERO (&run_wfds); + FD_ZERO (&run_efds); + + while (1) + { + uint16_t i; + excenv_t * ee; + + if (! THREAD_EQUAL (sp->thread_handle, SELF_THREAD)) + { + scot_stream_pool_cmd ( + (struct scot_stream_pool *)sp->el, + SCOT_STREAM_POOL_STOP_FRACTION, sp); + break; + } + + for (i=0; is_list [i]; + + if (st == NULL) break; + + if (FD_ISSET (st->handle.sock, &run_rfds)) + eno = SCOT_EVENT_STREAM_POOL_READ; + else if (FD_ISSET (st->handle.sock, &run_wfds)) + eno = SCOT_EVENT_STREAM_POOL_WRITE; + else if (FD_ISSET (st->handle.sock, &run_efds)) + eno = SCOT_EVENT_STREAM_POOL_EXCEP; + + if (eno != 0) + { + while (!scot_stream_is_closed (st)) + { + /* call callback... */ + e = scot_stream_pool_event_new (e, eno, NULL, 0, st); + scot_event_listener_call_cb (sp->el, (struct scot_event *) e); + scot_event_free ((struct scot_event *) e); + e = NULL; + + if (scot_stream_read_pending (st) != 0) + eno = SCOT_EVENT_STREAM_POOL_READ; + else if (scot_stream_write_pending (st) != 0) + eno = SCOT_EVENT_STREAM_POOL_WRITE; + else + break; + } + } + + if (scot_stream_is_closed (st)) + { + scot_spf_remove (sp, st, SCOT_STREAM_POOL_FD_ADD_RMASK| + SCOT_STREAM_POOL_FD_ADD_WMASK| + SCOT_STREAM_POOL_FD_ADD_EMASK); + SCOT_STREAM_FREE (st); + + if (sp->s_count == 0) + { + scot_stream_pool_cmd ( + (struct scot_stream_pool *)sp->el, + SCOT_STREAM_POOL_STOP_FRACTION, sp); + break; + } + } + } + + run_rfds = sp->rfds; + run_wfds = sp->wfds; + run_efds = sp->efds; + + /* evtl. sollte man hier noch timeout support einbauen, sprich + * das letzte NULL mit einer time austauschen. */ + if (select (sp->max_fd+1, &run_rfds, &run_wfds, &run_efds, NULL) == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + } + } + CATCH (ee) + { + printf ("Exception in %s (%s, %d)\n", __FUNCTION__, __FILE__, __LINE__); + print_all_exceptions (ee); + + exit (EXIT_FAILURE); + } +} + +scot_thread_entry_fptr scot_spf_thread_func[] = +{ + scot_spf_select_entry_func, /* socket */ + scot_spf_select_entry_func, /* pipe */ + scot_spf_select_entry_func /* terminal */ +}; diff --git a/src/posix/stream_ctl.c b/src/posix/stream_ctl.c new file mode 100644 index 0000000..8e0cda9 --- /dev/null +++ b/src/posix/stream_ctl.c @@ -0,0 +1,44 @@ +#include +#include +#include + +#include +#include + +#include + +int +scot_stream_get_blocking (struct scot_stream * s) +{ + int flags = fcntl (s->handle.file, F_GETFL); + + if (flags == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + + return ! (flags & O_NONBLOCK); +} + +void +scot_stream_set_block (struct scot_stream * s) +{ + int flags = fcntl (s->handle.file, F_GETFL); + + if (flags == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + + if (fcntl (s->handle.file, F_SETFL, flags & ~O_NONBLOCK) == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); +} + +void +scot_stream_set_nonblock (struct scot_stream * s) +{ + int flags = fcntl (s->handle.file, F_GETFL); + + if (flags == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + + if (fcntl ( s->handle.file, F_SETFL, flags | O_NONBLOCK) == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); +} + diff --git a/src/posix/thread.c b/src/posix/thread.c new file mode 100644 index 0000000..69fe166 --- /dev/null +++ b/src/posix/thread.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +struct join_cond_st +{ + pthread_mutex_t join_alarm_mutex; + pthread_cond_t join_alarm_cond; + THREAD_T thread; +}; + +static +T_PROC_RET +join_thread (void * arg) +{ + int status; + struct join_cond_st * join_cond = (struct join_cond_st *) arg; + + THREAD_CANCEL_ENABLE; + THREAD_CANCEL_ASYNC; + + pthread_join (join_cond->thread, NULL); + + status = pthread_cond_signal (&(join_cond->join_alarm_cond)); + if (status != 0) + { + perror ("problem with sending signal within join"); + } + + return NULL; +} + +void +thread_new (THREAD_T * handle, T_PROC_RET (*t_proc)(void *), void* arg) +{ + if (pthread_create (handle, NULL, t_proc, arg) != 0) + { + SCOT_MEM_ZERO (handle, sizeof (THREAD_T)); + } +} + +void +thread_end (THREAD_T thread, uint32_t timeout) +{ + int err; + + err = CANCEL_THREAD (thread); + if (err != 0) + THROW (EXC (EXC_ERROR, + err, + "[EVENT_SOURCE_THREAD]cancel request failed")); + + err = JOIN_THREAD (thread, timeout); + if (err == JOIN_TIMEOUT) + THROW (EXC (EXC_ERROR, + err, + "[EVENT_SOURCE_THREAD]timeout exceeded while waiting " + "for threads end")); + if (err == JOIN_ERROR) + THROW (EXC (EXC_ERROR, + err, + "[EVENT_SOURCE_THREAD]failure on waiting for threads end")); +} + +int +thread_join (THREAD_T thread, uint32_t timeout) +{ + int status, + join_state = JOIN_OK; + struct timespec alarm; + pthread_t join_thr; + struct join_cond_st join_cond = + { + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_COND_INITIALIZER, + thread + }; + + if (timeout == INFINITE) + { + if (pthread_join (thread, NULL) == 0) + { + return JOIN_OK; + } + else + { + return JOIN_ERROR; + } + } + + alarm.tv_sec = time (NULL) + timeout; + alarm.tv_nsec = 0; + + status = pthread_mutex_lock (&join_cond.join_alarm_mutex); + if (status != 0) + { + perror ("problem achiving mutex within join"); + } + + NEW_THREAD (&join_thr, join_thread, (void *) &join_cond); + + status = pthread_cond_timedwait ( + &(join_cond.join_alarm_cond), + &(join_cond.join_alarm_mutex), + &alarm); + if (status != 0) + { + if (status == ETIMEDOUT) + { + join_state = JOIN_TIMEOUT; + + pthread_cancel (join_thr); + pthread_join (join_thr, NULL); + } + else + { + perror ("problem with condition wait within join"); + } + } + + return join_state; +} + +void +thread_mutex_new (THREAD_MUTEX_T * mutex) +{ + pthread_mutex_init (mutex, NULL); +} + +void +thread_cond_new (THREAD_COND_T * cond) +{ + pthread_cond_init (cond, NULL); +} + +int +thread_mutex_lock (THREAD_MUTEX_T * mutex) +{ + if (pthread_mutex_lock (mutex) == 0) + { + return MUTEX_LOCK_OK; + } + else + { + return MUTEX_LOCK_ERROR; + } +} + +int +thread_cond_wait (THREAD_COND_T * cond, THREAD_COND_CS_T * cs, uint32_t t) +{ + if (t == INFINITE) + return pthread_cond_wait (cond, cs); + else + { + struct timespec tout; + + tout.tv_sec = time (NULL) + t; + tout.tv_nsec = 0; + + return pthread_cond_timedwait (cond, cs, &tout); + } +} diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..897c84f --- /dev/null +++ b/src/queue.c @@ -0,0 +1,46 @@ +/** + * \file queue.c + * \author Georg Steffers + * \brief Internal implementations for queue handling. + * + * This implements the internals for the typesafe queue based on + * typesafe linked lists defines in list.h. + * The only thing one has to remember when one only wants to + * use queues within the own code is to link the code agains + * an object of this and list.c. This is normally done by linking agains + * libscot with an -lscot or similar compiler switch. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include + +/** + * \internal + * \brief Error messages given. + * + * This holds all error messages that go either to the exception system + * or will be printed on stderr if an error occurs within a queue function. + * \attention This is not used actually. + */ +const char *queue_err_msg[] = +{ + N_("No more elements left in queue."), + NULL +}; diff --git a/src/scot_common.c b/src/scot_common.c new file mode 100644 index 0000000..4c2a2dd --- /dev/null +++ b/src/scot_common.c @@ -0,0 +1,33 @@ +#include + +#include + + +const char * scot_common_errmsg[] = +{ + "[COMMON]the path-string exceeded the maximum allowed size" +}; + +/* + * returns the maximum exponent of the given value use when creating the + * value with a base2-description. That is for example all + * values between 16 an 31 (including both) will return 4. + * + * This is useful when creating an index from a bitmask value. A bitmask + * with a single bit set would result in an integer index, that is unique + * for every different bit. + * + * Maybe there is some issue with byte ordering here...i am not quite sure + * as i do not use the shifted value and i never interpret single bytes out + * of b2pot. + */ +int +base2exp (uint32_t b2pot) +{ + int exp = 0; + + for (; b2pot >>= 1; exp ++); + + return exp; +} + diff --git a/src/scot_exceptions.c b/src/scot_exceptions.c new file mode 100644 index 0000000..f909426 --- /dev/null +++ b/src/scot_exceptions.c @@ -0,0 +1,60 @@ +#include + +#include +#include +#include +#include +#include + +const char *scot_err_msg[] = { + N_("[SCOT]malloc failed"), + N_("[SCOT]NULL pointer not allowed"), + NULL +}; + + +void * +exc_malloc (SIZE_T size) +{ + return exc_malloc_fl (size, __FILE__, __LINE__); +} + +void * +exc_malloc_fl (SIZE_T size, const char *file, int line) +{ + void *a; + + bindtextdomain (PACKAGE, LOCALEDIR); + a = SCOT_MEM_GET (size); + + if (a == NULL) + THROW (exc_new ( + EXC_ERROR, + file, + line, + MALLOC_ERR, + D_(scot_err_msg [MALLOC_ERR]))); + + return a; +} + +void +check_null (const void *var) +{ + check_null_fl (var, __FILE__, __LINE__); +} + +void +check_null_fl (const void *var, const char* file, int line) +{ + bindtextdomain (PACKAGE, LOCALEDIR); + + if (var == NULL) + THROW (exc_new ( + EXC_ERROR, + file, + line, + NULL_PTR_ERR, + D_(scot_err_msg [NULL_PTR_ERR]))); +} + diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..dca41d5 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,184 @@ +/* + * some basic berkley socket stuff....far from beeing complete + */ +#define USE_STRUCT_SCOT_SOCKET + +#include + +#include +#include +#include +#include +#include +#ifndef WIN32 +# include +#endif /* WIN32 */ + +#include + +#define SCOT_SOCKET_NEW_FAIL 0 +#define SCOT_SOCKET_LISTEN_FAIL 1 +#define SCOT_SOCKET_ACCEPT_FAIL 2 +#define SCOT_SOCKET_CONNECT_FAIL 3 +#define SCOT_SOCKET_NO_VALID_HOST 4 +#define SCOT_SOCKET_AF_NOT_IMPLEMENTED 5 +const char * scot_socket_errmsg[] = +{ + "[SOCKET]failed to create new socket", + "[SOCKET]failed to listen on port specified with socket", + "[SOCKET]failed to accept connections on socket", + "[SOCKET]failed to connect with socket", + "[SOCKET]invalid hostname", + "[SOCKET]address family not implemented" +}; + +const char * scot_socket_wrnmsg[] = +{ + "[SOCKET]there was already an existent socket file.\n" + " i removed that file to let the new instance of the program\n" + " work, but notice that there might be another instance of this\n" + " program running, that is unusable hence now." +}; + + + +void +scot_socket_init (uint16_t major, uint16_t minor) +{ +#ifdef WIN32 + WORD version = MAKEWORD (major,minor); + WSADATA wsa_data; + + WSAStartup (version, &wsa_data); +#endif +} + +void +scot_socket_fini (void) +{ + int a; +#ifdef WIN32 + WSACleanup (); +#endif +} + +/* + * actualy i found no good reason to bind a socken if one + * dont wants to listen to it too. If i find one i will change this. + */ + void +scot_socket_listen (const struct scot_socket* s) +{ + excenv_t *ee; + + TRY + { + if (bind (s->socket.handle.sock, s->sa, s->addr_len) == -1) + THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + + if (listen (s->socket.handle.sock, SCOT_SOCKET_BACKLOG) == -1) + THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + } + CATCH (ee) + { + /* + * if we got an Address already in use error on a unix domain + * socket it is most likely because there is a stale socket file + * around. Then we can try to unlink it and retry the listen. + */ + exception_t * e; + + while (e = retrive_exception (ee)) + { +#ifndef WIN32 + if (exc_errnum_get (e) == EADDRINUSE && s->sa->sa_family == AF_UNIX) + { + if (unlink (((struct sockaddr_un *) s->sa)->sun_path) == -1) + { + THROW (e); + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_LISTEN_FAIL, + scot_socket_errmsg [SCOT_SOCKET_LISTEN_FAIL])); + } + else + { + THROW (EXC (EXC_WARNING, + SCOT_SOCKET_UN_FILE_EXISTS, + scot_socket_wrnmsg [SCOT_SOCKET_UN_FILE_EXISTS])); + + free_exception (e); + scot_socket_listen (s); + } + } + else +#endif + { + THROW (e); + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_LISTEN_FAIL, + scot_socket_errmsg [SCOT_SOCKET_LISTEN_FAIL])); + } + } + + free_catched (ee); + } +} + +struct scot_socket * +scot_socket_accept (const struct scot_socket* s) +{ + switch (s->sa->sa_family) + { +#ifndef WIN32 + case AF_UNIX: return scot_socket_un_accept (s); +#endif /* WIN32 */ + case AF_INET: return scot_socket_in_accept (s); + } +} + +void +scot_socket_connect (const struct scot_socket * s, const char * adr) +{ + excenv_t * ee; + + TRY + { + switch (s->sa->sa_family) + { +#ifndef WIN32 + case AF_UNIX: + scot_socket_un_prep_con (s, adr); break; +#endif /* WIN32 */ + case AF_INET: + scot_socket_in_prep_con (s, adr); break; + default: + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_AF_NOT_IMPLEMENTED, + scot_socket_errmsg [SCOT_SOCKET_AF_NOT_IMPLEMENTED])); + } + + if ((connect (s->socket.handle.sock, s->sa, s->addr_len)) == -1) + THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + } + CATCH (ee) + { + forward_all_exceptions (ee); + + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_CONNECT_FAIL, + scot_socket_errmsg [SCOT_SOCKET_CONNECT_FAIL])); + } +} + +void +scot_socket_free (struct scot_socket * s) +{ + if (s) + { + if (s->sa) + SCOT_MEM_FREE (s->sa); + + SCOT_SOCK_CLOSE (s->socket.handle.sock); + SCOT_MEM_FREE (s); + } +} diff --git a/src/socket_in.c b/src/socket_in.c new file mode 100644 index 0000000..fcb1659 --- /dev/null +++ b/src/socket_in.c @@ -0,0 +1,200 @@ +/* + * some basic berkley socket stuff....far from beeing complete + */ +#define USE_STRUCT_SCOT_SOCKET + +#include +#include +#include +#include +#include + +#ifdef WIN32 +# define hstrerror strerror +#endif + +struct scot_socket * +scot_socket_in_new (const char* proto, const char* service) +{ + struct protoent * ppe; + struct servent * pse; + struct scot_socket * sock = NULL; + int port; + SOCKET handle; + excenv_t * ee; + + TRY + { + if (scot_strisdigit (proto)) + ppe = getprotobynumber (atoi (proto)); + else + ppe = getprotobyname (proto); + + if (ppe == NULL) + THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + + handle = socket (PF_INET, SOCK_STREAM, ppe->p_proto); + + if (handle == INVALID_SOCKET) + THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + + if (scot_strisdigit (service)) + pse = getservbyport (atoi (service), proto); + else + pse = getservbyname (service, proto); + + if (pse == NULL) + port = htons ((unsigned short) atoi (service)); + else + port = pse->s_port; + + sock = SCOT_MEM_GET (sizeof (struct scot_socket)); + SCOT_MEM_ZERO (sock, sizeof (struct scot_socket)); + sock->sa = SCOT_MEM_GET (sizeof (struct sockaddr_in)); + SCOT_MEM_ZERO (sock->sa, sizeof (struct sockaddr_in)); + + sock->socket.handle.sock = handle; + sock->socket.s_type = SCOT_STREAM_TYPE_SOCKET; + + ((struct sockaddr_in *) sock->sa)->sin_port = port; + ((struct sockaddr_in *) sock->sa)->sin_addr.s_addr = INADDR_ANY; + ((struct sockaddr_in *) sock->sa)->sin_family = AF_INET; + + sock->addr_len = sizeof (struct sockaddr_in); + } + CATCH (ee) + { + forward_all_exceptions (ee); + + if (sock != NULL) + { + if (sock->socket.handle.sock <= 0) + SCOT_SOCK_CLOSE (sock->socket.handle.sock); + + if (sock->sa != NULL) + SCOT_MEM_FREE (sock->sa); + + SCOT_MEM_FREE (sock); + } + + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_NEW_FAIL, + scot_socket_errmsg [SCOT_SOCKET_NEW_FAIL])); + } + + return sock; +} + +struct scot_socket * +scot_socket_in_accept (const struct scot_socket* s) +{ + SOCKET handle; +#ifndef WIN32 + SIZE_T addr_len; +#else + int addr_len; +#endif + struct sockaddr sa; + + struct scot_socket * sock = NULL; + excenv_t * ee; + + TRY + { + addr_len = s->addr_len; + + handle = accept (s->socket.handle.sock, &sa, &addr_len); + if (handle == INVALID_SOCKET) + THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + + /* + * ich reserviere erst hier den speicher für den neuen socket falls + * ein signal accept unterbricht und das programm evtl. abbricht. + * so bleibt garantiert kein reservierter speicher in diesem Fall + * zurück. Solange das ganze Programm abbricht währe das kein Problem, + * falls der aufruf aber in einem thread erfolgt so würde diese thread + * abbrechen und das memory leaken....(wenn es vor dem accept reserviert + * worden währe.) + */ + sock = SCOT_MEM_GET (sizeof (struct scot_socket)); + SCOT_MEM_ZERO (sock, sizeof (struct scot_socket)); + sock->sa = SCOT_MEM_GET (sizeof (struct sockaddr_in)); + SCOT_MEM_ZERO (sock->sa, sizeof (struct sockaddr_in)); + + sock->addr_len = addr_len; + sock->socket.handle.sock = handle; + sock->socket.s_type = SCOT_STREAM_TYPE_SOCKET; + SCOT_MEM_COPY (sock->sa, &sa, sizeof (struct sockaddr_in)); + } + CATCH (ee) + { + forward_all_exceptions (ee); + + if (sock != NULL) + { + if (sock->socket.handle.sock <= 0) + SCOT_SOCK_CLOSE (sock->socket.handle.sock); + + if (sock->sa != NULL) + SCOT_MEM_FREE (sock->sa); + + SCOT_MEM_FREE (sock); + } + + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_ACCEPT_FAIL, + scot_socket_errmsg [SCOT_SOCKET_ACCEPT_FAIL])); + } + + return sock; +} + +const +char * +scot_socket_in_get_host (struct scot_socket * s) +{ + struct hostent * phe; + + phe = gethostbyaddr ( + (char *) &(((struct sockaddr_in *) s->sa)->sin_addr.s_addr), + sizeof (unsigned long), + AF_INET); + + if (!phe) + THROW (EXC (EXC_WARNING, SCOT_H_ERRNO, hstrerror (SCOT_H_ERRNO))); + + return phe->h_name; +} + +const +char * +scot_socket_in_get_ddc (struct scot_socket * s) +{ + return inet_ntoa (((struct sockaddr_in *) s->sa)->sin_addr); +} + +void +scot_socket_in_prep_con (const struct scot_socket * s, const char * adr) +{ + struct hostent * phe; + + phe = gethostbyname (adr); + + if (phe) + { + SCOT_MEM_COPY ( + (char*) &(((struct sockaddr_in *) s->sa)->sin_addr), + phe->h_addr, + phe->h_length); + } +#ifndef WIN32 + else + { + if (inet_aton (adr, &(((struct sockaddr_in *) s->sa)->sin_addr)) == 0) + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_NO_VALID_HOST, + scot_socket_errmsg [SCOT_SOCKET_NO_VALID_HOST])); + } +#endif +} + diff --git a/src/socket_un.c b/src/socket_un.c new file mode 100644 index 0000000..1407554 --- /dev/null +++ b/src/socket_un.c @@ -0,0 +1,149 @@ +/* + * some basic berkley socket stuff....far from beeing complete + */ +#include +#include +#include +#include +#include + +#define USE_STRUCT_SCOT_SOCKET + +#include +#include +#include +#include +#include + +struct scot_socket * +scot_socket_un_new (const char* path) +{ + struct scot_socket * sock = NULL; + excenv_t * ee; + SOCKET handle; + + TRY + { + handle = socket (PF_UNIX, SOCK_STREAM, 0); + + if (handle == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + + if (SCOT_STR_LENGTH (path) > UNIX_PATH_MAX) + THROW (EXC (EXC_ERROR, + SCOT_UNX_PATH_TO_LONG, + scot_common_errmsg [SCOT_UNX_PATH_TO_LONG])); + + sock = SCOT_MEM_GET (sizeof (struct scot_socket)); + SCOT_MEM_ZERO (sock, sizeof (struct scot_socket)); + sock->sa = SCOT_MEM_GET (sizeof (struct sockaddr_un)); + SCOT_MEM_ZERO (sock->sa, sizeof (struct sockaddr_un)); + + ((struct sockaddr_un *) sock->sa)->sun_family = AF_UNIX; + SCOT_STRN_COPY ( + ((struct sockaddr_un *) sock->sa)->sun_path, + path, + UNIX_PATH_MAX); + + sock->socket.handle.sock = handle; + sock->socket.s_type = SCOT_STREAM_TYPE_SOCKET; + sock->addr_len = + sizeof (((struct sockaddr_un *) sock->sa)->sun_family) + + SCOT_STR_LENGTH (((struct sockaddr_un *) sock->sa)->sun_path); + } + CATCH (ee) + { + forward_all_exceptions (ee); + + if (sock != NULL) + { + if (sock->socket.handle.sock <= 0) + SCOT_SOCK_CLOSE (sock->socket.handle.sock); + + if (sock->sa != NULL) + SCOT_MEM_FREE (sock->sa); + + SCOT_MEM_FREE (sock); + } + + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_NEW_FAIL, + scot_socket_errmsg [SCOT_SOCKET_NEW_FAIL])); + } + + return sock; +} + +const +char * +scot_socket_un_get_path (const struct scot_socket* s) +{ + return ((struct sockaddr_un *) s->sa)->sun_path; +} + +struct scot_socket * +scot_socket_un_accept (const struct scot_socket* s) +{ + struct scot_socket * sock = NULL; + excenv_t * ee; + SOCKET handle; + struct sockaddr sa; + SIZE_T addr_len; + + TRY + { + addr_len = s->addr_len; + + handle = accept (s->socket.handle.sock, &sa, &addr_len); + if (handle == -1) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + + sock = SCOT_MEM_GET (sizeof (struct scot_socket)); + SCOT_MEM_ZERO (sock, sizeof (struct scot_socket)); + sock->sa = SCOT_MEM_GET (sizeof (struct sockaddr_un)); + SCOT_MEM_ZERO (sock->sa, sizeof (struct sockaddr_un)); + + sock->addr_len = addr_len; + sock->socket.handle.sock = handle; + sock->socket.s_type = SCOT_STREAM_TYPE_SOCKET; + SCOT_MEM_COPY (sock->sa, &sa, sizeof (struct sockaddr_un)); + } + CATCH (ee) + { + forward_all_exceptions (ee); + + if (sock != NULL) + { + if (sock->socket.handle.sock <= 0) + SCOT_SOCK_CLOSE (sock->socket.handle.sock); + + if (sock->sa != NULL) + SCOT_MEM_FREE (sock->sa); + + SCOT_MEM_FREE (sock); + } + + THROW (EXC (EXC_ERROR, + SCOT_SOCKET_ACCEPT_FAIL, + scot_socket_errmsg [SCOT_SOCKET_ACCEPT_FAIL])); + } + + return sock; +} + +void +scot_socket_un_prep_con (struct scot_socket * s, const char * adr) +{ + if (adr) + { + SCOT_STRN_COPY ( + ((struct sockaddr_un *) s->sa)->sun_path, + adr, + UNIX_PATH_MAX); + + s->addr_len = + sizeof (((struct sockaddr_un *) s->sa)->sun_family) + + SCOT_STR_LENGTH (((struct sockaddr_un *) s->sa)->sun_path); + } +} + diff --git a/src/stack.c b/src/stack.c new file mode 100644 index 0000000..f8d214b --- /dev/null +++ b/src/stack.c @@ -0,0 +1,47 @@ +/** + * \file stack.c + * \author Georg Steffers + * \brief Internal implementations for stack handling. + * + * This implements the internals for the typesafe stack based on + * typesafe linked lists defines in list.h. + * The only thing one has to remember when one only wants to + * use stacks within the own code is to link the code agains + * an object of this and list.c. This is normally done by linking agains + * libscot with an -lscot or similar compiler switch. + * + * Copyright (C)2006 Georg Steffers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include + +#include +#include + +/** + * \internal + * \brief Error messages given. + * + * This holds all error messages that go either to the exception system + * or will be printed on stderr if an error occurs within a stack function. + * \attention This is not used actually. + */ +const char *stack_err_msg[] = +{ + N_("No more elements left in stack."), + NULL +}; diff --git a/src/stream.c b/src/stream.c new file mode 100644 index 0000000..7877cf7 --- /dev/null +++ b/src/stream.c @@ -0,0 +1,189 @@ +#include +#include +#include + +#ifndef WIN32 +# include +#else +# include +# include +#endif + +#include +#include +#include +#include +#include + +#include + +static +SSIZE_T +scot_read (struct scot_stream * st, SIZE_T count) +{ + switch (st->s_type) + { + case (SCOT_STREAM_TYPE_SOCKET): + return recv (st->handle.sock, st->rbuf, count, 0); + case (SCOT_STREAM_TYPE_FILE): + case (SCOT_STREAM_TYPE_PIPE): + case (SCOT_STREAM_TYPE_INOTIFY): + return SCOT_FILE_READ (st->handle.file, st->rbuf, count); + } +} + +int +scot_stream_eof (struct scot_stream * st) +{ + /* if there is still data in the buffer we have no eof */ + if (st->rbuf_idx != 0) + return 0; + + /* try to read data in the buffer */ + st->rbuf_idx = scot_read (st, SCOT_STREAM_BUF_SIZE); + + if (st->rbuf_idx == 0) + return 1; + + /* if no data was available reset the idx to 0 */ + if (st->rbuf_idx == -1) + switch (SCOT_ERRNO) + { +#ifndef WIN32 + case (EAGAIN): st->rbuf_idx = 0; return 0; +#else + case (WSAECONNRESET): st->rbuf_idx = 0; return 1; + case (WSAEWOULDBLOCK): st->rbuf_idx = 0; return 0; +#endif + default: THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + } + + return 0; +} + +static +SSIZE_T +scot_stream_read_copy_buffers (struct scot_stream * s, char * dest, SIZE_T c) +{ + SSIZE_T ret = 0; + + if (c <= s->rbuf_idx) + { + SCOT_MEM_COPY (dest, s->rbuf, c); + SCOT_MEM_MOVE (s->rbuf, s->rbuf + c, s->rbuf_idx - c); + s->rbuf_idx -= c; + ret = c; + } + else + { + SCOT_MEM_COPY (dest, s->rbuf, s->rbuf_idx); + ret = s->rbuf_idx; + s->rbuf_idx = 0; + } + + return ret; +} + +static +SSIZE_T +scot_stream_read_buffered (struct scot_stream * st, char * buffer, SIZE_T count) +{ + SSIZE_T ret; + SIZE_T size = SCOT_STREAM_BUF_SIZE; + + if ((ret = scot_stream_read_copy_buffers (st, buffer, count)) == count) + return count; + +#ifndef WIN32 + if (st->s_type == SCOT_STREAM_TYPE_INOTIFY) + { + ioctl (st->handle.file, FIONREAD, &size); + size = (size <= SCOT_STREAM_BUF_SIZE)?size:SCOT_STREAM_BUF_SIZE; + } +#endif + + while (ret != count) + { + SIZE_T read_c; + + read_c = st->rbuf_idx = scot_read (st, size); + + if (st->rbuf_idx == -1) + switch (SCOT_ERRNO) + { +#ifndef WIN32 + case (EAGAIN): st->rbuf_idx = 0; return 0; +#else + case (WSAECONNRESET): st->rbuf_idx = 0; return ret; + case (WSAEWOULDBLOCK): st->rbuf_idx = 0; return 0; +#endif + default: THROW (EXC (EXC_ERROR, SCOT_ERRNO, strerror (SCOT_ERRNO))); + } + + ret += scot_stream_read_copy_buffers (st, buffer+ret, count-ret); + + if (read_c != size) + break; + } + + return ret; +} + +SSIZE_T +scot_stream_read (struct scot_stream * st, char * buffer, SIZE_T count) +{ + return scot_stream_read_buffered (st, buffer, count); +} + +SSIZE_T +scot_stream_write (struct scot_stream * st, char * buffer, SIZE_T count) +{ +} + +SSIZE_T scot_stream_read_pending (struct scot_stream * st) +{ + return st->rbuf_idx; +} + +SSIZE_T scot_stream_write_pending (struct scot_stream * st) +{ + return st->wbuf_idx; +} + +void +scot_stream_flush (struct scot_stream * st) +{ +} + +int +scot_stream_is_closed (struct scot_stream * st) +{ + return (st!=NULL && st->rbuf_idx==-1)?1:0; +} + +void +scot_stream_close (struct scot_stream * st) +{ + switch (st->s_type) + { + case (SCOT_STREAM_TYPE_SOCKET): + SCOT_SOCK_CLOSE (st->handle.sock); + break; + default: +#ifndef WIN32 + close (st->handle.file); +#endif + break; + } + SCOT_MEM_ZERO (st->rbuf, SCOT_STREAM_BUF_SIZE); + SCOT_MEM_ZERO (st->wbuf, SCOT_STREAM_BUF_SIZE); + st->rbuf_idx = -1; + st->wbuf_idx = -1; +} + +void +scot_stream_free (struct scot_stream * st) +{ + SCOT_STREAM_FREE (st); +} + diff --git a/src/stream_pool_base.c b/src/stream_pool_base.c new file mode 100644 index 0000000..faf87ad --- /dev/null +++ b/src/stream_pool_base.c @@ -0,0 +1,328 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define GEN_LOCAL +# include +#undef GEN_LOCAL + +GEN_LIST_FUNC_PROTO (scot_sp_fraction); +GEN_LIST_IMPL (scot_sp_fraction); + +/* + * static functions for this file + * ---------------------------------------------------------------------- + */ +static +unsigned short +scot_stream_pool_default_cb (struct scot_event * _e) +{ + SSIZE_T n; + char puffer[1024]; + struct scot_stream_pool_event * e = (struct scot_stream_pool_event *) _e; + + if (_e->event == SCOT_EVENT_STREAM_POOL_READ) + { + if (scot_stream_eof (e->st)) + { + printf ("Connection closed by foreign host\n"); + scot_stream_close (e->st); + } + else + { + n = scot_stream_read (e->st, puffer, 1024); + printf ("read possible on %d (%d bytes read)\n", e->st->handle, n); + } + } + if (_e->event == SCOT_EVENT_STREAM_POOL_WRITE) + printf ("write possible on %d (%d bytes read)\n", e->st->handle, n); + if (_e->event == SCOT_EVENT_STREAM_POOL_EXCEP) + printf ("exception on %d (%d bytes read)\n", e->st->handle, n); + + fflush (stdout); + + return SCOT_EVENT_END; +} + +static +void +main_thread_fini (void * arg) +{ + struct scot_stream_pool * pool = (struct scot_stream_pool *) arg; + list_scot_sp_fraction_node_t * f_node = pool->pool; + struct scot_sp_fraction * f; + + LOCK_THREAD_MUTEX (&pool->mutex); + /* this assures that the thread_fini does not try + * to wake the main thread for cleanup.... + * (done because main thread is finalized here) */ + ((struct scot_event_listener *) arg)->thread_run_flg = 0; + + while (! list_scot_sp_fraction_eol (pool->pool, f_node)) + { + f_node = list_scot_sp_fraction_next (f_node); + f = list_scot_sp_fraction_retrive (f_node); + + if (f->thread_run_flg != 0) + { + DETACH_THREAD (f->thread_handle); + CANCEL_THREAD (f->thread_handle); + } + } + + UNLOCK_THREAD_MUTEX (&pool->mutex); + + exc_end (); +} + +/* + * entry point für einen stream-pool event-source thread. + */ +static +void +scot_stream_pool_main_loop (struct scot_stream_pool * sp) +{ + excenv_t * ee; + + LOCK_THREAD_MUTEX (&sp->mutex); + + ((struct scot_event_listener *) sp)->thread_id = THREAD_ID; + ((struct scot_event_listener *) sp)->thread_run_flg = 1; + + THREAD_CANCEL_ENABLE; + THREAD_CANCEL_DEFER; + + TRY + { + list_scot_sp_fraction_node_t * f_node = sp->pool; + struct scot_sp_fraction * f; + + THREAD_ATEXIT_BEGIN (main_thread_fini, sp); + + /* + * starte all threads und suspende in einen cancellation point... + * Das könnte ich nochmal überdenken, immerhin kann ich mit + * diesem thread evtl. noch mehr anfangen. Z.B. Funktionen die + * in wecken, damit er alle threads stoppt (bzw. startet). + * Einzelne threads neu starten etc. Die Logik dafür könnte + * glaube ich gut in dieser Funktion liegen..... + * Vielleicht aber auch nicht. + */ + while (! list_scot_sp_fraction_eol (sp->pool, f_node)) + { + f_node = list_scot_sp_fraction_next (f_node); + f = list_scot_sp_fraction_retrive (f_node); + + NEW_THREAD (&f->thread_handle, f->spf_entry_func, f); + f->thread_run_flg = 1; + } + + UNLOCK_THREAD_MUTEX (&sp->mutex); + + while (1) + { + THREAD_COND_ENTER_CS (&sp->cmd_cs); + + WAIT_THREAD_COND (&sp->cmd_cond, &sp->cmd_cs, INFINITE); + + switch (sp->cmd) + { + case SCOT_STREAM_POOL_RESTART_ALL_FRAC: + f_node = sp->pool; + while (! list_scot_sp_fraction_eol (sp->pool, f_node)) + { + f_node = list_scot_sp_fraction_next (f_node); + f = list_scot_sp_fraction_retrive (f_node); + + if (f->thread_run_flg != 0) + { + END_THREAD (f->thread_handle, INFINITE); + thread_exc_end (f->thread_handle); + } + NEW_THREAD (&f->thread_handle, f->spf_entry_func, f); + f->thread_run_flg = 1; + } + break; + + case SCOT_STREAM_POOL_START_ALL_FRAC: + f_node = sp->pool; + while (! list_scot_sp_fraction_eol (sp->pool, f_node)) + { + f_node = list_scot_sp_fraction_next (f_node); + f = list_scot_sp_fraction_retrive (f_node); + + if (f->thread_run_flg == 0) + { + NEW_THREAD (&f->thread_handle, f->spf_entry_func, f); + f->thread_run_flg = 1; + } + } + break; + + case SCOT_STREAM_POOL_STOP_ALL_FRAC: + f_node = sp->pool; + while (! list_scot_sp_fraction_eol (sp->pool, f_node)) + { + f_node = list_scot_sp_fraction_next (f_node); + f = list_scot_sp_fraction_retrive (f_node); + + if (f->thread_run_flg != 0) + { + END_THREAD (f->thread_handle, INFINITE); + thread_exc_end (f->thread_handle); + f->thread_run_flg = 0; + } + } + break; + + case SCOT_STREAM_POOL_RESTART_FRACTION: + f = (struct scot_sp_fraction *) sp->cmd_arg; + if (f->thread_run_flg != 0) + { + END_THREAD (f->thread_handle, INFINITE); + thread_exc_end (f->thread_handle); + } + NEW_THREAD (&f->thread_handle, f->spf_entry_func, f); + f->thread_run_flg = 1; + break; + + case SCOT_STREAM_POOL_START_FRACTION: + f = (struct scot_sp_fraction *) sp->cmd_arg; + if (f->thread_run_flg == 0) + { + NEW_THREAD (&f->thread_handle, f->spf_entry_func, f); + f->thread_run_flg = 1; + } + break; + + case SCOT_STREAM_POOL_STOP_FRACTION: + f = (struct scot_sp_fraction *) sp->cmd_arg; + if (f->thread_run_flg != 0) + { + END_THREAD (f->thread_handle, INFINITE); + thread_exc_end (f->thread_handle); + f->thread_run_flg = 0; + } + } + + /* + * now we do a cleanup. That is + * we remove empty fractions from the stream pool. + */ + f_node = sp->pool; + while (!list_scot_sp_fraction_eol (sp->pool, f_node)) + { + int i; + + f_node = list_scot_sp_fraction_next (f_node); + f = list_scot_sp_fraction_retrive (f_node); + + if (f->s_count == 0) + { + if (f->thread_run_flg != 0) + { + END_THREAD (f->thread_handle, INFINITE); + thread_exc_end (f->thread_handle); + f->thread_run_flg = 0; + } + + list_scot_sp_fraction_delete (f_node); + } + } + + THREAD_COND_LEAVE_CS (&sp->cmd_cs); + } + + THREAD_ATEXIT_END (0); + } + CATCH (ee) + { + print_all_exceptions (ee); + + exit (EXIT_FAILURE); + } + + main_thread_fini (sp); +} + +/* ---------------------------------------------------------------------- */ + + +struct scot_stream_pool * +scot_stream_pool_new (struct scot_stream_pool * sp) +{ + sp = SCOT_MEM_GET (sizeof (struct scot_stream_pool)); + SCOT_MEM_ZERO (sp, sizeof (struct scot_stream_pool)); + + sp->pool = list_scot_sp_fraction_new (sp->pool); + + if (! list_scot_sp_fraction_elem_free_is_set ()) + list_scot_sp_fraction_set_elem_free (scot_spf_free); + + scot_event_listener_init ( + (struct scot_event_listener *) sp, + SCOT_EG_STREAM_POOL, + (scot_thread_entry_fptr) scot_stream_pool_main_loop); + + scot_event_listener_register_cb ( + (struct scot_event_listener *) sp, + SCOT_EVENT_STREAM_POOL_READ, + scot_stream_pool_default_cb, NULL); + scot_event_listener_register_cb ( + (struct scot_event_listener *) sp, + SCOT_EVENT_STREAM_POOL_WRITE, + scot_stream_pool_default_cb, NULL); + scot_event_listener_register_cb ( + (struct scot_event_listener *) sp, + SCOT_EVENT_STREAM_POOL_EXCEP, + scot_stream_pool_default_cb, NULL); + + NEW_THREAD_MUTEX (&sp->mutex); + NEW_THREAD_COND (&sp->cmd_cond); + NEW_THREAD_COND_CS (&sp->cmd_cs); + + return sp; +} + +void +scot_stream_pool_free (struct scot_stream_pool * sp) +{ + scot_stream_pool_cmd (sp, SCOT_STREAM_POOL_STOP_ALL_FRAC, NULL); + + scot_event_listener_fini ((struct scot_event_listener *) sp); + + list_scot_sp_fraction_free (sp->pool); + + FREE_THREAD_MUTEX (&sp->mutex); + FREE_THREAD_COND (&sp->cmd_cond); + FREE_THREAD_COND_CS (&sp->cmd_cs); + + SCOT_MEM_FREE (sp); +} + +void +scot_stream_pool_free_s (struct scot_stream_pool * sp, int free_s) +{ + list_scot_sp_fraction_node_t * spf_node = sp->pool; + + while (! list_scot_sp_fraction_eol (sp->pool, spf_node)) + { + struct scot_sp_fraction * f; + + spf_node = list_scot_sp_fraction_next (spf_node); + f = list_scot_sp_fraction_retrive (spf_node); + + scot_spf_free_s (f, free_s); + } +} diff --git a/src/stream_pool_fraction.c b/src/stream_pool_fraction.c new file mode 100644 index 0000000..1656bc0 --- /dev/null +++ b/src/stream_pool_fraction.c @@ -0,0 +1,154 @@ +#ifndef WIN32 +# include +# include +# include +# include +#else +# include +# include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + + +/* + * static functions for this file + * ---------------------------------------------------------------------- + */ +/* ---------------------------------------------------------------------- */ + +struct scot_sp_fraction * +scot_spf_new (struct scot_sp_fraction * f, + struct scot_event_listener * e, + int s_type) +{ + f = SCOT_MEM_GET (sizeof (struct scot_sp_fraction)); + SCOT_MEM_ZERO (f, sizeof (struct scot_sp_fraction)); + +#ifdef WIN32 + f->Event = WSACreateEvent(); +#endif + + f->el = e; + f->stream_type = s_type; + f->spf_entry_func = scot_spf_thread_func[s_type]; + + return f; +} + +void +scot_spf_set_free_streams (struct scot_sp_fraction * f, int free_s) +{ + f->free_s = free_s; +} + +void +scot_spf_free (struct scot_sp_fraction * f) +{ + int i; + + if (f->s_count != 0 && f->free_s != 0) + { + for (i=0; is_list[i] != NULL) + SCOT_STREAM_FREE (f->s_list[i]); + } + } + +#ifdef WIN32 + WSACloseEvent(f->Event); +#endif + + SCOT_MEM_FREE (f); +} + +void +scot_spf_free_s (struct scot_sp_fraction * f, int free_s) +{ + f->free_s = free_s; +} + +void +scot_spf_add (struct scot_sp_fraction * spf, + struct scot_stream * s, + int rwe_mask) +{ + int i; + excenv_t * ee; + + TRY + { + for (i = 0; spf->s_list [i] != NULL && i < SCOT_MAX_FRACTION; i++); + + if (i >= SCOT_MAX_FRACTION) + THROW (EXC (EXC_ERROR, 123, "fraction count < SCOT_MAX_FRACTION" + " but no element != NULL found")); + + spf->s_count ++; + spf->s_list [i] = s; + + scot_stream_set_nonblock (s); + + /* for select */ + if (s->handle.sock > spf->max_fd) + spf->max_fd = s->handle.sock; + if (rwe_mask & SCOT_STREAM_POOL_FD_ADD_RMASK) + FD_SET (s->handle.sock, &spf->rfds); + if (rwe_mask & SCOT_STREAM_POOL_FD_ADD_WMASK) + FD_SET (s->handle.sock, &spf->wfds); + if (rwe_mask & SCOT_STREAM_POOL_FD_ADD_EMASK) + FD_SET (s->handle.sock, &spf->efds); + } + CATCH (ee) + { + printf ("Exception in %s (%s, %d)\n", __FUNCTION__, __FILE__, __LINE__); + forward_all_exceptions (ee); + } +} + +void +scot_spf_remove (struct scot_sp_fraction * spf, + struct scot_stream * s, + int rwe_mask) +{ + int i; + + if (rwe_mask & SCOT_STREAM_POOL_FD_ADD_RMASK) + FD_CLR (s->handle.sock, &spf->rfds); + if (rwe_mask & SCOT_STREAM_POOL_FD_ADD_WMASK) + FD_CLR (s->handle.sock, &spf->wfds); + if (rwe_mask & SCOT_STREAM_POOL_FD_ADD_EMASK) + FD_CLR (s->handle.sock, &spf->efds); + + if (! FD_ISSET (s->handle.sock, &spf->rfds) && + ! FD_ISSET (s->handle.sock, &spf->wfds) && + ! FD_ISSET (s->handle.sock, &spf->efds)) + { + spf->max_fd = 0; + + for (i=0; is_list [i] == s) + { + SCOT_MEM_MOVE ( + &(spf->s_list [i]), &(spf->s_list [i+1]), + (SCOT_MAX_FRACTION-i) * sizeof (struct scot_stream *)); + spf->s_list [SCOT_MAX_FRACTION-1-i] = NULL; + spf->s_count --; + } + + if (spf->s_list [i] != NULL && + spf->s_list [i]->handle.sock > spf->max_fd) + spf->max_fd = spf->s_list[i]->handle.sock; + } + } +} diff --git a/src/stream_pool_management.c b/src/stream_pool_management.c new file mode 100644 index 0000000..101de3a --- /dev/null +++ b/src/stream_pool_management.c @@ -0,0 +1,245 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define GEN_LOCAL +# include +#undef GEN_LOCAL + +GEN_LIST_FUNC_PROTO (scot_sp_fraction); +GEN_LIST_IMPL (scot_sp_fraction); + +/* + * static functions for this file + * ---------------------------------------------------------------------- + */ +static +int +scot_get_free_spf_by_type (const struct scot_sp_fraction * a, + const struct scot_sp_fraction * b) +{ + if (a->stream_type != b->stream_type || + b->s_count >= SCOT_MAX_FRACTION) /* b ist der node bei find. */ + return -1; + + return 0; +} + +static +int +scot_get_spf_by_stream_handle (const struct scot_sp_fraction * a, + const struct scot_sp_fraction * b) +{ + int i; + + for (i = 0; i < SCOT_MAX_FRACTION && b->s_list [i] != a->s_list [0]; i++); + if (i >= SCOT_MAX_FRACTION) + return -1; + + return 0; +} + +/* ---------------------------------------------------------------------- */ + + +/* + * Es wäre sicher auch schön eine Funktion zu haben, die einen Stream + * nach dem anderen (die in dem Stream Pool registriert sind)....oder aber + * eine komplette Liste aller in einem Pool registrierten Streams zurückgibt. + * So kann man bevor man einen StreamPool freigibt erstmal alle Streams + * rausholen (und evtl. auch freigeben.) + */ +struct scot_stream * +scot_sp_get_all_streams (struct scot_stream_pool * sp) +{ +} + +/* + * adds the given stream s->handle to the fd_set specified via + * rwe_mask. If the stream is not already under control of this + * stream pool it is added to s_list. Else only the flags within + * rfds, wfds and efds are updated. This function will never clean + * a flag, but only set them. + */ +void +scot_stream_pool_add ( + struct scot_stream_pool * sp, + struct scot_stream * s, + int rwe_mask) +{ + struct scot_event_listener * l = (struct scot_event_listener *) sp; + struct scot_sp_fraction * f_search; + list_scot_sp_fraction_node_t * f_node; + struct scot_sp_fraction * f; + + /* + * soweit ich das in erinnerung habe sind meine listen nicht per se + * thread save. Daher sollte hier unbedingt noch ein mutex her, + * damit nicht gleichzeitig in die liste geschrieben (hier) + * und gelöscht wird (in remove) + */ + LOCK_THREAD_MUTEX (&sp->mutex); + + /* + * it would be nice to check if the stream is already in the pool before + * doing anything else + */ + + /* + * first seek a not full fraction for s->stream_type + */ + f_search = scot_spf_new (f_search, l, s->s_type); + scot_spf_add (f_search, s, rwe_mask); + + list_scot_sp_fraction_set_cmp (scot_get_free_spf_by_type); + f_node = list_scot_sp_fraction_find (sp->pool, f_search); + list_scot_sp_fraction_set_cmp (list_scot_sp_fraction_default_cmp); + + if (f_node == NULL) + { + f = f_search; + list_scot_sp_fraction_insert (sp->pool, f); + } + else + { + scot_spf_free_s (f_search, SCOT_STREAM_POOL_KEEP_STREAMS); + scot_spf_free (f_search); + + f = list_scot_sp_fraction_retrive (f_node); + scot_spf_add (f, s, rwe_mask); + } + + scot_stream_pool_cmd (sp, SCOT_STREAM_POOL_RESTART_FRACTION, f); + + UNLOCK_THREAD_MUTEX (&sp->mutex); +} + +/* + * removes the given stream s->handle from the fd_set specified via + * rwe_mask. If the stream isn't left in at least one of the sets, either + * rfds, wfds or efds it is removed from s_list. This function will never + * set a flag, but the mask is used to determine where to remove fd. + * + * Diese Funktion und scot_stream_pool_add sollten sich gegenseitig + * ausschließen, um die liste immer in einem konsistenten zustand zu halten. + * Relevant sind da wohl nur threads, also sollte es ein thread_mutex tun. + */ +void +scot_stream_pool_remove ( + struct scot_stream_pool * sp, + struct scot_stream * s, + int rwe_mask) +{ + struct scot_event_listener * l = (struct scot_event_listener *) sp; + struct scot_sp_fraction * f_search; + list_scot_sp_fraction_node_t * f_node; + excenv_t * ee; + + TRY + { + LOCK_THREAD_MUTEX (&sp->mutex); + + /* + * first seek a not full fraction for s->stream_type + */ + f_search = scot_spf_new (f_search, l, s->s_type); + scot_spf_add (f_search, s, rwe_mask); + + list_scot_sp_fraction_set_cmp (scot_get_spf_by_stream_handle); + f_node = list_scot_sp_fraction_find (sp->pool, f_search); + list_scot_sp_fraction_set_cmp (list_scot_sp_fraction_default_cmp); + + scot_spf_free (f_search); + + if (f_node == NULL) + THROW (EXC (EXC_ERROR, 345, "Can't find stream in any fraction.")); + else + { + struct scot_sp_fraction * f; + + scot_stream_pool_cmd (sp, SCOT_STREAM_POOL_STOP_FRACTION, f); + + f = list_scot_sp_fraction_retrive (f_node); + scot_spf_remove (f, s, rwe_mask); + + scot_stream_pool_cmd (sp, SCOT_STREAM_POOL_START_FRACTION, f); + } + + UNLOCK_THREAD_MUTEX (&sp->mutex); + } + CATCH (ee) + { + printf ("Exception in %s (%s, %d)\n", __FUNCTION__, __FILE__, __LINE__); + forward_all_exceptions (ee); + } +} + +int /* rwe_mask */ +scot_stream_pool_get_rwe ( + struct scot_stream_pool * sp, + struct scot_stream * s) +{ + struct scot_event_listener * l = (struct scot_event_listener *) sp; + struct scot_sp_fraction * f_search; + list_scot_sp_fraction_node_t * f_node; + struct scot_sp_fraction * f; + int ret = 0; + excenv_t * ee; + + TRY + { + f_search = scot_spf_new (f_search, l, s->s_type); + scot_spf_add (f_search, s, 0); + + list_scot_sp_fraction_set_cmp (scot_get_spf_by_stream_handle); + f_node = list_scot_sp_fraction_find (sp->pool, f_search); + list_scot_sp_fraction_set_cmp (list_scot_sp_fraction_default_cmp); + + scot_spf_free (f_search); + + if (f_node == NULL) + THROW (EXC (EXC_ERROR, 987, "could not find stream in stream pool")); + else + f = list_scot_sp_fraction_retrive (f_node); + + /* + * !!!FIXME!!! I will only work with sockets right now..... :-( + * Much more abstraction is in the stream code. + */ + if (FD_ISSET (s->handle.sock, &f->rfds)) + ret = ret | SCOT_STREAM_POOL_FD_ADD_RMASK; + if (FD_ISSET (s->handle.sock, &f->wfds)) + ret = ret | SCOT_STREAM_POOL_FD_ADD_WMASK; + if (FD_ISSET (s->handle.sock, &f->efds)) + ret = ret | SCOT_STREAM_POOL_FD_ADD_EMASK; + } + CATCH (ee); + { + printf ("Exception in %s (%s, %d)\n", __FUNCTION__, __FILE__, __LINE__); + forward_all_exceptions (ee); + } + + return ret; +} + + void +scot_stream_pool_cmd (struct scot_stream_pool * sp, uint16_t cmd, void * arg) +{ + THREAD_COND_ENTER_CS (&sp->cmd_cs); + + sp->cmd = cmd; + sp->cmd_arg = arg; + SIGNAL_THREAD_COND (&sp->cmd_cond); + + THREAD_COND_LEAVE_CS (&sp->cmd_cs); +} + diff --git a/src/stream_win.c b/src/stream_win.c new file mode 100644 index 0000000..b54cbba --- /dev/null +++ b/src/stream_win.c @@ -0,0 +1,16 @@ +#include + +#include +#include +#include +#include + +SSIZE_T win_file_read (HANDLE h, void * buf, SIZE_T count) +{ + DWORD got = 0; + + if (ReadFile (h, buf, count, &got, NULL) == 0) + THROW (EXC (EXC_ERROR, GetLastError (), strerror (GetLastError ()))); + + return got; +} diff --git a/src/win32/dir.c b/src/win32/dir.c new file mode 100644 index 0000000..6d250fa --- /dev/null +++ b/src/win32/dir.c @@ -0,0 +1,135 @@ +/* + * dir.c: This is the home of the windows-style get_dir_first, get_dir_next. + * Right now, those function only return non-directory filenames. + * (This is because thats what i wanted for checkout_files.) + * + * Windows has no real directory functions... + * (i guess that's because win does not organize directories as files) + * so what it does is, it simply applies + * a valid filepattern and fires FindFirstFile, which finds the + * first file that fits to that pattern. + * After that FindNextFile is used with the handle that FindFirstFile + * returned to get more files that fit to that pattern. + * + * Copyright (C) 2006 Georg Steffers + * + * Author: Georg Steffers [GST] + * Developer: + * + * Changes (for this file only): + * (2006-06-12) [GST] Started this changelog...well the program is + * ready since some weeks right now. + */ +#include +#include +#include + +#include +#include + + +/* + * This functions tries an opendir on the given path and uses the + * get_dir_next function to retrieve the first file within that directory. + * + * parameters: + * path: The path to the directory that should be read. + * file: A pointer to a structure that holds further information to the + * found file. (e.g. the path to the file) + * fsym: this has actually no meaning on win32 systems. + * + * returns: + * NULL if an error occures or an handle to the opened directory on + * success. This handle should be closed later in the program. + * There should be a function that does this, but as this program + * actually exits anyway after directory reading i have not done it + * right now. + */ +DIR_HANDLE +get_dir_first (const char * path, FILE_DATA * file, int fsym) +{ + TCHAR pattern[MAX_PATH+1]; + HANDLE dir; + + SCOT_STRN_COPY (pattern, path, MAX_PATH); + + /* add a \* to path but don't write more than MAX_PATH */ + if (SCOT_STR_LENGTH (pattern) < MAX_PATH && + pattern [SCOT_STR_LENGTH (pattern)-1] != '\\') + { + pattern [SCOT_STR_LENGTH (pattern) + 1] = '\0'; + pattern [SCOT_STR_LENGTH (pattern)] = '\\'; + } + + SCOT_STR_COPY (file->path, pattern); + + if (SCOT_STR_LENGTH (pattern) < MAX_PATH) + { + pattern [SCOT_STR_LENGTH (pattern) + 1] = '\0'; + pattern [SCOT_STR_LENGTH (pattern)] = '*'; + } + + // Get the first file + dir = FindFirstFile(pattern, &(file->file)); + if (dir == INVALID_HANDLE_VALUE) + { + /* lets hope errno or something is set */ + return NULL; + } + + SCOT_STRN_COPY ( + file->path+SCOT_STR_LENGTH (file->path), + file->file.cFileName, + MAX_PATH-SCOT_STR_LENGTH (file->path)); + + return dir; +} + + +/* + * This functions retrieves the next file within the given dir. + * + * parameters: + * dir: Handle to a directory opened with get_dir_first previously. + * path: The path to the directory that is read. + * (hmmm, this seems not that ideal to me...why should this + * function construct the full path to the found file. + * The filename is more that enough i think, as the caller knows + * the path already.) + * file: A pointer to a structure that holds further information to the + * found file. (e.g. the path to the file) + * fsym: this has actually no meaning on win32 systems. + * + * returns: + * This function returns always one of the following: + * GET_DIRENT_ERR: if an error occured...errno might or might not be set. + * NO_FILES_LEFT: if the last entry of the directory was read. + * GET_DIRENT_OK: if the next directory entry was read successfully. + */ +int +get_dir_next (DIR_HANDLE dir, const char * path, FILE_DATA * file, int fsym) +{ + if (SCOT_STR_LENGTH (file->path) < MAX_PATH && + file->path [SCOT_STR_LENGTH (file->path)-1] != '\\') + { + file->path [SCOT_STR_LENGTH (file->path) + 1] = '\0'; + file->path [SCOT_STR_LENGTH (file->path)] = '\\'; + } + + if (! FindNextFile(dir, &(file->file))) + { + if (GetLastError() == ERROR_NO_MORE_FILES) + { + return NO_FILES_LEFT; + } + + return GET_DIRENT_ERR; + } + + SCOT_STRN_COPY ( + file->path+SCOT_STR_LENGTH (file->path), + file->file.cFileName, + MAX_PATH-SCOT_STR_LENGTH (file->path)); + + return GET_DIRENT_OK; +} diff --git a/src/win32/memory.c b/src/win32/memory.c new file mode 100644 index 0000000..7b74bad --- /dev/null +++ b/src/win32/memory.c @@ -0,0 +1,42 @@ +#include +#include + +SIZE_T +str_length (const char * str) +{ + const char * s = str; + while (*(s++)); + return s-str-1; +} + +char * +str_copy (char * dest, const char * src) +{ + char * d = dest; + while (*(d++) = *(src++)); + return dest; +} + +char * +strn_copy (char * dest, const char * src, SIZE_T size) +{ + char * d = dest; + while ((d-dest) < size && (*(d++) = *(src++))); + return dest; +} + +char * +str_char (const char * str, int ch) +{ + while ((*str != ch) && *(str++)); + return (*str)?(char *)str:NULL; +} + +char * +strr_char (const char * str, int ch) +{ + const char * s = str; + while (*(s++)); + while ((*(--s) != ch) && (s != str)); + return (s!=str)?(char *)s:NULL; +} diff --git a/src/win32/spf_thread_impl.c b/src/win32/spf_thread_impl.c new file mode 100644 index 0000000..b157c7e --- /dev/null +++ b/src/win32/spf_thread_impl.c @@ -0,0 +1,184 @@ +#ifndef WIN32 +# include +# include +# include +# include +#else +# include +#endif + +#include +#include + +#include +#include +#include +#include + +#define GEN_LOCAL +# include +#undef GEN_LOCAL + +GEN_LIST_FUNC_PROTO (scot_sp_fraction); +GEN_LIST_IMPL (scot_sp_fraction); + + +static +T_PROC_RET +scot_spf_select_entry_func (void * _sp) +{ + struct scot_sp_fraction * sp = (struct scot_sp_fraction *)_sp; + fd_set run_rfds, run_wfds, run_efds; + excenv_t * ee; + + /* + * OK, i am not sure if i can do it this way but i hope i will get a + * cancellation point with this, that did the blocking i wanted to + * have. I try to initialize an event object with all sockets i + * want to observe and use it within pthreadCancelableWait to get an + * cancellation point. If it returns i have to figure out wich + * socket produced what event and call the callback accordingly. + */ + WSAEVENT EventArray [SCOT_MAX_FRACTION]; + SOCKET SocketArray [SCOT_MAX_FRACTION]; + WSANETWORKEVENTS NetworkEvents; + int Index; + + THREAD_CANCEL_ENABLE; + THREAD_CANCEL_ASYNC; + + sp->thread_id = THREAD_ID; + + TRY + { + FD_ZERO (&run_rfds); + FD_ZERO (&run_wfds); + FD_ZERO (&run_efds); + + while (1) + { + uint16_t i; + excenv_t * ee; + + SCOT_MEM_ZERO (SocketArray, sizeof (SOCKET) * SCOT_MAX_FRACTION); + SCOT_MEM_ZERO (EventArray, sizeof (WSAEVENT) * SCOT_MAX_FRACTION); + + if (! THREAD_EQUAL (sp->thread_handle, SELF_THREAD)) + { + scot_stream_pool_cmd ( + (struct scot_stream_pool *)sp->el, + SCOT_STREAM_POOL_STOP_FRACTION, sp); + break; + } + + for (i=0; is_list [i]; + long flags = FD_CLOSE; + + if (st == NULL) break; + + if (FD_ISSET (st->handle.sock, &sp->rfds)) + flags |= FD_READ; + if (FD_ISSET (st->handle.sock, &sp->wfds)) + flags |= FD_WRITE; + + /* + * Das ist jetzt etwas gefrickelt, eigentlich sollte unter + * Windows die Datenstruktur für eine sp_fraction angepasst werden + * und ebenso die add und remove methoden. + * Jetzt übertrage ich etwas mühsam die Daten auf windows + * was auch dazu führt, das ich öfter als nötig events + * closen und createn muß.... + */ + SocketArray [i] = st->handle.sock; + if (EventArray [i] == (WSAEVENT) NULL) + WSACreateEvent (EventArray [i]); + WSAEventSelect ( + SocketArray [i], + EventArray [i], + flags); + EventTotal ++; + } + + Index = WSAWaitForMultipleEvents (EventTotal, EventArray, + FALSE, WSA_INFINITE, TRUE); + Index = Index - WSA_WAIT_EVENT_0; + + for (i = Index; i < EventTotal; i++) + { + Index = WSAWaitForMultipleEvents (1, &EventArray[i], + TRUE, 1000, TRUE); + + if ((Index != WSA_WAIT_FAILED) && (Index != WSA_WAIT_TIMEOUT)) + { + struct scot_stream * st = sp->s_list [i]; + + WSAEnumNetworkEvents (SocketArray[i], + EventArray[i], + &NetworkEvents); + + if ((NetworkEvents & FD_READ) != 0) + eno = SCOT_EVENT_STREAM_POOL_READ; + else if ((NetworkEvents & FD_READ) != 0) + eno = SCOT_EVENT_STREAM_POOL_WRITE; + + if (eno != 0) + { + while (!scot_stream_is_closed (st)) + { + /* call callback... */ + e = scot_stream_pool_event_new (e, eno, NULL, 0, st); + scot_event_listener_call_cb ( + sp->el, (struct scot_event *) e); + scot_event_free ((struct scot_event *) e); + e = NULL; + + if (scot_stream_read_pending (st) != 0) + eno = SCOT_EVENT_STREAM_POOL_READ; + else if (scot_stream_write_pending (st) != 0) + eno = SCOT_EVENT_STREAM_POOL_WRITE; + else + break; + } + } + + if (scot_stream_is_closed (st)) + { + WSACloseEvent (EventArray [i]); + + scot_spf_remove (sp, st, + SCOT_STREAM_POOL_FD_ADD_RMASK| + SCOT_STREAM_POOL_FD_ADD_WMASK| + SCOT_STREAM_POOL_FD_ADD_EMASK); + SCOT_STREAM_FREE (st); + + if (sp->s_count == 0) + { + scot_stream_pool_cmd ( + (struct scot_stream_pool *)sp->el, + SCOT_STREAM_POOL_STOP_FRACTION, sp); + break; + } + } + } + } + } + } + CATCH (ee) + { + printf ("Exception in %s (%s, %d)\n", __FUNCTION__, __FILE__, __LINE__); + print_all_exceptions (ee); + + exit (EXIT_FAILURE); + } +} + +scot_thread_entry_fptr scot_spf_thread_func[] = +{ + scot_spf_select_entry_func, /* socket */ + NULL, /* pipe */ + NULL /* terminal */ +}; diff --git a/src/win32/stream_ctl.c b/src/win32/stream_ctl.c new file mode 100644 index 0000000..d6fab43 --- /dev/null +++ b/src/win32/stream_ctl.c @@ -0,0 +1,44 @@ +#include +#include + +#include +#include + +#include + +int +scot_stream_get_blocking (struct scot_stream * s) +{ + return s->s_blk; +} + +void +scot_stream_set_block (struct scot_stream * s) +{ + u_long blk = 0; + /* + * actually only socket can be set to non-blocking io + * under windows... + */ + if (s->s_type == SCOT_STREAM_TYPE_SOCKET) + { + s->s_blk = 1; + ioctlsocket (s->handle.sock, FIONBIO, &blk); + } +} + +void +scot_stream_set_nonblock (struct scot_stream * s) +{ + u_long blk = 1; + /* + * actually only socket can be set to non-blocking io + * under windows... + */ + if (s->s_type == SCOT_STREAM_TYPE_SOCKET) + { + s->s_blk = 0; + ioctlsocket (s->handle.sock, FIONBIO, &blk); + } +} + diff --git a/src/win32/thread.c b/src/win32/thread.c new file mode 100644 index 0000000..1a07e42 --- /dev/null +++ b/src/win32/thread.c @@ -0,0 +1,38 @@ +#include + +THREAD_T +thread_new (T_PROC_RET (*t_proc)(void *), void* arg) +{ + return (_beginthreadex (NULL, 0, t_proc, arg, 0, NULL)); +} + +int +thread_join (THREAD_T thread, unsigned int timeout) +{ + switch (WaitForSingleObject (thread, timeout * 1000)) + { + case WAIT_OBJECT_0: + CloseHandle (thread); + return JOIN_OK; + case WAIT_TIMEOUT: + return JOIN_TIMEOUT; + } +} + +int +thread_mutex_lock (THREAD_MUTEX_T * mutex) +{ + switch (WaitForSingleObject (*mutex, INFINITE)) + { + case WAIT_OBJECT_0: + return MUTEX_LOCK_OK; + default: + return MUTEX_LOCK_ERROR; + } +} + +THREAD_MUTEX_T +thread_mutex_new (void) +{ + return NULL; +} diff --git a/src/window-support.txt b/src/window-support.txt new file mode 100644 index 0000000..327aca2 --- /dev/null +++ b/src/window-support.txt @@ -0,0 +1,32 @@ +Fragen und Ideen zu Fenstern: + +- Ich brauch einen einheitlichen Weg um das Fenstersystem zu initialisieren. + Also ein Abstraction die das kleinste gemeinsame von Windows und + XWindows zusammenfast. Idealerweise sollte das Interface auch von + anderen Fenstersystemen (bsp. Apple) nutzbar sein. +- erster Schritt: Fenstersystem initialisieren.... + * bei X heisst das: + = verbindung mit einem X-Server aufnehmen, dann bekommen wir ein Display + = den Defaultscreen des Display holen...und evtl noch + = den GC zum defaultscreen holen + (vermutlich fasse ich den gc und den screen in einer Struktur + zusammen um dann über eine richtig initialisierte Variable dieser + Struktur alle Grafikfunktionen zur verfügung zu habe.) + * bei GTK heisst das (soweit ich weiß): + = gtk_init oder sowas aufrufen.... + * bei Windows heisst das: + = im Moment noch keine Ahnung + + Danach sollte ich eine art Handle haben über den alle weiteren Grafikopera + tionen stattfinden können. (z.B. Fenster erzeugen, OpenGL initialisieren, + etc.) + +- Der reine X-handle ist im Vergleich mit dem Windows oder GTK handle + stark eingeschränkt, ich kann niemals alle Funktionen die mit einem + Windows oder GTK Handle möglich sind auch mit einem X-Handle machen... + + (Möglich wäre dies nur wenn alle Widgets selbst implementiert würden... + ich könnte den X Handle aber als einen Basishandle ansehen, der allen + Widgetset handles gemein ist....evtl. falls möglich auch den Windos + Handles. Über diesen Handle wären dann Basisfunktionen des Window + Systems erreichbar.) diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..64ba7b9 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,55 @@ +LTLIBS = $(INTLLIBS) @SOCK_LIB@ +LIBS = $(INTLLIBS) @SOCK_LIB@ + +PROGS = scot_test list_test exc_test \ + tcp_socket_serv tcp_socket_clie \ + multiserv + +INCLUDES = -I../include @INOTIFY_INCLUDES@ + +if USE_THREADS +PROGS += exc_thr_test +endif + +if !WIN32 +PROGS += unx_socket_serv unx_socket_clie fs_events +endif + +bin_PROGRAMS = $(PROGS) + +scot_test_SOURCES = scot_test.c +scot_test_LDADD = -L"../src" -lscot + +list_test_SOURCES = list_test.c +list_test_LDADD = -L"../src" -lscot + +exc_test_SOURCES = exc_test.c +exc_test_LDADD = -L"../src" -lscot + +unx_socket_serv_SOURCES = unx_socket_serv.c +unx_socket_serv_LDADD = -L"../src" -lscot + +unx_socket_clie_SOURCES = unx_socket_clie.c +unx_socket_clie_LDADD = -L"../src" -lscot + +tcp_socket_serv_SOURCES = tcp_socket_serv.c +tcp_socket_serv_LDADD = -L"../src" -lscot + +tcp_socket_clie_SOURCES = tcp_socket_clie.c +tcp_socket_clie_LDADD = -L"../src" -lscot + +if USE_THREADS +if USE_INOTIFY +fs_events_SOURCES = fs_events.c +fs_events_LDADD = -L"../src" -lscot @THREAD_LIB@ +fs_events_CFLAGS = @THREAD_CFLAGS@ -DUSE_THREADS +endif + +multiserv_SOURCES = multiserv.c +multiserv_LDADD = -L"../src" -lscot @THREAD_LIB@ +multiserv_CFLAGS = @THREAD_CFLAGS@ -DUSE_THREADS + +exc_thr_test_SOURCES = exc_test.c +exc_thr_test_LDADD = -L"../src" -lscot @THREAD_LIB@ +exc_thr_test_CFLAGS = @THREAD_CFLAGS@ -DUSE_THREADS +endif diff --git a/test/exc_test.c b/test/exc_test.c new file mode 100644 index 0000000..ae36224 --- /dev/null +++ b/test/exc_test.c @@ -0,0 +1,137 @@ +#include +#include +#include + +#include +#include + +#define OPEN_FAILED 0 +#define CLOSE_FAILED 1 +#define WARNING1 2 +#define WARNING2 3 +#define WARNING3 4 +#define WARNING4 5 +#define ERROR1 6 +#define ERROR2 7 + +const char *errmsg[] = { + N_("Failed to open the file."), + N_("Failed to close the file."), + N_("This is WARNING no. 1"), + N_("This is WARNING no. 2"), + N_("This is WARNING no. 3"), + N_("This is WARNING no. 4"), + N_("This is ERROR no. 1"), + N_("This is ERROR no. 2"), + NULL +}; + +/* + * a simple test case + */ +void +testf2 (void) +{ + THROW (EXC (EXC_WARNING, WARNING3, D_(errmsg [WARNING3]))); + THROW (EXC (EXC_ERROR, ERROR2, D_(errmsg [ERROR2]))); + THROW (EXC (EXC_WARNING, WARNING4, D_(errmsg [WARNING4]))); +} + +void +testf (void) +{ + excenv_t *ee; + + TRY + testf2 (); + CATCH (ee) + { + forward_all_exceptions (ee); + + THROW (EXC (EXC_WARNING, WARNING1, D_(errmsg [WARNING1]))); + // THROW (EXC (EXC_ERROR, ERROR1, D_(errmsg [ERROR1]))); + THROW (EXC (EXC_WARNING, WARNING2, D_(errmsg [WARNING2]))); + } +} + +FILE * +exc_fopen (const char *path, const char *mode) +{ + excenv_t *ee; + FILE *fptr; + + TRY + if ((fptr = fopen (path, mode)) == NULL) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + CATCH (ee) + { + forward_all_exceptions (ee); + THROW (EXC (EXC_ERROR, OPEN_FAILED, D_(errmsg[OPEN_FAILED]))); + } + + return fptr; +} + +int +exc_fclose (FILE *fptr) +{ + excenv_t *ee; + int ret; + + TRY + if ((ret = fclose (fptr)) != 0) + THROW (EXC (EXC_ERROR, errno, strerror (errno))); + CATCH (ee) + { + forward_all_exceptions (ee); + THROW (EXC (EXC_ERROR, CLOSE_FAILED, D_(errmsg[CLOSE_FAILED]))); + } + + return ret; +} + +int +main (void) { + excenv_t *ee; + char *locale; + char fname[] = "./exc_test.fil"; + FILE *fptr; + + locale = setlocale (LC_ALL, ""); + + TRY + { + fptr = exc_fopen (fname, "w+"); + testf (); + exc_fclose (fptr); + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + if (EXC_IN_THIS_TRY (e)) + { + switch (exc_errnum_get (e)) + { + case CLOSE_FAILED: + /* here we do something for previous cleanups... */ + fprintf (stderr, "an open FILE* remains\n"); + case ERROR1: + fclose (fptr); + } + } + + print_exception (e); + free_exception (e); + } + + free_catched (ee); + } + /* print_all_exceptions (ee); */ + + exc_end (); + + return 0; +} diff --git a/test/fs_events.c b/test/fs_events.c new file mode 100644 index 0000000..764e37d --- /dev/null +++ b/test/fs_events.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +struct scot_fs_watcher * fsw; + +void +on_exit_func (void) +{ + scot_event_listener_stop ((struct scot_event_listener *) fsw); + scot_fs_watcher_free (fsw); + + exc_end (); +} + +void +exit_func (int inum) +{ + exit (0); +} + +unsigned short +fs_cb (struct scot_event * _e) +{ + struct scot_fs_watcher_event * fswe; + struct scot_event * e; + char * e_s; + + printf ("serialized %d bytes\n", scot_event_serialize (_e, &e_s)); + e = scot_event_deserialize (e, e_s); + printf ("deserialized event\n"); + fswe = (struct scot_fs_watcher_event *) e; + SCOT_MEM_FREE (e_s); + + switch (e->event) + { + case (SCOT_EVENT_FS_WATCHER_CREATE): + printf ("%s/%s created\n", fswe->path, fswe->name); + break; + case (SCOT_EVENT_FS_WATCHER_DELETE): + printf ("%s/%s deleted\n", fswe->path, fswe->name); + break; + case (SCOT_EVENT_FS_WATCHER_RENAME): + printf ("%s/%s renamed to %s/%s\n", + fswe->path, fswe->oldname, + fswe->path, fswe->name); + } + + scot_event_free (e); + + return SCOT_EVENT_END; +} + +int +main (int argc, char * argv []) +{ + excenv_t * ee; + + signal (SIGINT, exit_func); + atexit (on_exit_func); + + TRY + { + fsw = scot_fs_watcher_new (); + + scot_event_listener_register_cb ( + (struct scot_event_listener *) fsw, + SCOT_EVENT_FS_WATCHER_CREATE, + fs_cb, NULL); + + scot_event_listener_start ((struct scot_event_listener *) fsw); + } + CATCH (ee) + { + print_all_exceptions (ee); + exit (EXIT_FAILURE); + } + + + TRY + { + char buffer [1024]; + + fgets (buffer, 1024, stdin); + *strchr (buffer, '\n') = '\0'; + + while (strcmp ("quit", buffer) != 0) + { + scot_fs_watcher_add ( + fsw, buffer, 0 | SCOT_EVENT_FS_WATCHER_CREATE | + SCOT_EVENT_FS_WATCHER_DELETE | + SCOT_EVENT_FS_WATCHER_RENAME); + + fgets (buffer, 1024, stdin); + *strchr (buffer, '\n') = '\0'; + } + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + print_exception (e); + free_exception (e); + } + + free_catched (ee); + } + + return 0; +} diff --git a/test/list_test.c b/test/list_test.c new file mode 100644 index 0000000..cffba73 --- /dev/null +++ b/test/list_test.c @@ -0,0 +1,94 @@ +#include + +#include +#include +#define GEN_LOCAL +#include +#undef GEN_LOCAL + +GEN_LIST (int); + + + int +main (void) +{ + list_int_node_t *i_list, + *i_node, + *i_node_next; + int a1 = 5, + a2 = 2, + a3 = 8, + a4 = 12, + a5 = 3, + *val; + excenv_t *ee; + + char *locale; + + locale = setlocale (LC_ALL, ""); + + TRY + { + i_list = list_int_new (i_list); + i_node = i_list; + + i_node = list_int_insert (i_node, &a1); + i_node = list_int_insert (i_node, &a2); + i_node = list_int_insert (i_node, &a3); + i_node = list_int_insert (i_node, &a4); + //list_int_insert (NULL, &a5); + + i_node = i_list; + while (! list_int_eol (i_list, i_node)) + { + i_node = list_int_next (i_node); + printf ("Wert: %d\n", * list_int_retrive (i_node)); + } + + if (list_int_find (i_list, &a5) != NULL) + printf("JO, Wert %d gefunden\n", a5); + if (list_int_find (i_list, &a3) != NULL) + printf("JO, Wert %d gefunden\n", a3); + + list_int_free (i_list); + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + if (EXC_IN_THIS_TRY (e)) + { + switch (exc_errnum_get (e)) + { + /* i need to find more reasonable errors for list + * one must see if the creatin of a new list, or the creation + * of an node or the freeing of something or something else + * got wrong (for now i do the free in any case of an error, + * but this works only because i know that the list is + * already created.) */ + case LIST_FREE_ERR: + fprintf (stderr, "list list remains unfreed."); + case LIST_FIND_ERR: + case LIST_RETR_ERR: + case LIST_NEXT_ERR: + case LIST_EOL_ERR: + case LIST_INSERT_ERR: + list_int_free (i_list); + break; + case LIST_NEW_ERR: + fprintf (stderr, "UHH, OHH i got no list, i must die."); + } + } + + print_exception (e); + } + + free_catched (ee); + } + + exc_end (); + + return 0; +} diff --git a/test/multiserv.c b/test/multiserv.c new file mode 100644 index 0000000..d7ebfd0 --- /dev/null +++ b/test/multiserv.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +struct scot_stream_pool * con_pool; +struct scot_socket * sock; +struct scot_socket * con_sock; + +void +on_exit_func (void) +{ + scot_stream_pool_free (con_pool); + + if (con_sock != NULL) + scot_socket_free (con_sock); + + scot_socket_free (sock); + + scot_socket_fini (); + + exc_end (); +} + +void +exit_func (int inum) +{ + exit (0); +} + +unsigned short +stream_read_cb (struct scot_event * _e) +{ + SSIZE_T i, n; + char puffer[1024] = ""; + + struct scot_stream_pool_event * e = (struct scot_stream_pool_event *) _e; + + n = scot_stream_read (e->st, puffer, 1024); + + if (puffer [0] == 'q') + { + scot_stream_close (e->st); + + return SCOT_EVENT_END; + } + + printf ("Server: "); + for (i=0; i> 8), + ((struct scot_event_listener *) con_pool)->group); + fflush (stdout); + + scot_event_listener_register_cb ( + (struct scot_event_listener *) con_pool, + SCOT_EVENT_STREAM_POOL_READ, + stream_read_cb, NULL); + + scot_event_listener_start ((struct scot_event_listener *) con_pool); + } + CATCH (ee) + { + print_all_exceptions (ee); + exit (EXIT_FAILURE); + } + + + TRY + { + sock = scot_socket_in_new ("tcp", "7777"); + scot_socket_listen (sock); + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + print_exception (e); + + if (EXC_IN_THIS_TRY (e)) + { + switch (exc_errnum_get (e)) + { + case SCOT_SOCKET_LISTEN_FAIL: + scot_socket_free (sock); + case SCOT_SOCKET_NEW_FAIL: + break; + } + } + + free_exception (e); + } + + free_catched (ee); + } + + TRY + { + while ((con_sock = scot_socket_accept (sock)) >= 0) + { + struct scot_stream * sock_stream; + int i, n; + char puffer [1024]; + + sock_stream = (struct scot_stream *) con_sock; + + scot_stream_pool_add ( + con_pool, sock_stream, SCOT_STREAM_POOL_FD_ADD_RMASK); + + con_sock = NULL; + } + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + print_exception (e); + free_exception (e); + } + + free_catched (ee); + } + + scot_socket_free (sock); + + scot_event_listener_stop ((struct scot_event_listener *) con_pool); + scot_stream_pool_free (con_pool); + + return 0; +} diff --git a/test/po/LINGUAS b/test/po/LINGUAS new file mode 100644 index 0000000..7673daa --- /dev/null +++ b/test/po/LINGUAS @@ -0,0 +1 @@ +de diff --git a/test/po/Makevars b/test/po/Makevars new file mode 100644 index 0000000..f5f40e3 --- /dev/null +++ b/test/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE)_test + +# These two variables depend on the location of this directory. +subdir = test/po +top_builddir = ../.. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=D_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Georg Steffers + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = georg@steffers.org + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/test/po/POTFILES.in b/test/po/POTFILES.in new file mode 100644 index 0000000..3cba231 --- /dev/null +++ b/test/po/POTFILES.in @@ -0,0 +1 @@ +test/scot_test.c diff --git a/test/po/de.gmo b/test/po/de.gmo new file mode 100644 index 0000000000000000000000000000000000000000..8200061923e02f8690e8bab97828cfaba71ef9cb GIT binary patch literal 3633 zcmbtXzmFtW5pH9H?Z$xZ#KHzqhQN1Xx_5Sstkawiv9~{tm3MtB-<*-0tlo6jbU$zR z>vq5QcGw9ZLINQI0!T@mA`xBWY$XyTA`%i3{sEBq1N_zNo|!w_C!fSfJvHxF)mK&D ztLpjJ%g_8F!sjJC-@)@So>%aUp28PCzlQw9(^2#r$iHFy+*hOMHOPNJ66Ajd`O?>- z=$jbtKt2PxJIKR9zB9;ENZEIP80U~bz*s|m5At`A1o>yk??e6_vVraYLjDyv)N@hv zZ_HnNq2KqyH=^iuj1$Q3Lf(ZGUusA|i+(xCUk&oNkb>{`kl%*<<1qghNU`?-@>R%x zLW-aNgCxio0U9vES%~lfoCwRopRe>&;TupRzzg;PH+m7zvv{7z^L0G(iF*-!3sUe# zdnfzHyJypbdsEVuyycu+Lpj%tv942&$(4+GRWLPk3ft2-2(nI@+|s=1Xs+7402UDRvr~F)3?xVc?>oOjXrPW#9cAWEE19rjgEUWWMwP)FE4I+)F0jUC7w`k}bTdjn9Wm@9Z$DZ1*$=F)96S52K zvJzHlQR=LW_Z#zugS!{-)R4+^Ex=U;e3j=>NXI%jt5XQ~rFbI3G%kMJanZ+l8lSGB zYlDl3s%1yM9U;ddui`OOanGdBs_U_&@zDn0klZ0>0B8~609KL|ez)+tMT|kmRpJoq zo`qS*n77Dj$rZBYUF#jqE_=?ENR*{QUmz_=pLS`yTU8-L121ySg2WoLO492y=aC0t zH9T1dnJyc@ifta4vfA-*Naw-rlI!49pDIcCnea9E1T}9!JwWG|X|&h0mz^$3N7weQ z)6RJOmLR{wO;^x8$MZS3O>Y-6{t0eJ7^fKg7>o(mYVH=o8vL$w#zyY1H#zs%aWm?` zBXQ);yZ52ka0f!1`Wd#z$8scNx_0OE_|vT20q1wxVw7uaA;4V560E2|2nk#*?*C>`$6!y6ia>}1hvT$jgg z=xszZAdNB*JDST^iTV<0gDm0(dnYo|ve1;^JypuO$E)t~24KDI{#LKSe{Q`m0vqtd z23-75DiC0>jtjW6)^XZ3A9CiBqda*ZImNx4P$&dv;I5-_I!?Fa_qc63mz>x_=gDnf z*ksyFP%}Q8{rL3mG}#m0Lr<~~`cDFPlJQQmvrXfj$7h3E+vTr-t z`4Mg3n7ldcJ4S$#X{U^>lxw=l6H)M3nZlznBvV$k9O+P+O?bPTg;aEzNd zzxw2H4Gve0a zC4;(V-9lv*h`?->m8f0u2cAAeGpT7^hpF~ge-h(Nd#k3l!T@^(csPTa1$Xd1rJu1e zG4e#lcXT)K6|1jHh<8f~@PQQNnWN=`HN2HG5{4BZ#0O#P3p@g#x*2PP?UA0x70|@z;<>no zb)*jdb*TPqwb+qbxQP_{WH@6aTK&eQm)n&acCnSo;8XBEP{hDtM12$M^Q#9&%%Rs| zdw651D%S_MmZTyT7RY;0J(j>W@%aO9uReB5l+luwzV6>>Bz)ZlV?ey?=bfJiB^JBT z?LnJOP;JcJl6|pyB@kouQ(etjd0QxX(eYMJgN}x#y0syik}B{4Q4c*3Epq|t%B=H9 zq_9Y%6a{S{gFi* z87^(~#H*X`scn>@Cb(!GzDw41bO1_&J~@ehnM, 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: cmdla_test 0.0.1\n" +"Report-Msgid-Bugs-To: georg@steffers.org\n" +"POT-Creation-Date: 2006-02-21 02:26+0100\n" +"PO-Revision-Date: 2006-01-24 18:36+0100\n" +"Last-Translator: Georg Steffers \n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: test/scot_test.c:54 +msgid "" +"CMDLA_TEST is used to demonstrate the usage of CMDLA.\n" +"CMDLA stands for CoMmanD Line Argument and it is some functions that,\n" +"as the name suggested, makes it convinient to parse command line\n" +"parameters.\n" +"\n" +"To every parameter there has to be assigned a callback funktion.If\n" +"one only wants to map options into string, int or float variables,\n" +"then one can use the predefind get_cmdlap_cb callback.\n" +"There are two kinds of options that CMDLA processes: switches, which\n" +"Don't have an additional value, and parameters, which may or may not\n" +"have an additional value added.\n" +"Parameters can be set up, so that they need to have an addition value.\n" +"In addition to parameter parsing and calling appropriate callbacks,\n" +"CMDLA creates a help output by some hints given to it.\n" +"All one has to do is fill a structure for the switches you have and\n" +"one for the parameters, feed them to process_cmd_line and you're ready." +msgstr "" +"CMDLA_TEST dient dazu den Gebrauch von CMDLA zu demonstrieren.\n" +"CMDLA steht für CoMmanD Line Argument und das sind einige Funktionen die,\n" +"wie der Name andeutet, das parsen von Kommandozeilen Argumenten " +"vereinfacht.\n" +"\n" +"Zu jedem Parameter muß eine Callback Funktion angegeben werden. Wenn\n" +"man nur Optionen in String, Int oder Float Variablen speichern will,\n" +"kann man den vordefinierten Callback get_cmdlap_cb benutzen.\n" +"Es gibt zwei Arten von Optionen, die CMDLA verarbeitet: switches, welche\n" +"keinen weiteren Wert besitzen und Parameter, welchen ein Wert zugeordnet\n" +"sein kann oder auch nicht.\n" +"Parameter können so eingestellt werdem, daß sie einen Wert brauchen.\n" +"Zusätzlich zum parsen der Parameter und Aufruf eines passenden Callbacks\n" +"erzeugt CMDLA eine Hilfeausgabe aus einigen Tips die man angibt.\n" +"Alles was man tun muß ist eine Struktur für die switches die man hat und\n" +"eine für die Parameter auszufüllen. Diese gibt man an process_cmd_line\n" +"und man ist fertig." + +#: test/scot_test.c:72 +msgid "" +"Copyright (C) 2006 by Georg Steffers \n" +"This software is licensed under the terms of the GNU General Public\n" +"Licence (GPL). Please refer http://www.gnu.org/licenses/gpl.html\n" +"for details." +msgstr "" +"Copyright (C) 2006 by Georg Steffers \n" +"Dieses Programm unterliegt den Bestimmungen der GNU General Public\n" +"Licence (GPL). Bitte lesen Sie unter http://www.gnu.org/licenses/gpl.html\n" +"bezüglich weiterer Details." + +#: test/scot_test.c:78 +msgid "infile outfile" +msgstr "Eingabedatei Ausgabedatei" + +#: test/scot_test.c:83 +msgid "cval" +msgstr "cwert" + +#: test/scot_test.c:85 +msgid "takes a required string argument" +msgstr "braucht ein String Argument" + +#: test/scot_test.c:88 +msgid "ival" +msgstr "iwert" + +#: test/scot_test.c:90 +msgid "takes an optional integer argument" +msgstr "kann opt. Integer Argument haben" + +#: test/scot_test.c:93 +msgid "fval" +msgstr "fwert" + +#: test/scot_test.c:95 +msgid "takes a required float argument" +msgstr "braucht ein Float argument" + +#: test/scot_test.c:98 +msgid "string" +msgstr "" + +#: test/scot_test.c:100 +msgid "another required string argument" +msgstr "wieder ein benötigter String" + +#: test/scot_test.c:103 +msgid "base" +msgstr "basis" + +#: test/scot_test.c:105 +msgid "and again a req. string argument" +msgstr "und wieder ein benötigter String" + +#: test/scot_test.c:108 +msgid "home" +msgstr "heim" + +#: test/scot_test.c:110 +msgid "oh, a last required string argument" +msgstr "oh, ein letzter benötigter String" + +#: test/scot_test.c:118 +#, fuzzy +msgid "switch" +msgstr "Schalter" + +#: test/scot_test.c:120 +msgid "a switch takes nor argument" +msgstr "ein switch braucht kein argument" + +#: test/scot_test.c:125 +msgid "another switch" +msgstr "wieder ein switch" diff --git a/test/po/scot_test.pot b/test/po/scot_test.pot new file mode 100644 index 0000000..7e8cb1c --- /dev/null +++ b/test/po/scot_test.pot @@ -0,0 +1,109 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Georg Steffers +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: georg@steffers.org\n" +"POT-Creation-Date: 2006-02-21 02:26+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: test/scot_test.c:54 +msgid "" +"CMDLA_TEST is used to demonstrate the usage of CMDLA.\n" +"CMDLA stands for CoMmanD Line Argument and it is some functions that,\n" +"as the name suggested, makes it convinient to parse command line\n" +"parameters.\n" +"\n" +"To every parameter there has to be assigned a callback funktion.If\n" +"one only wants to map options into string, int or float variables,\n" +"then one can use the predefind get_cmdlap_cb callback.\n" +"There are two kinds of options that CMDLA processes: switches, which\n" +"Don't have an additional value, and parameters, which may or may not\n" +"have an additional value added.\n" +"Parameters can be set up, so that they need to have an addition value.\n" +"In addition to parameter parsing and calling appropriate callbacks,\n" +"CMDLA creates a help output by some hints given to it.\n" +"All one has to do is fill a structure for the switches you have and\n" +"one for the parameters, feed them to process_cmd_line and you're ready." +msgstr "" + +#: test/scot_test.c:72 +msgid "" +"Copyright (C) 2006 by Georg Steffers \n" +"This software is licensed under the terms of the GNU General Public\n" +"Licence (GPL). Please refer http://www.gnu.org/licenses/gpl.html\n" +"for details." +msgstr "" + +#: test/scot_test.c:78 +msgid "infile outfile" +msgstr "" + +#: test/scot_test.c:83 +msgid "cval" +msgstr "" + +#: test/scot_test.c:85 +msgid "takes a required string argument" +msgstr "" + +#: test/scot_test.c:88 +msgid "ival" +msgstr "" + +#: test/scot_test.c:90 +msgid "takes an optional integer argument" +msgstr "" + +#: test/scot_test.c:93 +msgid "fval" +msgstr "" + +#: test/scot_test.c:95 +msgid "takes a required float argument" +msgstr "" + +#: test/scot_test.c:98 +msgid "string" +msgstr "" + +#: test/scot_test.c:100 +msgid "another required string argument" +msgstr "" + +#: test/scot_test.c:103 +msgid "base" +msgstr "" + +#: test/scot_test.c:105 +msgid "and again a req. string argument" +msgstr "" + +#: test/scot_test.c:108 +msgid "home" +msgstr "" + +#: test/scot_test.c:110 +msgid "oh, a last required string argument" +msgstr "" + +#: test/scot_test.c:118 +msgid "switch" +msgstr "" + +#: test/scot_test.c:120 +msgid "a switch takes nor argument" +msgstr "" + +#: test/scot_test.c:125 +msgid "another switch" +msgstr "" diff --git a/test/po/stamp-po b/test/po/stamp-po new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/test/po/stamp-po @@ -0,0 +1 @@ +timestamp diff --git a/test/scot_test.c b/test/scot_test.c new file mode 100644 index 0000000..a49c176 --- /dev/null +++ b/test/scot_test.c @@ -0,0 +1,176 @@ +/* + * san_test.c: A small app to test the functionality of san, my + * Swiss Army Nife tool-lib and to show how to use it. + * Actually only focused on cmdla which parses commandline + * arguments. + * + * Copyright (C) 2006 Georg Steffers. All rights reserved. + * + * This software is licensed under the terms of the GNU Genral Public + * License (GPL). Please see gpl.txt for details or refer to + * http://www.gnu.org/licenses/gpl.html + * + * Author: Georg Steffers + * + * 01/14/2006: Georg Steffers - First implemented + * 01/15/2006: Georg Steffers - V0.1 ready. Modified much and many + * now long arguments are supported as + * well as arguments with am optional + * parameter. + * 01/23/2006: Georg Steffers - add support for gettext. + * 01/24/2006: Georg Steffers - renamed to san_test.c as it should reflect + * the whole lib as it grows. + */ + +#include + +#include +#include +//#include "../config.h" + +int +switch_cb (void *arg) +{ + int *sw = (int *) arg; + + * sw = 1; +} + +int +main (int argc, char *argv[]) +{ + char *cval = NULL; + char *string = NULL; + char *base = NULL; + char *home = NULL; + int ival = 0; + float fval = 0.0; + int sw1 = 0, sw2 = 0; + + char *locale; + char domain[1024]; + + char about[] = + N_("CMDLA_TEST is used to demonstrate the usage of CMDLA.\n" + "CMDLA stands for CoMmanD Line Argument and it is some functions that,\n" + "as the name suggested, makes it convinient to parse command line\n" + "parameters.\n\n" + "To every parameter there has to be assigned a callback funktion.If\n" + "one only wants to map options into string, int or float variables,\n" + "then one can use the predefind get_cmdlap_cb callback.\n" + "There are two kinds of options that CMDLA processes: switches, which\n" + "Don't have an additional value, and parameters, which may or may not\n" + "have an additional value added.\n" + "Parameters can be set up, so that they need to have an addition value.\n" + "In addition to parameter parsing and calling appropriate callbacks,\n" + "CMDLA creates a help output by some hints given to it.\n" + "All one has to do is fill a structure for the switches you have and\n" + "one for the parameters, feed them to process_cmd_line and you're " + "ready."); + + char copyright[] = + N_("Copyright (C) 2006 by Georg Steffers \n" + "This software is licensed under the terms of the GNU General Public\n" + "Licence (GPL). Please refer http://www.gnu.org/licenses/gpl.html\n" + "for details."); + + + char usage_add[] = N_("infile outfile"); + + /* an entry with the pointer to the cb-funktion as NULL is end sign */ + struct cmdlap_cbt cmdlap_cbs[] = { + { + 'c', N_("cval"), CMDLA_REQ_ARG, + get_cmdlap_cb, CMDLA_TYPE_STRING, &cval, + N_("takes a required string argument") + }, + { + 'i', N_("ival"), CMDLA_OPT_ARG, + get_cmdlap_cb, CMDLA_TYPE_INT, &ival, + N_("takes an optional integer argument") + }, + { + 'f', N_("fval"), CMDLA_REQ_ARG, + get_cmdlap_cb, CMDLA_TYPE_FLOAT, &fval, + N_("takes a required float argument") + }, + { + 0, N_("string"), CMDLA_REQ_ARG, + get_cmdlap_cb, CMDLA_TYPE_STRING, &string, + N_("another required string argument") + }, + { + 0, N_("base"), CMDLA_REQ_ARG, + get_cmdlap_cb, CMDLA_TYPE_STRING, &base, + N_("and again a req. string argument") + }, + { + 0, N_("home"), CMDLA_REQ_ARG, + get_cmdlap_cb, CMDLA_TYPE_STRING, &home, + N_("oh, a last required string argument") + }, + CMDLAP_CBT_END + }; + + /* an entry with the pointer to the cb-funktion as NULL is end sign */ + struct cmdlas_cbt cmdlas_cbs[] = { + { + 's', N_("switch"), + switch_cb, &sw1, + N_("a switch takes nor argument") + }, + { + 'S', NULL, + switch_cb, &sw2, + N_("another switch") + }, + CMDLAS_CBT_END + }; + + locale = setlocale (LC_ALL, ""); + + if (locale == NULL) + printf ("uable to set locale\n"); + else + printf ("use current locale: %s\n", locale); + + printf ("LOCALEDIR: %s\n", LOCALEDIR); + printf ("PACKAGE: %s\n", PACKAGE); + + snprintf (domain, 1024, "%s_test", PACKAGE); + + bindtextdomain (domain, LOCALEDIR); + textdomain ("scot_test"); + + process_cmd_line ( + argc, argv, about, copyright, usage_add, cmdlap_cbs, cmdlas_cbs); + + printf("CVAL: %s\n", cval); + printf("STRING: %s\n", string); + printf("BASE: %s\n", base); + printf("HOME: %s\n", home); + printf("IVAL: %d\n", ival); + printf("FVAL: %f\n", fval); + printf( + "SWITCH1 %scalled\n", + sw1 == 0 ? + "not " : + ""); + printf( + "SWITCH2 %scalled\n", + sw2 == 0 ? + "not " : + ""); + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + + while (optind < argc) + printf ("%s ", argv[optind++]); + + printf ("\n"); + } + + return 0; +} diff --git a/test/stream_pool_test.c b/test/stream_pool_test.c new file mode 100644 index 0000000..4ae07a4 --- /dev/null +++ b/test/stream_pool_test.c @@ -0,0 +1,61 @@ +#include +#include + +/* + * ok, zunächst ein paar Gedanken. + * Also ein großes Problem sehe ich leider jetzt erst. Als Beispiel + * dient ein filedescriptor (fd) von dem gelesen werden soll. + * Nachdem dieser in einem stream_pool registriert wurde und der + * stream pool thread gestartet wurde: + * sobald der fd bereit zu lesen ist, schreibt der event pool thread ein + * event nach dem nächsten in die event-queue, solange bis alle daten + * weggelesen wurden. + * + * Es muessen also immer alle passenden read-events aus der queue geflushed + * werden (solange events aus der queue gelesen, solange sie mit dem + * ersten gefundenen event übereinstimmen) + * und dann kann solange gelesen werden bis der zurückgegebene + * wert von read kleiner ist als die gewünschte (puffer) größe. + * + * Es kann trotzdem vorkommen, das noch read events in der queue sind, + * obwohl bereits alle daten gelesen wurden. Die wurden in die queue + * gestellt bevor die Daten gelesen wurden. + * + * Eine mögliche Lösung schein nur non-blocking file operation zu nutzen. + * wenn dann ein read event auftaucht aber keine daten da sind + * blockiert der callback nicht und kann evtl. darauf reagieren das + * keine Daten vorlagen. + * + * Eine weitere option ist, ein event zu lesen, und danach die gesamte queue + * leer zu lesen....dann währen alle weiteren read events weg, wenn noch + * daten zu lesen wären würde aber wieder ein read event erzeugt. Auf + * das dann wieder reagiert werden könnte. + * + * Die beste option wäre sicherlich eine programminterne queue + * zu verwenden (queue_scot_event_t_node_t) und semaphore um + * blocking zu realisieren. Dann kann eine event source aber kein + * sub prozess sein, sondern nur ein thread...es sei denn wir propagieren + * die queueu irgendwie in shared memory oder mmio oder so was. + * Vorteil die queue läst sich direkt von source und sink manipulieren. + * + * Oder aber ich implementier tatsächlich ein Kommunikationsprotokoll + * zwischen event source und event sink mit dem die sink anforderungen + * an sie source schicken kann. Auch dann wäre eine Liste ohne pipe + * und mit semaphoren zum blockieren die interne Struktur der wahl. + * !!!anmerkung, da ich ja nur threads verwenden will tuts auch ein + * pthread_mutex, bzw ein windows mutex object. !!! + */ + +int +read_cb (struct scot_stream_pool_event * ev) +{ + char buffer [1024]; + + return 0; +} + +int +main (int argc, char * argv []) +{ + return 0 +} diff --git a/test/tcp_socket_clie.c b/test/tcp_socket_clie.c new file mode 100644 index 0000000..69c8629 --- /dev/null +++ b/test/tcp_socket_clie.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +int +main (int argc, char * argv []) +{ + excenv_t * ee; + struct scot_socket * sock; + char puffer [1024]; + struct scot_stream * sock_stream; + + scot_socket_init (2,2); + + atexit (exc_end); + atexit (scot_socket_fini); + + TRY + { + sock = scot_socket_in_new ("tcp", "7777"); + /*scot_socket_connect (sock, "10.2.5.50");*/ + scot_socket_connect (sock, "localhost"); + + sock_stream = (struct scot_stream *) sock; + + printf ("Client: "); fflush (stdout); + while (fgets (puffer, 1024, stdin) != NULL) + { + if (send (sock_stream->handle.sock, puffer, strlen (puffer), 0) != + strlen (puffer)) + THROW (EXC (EXC_ERROR, errno, "Client: Fehler beim schreiben.")); + + if (strlen (puffer) == 2 && puffer [0] == 'q') + break; + printf ("Client: "); fflush (stdout); + } + + scot_socket_free (sock); + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + switch (exc_errnum_get (e)) + { + case SCOT_SOCKET_CONNECT_FAIL: + scot_socket_free (sock); + case SCOT_SOCKET_NEW_FAIL: + break; + } + + print_exception (e); + free_exception (e); + } + + free_catched (ee); + } + + return 0; +} diff --git a/test/tcp_socket_serv.c b/test/tcp_socket_serv.c new file mode 100644 index 0000000..162c4fd --- /dev/null +++ b/test/tcp_socket_serv.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +struct scot_socket * sock; +struct scot_socket * con_sock; + +void +on_exit_func (void) +{ + if (con_sock != NULL) + scot_socket_free (con_sock); + + scot_socket_free (sock); + + scot_socket_fini (); + + exc_end (); +} + +void +exit_func (int inum) +{ + exit (0); +} + +int +main (int argc, char * argv []) +{ + excenv_t * ee; + + scot_socket_init (2,2); + +#ifndef WIN32 + signal (SIGINT, exit_func); +#endif + atexit (on_exit_func); + + TRY + { + sock = scot_socket_in_new ("tcp", "7777"); + scot_socket_listen (sock); + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + print_exception (e); + + if (EXC_IN_THIS_TRY (e)) + { + switch (exc_errnum_get (e)) + { + case SCOT_SOCKET_LISTEN_FAIL: + scot_socket_free (sock); + case SCOT_SOCKET_NEW_FAIL: + break; + } + } + + free_exception (e); + } + + free_catched (ee); + } + + TRY + { + while ((con_sock = scot_socket_accept (sock)) >= 0) + { + struct scot_stream * sock_stream; + int i, n; + char puffer [1024]; + + sock_stream = (struct scot_stream *) con_sock; + + while ((n = recv (sock_stream->handle.sock, puffer, 1024, 0)) > 0) + { + printf ("Server: "); + if (puffer [0] == 'q') + break; + + for (i=0; i +#include +#include +#include + +#include +#include +#include +#include + +#include + + +int +main (int argc, char * argv []) +{ + excenv_t * ee; + struct scot_socket * sock; + char puffer [1024]; + struct scot_stream * sock_stream; + + atexit (exc_end); + + TRY + { + sock = scot_socket_un_new ("/tmp/socket"); + scot_socket_connect (sock, NULL); + + sock_stream = (struct scot_stream *) sock; + + printf ("Client: "); + while (fgets (puffer, 1024, stdin) != NULL) + { + if (send (sock_stream->handle.sock, puffer, strlen (puffer), 0) != + strlen (puffer)) + THROW (EXC (EXC_ERROR, errno, "Client: Fehler beim schreiben.")); + + if (strlen (puffer) == 2 && puffer [0] == 'q') + break; + printf ("Client: "); + } + + scot_socket_free (sock); + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + switch (exc_errnum_get (e)) + { + case SCOT_SOCKET_CONNECT_FAIL: + scot_socket_free (sock); + case SCOT_SOCKET_NEW_FAIL: + break; + } + + print_exception (e); + free_exception (e); + } + + free_catched (ee); + } + + return 0; +} diff --git a/test/unx_socket_serv.c b/test/unx_socket_serv.c new file mode 100644 index 0000000..fb34836 --- /dev/null +++ b/test/unx_socket_serv.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +struct scot_socket * sock; +struct scot_socket * con_sock; + +void +on_exit_func (void) +{ + if (con_sock != NULL) + scot_socket_free (con_sock); + + scot_socket_free (sock); + + exc_end (); +} + +void +exit_func (int inum) +{ + exit (0); +} + +int +main (int argc, char * argv []) +{ + excenv_t * ee; + + signal (SIGINT, exit_func); + atexit (on_exit_func); + + TRY + { + sock = scot_socket_un_new ("/tmp/socket"); + scot_socket_listen (sock); + } + CATCH (ee) + { + exception_t *e; + + while (e = retrive_exception (ee)) + { + print_exception (e); + + if (EXC_IN_THIS_TRY (e)) + { + switch (exc_errnum_get (e)) + { + case SCOT_SOCKET_LISTEN_FAIL: + scot_socket_free (sock); + case SCOT_SOCKET_NEW_FAIL: + break; + } + } + + free_exception (e); + } + + free_catched (ee); + } + + TRY + { + while ((con_sock = scot_socket_accept (sock)) >= 0) + { + struct scot_stream * sock_stream; + int i, n; + char puffer [1024]; + + sock_stream = (struct scot_stream *) con_sock; + + while ((n = recv (sock_stream->handle.sock, puffer, 1024, 0)) > 0) + { + printf ("Server: "); + if (puffer [0] == 'q') + break; + + for (i=0; i