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 0000000..ccecbb4 Binary files /dev/null and b/design/scot-event-subsystem.dia differ 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 0000000..f1e041e Binary files /dev/null and b/src/po/de.gmo differ 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 0000000..8200061 Binary files /dev/null and b/test/po/de.gmo differ diff --git a/test/po/de.po b/test/po/de.po new file mode 100644 index 0000000..e853621 --- /dev/null +++ b/test/po/de.po @@ -0,0 +1,132 @@ +# 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: 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