summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore17
-rw-r--r--GNUmakefile5
-rw-r--r--doc/GNUmakefile68
-rw-r--r--doc/README.Daemon9
-rw-r--r--doc/README.GenGetopt8
-rw-r--r--doc/README.Makefile4
-rw-r--r--doc/README.Porting58
-rw-r--r--doc/README.SunPro5
-rw-r--r--doc/daemon.php.html367
-rw-r--r--doc/makedepend.html1771
-rw-r--r--makefiles/clean.mk29
-rw-r--r--makefiles/compiler.mk174
-rw-r--r--makefiles/depend.mk29
-rwxr-xr-xmakefiles/guess_env106
-rw-r--r--makefiles/platform.mk34
-rw-r--r--makefiles/sub.mk20
-rw-r--r--makefiles/top.mk30
-rw-r--r--src/GNUmakefile41
-rw-r--r--src/daemon.c551
-rw-r--r--src/daemon.ggo70
-rw-r--r--src/daemon.h36
-rw-r--r--src/errors.h25
-rw-r--r--src/log.c160
-rw-r--r--src/log.h60
-rw-r--r--src/pidfile.c245
-rw-r--r--src/pidfile.h32
-rw-r--r--src/port/limits.h8
-rw-r--r--src/port/lockf.c62
-rw-r--r--src/port/lockf.h20
-rw-r--r--src/port/noreturn.h10
-rw-r--r--src/port/snprintf.c2109
-rw-r--r--src/port/snprintf.h59
-rw-r--r--src/port/stdbool.h35
-rw-r--r--src/port/stdint.h15
-rw-r--r--src/port/stdio.h10
-rw-r--r--src/port/string.h16
-rw-r--r--src/port/sys.h119
-rw-r--r--src/port/unistd.h12
-rw-r--r--src/port/unused.h8
-rw-r--r--src/signals.c560
-rw-r--r--src/signals.h46
-rw-r--r--src/testd.c149
-rw-r--r--tests/GNUmakefile31
-rw-r--r--tests/stdargs_for_signal_functions.c33
44 files changed, 7256 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..712b246
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+# generated stuff
+cmdline.h
+cmdline.c
+
+# myself
+.gitignore
+
+# backup files
+*~
+
+# dependeny files
+*.d
+
+# object files
+*.o
+
+# binaries
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
index 0000000..a6ddcb8
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,5 @@
+TOPDIR = .
+
+SUBDIRS = src tests
+
+-include $(TOPDIR)/makefiles/top.mk
diff --git a/doc/GNUmakefile b/doc/GNUmakefile
new file mode 100644
index 0000000..f1b80aa
--- /dev/null
+++ b/doc/GNUmakefile
@@ -0,0 +1,68 @@
+.PHONY: all clean
+
+BINS = \
+ testd
+
+all: $(BINS)
+
+PLATFORM = $(shell makefiles/guess_env --platform)
+OS_MAJOR_VERSION = $(shell makefiles/guess_env --os-major-version)
+OS_MINOR_VERSION = $(shell makefiles/guess_env --os-minor-version)
+
+COMPILE_FLAGS = -g -O2 -D_REENTRANT -Wall -W -Werror -std=c99 -pedantic -I.
+LDFLAGS =
+LIBS =
+
+CFLAGS = $(COMPILE_FLAGS) -D$(PLATFORM) -DOS_MAJOR_VERSION=$(OS_MAJOR_VERSION) -DOS_MINOR_VERSION=$(OS_MINOR_VERSION)
+CC = gcc
+MAKEDEPEND = $(CC) -MM
+
+OBJS = \
+ port/snprintf.o \
+ port/lockf.o \
+ cmdline.o \
+ log.o \
+ signals.o \
+ pidfile.o \
+ daemon.o \
+ testd.o
+
+# ABa: currently a special rule for cmdline.c as gengetopt is not
+# completly fixed yet
+cmdline.c : daemon.ggo
+ gengetopt --include-getopt -i $<
+
+cmdline.o : cmdline.c
+ $(CC) -c -o $@ $<
+
+%.o : %.c
+ $(CC) -c -o $@ $(CFLAGS) $<
+
+testd : $(OBJS)
+ $(CC) -o $@ $(LDFLAGS) $(LIBS) $^
+
+clean:
+ -@rm *.bak port/*.bak 2>/dev/null
+ -@rm *~ port/*~ 2>/dev/null
+ -@rm *.d port/*.d 2>/dev/null
+ -@rm $(BINS) *.exe 2>/dev/null
+ -@rm $(OBJS) 2>/dev/null
+
+distclean: clean
+ -@rm cmdline.c cmdline.h
+
+%.d : %.c
+ @$(MAKEDEPEND) $(CFLAGS) $< 2>/dev/null | \
+ sed "s,\($*\.o\)[ :]*\(.*\),$@ : $$\(wildcard \2\)___\1 : \2,g" | tr -s "_" "\n" > $@
+
+-include $(OBJS:.o=.d)
+
+test: all
+ @fakeroot ./testd -d --pidfile /tmp/testd.pid && \
+ sleep 3 && \
+ ls -altr /tmp/testd* && \
+ cat /tmp/testd.pid
+ -@ps -alef | grep test | grep -v grep
+ @sleep 1
+ @pkill testd
+ -@ls -altr /tmp/testd*
diff --git a/doc/README.Daemon b/doc/README.Daemon
new file mode 100644
index 0000000..faf774e
--- /dev/null
+++ b/doc/README.Daemon
@@ -0,0 +1,9 @@
+http://www.enderunix.org/documents/eng/daemon.php
+
+libraries:
+
+- libdaemon: C
+ http://0pointer.de/lennart/projects/libdaemon/
+
+- see:
+ http://www.gmonline.demon.co.uk/cscene/CS4/CS4-07.html
diff --git a/doc/README.GenGetopt b/doc/README.GenGetopt
new file mode 100644
index 0000000..1763029
--- /dev/null
+++ b/doc/README.GenGetopt
@@ -0,0 +1,8 @@
+gengetopt: generates plain C code using getopt_long/getopt and
+has a local getopt_long replacement for Unixes which don't
+support getopt_long properly
+
+popt is happily broken
+
+self-made ones are usually quite funny to use and we don't use
+some getopt features (like the ones on Solaris) \ No newline at end of file
diff --git a/doc/README.Makefile b/doc/README.Makefile
new file mode 100644
index 0000000..0a4ade0
--- /dev/null
+++ b/doc/README.Makefile
@@ -0,0 +1,4 @@
+http://make.paulandlesley.org/multi-arch.html
+http://mattmccutchen.net/buildsys/index.html
+http://cdrecord.berlios.de/private/makefiles.html
+http://miller.emu.id.au/pmiller/books/rmch/
diff --git a/doc/README.Porting b/doc/README.Porting
new file mode 100644
index 0000000..9c13a99
--- /dev/null
+++ b/doc/README.Porting
@@ -0,0 +1,58 @@
+How to support a new operating system, platform or version thereof?
+-------------------------------------------------------------------
+
+Make sure 'port/guess_env' reliably detects the version of your
+new operating system. If you have a new operating system choose
+a new label for the platform like 'SUNOS'.
+
+Never port for the future with 'OS_MINOR_VERSION >= 5', make sure
+you check the new version first.
+
+We try to follow the X-Open group if possible (and POSIX). Try
+to avoid BSD emulations of functions on SysV systems and vice
+versa.
+
+Defines in 'port/sys.h'
+-----------------------
+
+Don't port using '#ifdef LINUX'! Instead use the platform flags
+only in 'port/sys.h' and define descriptive macros for features
+of the system like 'HAVE_VSNPRINTF'.
+
+Currently there are the following definitions which must be set:
+- HAVE_STDBOOL_H and HAVE_ENUM_BOOL:
+ HAVE_STDBOOL_H whether the platform has a C99 bool type in stdbool.h
+ HAVE_ENUM_BOOL for platforms which define an internal _Bool somewhere
+ but not the official bool data type
+- HAVE_VSNPRINTF, HAVE_SNPRINTF: vsnprintf and snprintf, there
+ is a stub implementation if this function doesn't exist or
+ is buggy (see http://www.jhweiss.de/software/snprintf.html)
+- HAVE_STRDUP: a string duplication function (there is a stub for
+ really old platforms)
+- HAVE_STRERROR_R: whether we have a reentrant strerror function
+- HAVE_LOCKF: whether we have a POSIX lockf interface. A stub implemented
+ with fcntl is available for platform which don't have a lockf function.
+
+Currently tested on:
+--------------------
+
+- x86 Linux 2.6.x
+- x86 FreeBSD 6.2
+- x86 OpenBSD 4.2
+- x86 Solaris 10
+- SPARC Solaris 8
+
+How to use the porting layer in your code
+-----------------------------------------
+
+Don't include system header files if there is a similar file in the
+'ports' subdir:
+
+#include "port/limits.h"
+#include "port/stdbool.h"
+#include "port/stdio.h"
+#include "port/string.h"
+#include "port/unistd.h"
+
+You also may to have new such stubs when porting to new platforms
+or when you start to use new features.
diff --git a/doc/README.SunPro b/doc/README.SunPro
new file mode 100644
index 0000000..6808732
--- /dev/null
+++ b/doc/README.SunPro
@@ -0,0 +1,5 @@
+Sun C 5.9:
+gmake \
+ CC=/opt/SUNWspro/bin/c99 COMPILE_FLAGS="-I. -O2 -mt -v" \
+ MAKEDEPEND="/opt/SUNWspro/bin/c99 -xM1" clean all
+
diff --git a/doc/daemon.php.html b/doc/daemon.php.html
new file mode 100644
index 0000000..c86ccff
--- /dev/null
+++ b/doc/daemon.php.html
@@ -0,0 +1,367 @@
+<html><head><!--
+-->
+
+
+
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-9">
+<meta http-equiv="Description" content="Unix Daemon Server Programming">
+<meta http-equiv="Keywords" content="Levent, Karakaž, Karakas, Unix, Daemon, Server, Programming"><title>Unix Daemon Server Programming</title>
+
+<style type="text/css">
+<!--
+BODY, TD {
+ background : #FFFFFF;
+ color : #000000;
+ font-family : normal arial, helvetica;
+ font-size: 12px;
+ text-align: justify;
+}
+H1 {
+ font-size : 20px;
+ text-align : center;
+}
+H2 {
+ font-size : 16px;
+ text-align : left;
+}
+A:link, A:visited {
+ color: #0000FF;
+ text-decoration: none;
+}
+//-->
+</style></head><body>
+
+<div align="center">
+
+<table border="0" cellpadding="0" cellspacing="0" width="600">
+ <tbody><tr>
+ <td>
+
+<h1>Unix Daemon Server Programming</h1>
+
+<h2>Introduction</h2>
+
+<p>Unix processes works either in foreground or background. A process
+running in foreground interacts with the user in front of the terminal
+(makes I/O), whereas a background process runs by itself. The user can
+check its status but he doesn't (need to) know what it is doing. The
+term 'daemon' is used for processes that performs service in
+background. A server is a process that begins execution at startup (not
+neccessarily), runs forever, usually do not die or get restarted,
+operates in background, waits for requests to arrive and respond to
+them and frequently spawn other processes to handle these requests.</p>
+
+<p>Readers are suppossed to know Unix fundamentals and C language. For
+further description on any topic use "man" command (I write useful
+keywords in brackets), it has always been very useful, trust me :))
+Keep in mind that this document does not contain everything, it is just
+a guide.</p>
+
+
+<h2>1) Daemonizing (programming to operate in background) [fork]</h2>
+
+<p>First the fork() system call will be used to create a copy of our
+process(child), then let parent exit. Orphaned child will become a
+child of init process (this is the initial system process, in other
+words the parent of all processes). As a result our process will be
+completely detached from its parent and start operating in background.</p>
+
+<pre> i=fork();
+ if (i&lt;0) exit(1); /* fork error */
+ if (i&gt;0) exit(0); /* parent exits */
+ /* child (daemon) continues */
+</pre>
+
+<h2>2) Process Independency [setsid]</h2>
+
+<p>A process receives signals from the terminal that it is connected
+to, and each process inherits its parent's controlling tty. A server
+should not receive signals from the process that started it, so it must
+detach itself from its controlling tty.</p>
+
+<p>In Unix systems, processes operates within a process group, so that
+all processes within a group is treated as a single entity. Process
+group or session is also inherited. A server should operate
+independently from other processes.</p>
+
+<pre> setsid() /* obtain a new process group */
+</pre>
+
+<p>This call will place the server in a new process group and session
+and detach its controlling terminal. (setpgrp() is an alternative for
+this)</p>
+
+<h2>3) Inherited Descriptors and Standart I/0 Descriptors [gettablesize,fork,open,close,dup,stdio.h]</h2>
+
+<p>Open descriptors are inherited to child process, this may cause the
+use of resources unneccessarily. Unneccesarry descriptors should be
+closed before fork() system call (so that they are not inherited) or
+close all open descriptors as soon as the child process starts running.</p>
+
+<pre> for (i=getdtablesize();i&gt;=0;--i) close(i); /* close all descriptors */
+</pre>
+
+<p>There are three standart I/O descriptors: standart input 'stdin'
+(0), standart output 'stdout' (1), standart error 'stderr' (2). A
+standard library routine may read or write to standart I/O and it may
+occur to a terminal or file. For safety, these descriptors should be
+opened and connectedthem to a harmless I/O device (such as /dev/null).</p>
+
+<pre> i=open("/dev/null",O_RDWR); /* open stdin */
+ dup(i); /* stdout */
+ dup(i); /* stderr */
+</pre>
+
+<p>As Unix assigns descriptors sequentially, fopen call will open stdin and dup calls will provide a copy for stdout and stderr.</p>
+
+<h2>4) File Creation Mask [umask]</h2>
+
+<p>Most servers runs as super-user, for security reasons they should
+protect files that they create. Setting user mask will pre vent
+unsecure file priviliges that may occur on file creation.</p>
+
+<pre> umask(027);
+</pre>
+
+<p>This will restrict file creation mode to 750 (complement of 027).</p>
+
+<h2>5) Running Directory [chdir]</h2>
+
+<p>A server should run in a known directory. There are many advantages,
+in fact the opposite has many disadvantages: suppose that our server is
+started in a user's home directory, it will not be able to find some
+input and output files.</p>
+
+<pre> chdir("/servers/");
+</pre>
+
+<p>The root "/" directory may not be appropriate for every server, it
+should be choosen carefully depending on the type of the server.</p>
+
+<h2>6) Mutual Exclusion and Running a Single Copy [open,lockf,getpid]</h2>
+
+<p>Most services require running only one copy of a server at a time.
+File locking method is a good solution for mutual exclusion. The first
+instance of the server locks the file so that other instances
+understand that an instance is already running. If server terminates
+lock will be automatically released so that a new instance can run.
+Recording the pid of the running instance is a good idea. It will
+surely be efficient to make 'cat mydaamon.lock' instead of 'ps -ef|grep
+mydaemon'</p>
+
+<pre> lfp=open("exampled.lock",O_RDWR|O_CREAT,0640);
+ if (lfp&lt;0) exit(1); /* can not open */
+ if (lockf(lfp,F_TLOCK,0)&lt;0) exit(0); /* can not lock */
+ /* only first instance continues */
+
+ sprintf(str,"%d\n",getpid());
+ write(lfp,str,strlen(str)); /* record pid to lockfile */
+</pre>
+
+<h2>7) Catching Signals [signal,sys/signal.h]</h2>
+
+<p>A process may receive signal from a user or a process, its best to
+catch those signals and behave accordingly. Child processes send
+SIGCHLD signal when they terminate, server process must either ignore
+or handle these signals. Some servers also use hang-up signal to
+restart the server and it is a good idea to rehash with a signal. Note
+that 'kill' command sends SIGTERM (15) by default and SIGKILL (9)
+signal can not be caught.</p>
+
+<pre> signal(SIG_IGN,SIGCHLD); /* child terminate signal */
+</pre>
+
+<p>The above code ignores the child terminate signal (on BSD systems
+parents should wait for their child, so this signal should be caught to
+avoid zombie processes), and the one below demonstrates how to catch
+the signals.</p>
+
+<pre> void Signal_Handler(sig) /* signal handler function */
+ int sig;
+ {
+ switch(sig){
+ case SIGHUP:
+ /* rehash the server */
+ break;
+ case SIGTERM:
+ /* finalize the server */
+ exit(0)
+ break;
+ }
+ }
+
+ signal(SIGHUP,Signal_Handler); /* hangup signal */
+ signal(SIGTERM,Signal_Handler); /* software termination signal from kill */
+</pre>
+
+<p>First we construct a signal handling function and then tie up signals to that function.</p>
+
+<h2>8) Logging [syslogd,syslog.conf,openlog,syslog,closelog]</h2>
+
+<p>A running server creates messages, naturally some are important and
+should be logged. A programmer wants to see debug messages or a system
+operator wants to see error messages. There are several ways to handle
+those messages.</p>
+
+<p><b>Redirecting all output to standard I/O : </b>This is what ancient
+servers do, they use stdout and stderr so that messages are written to
+console, terminal, file or printed on paper. I/O is redirected when
+starting the server. (to change destination, server must be restarted)
+In fact this kind of a server is a program running in foreground (not a
+daemon).</p>
+
+<pre> # mydaemon 2&gt; error.log
+</pre>
+
+<p>This example is a program that prints output (stdout) messages to
+console and error (stderr) messages to a file named "error.log". Note
+that this is not a daemon but a normal program.</p>
+<p><b>Log file method : </b>All messages are logged to files (to different files as needed). There is a sample logging function below.</p>
+
+<pre> void log_message(filename,message)
+ char *filename;
+ char *message;
+ {
+ FILE *logfile;
+ logfile=fopen(filename,"a");
+ if(!logfile) return;
+ fprintf(logfile,"%s\n",message);
+ fclose(logfile);
+ }
+
+ log_message("conn.log","connection accepted");
+ log_message("error.log","can not open file");
+</pre>
+
+<p><b>Log server method : </b>A more flexible logging technique is
+using log servers. Unix distributions have system log daemon named
+"syslogd". This daemon groups messages into classes (known as facility)
+and these classes can be redirected to different places. Syslog uses a
+configuration file (/etc/syslog.conf) that those redirection rules
+reside in.</p>
+
+<pre> openlog("mydaemon",LOG_PID,LOG_DAEMON)
+ syslog(LOG_INFO, "Connection from host %d", callinghostname);
+ syslog(LOG_ALERT, "Database Error !");
+ closelog();
+</pre>
+
+<p>In openlog call "mydaemon" is a string that identifies our daemon,
+LOG_PID makes syslogd log the process id with each message and
+LOG_DAEMON is the message class. When calling syslog call first
+parameter is the priority and the rest works like printf/sprintf. There
+are several message classes (or facility names), log options and
+priority levels. Here are some examples :</p>
+
+<dl>
+<dd>Message classes : LOG_USER, LOG_DAEMON, LOG_LOCAL0 to LOG_LOCAL7</dd>
+<dd>Log options : LOG_PID, LOG_CONS, LOG_PERROR</dd>
+<dd>Priority levels : LOG_EMERG, LOG_ALERT, LOG_ERR, LOG_WARNING, LOG_INFO</dd>
+</dl>
+
+<h2>About</h2>
+
+<p>This text is written by Levent Karakas <a href="mailto:levent%20at%20mektup%20dot%20at">&lt;levent at mektup dot at &gt;</a>.
+Several books, sources and manual pages are used. This text includes a
+sample daemon program (compiles on Linux 2.4.2, OpenBSD 2.7, SunOS 5.8,
+SCO-Unix 3.2 and probably on your flavor of Unix). You can also
+download plain source file : <a href="http://www.enderunix.org/documents/eng/exampled.c">exampled.c</a>. Hope you find this document useful. We do love Unix.</p>
+
+<pre>
+/*
+UNIX Daemon Server Programming Sample Program
+Levent Karakas &lt;levent at mektup dot at&gt; May 2001
+
+To compile: cc -o exampled examped.c
+To run: ./exampled
+To test daemon: ps -ef|grep exampled (or ps -aux on BSD systems)
+To test log: tail -f /tmp/exampled.log
+To test signal: kill -HUP `cat /tmp/exampled.lock`
+To terminate: kill `cat /tmp/exampled.lock`
+*/
+
+#include &lt;stdio.h&gt;
+#include &lt;fcntl.h&gt;
+#include &lt;signal.h&gt;
+#include &lt;unistd.h&gt;
+
+#define RUNNING_DIR "/tmp"
+#define LOCK_FILE "exampled.lock"
+#define LOG_FILE "exampled.log"
+
+void log_message(filename,message)
+char *filename;
+char *message;
+{
+FILE *logfile;
+ logfile=fopen(filename,"a");
+ if(!logfile) return;
+ fprintf(logfile,"%s\n",message);
+ fclose(logfile);
+}
+
+void signal_handler(sig)
+int sig;
+{
+ switch(sig) {
+ case SIGHUP:
+ log_message(LOG_FILE,"hangup signal catched");
+ break;
+ case SIGTERM:
+ log_message(LOG_FILE,"terminate signal catched");
+ exit(0);
+ break;
+ }
+}
+
+void daemonize()
+{
+int i,lfp;
+char str[10];
+ if(getppid()==1) return; /* already a daemon */
+ i=fork();
+ if (i&lt;0) exit(1); /* fork error */
+ if (i&gt;0) exit(0); /* parent exits */
+ /* child (daemon) continues */
+ setsid(); /* obtain a new process group */
+ for (i=getdtablesize();i&gt;=0;--i) close(i); /* close all descriptors */
+ i=open("/dev/null",O_RDWR); dup(i); dup(i); /* handle standart I/O */
+ umask(027); /* set newly created file permissions */
+ chdir(RUNNING_DIR); /* change running directory */
+ lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0640);
+ if (lfp&lt;0) exit(1); /* can not open */
+ if (lockf(lfp,F_TLOCK,0)&lt;0) exit(0); /* can not lock */
+ /* first instance continues */
+ sprintf(str,"%d\n",getpid());
+ write(lfp,str,strlen(str)); /* record pid to lockfile */
+ signal(SIGCHLD,SIG_IGN); /* ignore child */
+ signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
+ signal(SIGTTOU,SIG_IGN);
+ signal(SIGTTIN,SIG_IGN);
+ signal(SIGHUP,signal_handler); /* catch hangup signal */
+ signal(SIGTERM,signal_handler); /* catch kill signal */
+}
+
+main()
+{
+ daemonize();
+ while(1) sleep(1); /* run */
+}
+
+/* EOF */
+
+</pre>
+
+<p>&nbsp;</p>
+<p>Last Update : 16.05.2001</p>
+<p>&nbsp;</p>
+
+
+ </td>
+ </tr>
+</tbody></table><br>
+
+</div>
+
+
+</body></html> \ No newline at end of file
diff --git a/doc/makedepend.html b/doc/makedepend.html
new file mode 100644
index 0000000..c5b2e77
--- /dev/null
+++ b/doc/makedepend.html
@@ -0,0 +1,1771 @@
+<html><head><!-- Title: gatekeeper --><!--SiteCatalyst code version: G.7. Copyright 1997-2004 Omniture, Inc.
+More info available at http://www.omniture.com
+-->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<script language="JavaScript">
+<!--
+var s_pageName="";
+var s_server="";
+var s_channel="";
+var s_pageType="";
+var s_prop1="";
+var s_prop2="";
+var s_prop3=" | 184406479 | Dependency Management";
+var s_prop4="Dependency Management";
+var s_prop5="Portal | Architecture & Design";
+var s_prop6="";
+var s_prop7="John Graham-Cumming";
+var s_prop8="217.162.109.174 | Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080211 Firefox/2.0.0.12";
+var s_prop9="";
+var s_prop10="";
+var s_prop11="";
+var s_prop12="";
+var s_prop13="http://www.ddj.com/linux-open-source/dhandler.jhtml";
+var s_prop14="";
+var s_prop15="";
+var s_prop16="";
+
+/* E-commerce Variables */
+var s_campaign="";
+var s_state="";
+var s_zip="";
+var s_events="event5";
+var s_products="";
+var s_purchaseID="";
+var s_eVar1="";
+var s_eVar2="";
+var s_eVar3="";
+var s_eVar4="";
+var s_eVar5="";
+//-->
+</script><title>Dr. Dobb's | Dependency Management | April 1, 2006</title>
+
+
+ <meta name="description" content="John explores the irony of Make.">
+ <meta name="keywords" content="john,explores,irony,make">
+ <meta name="headline" content="Dependency Management">
+ <meta name="created" content="">
+
+
+<link rel="Stylesheet" rev="Stylesheet" href="makedepend_files/Layout.css" type="text/css">
+<link rel="Stylesheet" rev="Stylesheet" href="makedepend_files/FontStyles.css" type="text/css">
+<link rel="Stylesheet" rev="Stylesheet" href="makedepend_files/newarticle.css" type="text/css">
+<script src="makedepend_files/popwindow.js"></script>
+<script language="javascript">
+<!--
+function jobsopen(){
+ document.getElementById("jobscontainer").style.height = '';
+ }
+
+function jobsclose(){
+ document.getElementById("jobscontainer").style.height = "41px";
+ }
+
+//-->
+</script>
+<script language="JavaScript" type="text/javascript">
+var addtoMethod=1;
+var AddURL = document.location.href;
+var AddTitle = escape(document.title);
+
+</script>
+<script language="JavaScript" src="makedepend_files/add2.js" type="text/javascript"></script></head><body topmargin="0" leftmargin="0" bgcolor="#ffffff" link="#006699" marginheight="0" marginwidth="0" vlink="#006699"><script type="text/javascript" src="makedepend_files/al.html"></script><script type="text/javascript" src="makedepend_files/al_002.html"></script><script type="text/javascript" src="makedepend_files/spit.js"></script><script type="text/javascript" src="makedepend_files/chunks_002.js"></script><script type="text/javascript" src="makedepend_files/chunks_003.js"></script><script type="text/javascript" src="makedepend_files/chunks.js"></script><script type="text/javascript" src="makedepend_files/chunks_004.js"></script><script type="text/javascript" src="makedepend_files/func_200802221208.js"></script><script type="text/javascript" src="makedepend_files/door.js"></script><script type="text/javascript" src="makedepend_files/tsc.js"></script>
+<table cellpadding="0" cellspacing="0">
+<tbody><tr>
+<td><script> var ckRef=document.referrer; if(ckRef && ckRef.indexOf('/as5/redirect/')==-1 || !ckRef) { document.write('<script language="JavaScript" src="http://i.cmpnet.com/shared/omniture/s_code_remote.js"><\/script>'); document.close(); }</script><script language="JavaScript" src="makedepend_files/s_code_remote.js"></script><img src="makedepend_files/s92783574014196.gif" name="s_i_cmpglobalvista" alt="" border="0" height="1" width="1"></td>
+</tr>
+</tbody></table>
+<!-- End SiteCatalyst code version: G.7. -->
+
+
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=fullpage&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=75640&amp;AdID=125688&amp;TargetID=2878&amp;Segments=3108,3448,4875,11070,12139&amp;Targets=2625,2878,8607&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3216,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.cmp.com"><img src="makedepend_files/iopsblank.gif" alt="" border="0" height="1" width="1"></a><img src="makedepend_files/TypecountClientType2AdID125688FlightID75640TargetID2878S_004.gif" border="0" height="1" width="1">
+
+
+
+
+<!-- CMP LOGO and AD BANNER AT TOP -->
+<div id="TopAdContainer">
+ <img src="makedepend_files/logo_ThinkServices.gif" alt="" align="left" border="0" height="57" hspace="15" vspace="0" width="125">
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=top&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=114710&amp;AdID=188279&amp;TargetID=1251&amp;Segments=1551,3108,3317,3382,3448,4653,4875,5470,5750,5766,6111,6877,7717,10552&amp;Targets=1251,2625,2878,4879&amp;Values=34,46,51,63,77,87,91,102,140,203,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.realsoftware.com"><img src="makedepend_files/REAL_Software_Banner.gif" alt="" border="0" height="90" width="728"></a><img src="makedepend_files/TypecountClientType2AdID188279FlightID114710TargetID1251Site.gif" border="0" height="1" width="1">
+
+
+</div>
+<!-- /CMP LOGO and AD BANNER AT TOP -->
+<!-- LOGO and USER/PORTAL NAV and SEARCH -->
+<table class="elfixo" border="0" cellpadding="0" cellspacing="0" width="990">
+ <tbody><tr>
+ <td rowspan="2" bgcolor="#000000" width="1"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="1"></td>
+ <td colspan="2" bgcolor="#000000" width="362"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="362"></td>
+
+ <td bgcolor="#000000" width="397"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="397"></td>
+ <td rowspan="2" bgcolor="#000000" width="1"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="1"></td>
+ </tr>
+ <tr>
+ <td align="left" bgcolor="#349f2c" valign="middle" width="362">
+ <a href="http://www.ddj.com/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN"><img src="makedepend_files/logo_ddj.gif" alt="" border="0" hspace="3" vspace="5"></a></td>
+ <td align="left" bgcolor="#349f2c" valign="top" width="228"><span class="fphead">FOCAL POINTS: Sponsored Links</span><br>
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=focallink1&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a class="fp" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=110047&amp;AdID=182226&amp;TargetID=9101&amp;Segments=3108,3448,4875,12765&amp;Targets=2625,2878,9101&amp;Values=34,46,51,63,77,87,91,102,140,442,656,897,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://www.ddj.com/focal/msoffice-oba">
+Focus on Office Business Applications (OBA)
+</a>
+<br><img src="makedepend_files/TypecountClientType2AdID182226FlightID110047TargetID9101Site.gif" border="0" height="1" width="1">
+
+
+ </td>
+ <td align="right" bgcolor="#349f2c" valign="middle"><!-- SEARCH -->
+ <form action="/TechSearch/searchResults.jhtml;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ <input name="queryText" value="" style="width: 290px; height: 20px;" size="11" class="searchInput" onfocus="this.value=''" type="text">
+ <input src="makedepend_files/search.gif" alt="" align="absbottom" hspace="3" type="image" vspace="0">
+ </form>
+ <!-- /SEARCH -->
+
+ <a style="font-family: verdana,sans-serif bold; font-style: normal; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(255, 194, 14);" href="http://www.ddj.com/TechSearch/searchResults.jhtml;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">Site Archive (Complete)</a></td>
+ </tr>
+ <!-- /LOGO, and SEARCH -->
+ <tr>
+ <td colspan="5" bgcolor="#000000"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="250"></td>
+ </tr>
+ <tr>
+
+
+<td bgcolor="#000000"></td>
+<td colspan="3" bgcolor="#ffb305">
+ <div class="portalNAVPad">
+ <span class="noLINE">
+ <a href="http://www.ddj.com/about.html;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">ABOUT US</a> |
+ <a href="http://www.ddj.com/contact.html;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">CONTACT</a> |
+ <a href="http://www.ddj.com/advertise/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">ADVERTISE</a> |
+ <a href="http://www.ddj.com/subscribe/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">SUBSCRIBE</a> |
+ <!-- <a href="/pubs/" CLASS=portalNAV>ARCHIVES</a> | -->
+ <a href="http://www.ddj.com/code/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">SOURCE CODE</a> |
+ <a href="http://www.ddj.com/currentIssuePage.html;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">CURRENT PRINT ISSUE</a> |
+ <!-- <a href="`request.getParameter("jiveDomain")+"/jive3/index.jspa"`" CLASS=portalNAV>
+ <param name="categoryID" value="param:ddjCategoryID">FORUMS
+ </a>| -->
+ <a href="http://www.ddj.com/newsletters/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">NEWSLETTERS</a>
+ |
+ <a href="http://www.ddj.com/resources/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">RESOURCES</a>
+ |
+ <a href="http://www.ddj.com/blog/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">BLOGS</a>
+ |
+ <a href="http://www.ddj.com/mediaCenter/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN" class="portalNAV">PODCASTS</a>
+ |
+ <a href="http://www.techcareers.com/?affiliate=ddj" class="portalNAV">CAREERS</a>
+ </span>
+ </div>
+</td>
+<td bgcolor="#000000"></td>
+ </tr>
+ <tr>
+ <td colspan="5" bgcolor="#000000"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="250"></td>
+ </tr>
+</tbody></table>
+<div style="height: 14px; width: 990px;">
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=txtlink20&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=75640&amp;AdID=125688&amp;TargetID=2878&amp;Segments=3108,3448,4875,12180&amp;Targets=2625,2878,8689&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1646,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.cmp.com"><img src="makedepend_files/iopsblank.gif" alt="" border="0" height="1" width="1"></a><img src="makedepend_files/TypecountClientType2AdID125688FlightID75640TargetID2878S_002.gif" border="0" height="1" width="1">
+
+
+</div>
+ <table class="elfixo" border="0" cellpadding="0" cellspacing="0" width="900">
+
+ <tbody><tr>
+ <td valign="top">
+ <!-- LEFT COL -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<script language="Javascript">
+ function launcher(art_id,urlPrefix,siteUrl) {
+ uri = "/article/emailBox.jhtml?articleID=" +art_id+"&urlPrefix="+ urlPrefix+"&host_url="+siteUrl;
+ window.open(uri,"","toolbar=no,scrollbars=auto,location=no,status=no,width=480,height=415,resizable=1");
+ }
+</script>
+
+
+
+
+
+
+
+
+
+
+
+<div class="LColMargin">
+
+ <a href="http://www.ddj.com/linux-open-source/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN"><img src="makedepend_files/header_opensourcen.gif" alt="Open Source" border="0" hspace="0" vspace="0"></a>
+ <img src="makedepend_files/rule.gif" align="left" border="0" height="1" hspace="0" vspace="0" width="405"><br>
+
+</div>
+
+<div class="articlepadding">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <table class="elfixo" align="right" border="0" cellpadding="0" cellspacing="0" width="160">
+ <tbody><tr>
+ <td rowspan="5" width="10"><img src="makedepend_files/blank.gif" border="0" height="1" width="10"></td>
+ <td width="75"><img src="makedepend_files/blank.gif" border="0" height="1" width="75"></td>
+ <td width="75"><img src="makedepend_files/blank.gif" border="0" height="1" width="75"></td>
+ </tr>
+ <tr><td valign="top">
+
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><a href="javascript:launcher(184406479,'/linux-open-source/','www.ddj.com')" title="Send As Email">Email</a><br> <!--
+ <img src="http://i.cmpnet.com/ddj/redsquare.gif" width="15" height="12" hspace="0" vspace="0" border="0" align="absmiddle"><a href="#articleComments">Discuss</a>
+ -->
+ </td>
+ <td valign="top">
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><a href="http://www.ddj.com/article/printableArticle.jhtml;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?articleID=184406479&amp;dept_url=/linux-open-source/">Print</a><br>
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><a href="http://www.cmpreprints.com/faxback.jhtml" target="_new">Reprint</a>
+ </td></tr>
+ <tr>
+ <td colspan="2" style="padding: 7px 0pt 0pt;" height="7" valign="middle"><div align="center"><img src="makedepend_files/dotted_line3.gif" border="0" height="1" hspace="0" vspace="0" width="140"><br><span class="add2header">add to:</span></div></td>
+ </tr>
+ <tr>
+ <td valign="top"><img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Delicious" onclick="addto(2)"><u>Del.icio.us</u></span><br>
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Digg" onclick="addto(3)"><u>Digg</u></span><br>
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Google" onclick="addto(5)"><u>Google</u></span><br>
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Spurl" onclick="addto(8)"><u>Spurl</u></span>
+ </td>
+ <td valign="top"> <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Slashdot" onclick="addto(6)"><u>Slashdot</u></span><br>
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Yahoo! MyWeb" onclick="addto(7)"><u>Y! MyWeb</u></span><br>
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Blink" onclick="addto(1)"><u>Blink</u></span><br>
+ <img src="makedepend_files/redsquare.gif" align="absmiddle" border="0" height="12" hspace="0" vspace="0" width="15"><span class="add2ddj" title="Add this page to Furl" onclick="addto(4)"><u>Furl</u></span>
+ </td>
+ </tr>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ </tbody></table>
+ <div class="Bspace4">
+ <em>April 01, 2006</em><br>
+
+ <h5>Dependency Management</h5>
+ <h2>The irony of Make</h2>
+ </div>
+
+
+
+
+
+
+ <div class="Bspace15"><em>John Graham-Cumming</em></div>
+
+
+
+
+
+
+
+
+ <!-- teaser -->
+ <div class="Bspace15"><span class="greenBlurb">John explores the irony of Make.</span></div>
+ <!-- end teaser -->
+
+
+
+
+<p>
+<i>John is vice president of engineering at Electric Cloud, which
+focuses on reducing software build times. He can be contacted at
+jgc@electric-cloud.com.</i>
+</p><p>
+</p><hr>
+<p>
+
+</p><p>
+
+</p><p>
+
+</p><p>
+Make giveth and Make taketh away. It's ironic that the tool Make,
+designed to solve the "I've changed some source now what do I need to
+recompile?" problem, creates dependency nightmares.</p>
+<p>
+
+</p><p>
+Any project larger than a "Hello World" example faces dependency
+management problems. Dependencies must be generated and kept up to date
+as you add to, modify, and delete from the project. And Make itself
+provides no tools for dealing with this problem&#8212;all Make provides is a
+mechanism for expressing the relationships between <a itxtdid="4050710" target="_blank" href="#" style="border-bottom: medium none; font-weight: bold; text-decoration: none; padding-bottom: 0px; color: darkblue; background-color: transparent; cursor: pointer;" classname="iAs" class="iAs"><nobr>files<img style="border: 0pt none ; margin: 0pt; padding: 0pt; height: 10px; width: 10px; position: relative; top: 1px; left: 1px; float: none; display: inline;" src="makedepend_files/3.gif"></nobr></a> with its familiar<i> target : prerequisite1 prerequisite2... </i>syntax.</p>
+<p>
+
+</p><p>
+The target of the rule (the file that will be built) appears before the
+colon (:) and the files that the target depends upon (called either the
+"dependencies" or "prerequisites") appears after the colon (:). For
+example:</p>
+<p>
+</p><blockquote>
+foo.o : foo.c header.h<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+has target <i>foo.o</i> and prerequisites <i>foo.c</i> and <i>header.h</i>.</p>
+<p>
+
+</p><p>
+Even Make's dependency syntax is flawed because it incorporates both
+"foo.o must be updated if header.h or foo.c are changed" and "foo.o is
+the result of compiling foo.c". Thus, anything to the right of the ":"
+is a prerequisite, but the first prerequisite where there's a rule body
+(that is, commands) is special&#8212;it's the prerequisite that's passed to
+the compiler (or other command) to actually generate the target. </p>
+<p>
+
+</p><p>
+Look at this GNU Make example (I use GNU Make throughout this article
+because of its wide platform coverage and large set of features): </p>
+<p>
+</p><blockquote>
+foo.o: foo.c header.h system.h<br>
+ @echo Compiling $@ from $&lt;...<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+which outputs:</p>
+<p>
+</p><blockquote>
+Compiling foo.o from foo.c...<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+Here, <i>foo.o</i> is built if <i>foo.c</i>, <i>header.h</i>, or <i>system.h</i> change, but the rule also states that <i>foo.o</i> is made from <i>foo.c</i> (in GNU Make terms, the target of the rule to the left side of the ":" is written <i>$@</i> and the first prerequisite is <i>$&lt;</i>). If the example were written like this:</p>
+<p>
+</p><blockquote>
+foo.o: foo.c<br>
+foo.o: header.h system.h<br>
+ @echo Compiling $@ from $&lt;...<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+the output would be:</p>
+<p>
+</p><blockquote>
+Compiling foo.o from header.h...<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+which is clearly wrong. If you want to continue exploring Make's idiosyncrasies with <i>$&lt;</i>, play around with this Makefile:</p>
+<p>
+</p><blockquote>
+foo.o: header.h<br>
+foo.o: system.h<br>
+foo.o: foo.c<br>
+foo.o:<br>
+ @echo Compiling $@ from $&lt;...<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+and try permuting the first three lines to see how Make's interpretation of the Makefile changes.</p>
+<p>
+
+</p><p>
+The biggest problem of all is generating these rules for a large
+project. In the rest of this article, I use this contrived example
+Makefile as a starting point:</p>
+<p>
+</p><blockquote>
+.PHONY: all<br>
+ all: foo.o bar.o baz.o<br>
+<br>
+ foo.o: foo.c foo.h common.h header.h<br>
+ bar.o: bar.c bar.h common.h header.h ba.h<br>
+ baz.o: baz.c baz.h common.h header.h ba.h<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+Three object files (foo.o, bar.o, and baz.o) are built from
+corresponding C files (foo.c, bar.c, and baz.c). Each .o file has
+dependencies on various header files as expressed in the Makefile. The
+Makefile uses GNU Make's built-in rules to perform compilation using
+the system's compiler.</p>
+<p>
+
+</p><p>
+There's no mention here of the final executable being built because I
+concentrate on dealing with dependencies between sources and objects.
+Relationships between objects are usually easier to maintain by hand as
+there are fewer of them and the relationships are part of the product
+design.</p>
+<p>
+
+</p><p>
+Because maintaining any real Makefile by hand is impossible, many
+projects use the widely available makedepend program (makedepend is
+usually installed on UNIX and CYGWIN systems). makedepend reads C and
+C++ files looking at the <i>#include </i>statements, follows the <i>#include</i>s, and builds the dependency lines for you. A basic way of incorporating makedepend in a project is a special <i>depend</i> target:</p>
+<p>
+</p><blockquote>
+.PHONY: all<br>
+ all: foo.o bar.o baz.o<br>
+<br>
+ SRCS = foo.c bar.c baz.c<br>
+<br>
+ DEPENDS = dependencies.mk<br>
+ .PHONY: depend<br>
+ depend:<br>
+ @makedepend -f - $(SRCS) &gt; $(DEPENDS)<br>
+ -include $(DEPENDS)<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+Doing <i>makedepend </i>with this Makefile causes the depend rule to execute, which runs makedepend on the sources (defined in the <i>SRCS</i> variable) and outputs the dependency lines to dependencies.mk (defined by the <i>DEPENDS</i> variable).</p>
+<p>
+
+</p><p>
+The Makefile includes the dependencies lines in its final line. dependencies.mk looks like this:</p>
+<p>
+</p><blockquote>
+# DO NOT DELETE<br>
+<br>
+foo.o: foo.h header.h common.h<br>
+bar.o: bar.h header.h common.h ba.h<br>
+baz.o: baz.h header.h common.h ba.h<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+Notice that makedepend doesn't try to define the relationship between
+an object file (for example, foo.o) and the source file it is made from
+(foo.c). In this case, GNU Make's standard rules automatically find the
+related .c file. There are two problems with the makedepend<i> </i>style: </p>
+<p>
+
+</p><p>
+</p><ul>
+ <li>Running makedepend can be slow, as every source file has to be searched even if there are no changes.</li>
+ <li>It's a manual step. Before every make, users have to do <i>makedepend</i> to ensure that the dependencies are correct. </li>
+</ul>
+<p>
+
+</p><p>
+
+</p><p>
+The answer to these problems is automation. Here's a version of the
+Makefile that still uses makedepend to generate dependencies, but
+automates the process, and only runs makedepend for sources that have
+changed: </p>
+<p>
+</p><blockquote>
+.PHONY: all<br>
+ all: foo.o bar.o baz.o<br>
+ SRCS = foo.c bar.c baz.c<br>
+ %.d : %.c<br>
+ @makedepend -f - $&lt; | sed 's,\($*\.o\)[ :]*,\1<br>
+ $@ : ,g' &gt; $@<br>
+ -include $(SRCS:.c=.d)<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+It works by associating a .d file with each .c; for example, foo.o has
+a foo.d file that only contains the dependency line for foo.o. Here are
+foo.d's contents:</p>
+<p>
+</p><blockquote>
+# DO NOT DELETE<br>
+foo.o foo.d : foo.h header.h common.h<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+This line specifies when to rebuild foo.o, but also that foo.d should
+be rebuilt under the same conditions&#8212;if any of the sources associated
+with foo.o change, then foo.d gets rebuilt. foo.c isn't mentioned in
+this list because it's mentioned as part of the pattern rule for
+rebuilding a .d file (<i>%.d : %.c</i>
+means that foo.d gets rebuilt if foo.c itself changes). foo.d got added
+to the dependency line created by makedepend using the aforementioned <i>sed</i> magic.</p>
+<p>
+
+</p><p>
+The final line of the Makefile includes all the .d files: The <i>$(SRCS:.c=.d)</i> macro transforms the list of sources in the <i>SRCS</i> variable by changing the extension from .c to .d. The <i>include</i>
+also tells GNU Make to check to see if the .d files need rebuilding.
+GNU Make looks to see if there are rules to rebuild included Makefiles
+(in this case, the .d files), rebuilds them if necessary (following the
+dependencies specified in the Makefile), then restarts. This "Makefile
+remaking" feature (see section 3.7 "How Makefiles Are Remade" in the
+FSF's GNU Make manual;
+http://www.gnu.org/software/make/manual/html_mono/make.html#SEC20)
+means that simply typing "make" will rebuild any dependency files that
+need rebuilding&#8212;but only if the sources have changed. Then, GNU Make
+will perform the build, taking into account the new dependencies.</p>
+<p>
+
+</p><p>
+Unfortunately, this Makefile breaks with a fatal error if a header file
+is removed. If header.h is no longer needed and all references to it
+are removed from the .c files and the file is removed from disk, this
+error occurs when Make is run:</p>
+<p>
+</p><blockquote>
+No rule to make target 'header.h', <br>
+ needed by 'foo.d'.<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+This happens because header.h is still mentioned in foo.d as being a
+prerequisite of foo.d; hence, foo.d cannot be rebuilt. This Catch-22
+can be fixed by making the generation of foo.d smarter. The new foo.d
+includes the dependencies for foo.o and foo.d separately. foo.d's
+dependencies are wrapped in a call to GNU Make's <i>$(wildcard)</i>
+function (see section 4.4.3 "The Function Wildcard" in the FSF's GNU
+Make manual; http://www.gnu.org/software/make/manual/html_mono/make
+.html#SEC33). Here's the new foo.d:</p>
+<p>
+</p><blockquote>
+# DO NOT DELETE<br>
+foo.d : <br>
+ $(wildcard foo.h header.h common.h)<br>
+foo.o : foo.h header.h common.h<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+And here's the updated Makefile with a new invocation of <i>makedepend</i>, followed by a <i>sed</i> line that creates the modified .d file:</p>
+<p>
+</p><blockquote>
+.PHONY: all<br>
+ all: foo.o bar.o baz.o<br>
+ SRCS = foo.c bar.c baz.c<br>
+ %.d : %.c<br>
+ @makedepend -f - $&lt; | sed 's,<br>
+ \($*\.o\)[ :]*\(.*\),<br>
+ $@ : $$\(wildcard \2\)\n\1 : \2,g' &gt; $@<br>
+ -include $(SRCS:.c=.d)<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+Now removing a header file doesn't break the Make: When foo.d is parsed, the dependency line for foo.d is passed through <i>$(wildcard)</i>. When there are no globbing symbols such as "*" or "?" in the filename, <i>$(wildcard) </i>acts
+as an existence filter, removing those files that don't exist from the
+list. So, if header.h had been removed, the first line of foo.d would
+be equivalent to: </p>
+<p>
+</p><blockquote>
+foo.d : foo.h common.h<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+and the Make would work correctly. This example Makefile now works when .c files are added (users just update <i>SRCS</i> and the new .d file is created automatically), when .c files are removed (users update <i>SRCS</i>
+and the old .d file is ignored), and when headers are added (because
+that requires touching an existing .c or .h, the .d file is
+regenerated). And when they are removed, the <i>$(wildcard) </i>hides the deletion and the .d file is regenerated.</p>
+<p>
+
+</p><p>
+An optimization is to remove the need for GNU Make to restart by
+merging the rule that makes the .d file into the rule that makes the .o
+file. Because the .d file is updated if (and only if) the .o file needs
+to be updated (both are updated when any of the sources for the .o
+change), it's possible to have the <i>makedepend</i> occur at the same time as the compilation:</p>
+<p>
+</p><blockquote>
+.PHONY: all<br>
+ all: foo.o bar.o baz.o<br>
+ SRCS = foo.c bar.c baz.c<br>
+ %.o : %.c<br>
+ @makedepend -f - $&lt; | <br>
+ sed 's,\($*\.o\)[ :]*\(.*\),<br>
+ $@ : $$\(wildcard \2\)\n\1 : \2,g' &gt; $*.d<br>
+ @$(COMPILE.c) -o $@ $&lt;<br>
+ -include $(SRCS:.c=.d)<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+This rule makes use of <i>$*</i>, another GNU Make variable. <i>$*</i> is the part of the pattern <i>%.c</i> that matches the %. If this rule is building foo.o from foo.c, then <i>$*</i> is just <i>foo</i>. <i>$*</i>
+is used to create the name of the .d file that makedepend writes to.
+This final version does not use GNU Make's "Makefile remaking" system.
+There are no rules for making .d files (they are made as a side effect
+of making the .o); hence, GNU Make does not have to restart, providing
+the best combination of accuracy and speed possible.</p>
+<p>
+
+</p><p>
+In general, it's a bad idea to have a rule that makes multiple files
+because it's impossible for GNU Make to find the rule that makes a file
+if it's created as a side effect of something else. In this case, that
+behavior is desired: You want to hide the creation of .d files from GNU
+Make so that it doesn't try to make them and then have to restart. A
+similar idea was proposed by Tom Tromey, without the <i>$(wildcard) </i>trick,
+and more information about building dependency files can be found on
+the GNU Make maintainer Paul Smith's web site at
+http://make.paulandlesley.org/autodep.html. Another good resource for
+any GNU Make developer&#8212;once you've purchased the FSF's GNU Make
+manual&#8212;is Robert Mecklenburg's <i>Managing Projects with GNU Make </i>(O'Reilly &amp; Associates, 2004). </p>
+<p>
+
+</p><p>
+Finally, it's possible to omit makedepend altogether if you are using the GNU GCC compiler. It has a <i>-MD</i> option that does the work of makedepend at the same time as the compilation:</p>
+<p>
+</p><blockquote>
+.PHONY: all<br>
+ all: foo.o bar.o baz.o<br>
+ SRCS = foo.c bar.c baz.c<br>
+ %.o : %.c<br>
+ @$(COMPILE.c) -MD -o $@ $&lt;<br>
+ @sed -i 's,\($*\.o\)[ :]*\(.*\),<br>
+ $@ : $$\(wildcard<br>
+ \2\)\n\1 : \2,g' $*.d<br>
+ -include $(SRCS:.c=.d)<br>
+<p>
+</p></blockquote>
+<p>
+
+</p><p>
+For example, the compilation step for foo.o will create foo.d from foo.c and then <i>sed</i> is run on the foo.d to add the extra line for foo.d containing the <i>$(wildcard)</i>.</p>
+<p>
+
+</p><p>
+The use of GCC <i>-MD</i> is an example of creating
+dependencies without using makedepend. There are a number of other
+possibilities. For example, GCC has <i>-M </i>and <i>-MM</i> options that just output dependency information (<i>-M</i> outputs all dependencies and <i>-MM</i> omits the system headers because they are unlikely to change).</p>
+<p>
+
+</p><p>
+Another option is the program fastdep
+(http://www.irule.be/bvh/c++/fastdep/), which aims to be a fast
+replacement for makedepend. Finally, Windows programmers can use
+Microsoft CL's <i>/showincludes </i>option to get include information and build dependency information.</p>
+<p>
+
+</p><p>
+All of these solutions share some common problems. They only work well
+for C and C++ code and need to be modified to handle other languages
+(although luckily, not all languages have the same dependency forests
+as C and C++). In addition, the files included might change as the
+result of preprocessor defines; hence, makedepend (or an equivalent
+program) needs to be told about any command line <i>-D</i> defines so that it builds the right dependency information.</p>
+<p>
+
+</p><p>
+Despite the problems outlined here, these Makefile snippets do provide
+a reliable way of keeping C and C++ dependencies up to date
+automatically and quickly.</p>
+<p>
+
+</p><p>
+
+<b>DDJ</b></p>
+<p>
+
+</p><p>
+
+</p><div class="Bspace15">
+ <em><a href="http://www.ddj.com/authors/authorInfo.jhtml;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?authorID="><strong></strong></a> <a href="mailto:"><strong></strong></a></em>
+ </div>
+
+
+ <center>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ </center>
+
+
+
+
+<!-- ARTICLE COMMENTS-->
+<!--
+<a name="articleComments"></a>
+<br>
+<droplet src="/article/articleComments.jhtml">
+<param name="articleTitle" value="param:element.headline">
+<param name="articleID" value="param:articleID">
+<param name="commentsOpen" value="true">
+</droplet>
+-->
+<!-- AROUND THE WEB AND TOP 5 ARTICLES -->
+<table class="elfixo" border="0" cellpadding="0" cellspacing="0" width="467">
+<tbody><tr>
+ <td width="229"><img src="makedepend_files/blank.gif" border="0" height="1" width="229"></td>
+ <td width="9"><img src="makedepend_files/blank.gif" border="0" height="1" width="9"></td>
+ <td width="229"><img src="makedepend_files/blank.gif" border="0" height="1" width="229"></td>
+</tr>
+<tr>
+ <td valign="top">
+ <!-- AROUND THE WEB -->
+ <!-- AROUND THE WEB -->
+ <div id="GreenContainerBoxSM2">
+ <div id="GreenBARSM2"><span class="BARTitles">RELATED ARTICLES</span></div>
+
+
+
+
+
+
+
+
+<div class="PaddingStyle2">
+
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/206902785;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Wireless Emergency Communications Tests Looking Good</a>
+ </div>
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/security/206902613;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ The Silverlight 2.0 Security Model</a>
+ </div>
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/mobile/206901832;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Nokia to Add Silverlight Support</a>
+ </div>
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/embedded/206901624;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Texture for Haptic Interfaces</a>
+ </div>
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/hpc-high-performance-computing/206901618;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Guibas Receives ACM/AAAI Award for Algorithm Development</a>
+ </div>
+
+
+</div>
+ </div>
+ </td>
+ <td width="9"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="9"></td>
+ <td valign="top">
+ <!-- TOP 5 ARTICLES -->
+ <div id="GreenContainerBoxSM2"><div id="GreenBARSM2"><span class="BARTitles">TOP 5 ARTICLES</span></div>
+ <div class="PaddingStyle2">
+
+
+
+
+
+
+
+
+
+
+<div class="PaddingStyle2">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/cpp/204202899;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Building Your Own Plugin Framework: Part 1
+ </a></div>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/cpp/206503957;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Building Your Own Plugin Framework: Part 5
+ </a></div>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/cpp/204702751;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Building Your Own Plugin Framework: Part 2
+ </a></div>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/architect/186100398;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Maven: Building Complex Systems
+ </a></div>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <div class="Bspace5">
+ <a href="http://www.ddj.com/database/202802959;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN">
+ Query Anything with SQLite
+ </a></div>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+</div>
+
+ </div>
+ </div></td>
+</tr>
+</tbody></table>
+</div>
+
+
+ </td>
+ <td width="10"><img src="makedepend_files/blank.gif" alt="" border="0" height="1" width="10"></td>
+ <td valign="top">
+ <!-- RIGHT COL -->
+
+
+
+
+<!-- RIGHT COL -->
+
+<!-- ARTICLE contaier table for media center and departments -->
+<table class="elfixo" border="0" cellpadding="0" cellspacing="0" width="575">
+<tbody><tr> <td rowspan="2" valign="top" width="400">
+
+<!-- YET TO RECEIVE AD PARAMS-->
+
+ <div class="clear">
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=sponsbox&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<div id="SponsorBox">
+<div id="SponsorBar">
+
+<span class="miniBARTitles">LIFE 2.0 SUMMIT SPRING 2008</span>
+</div>
+<div class="SPONSORPad">
+Join us on the CMP Region for Life 2.0 Summit Spring, a virtual conference, happening in Second Life.<a target="_blank" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=113304&amp;AdID=186384&amp;TargetID=9027&amp;Segments=3108,3448,4875,7711,7716,12683&amp;Targets=2625,2878,9027&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3544,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://www.life20.net/register.php?event=l20spring08"> Register for your COMPLIMENTARY PASS today!</a></div>
+</div><img src="makedepend_files/TypecountClientType2AdID186384FlightID113304TargetID9027Site.gif" border="0" height="1" width="1">
+
+ </div>
+
+
+<!------------------------DDJ UNIVERSITY --------------->
+<!--
+<div id="SponsorBar" style="background-color: blue;color: white;">
+<span class="miniBARTitles">DR. DOBB'S UNIVERSITY</span>
+</div>
+<div style="width: 100%; padding: 5px 0px 5px 5px;border: 1px solid black;margin-bottom: 5px;">
+Dr. Dobb's new e-learning platform, Dr. Dobb's University, gives you access to select conference sessions from Dr. Dobb's world-class developer events. Available courses focus on such topics as <a href="http://www.ddj.com/architect/ddjuniversity.jhtml">Architecture & Design</a>, <a href="http://www.ddj.com/cpp/ddjuniversity.jhtml">C/C++</a>, <a href="http://www.ddj.com/database/ddjuniversity.jhtml">Database engineering</a>, <a href="http://www.ddj.com/development-tools/ddjuniversity.jhtml">Development tools</a>, <a href="http://www.ddj.com/embedded/ddjuniversity.jhtml">Embedded systems</a>, <a href="http://www.ddj.com/hpc-high-performance-computing/ddjuniversity.jhtml">HPC</a>, <a href="http://www.ddj.com/java/ddjuniversity.jhtml">Java</a>, <a href="http://www.ddj.com/mobile/ddjuniversity.jhtml">Mobility</a>, <a href="http://www.ddj.com/linux-open-source/ddjuniversity.jhtml">Open Source</a>, <a href="http://www.ddj.com/security/ddjuniversity.jhtml">Security</a>, <a href="http://www.ddj.com/web-development/ddjuniversity.jhtml">Web Development</a> and <a href="http://www.ddj.com/windows/ddjuniversity.jhtml">Windows/.NET</a>.
+
+</div> -->
+
+
+<!----------------------SEARCH JOBS BOX----------------->
+
+<div id="jobscontainer" style="overflow: hidden; height: 41px; width: 402px;">
+
+<form method="post" action="http://www.TechCareers.com/js/action/searchresults.asp?affiliate=ddj&amp;_DARGS=/template_parts/dept/right/right.jhtml" id="form1" name="form1">
+
+<input name="FOrderBy" value="modifydate[d]" type="hidden">
+<div id="TechCareersBox">
+<div id="SponsorBar" style="background-color: teal;">
+<span class="miniBARTitles">DR. DOBB'S CAREER CENTER</span>
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=careers1&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=75640&amp;AdID=125688&amp;TargetID=2878&amp;Segments=3108,3448,4875,11102&amp;Targets=2625,2878&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1339,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4377,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.cmp.com"><img src="makedepend_files/iopsblank.gif" alt="" border="0" height="1" width="1"></a><img src="makedepend_files/TypecountClientType2AdID125688FlightID75640TargetID2878S_003.gif" border="0" height="1" width="1">
+
+
+</div>
+<div style="padding: 5px 0px 5px 5px; width: 100%;">
+Ready to take that job and shove it? <span style="text-decoration: underline;" class="pointerhand" onclick="jobsopen()">open</span> | <span style="text-decoration: underline;" class="pointerhand" onclick="jobsclose()">close</span><br>
+</div>
+</div>
+<table bgcolor="#000000" border="0" cellpadding="5" cellspacing="1" width="100%">
+
+ <tbody><tr bgcolor="#ffffff">
+ <td colspan="2" class="noLINE" bgcolor="#cde5e8" valign="top"> <b>Search jobs on</b> <a class="thirteenBOLD" href="http://www.techcareers.com/?affiliate=ddj">Dr. Dobb's TechCareers</a></td>
+ </tr>
+ <tr bgcolor="#ffffff">
+ <td valign="top" width="50%">
+<table border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tbody><tr>
+ <td valign="top">
+
+ <div><strong>Function:</strong><br> <select name="FCareerFocus1" size="1">
+ <option value="23">Information Technology</option>
+ <option value="25">Engineering</option>
+ </select></div>
+ <br>
+ <div><strong>Keyword(s):</strong><br>
+ <input name="FKeywords" size="25"></div>
+ <br>
+ <div><b>State:</b>&nbsp;&nbsp; <input name="FState" size="8" maxlength="2"> <input value="Go" id="submit1" name="submit1" align="right" type="submit"></div>
+ </td>
+
+ </tr>
+ </tbody></table>
+
+ </td>
+ <td class="noLINE" valign="top" width="50%">
+ <!------------------FEATURED JOB----------------->
+
+<li><a href="http://www.techcareers.com/JS/Form/SignUpForm.asp?affiliate=ddj">Post Your Resume</a>
+</li><li><a href="http://www.techcareers.com/MKT/Content/EMP/Default.asp?affiliate=ddj">Employers Area</a>
+</li><li><a href="http://www.techcareers.com/content/news.asp?affiliate=ddj">News &amp; Features</a>
+</li><li><a href="http://www.techcareers.com/content/forum.asp?affiliate=ddj">Blogs &amp; Forums</a>
+</li><li><a href="http://www.techcareers.com/JS/CareerResources/?affiliate=ddj">Career Resources</a>
+<br><br>
+<b>Browse By:</b> <br>
+ <a href="http://www.techcareers.com/JS/JobSearch/?affiliate=ddj">Location</a> | <a href="http://www.techcareers.com/JS/JobSearch/?affiliate=ddj">Employer</a> | <a href="http://www.techcareers.com/JS/JobSearch/?affiliate=ddj">City</a><br>
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=careers2&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=75634&amp;AdID=125682&amp;TargetID=2625&amp;Segments=3108,3448,4875,11103&amp;Targets=2625,2878&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1339,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4378,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.cmp.com"><img src="makedepend_files/iopsblank.gif" alt="" border="0" height="1" width="1"></a><img src="makedepend_files/TypecountClientType2AdID125682FlightID75634TargetID2625S_002.gif" border="0" height="1" width="1">
+
+
+ </li></td>
+ </tr>
+
+ <tr bgcolor="#ffffff">
+ <td colspan="2" bgcolor="#cde5e8" valign="top"><b>Most Recent Posts:</b></td>
+ </tr>
+ <tr bgcolor="#ffffff">
+ <td colspan="2" class="noLINE" valign="top">
+ <!-- <script language=javascript
+src=http://www.4jobs.com/common/partnership/jobs/display.asp?max=4&col=1&par=http://www.techcareers.com&slo=N&vj=N&des=y&ran=Y&i1=23&aor=OR&loc=Y&ind=N&sal=N&comp=Y&bc=FFFFFF&bg=FFFFFF&jbc=FFFFFF&jbg=FFFFFF&pad=5&wid=&tar=n&qsn1=affiliate&qsv1=ddj&date=Y></script> -->
+<!-- <script language="javascript" src="http://www.dobbsprojects.com/prox/tcrss.js"></script> -->
+<script language="javascript" src="makedepend_files/tcrss.js"></script><a href="http://www.techcareers.com/JS/General/Job.asp?id=15331393&amp;affiliate=ddj">Software Sales Specialist</a><br>Hewlett Packard seeking Software Sales Specialist in San Francisco, CA<br><br><a href="http://www.techcareers.com/JS/General/Job.asp?id=16147525&amp;affiliate=ddj">Programmer / System Analyst</a><br>American First CU seeking Programmer / System Analyst in La Habra, CA<br><br><a href="http://www.techcareers.com/JS/General/Job.asp?id=16181149&amp;affiliate=ddj">Senior Engineer</a><br>Mine Safety Appliances seeking Senior Engineer in Cranberry Twp, PA<br><br><a href="http://www.techcareers.com/JS/General/Job.asp?id=14466217&amp;affiliate=ddj">Sr Staff Circuit Designer</a><br>Xilinx seeking Sr Staff Circuit Designer in San Jose, CA<br><br><a href="http://www.techcareers.com/JS/General/Job.asp?id=15501072&amp;affiliate=ddj">Senior Embedded Applications Software Developer</a><br>SigmaTel seeking Senior Embedded Applications Software Developer in Austin, TX<br><br></td>
+ </tr>
+</tbody></table>
+</form>
+</div>
+<div class="Bspace15"></div>
+<!----------------------END SEARCH JOBS BOX----------------->
+<br>
+<!-- VIDEO BOX -->
+
+
+<!-- END VIDEO BOX -->
+
+<br>
+
+<div align="center">
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=jumbobox&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=107968&amp;AdID=182668&amp;TargetID=3885&amp;Segments=3108,3448,4569,4669,4827,4875,5385,5472,6879,6939,7714,10554,14042,14113&amp;Targets=2625,2878,3885,4532,10200&amp;Values=34,46,51,63,77,87,91,102,140,290,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.eetimes.semiconductor.com%3Fcid%3DSDW"><img src="makedepend_files/ips_ostd1_336x280_ddj.gif" alt="" border="0" height="280" width="336"></a><img src="makedepend_files/TypecountClientType2AdID182668FlightID107968TargetID3885Site.gif" border="0" height="1" width="1">
+
+ </div>
+ <br>
+
+
+
+ <table>
+ <tbody><tr>
+ <td valign="top">
+
+
+ <!-- <img src="http://i.cmpnet.com/ddj/rule.gif" width="180" height="1" hspace="0" vspace="0" border="0" alt=""> -->
+ <!-- INFO LINK-->
+
+
+<table border="0" width="100">
+ <tbody><tr>
+ <td valign="top"><!-- MICROSITES -->
+ <div id="ArticlerightcolBox">
+ <div id="TOCBAR"><span class="BARTitles">MICROSITES</span></div>
+ <div class="PaddingStyle2">
+ <span class="blogName">FEATURED TOPIC</span>
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=microsite1&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=110126&amp;AdID=182335&amp;TargetID=8824&amp;Segments=3108,3448,4875,12454&amp;Targets=2625,2878,8824&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,5161,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.ddj.com/focal/msoffice-oba"><img src="makedepend_files/125x125_411_PDQ.gif" alt="" border="0" height="125" width="125"></a><img src="makedepend_files/TypecountClientType2AdID182335FlightID110126TargetID8824Site.gif" border="0" height="1" width="1">
+
+
+ <br><br>
+ <span class="blogName">ADDITIONAL TOPICS</span>
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=microsite2&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_blank" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=110127&amp;AdID=182336&amp;TargetID=8823&amp;Segments=3108,3448,4875,12455&amp;Targets=2625,2878,8823&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,5162,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://www.ddj.com/focal/msoffice-oba">"Get the 411 on OBA PDQ! Learn to build and deploy Microsoft Office Business Applications"</a><img src="makedepend_files/TypecountClientType2AdID182336FlightID110127TargetID8823Site.gif" border="0" height="1" width="1">
+
+
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=microsite3&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_blank" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=110421&amp;AdID=182683&amp;TargetID=8825&amp;Segments=3108,3448,4875,12456&amp;Targets=2625,2878,8825&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,5163,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://www.ddjresources.com/silverlight/">Are you a Windows programmer? Get the direct route to design interactive environments. Learn Silverlight here.</a><img src="makedepend_files/TypecountClientType2AdID182683FlightID110421TargetID8825Site.gif" border="0" height="1" width="1">
+
+
+ </div>
+ </div>
+ <!-- END MICROSITES --></td>
+ <td valign="top"><!-- INFO LINK-->
+ <div id="ArticlerightcolBox">
+<span class="blogName">INFO-LINK</span>
+ <div class="Bspace5">
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=infolink1&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_blank" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=110048&amp;AdID=182227&amp;TargetID=4845&amp;Segments=3108,3448,4875,6084,6088,6876,8025,8050,9374&amp;Targets=2625,2878,4845,4855&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3183,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://www.ddj.com/focal/msoffice-oba">Learn how to build OBAs using the Microsoft Office system.</a><img src="makedepend_files/TypecountClientType2AdID182227FlightID110048TargetID4845Site.gif" border="0" height="1" width="1">
+
+ </div>
+ <div class="Bspace5">
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=infolink2&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_blank" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=110414&amp;AdID=182665&amp;TargetID=4921&amp;Segments=3108,3448,4875,6085,6097,8026,8051&amp;Targets=2625,2878,7437,4921&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3184,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://www.dobbsprojects.com/lcap/microsoft1/?method=text">Learn about the benefits of Protocol Licensing: Download this free Whitepaper from Microsoft</a><img src="makedepend_files/TypecountClientType2AdID182665FlightID110414TargetID4921Site.gif" border="0" height="1" width="1">
+
+ </div>
+ <div class="Bspace5">
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=infolink3&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_blank" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=112205&amp;AdID=185099&amp;TargetID=5776&amp;Segments=3108,3448,4875,6089,8027,8052,8088&amp;Targets=2625,2878,5776&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3185,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://w.on24.com/r.htm?e=102904&amp;s=1&amp;k=EE435A18D1C8A918764935B406A34FE9&amp;partnerref=4SD">Register Today for Microsoft's Silverlight Webinar Series</a><img src="makedepend_files/TypecountClientType2AdID185099FlightID112205TargetID5776Site.gif" border="0" height="1" width="1">
+
+ </div>
+ <div class="Bspace5">
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=infolink4&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_blank" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=112620&amp;AdID=185513&amp;TargetID=5775&amp;Segments=3108,3448,4875,6090,8028,8053,8089&amp;Targets=2625,2878,5775&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3186,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=&amp;Redirect=http://www.dobbsprojects.com/lcap/recursion2/?method=text">Free Whitepaper: Building a real-time C++ robotics framework using Recursion's C++ Toolkit. Download Now!</a><img src="makedepend_files/TypecountClientType2AdID185513FlightID112620TargetID5775Site.gif" border="0" height="1" width="1">
+
+ </div>
+</div>
+ <br></td>
+ </tr>
+</tbody></table>
+<br>
+<div align="center">
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=sec2&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=107504&amp;AdID=179387&amp;TargetID=10444&amp;Segments=3108,3448,4875,14374&amp;Targets=2625,2878,10444&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3380,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.ddjresources.com/silverlight"><img src="makedepend_files/silverlight_center_imu2dec.gif" alt="" border="0" height="280" width="336"></a><img src="makedepend_files/TypecountClientType2AdID179387FlightID107504TargetID10444Sit.gif" border="0" height="1" width="1">
+
+ </div>
+ <br>
+
+
+ </td>
+
+ <td valign="top">
+ <!-- MICROSITES -->
+ <!-- <DIV id="ArticlerightcolBox">
+ <div id="TOCBAR"><span class="BARTitles">MICROSITES</span></div>
+ <DIV class="PaddingStyle2">
+ <span class="blogName">FEATURED TOPIC</span>
+ <droplet src="/viewAd.jhtml">
+ <param name="pagepos" value="microsite1">
+ </droplet>
+ <br /><br />
+ <span class="blogName">ADDITIONAL TOPICS</span>
+ <droplet src="/viewAd.jhtml">
+ <param name="pagepos" value="microsite2">
+ </droplet>
+ <droplet src="/viewAd.jhtml">
+ <param name="pagepos" value="microsite3">
+ </droplet>
+ </div>
+ </div> -->
+ <!-- END MICROSITES -->
+ <br>
+
+ <!--
+<div align="center"><droplet src="/viewAd.jhtml">
+ <param name="pagepos" value="jumbobox">
+ </droplet></div>
+ <br> -->
+ <!-- <droplet src="/viewAd.jhtml">
+ <param name="pagepos" value="testdrive">
+ </droplet> -->
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <!-- Industry Brains -->
+ <script type="text/javascript" src="makedepend_files/jsct.js"></script><table style="border: 1px solid rgb(197, 174, 80); width: 100%; background-color: rgb(255, 255, 255); border-collapse: collapse;" xmlns:ibn="urn:industrybrains.com:linkserver" cellspacing="0"><tbody><tr><td style="padding: 4px; font-family: Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: bold; font-size: 12px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 0, 0); text-align: left; background-color: rgb(249, 248, 247);" colspan="2"> MARKETPLACE (Sponsored Links) </td></tr><tr><td style="padding: 3px;"></td><td style="border: 0px solid rgb(24, 77, 49); padding: 3px;"><div><a style="font-family: Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: bold; font-size: 12px; line-height: normal; font-size-adjust: none; font-stretch: normal; text-decoration: underline; color: rgb(64, 64, 255);" target="_new" href="http://links.industrybrains.com/click?sid=644&amp;rqctid=134&amp;pos=1&amp;lid=458815&amp;cid=101147&amp;pr=2&amp;tstamp=20080311173820&amp;url=http://www.techexcel.com/products/servicesuite/servicewise.html%3futm_source%3dindustrybrains%26utm_medium%3dcpc%26utm_content%3dServiceWise%26utm_campaign%3dITILinfo">Workflow Enabled Help Desk &amp; IT Service Management</a></div><div style="font-family: Verdana,Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 11px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 0, 0);">Automate service desk activities and integrate processes across IT. Learn more here.</div></td></tr><tr><td style="padding: 3px;"></td><td style="border: 0px solid rgb(24, 77, 49); padding: 3px;"><div><a style="font-family: Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: bold; font-size: 12px; line-height: normal; font-size-adjust: none; font-stretch: normal; text-decoration: underline; color: rgb(64, 64, 255);" target="_new" href="http://links.industrybrains.com/click?sid=644&amp;rqctid=134&amp;pos=2&amp;lid=484014&amp;cid=135265&amp;pr=2&amp;tstamp=20080311173820&amp;url=http://clk.atdmt.com/MRT/go/ndstritp0450006421mrt/direct/01/">Download MICROSOFT SEARCH SERVER EXPRESS 2008 FREE</a></div><div style="font-family: Verdana,Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 11px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 0, 0);">Search file shares, SharePoint sites, Exchange Public Folders, Lotus Notes repositories, and more!</div></td></tr><tr><td style="padding: 3px;"></td><td style="border: 0px solid rgb(24, 77, 49); padding: 3px;"><div><a style="font-family: Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: bold; font-size: 12px; line-height: normal; font-size-adjust: none; font-stretch: normal; text-decoration: underline; color: rgb(64, 64, 255);" target="_new" href="http://links.industrybrains.com/click?sid=644&amp;rqctid=134&amp;pos=3&amp;lid=458552&amp;cid=108071&amp;pr=2&amp;tstamp=20080311173820&amp;url=http://www.windev.com/index%3fp%3dCMPWinDev">WinDev 11 - Powerful IDE</a></div><div style="font-family: Verdana,Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 11px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 0, 0);">Develop 10 times faster ! ALM, IDE, .Net, RAD, 5GL, Database, 5GL, 64-bit, etc. Free Express version</div></td></tr><tr><td style="padding: 3px;"></td><td style="border: 0px solid rgb(24, 77, 49); padding: 3px;"><div><a style="font-family: Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: bold; font-size: 12px; line-height: normal; font-size-adjust: none; font-stretch: normal; text-decoration: underline; color: rgb(64, 64, 255);" target="_new" href="http://links.industrybrains.com/click?sid=644&amp;rqctid=134&amp;pos=4&amp;lid=457884&amp;cid=6237&amp;pr=2&amp;tstamp=20080311173820&amp;url=http://www.sgvsarc.com/adv_default.asp%3fsrcid%3dAutoD">Flowcharts from C/C++ code -- Free trial download</a></div><div style="font-family: Verdana,Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 11px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 0, 0);">Understand C/C++ code in less time. A new team member ? Inherited legacy code ? Get up to speed fast... </div></td></tr><tr><td style="padding: 3px;"></td><td style="border: 0px solid rgb(24, 77, 49); padding: 3px;"><div><a style="font-family: Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: bold; font-size: 12px; line-height: normal; font-size-adjust: none; font-stretch: normal; text-decoration: underline; color: rgb(64, 64, 255);" target="_new" href="http://links.industrybrains.com/click?sid=644&amp;rqctid=134&amp;pos=5&amp;lid=457902&amp;cid=2193&amp;pr=2&amp;tstamp=20080311173820&amp;url=http://www.visualbuild.com/%3fcmp">Automate Software Builds with Visual Build Pro</a></div><div style="font-family: Verdana,Arial,Helvetica,sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 11px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 0, 0);">Easily create an automated, repeatable process for building and deploying software.</div></td></tr><tr><td style="padding: 0.2em; font-family: Arial,Helvetica,sans-serif; font-style: italic; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 51, 102); text-align: right;" colspan="2"><a href="http://www.industrybrains.com/cmpsd" style="font-family: Verdana,Arial,Helvetica,sans-serif; font-style: italic; font-variant: normal; font-weight: normal; font-size: 10px; line-height: normal; font-size-adjust: none; font-stretch: normal; color: rgb(0, 0, 170);"> Advertise With Us </a></td></tr></tbody></table><a href="http://shlinks.industrybrains.com/sh?sid=644&amp;a=23abf02755a5877441daafff0e0a20d0006c03f08ff1275116277e26c0ce3851"></a>
+ <!-- end Industry Brains -->
+ </td>
+ </tr>
+
+ </tbody></table>
+
+
+
+ </td><td rowspan="2">
+</td><td rowspan="2" valign="top" width="10">&nbsp;</td>
+ <td valign="top" width="165">
+ <!-- DEPARTMENTS -->
+
+
+<div id="DepartmentBOX"><div id="DepartmentBar"><span class="miniBARTitles">DEPARTMENTS</span></div>
+<span class="deptLinks">
+
+<div class="DeptLinkPad"><a href="http://www.ddj.com/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Home</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/architect/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Architecture &amp; Design</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/cpp/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">C/C++</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/database/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Database</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/development-tools/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Development Tools</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/embedded/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Embedded Systems</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/hpc-high-performance-computing/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">High Performance Computing</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/java/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Java</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/mobile/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Mobility</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/linux-open-source/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Open Source</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/security/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Security</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/web-development/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Web Development</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <div class="DeptLinkPad"><a href="http://www.ddj.com/windows/;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN?cid=vnav" class="deptLinks">Windows/.NET</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+</span>
+<!--<div id="EventBar"><span class="eventBARTitles"><a href="/events/" class="eventBARTitles">Events</a></span></div>-->
+</div>
+
+<br>
+<div id="DepartmentBOXSponsored">
+<div class="deptSponsorHeader">&#9830; sponsored</div></div>
+ <div id="DepartmentBOXSilverlight">
+ <div class="DeptLinkPadSilverlight"><a href="http://www.ddjresources.com/silverlight/" class="deptLinksSilverlight">Resource Center for Microsoft® Silverlight&#8482;</a></div>
+ <div id="dividerLine"><img src="makedepend_files/blank.html" border="0" height="1" hspace="0" vspace="0" width="5"></div>
+
+ <!-- <div id="EventBar"><span class="eventBARTitles"><a href="/dept/events/" class="eventBARTitles">Events</a></span></div> -->
+ </div>
+
+
+ <br>
+ <div width="161" height="601" hspace="0" vspace="0" border="0" align="center">
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=sky&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<script type="text/javascript"><!--
+google_ad_client = "pub-7860495301058581";
+google_ad_width = 120;
+google_ad_height = 600;
+google_ad_format = "120x600_as";
+google_ad_type = "text";
+//2007-05-30: SDMG_Sky
+google_ad_channel = "4350521531";
+google_color_border = "FFFFFF";
+google_color_bg = "FFFFFF";
+google_color_link = "003399";
+google_color_text = "000000";
+google_color_url = "003399";
+//-->
+</script>
+<script type="text/javascript" src="makedepend_files/show_ads.js">
+</script><iframe name="google_ads_frame" src="makedepend_files/ads.html" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" frameborder="0" height="600" scrolling="no" width="120"></iframe><img src="makedepend_files/TypecountClientType2AdID185678FlightID110062TargetID2781Site.gif" border="0" height="1" width="1">
+
+
+ </div>
+ <br>
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=tile&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=97817&amp;AdID=161784&amp;TargetID=2780&amp;Segments=3108,3250,3448,4875,5387,5471,5751&amp;Targets=2625,2780,2878,4298&amp;Values=34,46,51,63,77,87,91,102,140,205,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.ddj.com/newsletters/"><img src="makedepend_files/newsletter125x125.gif" alt="" border="0" height="125" width="125"></a><img src="makedepend_files/TypecountClientType2AdID161784FlightID97817TargetID2780SiteI.gif" border="0" height="1" width="1">
+
+
+ </td>
+</tr>
+</tbody></table>
+<!-- IMU AD-->
+
+
+ </td>
+ </tr>
+ </tbody></table>
+
+<img src="makedepend_files/rule.gif" alt="" align="left" border="0" height="1" hspace="5" vspace="10" width="900">
+<br clear="left">
+<!-- AD BANNER AT BOTTOM -->
+<div id="BottomAdContainer">
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=bottom&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=107499&amp;AdID=178983&amp;TargetID=1251&amp;Segments=1551,3108,3317,3448,4875,5470,5750,5766,7712,7718,11862&amp;Targets=1251,2625,2878,4879&amp;Values=34,46,51,63,77,87,91,102,140,204,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.ddjresources.com/silverlight"><img src="makedepend_files/Silverlight_Cntr_Leadr3.jpg" alt="" border="0" height="90" width="728"></a><img src="makedepend_files/TypecountClientType2AdID178983FlightID107499TargetID1251Site.gif" border="0" height="1" width="1">
+
+
+</div>
+<!-- /AD BANNER AT BOTTOM -->
+<div class="LColMargin">
+ <table class="elfixo" border="0" cellpadding="0" cellspacing="0" width="900">
+ <tbody><tr>
+ <td valign="top" width="600">
+ </td>
+ <td valign="top" width="300">
+
+ <!-- put Microsoft Resource Center box on article pages -->
+
+
+
+
+ <table border="0" cellpadding="1"><tbody><tr><td>
+ <!-- http://as.cmpnet.com/html.ng/affiliate=ddj&pagepos=mslink1&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=75640&amp;AdID=125688&amp;TargetID=2878&amp;Segments=3108,3448,4875,7953&amp;Targets=2625,2878&amp;Values=34,46,51,63,77,87,91,102,140,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3556,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.cmp.com"><img src="makedepend_files/iopsblank.gif" alt="" border="0" height="1" width="1"></a><img src="makedepend_files/TypecountClientType2AdID125688FlightID75640TargetID2878SiteI.gif" border="0" height="1" width="1">
+
+ </td></tr></tbody></table>
+
+
+
+
+
+ </td>
+ </tr>
+ </tbody></table>
+</div>
+<img src="makedepend_files/rule.gif" alt="" align="left" border="0" height="1" hspace="5" vspace="8" width="900">
+<br clear="left">
+
+<!-- COPYRIGHT AND LINKS -->
+<div id="COPYRIGHTContainer">
+ <span class="tenBOLD">
+ <div class="Bspace8">
+ <!--<a href="http://www.sdmediagroup.com/reg/faq.html" class=nineBOLD>What are the <span class="green9">green links</span>?</a> |-->
+ <!-- The value for parameter category is the same as deptID. It has value when the blog pages are called. -->
+
+
+
+
+
+
+
+
+
+ <a href="http://www.ddj.com/rss/openSource.xml;jsessionid=4CBKBE5CTME2YQSNDLOSKH0CJUNN2JVN"> <img src="makedepend_files/rss.jpg" align="absbottom" border="0" hspace="3" vspace="0"></a> |
+ <!-- <a href="/sitemap/" class="tenBOLD">Site map</a> -->
+ </div>
+ </span> <span class="tenBOLD">
+ <div class="Bspace8">
+ <a href="http://www.think-services.com/" target="new" class="tenBOLD">© 2008 Think Services</a>,
+ <a href="http://www.unitedbusinessmedia.com/ubm/sitetools/privacy/" class="tenBOLD">Privacy Policy</a>,
+ <a href="http://www.cmp.com/delivery/privacy.html#california" class="tenBOLD">Your California Privacy Rights</a>,
+ <a href="http://www.unitedbusinessmedia.com/ubm/sitetools/disclaimer/" class="tenBOLD">Terms of Service</a>,
+ <a href="http://www.unitedbusinessmedia.com/" class="tenBOLD">United Business Media</a> <br>
+ Comments about the web site: <a href="mailto:webmaster@ddj.com" class="tenBOLD">webmaster@ddj.com</a>
+ </div>
+ Related Sites:
+ <a href="http://www.dotnetjunkies.com/" target="_blank" class="tenBOLD">DotNetJunkies</a>,
+ <a href="http://www.sdexpo.com/" target="_blank" class="tenBOLD">SD Expo</a>,
+ <a href="http://www.sqljunkies.com/" target="_blank" class="tenBOLD">SqlJunkies</a></span>
+</div>
+<!-- /COPYRIGHT AND LINKS -->
+
+
+
+
+ <!-- http://as.cmpnet.com/html.ng/pagepos=toptile&affiliate=ddj&site=sdmg&country=switzerland&server=atg&target=/linux-open-source/184406479 -->
+<a target="_parent" href="http://as.cmpnet.com/event.ng/Type=click&amp;FlightID=75634&amp;AdID=125682&amp;TargetID=2625&amp;Segments=3108,3448,4875,7725,8024,8049,8892,8904&amp;Targets=2625,2878,5537&amp;Values=34,46,51,63,77,87,91,102,140,265,442,656,944,945,975,1311,1603,1716,1765,1767,1785,1925,1970,2179,2299,2310,2326,2352,2678,2735,2761,2767,2862,2942,3052,3082,3392,3890,3904,4080,4799,6193,6293,6332,6392,6393,6422&amp;RawValues=IP,66.77.24.210,&amp;Redirect=http://www.cmp.com"><img src="makedepend_files/iopsblank.gif" alt="" border="0" height="1" width="1"></a><img src="makedepend_files/TypecountClientType2AdID125682FlightID75634TargetID2625SiteI.gif" border="0" height="1" width="1">
+
+
+<!-- start Vibrant Media IntelliTXT script section -->
+<script type="text/javascript" src="makedepend_files/ddj_edit.js"></script><script language="javascript" type="text/javascript" src="makedepend_files/front.js"></script>
+<!-- end Vibrant Media IntelliTXT script section -->
+</body></html> \ No newline at end of file
diff --git a/makefiles/clean.mk b/makefiles/clean.mk
new file mode 100644
index 0000000..711d6ae
--- /dev/null
+++ b/makefiles/clean.mk
@@ -0,0 +1,29 @@
+# cleans up directories
+#
+# requires:
+# - BINS, OBJS, CPPOBJS, BIN_OBJS
+# - CMODULES, CPPMODULES
+# - SUBDIRS
+#
+# provides:
+# - target: clean
+# - target: distclean
+
+.PHONY: clean local_clean
+clean: local_clean
+ @test -z "$(SUBDIRS)" || ( set -e; for d in $(SUBDIRS)""; do \
+ (set -e; $(MAKE) -C $$d clean || exit 1); done)
+ -@rm *.bak 2>/dev/null
+ -@rm *~ 2>/dev/null
+ -@rm *.d port/*.d 2>/dev/null
+ -@rm $(BINS) 2>/dev/null
+ -@rm $(OBJS) $(CPPOBJS) $(BIN_OBJS) 2>/dev/null
+ -@rm exec/*
+ -@rm *.core
+ -@rm $(CMODULES) $(CPPMODULES)
+ -@rm $(CMODULES .o=.d) $(CPPMODULES .o=.d)
+
+.PHONY: distclean local_distclean
+distclean: clean local_distclean
+ @test -z "$(SUBDIRS)" || ( set -e; for d in $(SUBDIRS)""; do \
+ (set -e; $(MAKE) -C $$d distclean || exit 1); done)
diff --git a/makefiles/compiler.mk b/makefiles/compiler.mk
new file mode 100644
index 0000000..a0176a5
--- /dev/null
+++ b/makefiles/compiler.mk
@@ -0,0 +1,174 @@
+# sets compiler settings
+#
+# requires:
+# - INCLUDE_DIRS
+#
+# provides:
+# - BIN_OBJS: the object files we need for the binaries (containing the main)
+#
+
+# TODO: sort out if we need so many flags :-)
+# TODO: why don't specify the CFLAGS directly :-)
+# - TODO: handle also -g -O2
+# -DTRACEDEBUG "$(DEBUGLEVEL)" "TRACE
+# -DDEBUG $(DEBUGLEVEL)" "DEBUG"
+# when nothing is defined, define -DNDEBUG (release)
+# TODO: do we use precompiled header files feature of gcc (also -Winvalid-pch)
+
+# debug/tracing flags
+ifeq "$(DEBUGLEVEL)" "TRACE"
+DEBUGLEVELFLAGS=-DTRACEDEBUG -DDEBUG
+else
+ifeq "$(DEBUGLEVEL)" "DEBUG"
+DEBUGLEVELFLAGS=-DDEBUG
+else
+DEBUGLEVELFLAGS=-DNDEBUG
+endif
+endif
+
+# -Wswitch-default: not good for switches with enums
+# -Wsystem-headers: bad idea, as header files are usually happily broken :-)
+# -Wtraditional: we don't want to program tradition K&R C anymore!
+# -Wunsafe-loop-optimizations: ??
+# -Wno-attributes, -Wmissing-format-attribute: ?? later
+# -Wpacked -Wpadded: ?? very questionable
+# -Wunreachable-code: doesn't work
+# -Wno-div-by-zero: we get NaN and friend over macros, so need for funny tricks :-)
+# -Wstrict-overflow=5 is relatively new, later maybe
+# -fstack-protector or -fstack-protector-all: should be used, but U
+# have currently big problems to get it around compiler gcc and -lssl
+# probing! FIXME later
+# -fstack-protector-all: does something funny to the shared objects..
+# -Wstack-protector makes no sense without SSP
+# everything implied by -Wall is not explicitly specified (gcc 4.2.3)
+
+# compilation flags and compilers
+COMMON_COMPILE_FLAGS = \
+ -g -O2 -D_REENTRANT \
+ -fstrict-aliasing \
+ -pedantic -Wall -Werror \
+ -Wunused -Wno-import \
+ -Wformat -Wformat-y2k -Wformat-nonliteral -Wformat-security -Wformat-y2k \
+ -Wswitch-enum -Wunknown-pragmas -Wfloat-equal \
+ -Wundef -Wshadow -Wpointer-arith \
+ -Wcast-qual -Wcast-align \
+ -Wwrite-strings -Wconversion -Waggregate-return \
+ -Wmissing-noreturn \
+ -Wno-multichar -Wparentheses -Wredundant-decls \
+ -Winline \
+ -Wdisabled-optimization \
+ $(INCLUDE_DIRS)
+
+ifeq "$(GCC_MAJOR_VERSION)" "4"
+COMMON_COMPILE_FLAGS += \
+ -Wfatal-errors -Wmissing-include-dirs -Wvariadic-macros \
+ -Wvolatile-register-var \
+ -Wstrict-aliasing=2 -Wextra -Winit-self
+endif
+
+ifeq "$(GCC_MAJOR_VERSION)" "3"
+
+# gcc 3.3, testend on OpenBSD 4.2
+ifeq "$(GCC_MINOR_VERSION)" "3"
+COMMON_COMPILE_FLAGS += \
+ -W
+endif
+
+# gcc 3.4, not tested yet
+ifeq "$(GCC_MINOR_VERSION)" "4"
+COMMON_COMPILE_FLAGS += \
+ -Wstrict-aliasing=2 -Wextra -Winit-self
+endif
+
+endif
+
+COMPILE_FLAGS = \
+ $(COMMON_COMPILE_FLAGS) \
+ -std=c99 \
+ -Wnonnull \
+ -Wbad-function-cast -Wstrict-prototypes \
+ -Wmissing-prototypes -Wmissing-declarations \
+ -Wnested-externs
+
+# gcc 4.x
+ifeq "$(GCC_MAJOR_VERSION)" "4"
+COMPILE_FLAGS += \
+ -Wc++-compat -Wdeclaration-after-statement -Wold-style-definition
+endif
+
+ifeq "$(GCC_MAJOR_VERSION)" "3"
+
+# gcc 3.4, not tested yet
+ifeq "$(GCC_MINOR_VERSION)" "4"
+COMPILE_FLAGS += \
+ -Wdeclaration-after-statement -Wold-style-definition
+endif
+
+# gcc 3.3, testend on OpenBSD 4.2
+ifeq "$(GCC_MINOR_VERSION)" "3"
+#COMPILE_FLAGS += \
+# -Wdeclaration-after-statement
+endif
+
+
+endif
+
+CCPP_COMPILE_FLAGS = \
+ $(COMMON_COMPILE_FLAGS) \
+ -std=c++98
+
+# gcc 4.x
+ifeq "$(GCC_MAJOR_VERSION)" "4"
+CCPP_COMPILE_FLAGS += \
+ -Wno-invalid-offsetof
+endif
+
+ifeq "$(GCC_MAJOR_VERSION)" "3"
+
+# gcc 3.4, not tested yet
+ifeq "$(GCC_MINOR_VERSION)" "4"
+CCPP_COMPILE_FLAGS += \
+ -Wno-invalid-offsetof
+endif
+
+# gcc 3.3, testend on OpenBSD 4.2
+ifeq "$(GCC_MINOR_VERSION)" "3"
+#CCPP_COMPILE_FLAGS += \
+# -Wdeclaration-after-statement
+endif
+
+endif
+
+CFLAGS = $(COMPILE_FLAGS) $(PLATFORM_COMPILE_FLAGS) $(DEBUGLEVELFLAGS)
+CCPPFLAGS = $(CCPP_COMPILE_FLAGS) $(PLATFORM_COMPILE_FLAGS) $(DEBUGLEVELFLAGS)
+CC = gcc
+CCPP = g++
+
+LDFLAGS =
+LIBS = $(LIBS_DL) $(LIBS_SSP)
+LINK = $(CC)
+CCPP_LINK = $(CCPP)
+
+%.o : %.c
+ $(CC) -c -o $@ $(CFLAGS) $<
+
+%.o : %.cc
+ $(CCPP) -c -o $@ $(CCPPFLAGS) $<
+
+%$(EXE): %.o $(OBJS)
+ $(LINK) -o $@ $(LIBS) $(OBJS) $<
+
+%.sho : %.c
+ $(CC) -c -o $@ -fPIC -DSHARED $(CFLAGS) $<
+
+%$(SO) : %.sho $(OBJS)
+ $(LINK) -shared -o $@ $(LDFLAGS) $(LIBS) $(OBJS) $<
+
+%.sho++ : %.cc
+ $(CCPP) -c -o $@ -fPIC -DSHARED $(CCPPFLAGS) $<
+
+%$(SO) : %.sho++ $(OBJS) $(CPPOBJS)
+ $(CCPP_LINK) -shared -o $@ $(LDFLAGS) $(LIBS) $(OBJS) $(CPPOBJS) $<
+
+BIN_OBJS = $(BINS:$(EXE)=.o)
+
diff --git a/makefiles/depend.mk b/makefiles/depend.mk
new file mode 100644
index 0000000..2ff7bd5
--- /dev/null
+++ b/makefiles/depend.mk
@@ -0,0 +1,29 @@
+# provides generic rules for C/C++ dependeny generation using
+# 'makedepend', 'gcc -MM' or similar mechanisms
+#
+# requires:
+# - compilers CC and CCPP
+# - INCLUDEDIRS
+# - OBJS, CPP_OBJS and BIN_OBJS
+#
+# provides:
+# - included dependency files
+#
+# author: Andreas Baumann, abaumann at yahoo dot com
+
+MAKEDEPEND = $(CC) -MM $(INCLUDE_DIRS)
+CCPP_MAKEDEPEND = $(CCPP) -MM $(INCLUDE_DIRS)
+
+%.d : %.c
+ @echo Generating dependencies for $<
+ @$(MAKEDEPEND) $(CFLAGS) $< | \
+ sed "s,\($*\.o\)[ :]*\(.*\),$@ : $$\(wildcard \2\)\&\&\&\1 : \2,g" | tr -s '&' "\n" > $@
+
+%.d : %.cc
+ @echo Generating dependencies for $<
+ @$(CCPP_MAKEDEPEND) $(CCPPFLAGS) $< | \
+ sed "s,\($*\.o\)[ :]*\(.*\),$@ : $$\(wildcard \2\)\&\&\&\1 : \2,g" | tr -s '&' "\n" > $@
+
+-include $(OBJS:.o=.d)
+-include $(CPP_OBJS:.o=.d)
+-include $(BIN_OBJS:.o=.d)
diff --git a/makefiles/guess_env b/makefiles/guess_env
new file mode 100755
index 0000000..f11ef5a
--- /dev/null
+++ b/makefiles/guess_env
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`
+UNAME_RELEASE=`(uname -r) 2>/dev/null`
+UNAME_VERSION=`(uname -v) 2>/dev/null`
+UNAME_MACHINE=`(uname -m) 2>/dev/null`
+
+# operating system and major, minor version, more should not be necessary
+case "$UNAME_SYSTEM.$UNAME_RELEASE" in
+ Linux*) PLATFORM=LINUX
+ OS_MAJOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 1`
+ OS_MINOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 2`
+ LIBS_DL='-ldl'
+ ;;
+
+ FreeBSD*) PLATFORM=FREEBSD
+ OS_MAJOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 1`
+ OS_MINOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 2 | cut -d - -f 1`
+ LIBS_DL=
+ LIBS_SSP=
+ ;;
+
+ OpenBSD*) PLATFORM=OPENBSD
+ OS_MAJOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 1`
+ OS_MINOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 2 | cut -d - -f 1`
+ LIBS_DL=
+ LIBS_SSP=
+ ;;
+
+ SunOS*) PLATFORM=SUNOS
+ OS_MAJOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 1`
+ OS_MINOR_VERSION=`echo $UNAME_RELEASE | cut -d . -f 2`
+ LIBS_DL='-ldl'
+ LIBS_SSP=
+ ;;
+
+ CYGWIN_NT*) PLATFORM=CYGWIN
+ _tmp=`echo $UNAME_SYSTEM | cut -d - -f 2`
+ OS_MAJOR_VERSION=`echo $_tmp | cut -d . -f 1`
+ OS_MINOR_VERSION=`echo $_tmp | cut -d . -f 2`
+ LIBS_SSP=
+ ;;
+
+ *)
+ PLATFORM=UNKNOWN
+ echo "Unknown platform '$UNAME_SYSTEM#$UNAME_RELEASE'"
+ exit 1
+esac
+
+# the architecture
+case "$UNAME_MACHINE" in
+ i*86*) ARCH=x86
+ ;;
+
+ sun4u) ARCH=sun4u
+ ;;
+
+ *) ARCH=UNKNOWN
+ echo "Unknown architecture '$UNAME_MACHINE'"
+ exit 1
+
+esac
+
+# the compiler and version
+GCC_VERSION=`gcc -dumpversion`
+GCC_MAJOR_VERSION=`echo $GCC_VERSION | cut -d . -f 1`
+GCC_MINOR_VERSION=`echo $GCC_VERSION | cut -d . -f 2`
+
+case "$1" in
+ --platform) echo $PLATFORM
+ ;;
+
+ --os-major-version) echo $OS_MAJOR_VERSION
+ ;;
+
+ --os-minor-version) echo $OS_MINOR_VERSION
+ ;;
+
+ --arch) echo $ARCH
+ ;;
+
+ --libs-dl) echo $LIBS_DL
+ ;;
+
+ --libs-ssl) echo $LIBS_SSL
+ ;;
+
+ --gcc-major-version) echo $GCC_MAJOR_VERSION
+ ;;
+
+ --gcc-minor-version) echo $GCC_MINOR_VERSION
+ ;;
+
+ *)
+ cat <<EOF
+ARCH = $ARCH
+PLATFORM = $PLATFORM
+OS_MAJOR_VERSION = $OS_MAJOR_VERSION
+OS_MINOR_VERSION = $OS_MINOR_VERSION
+LIBS_DL = $LIBS_DL
+LIBS_SSL = $LIBS_SSL
+GCC_MAJOR_VERSION = $GCC_MAJOR_VERSION
+GCC_MINOR_VERSION = $GCC_MINOR_VERSION
+EOF
+ ;;
+esac
diff --git a/makefiles/platform.mk b/makefiles/platform.mk
new file mode 100644
index 0000000..175e25f
--- /dev/null
+++ b/makefiles/platform.mk
@@ -0,0 +1,34 @@
+# sets e. g. to LINUX, OS_MAJOR_VERSION to 2 and OS_MINOR_VERSION to 6
+# by calling the 'guess_env' shell script, where the actual probing happens
+# Also sets PLATFORM_COMPILE_FLAGS to be included when compiling C/C++ code
+#
+# requires:
+# - TOPDIR
+#
+# provides:
+# - PLATFORM
+# - OS_MAJOR_VERSION
+# - OS_MINOR_VERSION
+# - PLATFORM_COMPILE_FLAGS
+# - EXE
+# - SO
+#
+# author: Andreas Baumann, abaumann at yahoo dot com
+
+PLATFORM = $(shell $(TOPDIR)/makefiles/guess_env --platform)
+OS_MAJOR_VERSION = $(shell $(TOPDIR)/makefiles/guess_env --os-major-version)
+OS_MINOR_VERSION = $(shell $(TOPDIR)/makefiles/guess_env --os-minor-version)
+
+PLATFORM_COMPILE_FLAGS = \
+ -D$(PLATFORM) \
+ -DOS_MAJOR_VERSION=$(OS_MAJOR_VERSION) \
+ -DOS_MINOR_VERSION=$(OS_MINOR_VERSION)
+
+LIBS_DL = $(shell $(TOPDIR)/makefiles/guess_env --libs-dl)
+LIBS_SSP = $(shell $(TOPDIR)/makefiles/guess_env --libs-ssl)
+
+EXE =
+SO = .so
+
+GCC_MAJOR_VERSION = $(shell $(TOPDIR)/makefiles/guess_env --gcc-major-version $(CC))
+GCC_MINOR_VERSION = $(shell $(TOPDIR)/makefiles/guess_env --gcc-minor-version $(CC))
diff --git a/makefiles/sub.mk b/makefiles/sub.mk
new file mode 100644
index 0000000..644dc3c
--- /dev/null
+++ b/makefiles/sub.mk
@@ -0,0 +1,20 @@
+# makefile for a sub package
+#
+# requires:
+# - TOPDIR
+# - SUBDIRS
+# - INCLUDE_DIRS
+#
+# provides:
+# - target: all targets
+
+-include $(TOPDIR)/makefiles/platform.mk
+-include $(TOPDIR)/makefiles/compiler.mk
+
+.PHONY: all $(SUBDIRS) local_all
+all: local_all $(OBJS) $(CPPOBJS) $(BIN_OBJS) $(BINS) $(CMODULES) $(CPPMODULES)
+ @test -z "$(SUBDIRS)" || ( set -e; for d in $(SUBDIRS)""; do \
+ (set -e; $(MAKE) -C $$d all || exit 1); done)
+
+-include $(TOPDIR)/makefiles/depend.mk
+-include $(TOPDIR)/makefiles/clean.mk
diff --git a/makefiles/top.mk b/makefiles/top.mk
new file mode 100644
index 0000000..98993f7
--- /dev/null
+++ b/makefiles/top.mk
@@ -0,0 +1,30 @@
+# top-level makefile for a package
+#
+# requires:
+# - TOPDIR
+# - SUBDIRS
+#
+# provides:
+# - target 'all'
+# - target 'clean'
+# - target 'distclean'
+# - target 'test'
+
+.PHONY: all
+all:
+ @test -z "$(SUBDIRS)" || ( set -e; for d in $(SUBDIRS)""; do \
+ (set -e; $(MAKE) -C $$d all || exit 1); done)
+
+.PHONY: clean
+clean:
+ @test -z "$(SUBDIRS)" || ( set -e; for d in $(SUBDIRS)""; do \
+ (set -e; $(MAKE) -C $$d clean || exit 1); done)
+
+.PHONY: distclean
+distclean: clean
+ test -z "$(SUBDIRS)" || ( set -e; for d in $(SUBDIRS)""; do \
+ (set -e; $(MAKE) -C $$d distclean || exit 1); done)
+
+.PHONY: test
+test: all
+ @$(MAKE) -C tests test
diff --git a/src/GNUmakefile b/src/GNUmakefile
new file mode 100644
index 0000000..0ed8c1b
--- /dev/null
+++ b/src/GNUmakefile
@@ -0,0 +1,41 @@
+TOPDIR = ..
+
+SUBDIRS =
+
+INCLUDE_DIRS = -I.
+
+BINS = \
+ testd
+
+OBJS = \
+ port/lockf.o \
+ port/snprintf.o \
+ cmdline.o \
+ log.o \
+ signals.o \
+ pidfile.o \
+ daemon.o
+
+
+-include $(TOPDIR)/makefiles/sub.mk
+
+# ABa: currently a special rule for cmdline.c as gengetopt is not
+# completly fixed yet
+testd.d : cmdline.h
+cmdline.h : daemon.ggo
+ gengetopt --include-getopt -i $<
+cmdline.c : daemon.ggo
+ gengetopt --include-getopt -i $<
+
+cmdline.o : cmdline.c cmdline.h
+ $(CC) -c -o $@ $<
+
+local_all: cmdline.h
+
+local_clean:
+ -@rm port/*.bak 2>/dev/null
+ -@rm port/*~ 2>/dev/null
+ -@rm port/*.d 2>/dev/null
+
+local_distclean:
+ -@rm cmdline.c cmdline.h
diff --git a/src/daemon.c b/src/daemon.c
new file mode 100644
index 0000000..a635053
--- /dev/null
+++ b/src/daemon.c
@@ -0,0 +1,551 @@
+#include "port/stdio.h" /* for snprintf */
+#include "port/string.h" /* for strdup */
+#include "port/limits.h" /* for PATH_MAX */
+#include "port/noreturn.h" /* for NORETURN */
+
+#include "daemon.h"
+#include "log.h"
+#include "signals.h"
+#include "pidfile.h"
+
+#include <sys/types.h> /* for pid_t, ssize_t, off_t */
+#include <unistd.h> /* for getppid, geteuid, fork,
+ chdir, pipe, sysconf */
+#include <stdlib.h> /* for malloc, free */
+#include <errno.h> /* for errno */
+#include <sys/stat.h> /* for umask */
+#include <fcntl.h> /* for O_RDWR */
+#include <grp.h> /* for getgrnam, setgid */
+#include <pwd.h> /* for getpwnam, setuid */
+
+static int exit_code_pipe[2] = { -1, -1 };
+
+int daemon_parent_pipe[2] = { -1, -1 };
+
+#define TERMINATE_EXIT_CODE 99
+#define TERMINATE_PARENT 98
+
+struct daemon_t {
+ daemon_params_t params; /**< the parameters for creation */
+ error_t error; /**< error of last start */
+ struct pidfile_t pidfile; /**< pid/lock file of the daemon */
+ struct group *groupent; /**< group entry */
+ struct passwd *userent; /**< password entry */
+};
+
+daemon_p daemon_new( daemon_params_t params,
+ error_t *error ) {
+ daemon_p d;
+
+ d = (struct daemon_t *)malloc( sizeof ( struct daemon_t ) );
+ if( d == NULL ) {
+ *error = ERR_OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ d->error = -1;
+ d->params = params;
+
+ if( params.daemon_name == NULL ) {
+ *error = ERR_INVALID_VALUE;
+ return NULL;
+ }
+
+ if( params.pid_filename == NULL ) {
+ pidfile_set_from_daemon_name( &d->pidfile, params.daemon_name );
+ } else {
+ pidfile_set_from_filename( &d->pidfile, params.pid_filename );
+ }
+
+ *error = OK;
+ return d;
+}
+
+void daemon_free( daemon_p d ) {
+ free( d );
+ d = NULL;
+}
+
+static error_t close_fd( int fd ) {
+ if( close( fd ) < 0 ) {
+ switch( errno ) {
+ case EBADF:
+ /* skip */
+ break;
+
+ default:
+ LOG( LOG_EMERG, "Error while closing file descriptor %d: %s (%d)",
+ fd, strerror( errno ), errno );
+ return ERR_INTERNAL;
+ }
+ }
+
+ return OK;
+}
+
+static error_t daemon_close_standard_fds( void ) {
+ error_t error;
+
+ if( ( error = close_fd( STDIN_FILENO ) ) != OK ) return error;
+ if( ( error = close_fd( STDOUT_FILENO ) ) != OK ) return error;
+ if( ( error = close_fd( STDERR_FILENO ) ) != OK ) return error;
+
+ return OK;
+
+}
+
+static error_t daemon_close_all_fds( void ) {
+ long nof_files;
+ int i;
+ error_t error;
+
+ nof_files = sysconf( _SC_OPEN_MAX );
+ if( nof_files < 0 ) {
+ LOG( LOG_EMERG, "Unable to retrieve maximal number of files: %s (%d)",
+ strerror( errno ), errno );
+ return ERR_INTERNAL;
+ }
+ LOG( LOG_DEBUG, "Closing all filedescriptors up to %ld", nof_files );
+
+ for( i = 0; i < nof_files; i++ ) {
+ if( ( i == STDIN_FILENO ) ||
+ ( i == STDOUT_FILENO ) ||
+ ( i == STDERR_FILENO ) ||
+ ( i == daemon_signal_pipe[0] ) ||
+ ( i == daemon_signal_pipe[1] ) ||
+ ( i == daemon_parent_pipe[0] ) ||
+ ( i == daemon_parent_pipe[1] ) ||
+ ( i == exit_code_pipe[0] ) ||
+ ( i == exit_code_pipe[1] ) ) {
+ continue;
+ }
+ if( ( error = close_fd( i ) ) != OK ) return error;
+ }
+
+ return OK;
+}
+
+static error_t open_null_fd( int must_fd, int flags ) {
+ int fd;
+
+ fd = open( "/dev/null", flags );
+ if( fd < 0 ) {
+ LOG( LOG_EMERG, "Unable to open fd %d as /dev/null: %s (%d)",
+ must_fd, strerror( errno ), errno );
+ return ERR_INTERNAL;
+ }
+ if( fd != must_fd ) {
+ LOG( LOG_EMERG, "Something is wrong with the file descriptors (expecting %d,got %d)!",
+ must_fd, fd );
+ return ERR_PROGRAMMING;
+ }
+
+ return OK;
+}
+
+static void atomar_write( int fd, void *data, size_t data_len ) {
+ ssize_t res;
+
+atomar_write_again:
+ res = write( fd, data, data_len );
+ if( res < 0 ) {
+ if( errno == EINTR ) goto atomar_write_again;
+ LOG( LOG_EMERG, "Error in atomar write to fd %d: %s (%d)",
+ fd, strerror( errno ), errno );
+ } else if( (size_t)res != data_len ) {
+ LOG( LOG_EMERG, "Unexpected number of octets %zd in atomar write to fd %d (expected %zd)",
+ res, fd, data_len );
+ }
+}
+
+static void atomar_read( int fd, void *data, size_t data_len ) {
+ ssize_t res;
+
+atomar_read_again:
+ res = read( fd, data, data_len );
+ if( res < 0 ) {
+ if( errno == EINTR ) goto atomar_read_again;
+ LOG( LOG_EMERG, "Error in atmoar read from fd %d: %s (%d)",
+ fd, strerror( errno ), errno );
+ } else if( (size_t)res != data_len ) {
+ LOG( LOG_EMERG, "Unexpected number of octets %zd in atomar read from fd %d (expected %zd)",
+ res, fd, data_len );
+ }
+}
+
+error_t daemon_start( daemon_p d ) {
+ pid_t pid;
+ mode_t mode;
+ error_t error;
+ int exit_code;
+
+ /* Our parent is already the init process (which has pid 1 by
+ * convention), we are already a daemon. This is a programming
+ * error..
+ */
+ if( getppid( ) == 1 ) {
+ LOG( LOG_EMERG, "Already running as daemon!" );
+ return( d->error = ERR_PROGRAMMING );
+ }
+
+ /* Are we starting as root? If not, bail out, we need root
+ * permissions because we have to listen to ports below 1024
+ * or we must open logfiles/pidfiles with proper permissions.
+ * Also we must change to the proper running user/group and
+ * we must be root (uid 0 by convention) for that.
+ */
+ if( geteuid( ) != 0 ) {
+ LOG( LOG_EMERG, "Unable to start daemon as not root user!" );
+ return( d->error = ERR_INVALID_STATE );
+ }
+
+ /* We fork so SIGCHLD signals get to the parent and grandfather.
+ * We don't want that and we are installing empty signal handlers.
+ */
+#if defined( SIGCHLD ) && defined( SIGCLD )
+#if SIGCLD != SIGCHLD
+ signal_install_empty( SIGCLD, 0 );
+ signal_install_empty( SIGCHLD, 0 );
+#else
+#if defined( SIGCHLD )
+ signal_install_empty( SIGCHLD, 0 );
+#endif
+#if defined( SIGCLD )
+ signal_install_empty( SIGCLD, 0 );
+#endif
+#endif
+#endif
+
+ /* first pipe communicates from parent of daemon and daemon
+ * itself back to the grand-parent (exit codes).
+ */
+ if( pipe( exit_code_pipe ) < 0 ) {
+ LOG( LOG_EMERG, "Unable to create exit code pipe: %s (%d)",
+ strerror( errno ), errno );
+ return ( d->error = ERR_INTERNAL );
+ }
+ LOG( LOG_DEBUG, "Created exit code pipe (%d,%d)", exit_code_pipe[0], exit_code_pipe[1] );
+
+ /* first fork: make sure we are no longer process group leader.
+ * So we can get our own process group leader by calling setsid
+ */
+ switch( ( pid = fork( ) ) ) {
+ case -1:
+ /* error */
+ LOG( LOG_EMERG, "Unable to fork the first time: %s", strerror( errno ) );
+ return ( d->error = ERR_INTERNAL );
+
+ case 0:
+ /* the child becomes the daemon */
+ LOG( LOG_DEBUG, "First fork reached" );
+ break;
+
+ default:
+ /* the parent waits till it gets feedback from
+ * the child (error pipe)
+ */
+ /* TODO: wait for some time for correct exit
+ * code from the error pipe
+ */
+ LOG( LOG_DEBUG, "Parent after first fork: child is %d", pid );
+ return ( d->error = TERMINATE_EXIT_CODE );
+ }
+
+ /* second pipe communicates from the daemon back to its parent
+ * (for termination and cleanup which can only be done as root)
+ */
+ if( pipe( daemon_parent_pipe ) < 0 ) {
+ LOG( LOG_EMERG, "Unable to create parent pipe: %s (%d)",
+ strerror( errno ), errno );
+ return ( d->error = ERR_INTERNAL );
+ }
+ LOG( LOG_DEBUG, "Created parent pipe (%d,%d)", daemon_parent_pipe[0], daemon_parent_pipe[1] );
+
+ /* Put the first child in it's own process group and finally detach it
+ * from its controlling terminal. This ensure we don't get funny
+ * signals which could kill the daemon.
+ */
+ if( setsid( ) < 0 ) {
+ /* should actually never fail */
+ LOG( LOG_EMERG, "Starting new process group session for the parent of the daemon failed: %s", strerror( errno ) );
+ return ERR_INTERNAL;
+ }
+
+ /* We fork so SIGCHLD signals get to the parent and grandfather.
+ * We don't want that and we are installing empty signal handlers.
+ */
+#if defined( SIGCHLD ) && defined( SIGCLD )
+#if SIGCLD != SIGCHLD
+ signal_install_empty( SIGCLD, 0 );
+ signal_install_empty( SIGCHLD, 0 );
+#else
+#if defined( SIGCHLD )
+ signal_install_empty( SIGCHLD, 0 );
+#endif
+#if defined( SIGCLD )
+ signal_install_empty( SIGCLD, 0 );
+#endif
+#endif
+#endif
+
+ /* Now that the parent is process group leader, fork again. This
+ * way the final child can not inherit a controlling terminal
+ */
+ switch( ( pid = fork( ) ) ) {
+ case -1:
+ /* error */
+ LOG( LOG_EMERG, "Unable to fork the second time: %s", strerror( errno ) );
+ return ( d->error = ERR_INTERNAL );
+
+ case 0:
+ /* the child becomes the daemon */
+ LOG( LOG_DEBUG, "Second fork reached" );
+ break;
+
+ default:
+ (void)signal_install_handlers_parent( );
+ LOG( LOG_DEBUG, "Parent after second fork: child (and daemon) is %d", pid );
+ return ( d->error = TERMINATE_PARENT );
+ }
+
+ /* Now install signal handlers */
+ if( ( error = signal_initialize( ) ) != OK )
+ return ( d->error = error );
+ if( ( error = signal_install_handlers_daemon( ) ) != OK )
+ return ( d->error = error );
+
+ /* Put the daemon in it's own process group and finally detach it
+ * from its controlling terminal. This ensure we don't get funny
+ * signals which could kill the daemon.
+ */
+ if( setsid( ) < 0 ) {
+ /* should actually never fail */
+ LOG( LOG_EMERG, "Starting new process group for daemon session failed: %s", strerror( errno ) );
+ return ERR_INTERNAL;
+ }
+
+ /* Change to the root directory for several reasons:
+ * - all but the root directory we may want to unmount without
+ * stopping the daemon
+ * - we don't have write permissions there so a core dump
+ * would not result in DoSA
+ * - non-priviledged users can't do anything in the root
+ * directory (especially they can't write files there)
+ */
+ if( chdir( "/" ) < 0 ) {
+ LOG( LOG_EMERG, "Changing to root diretory failed: %s", strerror( errno ) );
+ return ERR_INTERNAL;
+ }
+ LOG( LOG_DEBUG, "Changed to root directory /" );
+
+ /* Change the umask to 0133 temporarily so we don't have to
+ * chmod the logfiles, pidfiles later.
+ *
+ * Create pid files with permissions 0644 (rw-r--r--)
+ */
+ mode = umask( 0133 );
+ LOG( LOG_DEBUG, "Switched umask from %04o to %04o", mode, 0133 );
+
+ /* check if another daemon is already running, if yes, bail out */
+ if( is_daemon_running( &d->pidfile, &pid, &error ) || ( error != OK ) ) {
+ if( error == OK ) {
+ LOG( LOG_EMERG, "Another daemon is already running with pid '%d', can't start!", pid );
+ }
+ return ERR_INTERNAL;
+ }
+
+ /* we loose logfile and syslog file descriptors anyway, so close
+ * them here */
+ closelogtosyslog( );
+ closelogtofile( );
+ closelogtostderr( );
+
+ /* close all filedescriptors */
+ if( daemon_close_all_fds( ) != OK )
+ return ERR_INTERNAL;
+
+ /* reopen the logs to the logfile and syslog (not stderr) */
+ reopenlogtosyslog( );
+ reopenlogtofile( );
+
+ /* create and lock the pidfile now, write pid of daemon into it */
+ if( pidfile_create( &d->pidfile ) != OK ) {
+ return ERR_INTERNAL;
+ }
+
+ /* Install the final permissions for new files. The reason is
+ * simple: We don't want to create files in a defective part
+ * of the daemon, especially third party code. We don't know
+ * what kind of plugins we gonna integrate..
+ *
+ * Make sure that no files can be written except the ones belonging
+ * to us (because now we are no longer root) and that no files can
+ * be created with executable permission (code injection!)
+ *
+ * New files always get the permission 640 (rw-r-----)
+ */
+ mode = umask( 0137 );
+ LOG( LOG_DEBUG, "Switched umask from %04o to %04o", mode, 0137 );
+
+ /* drop permissions to a non-priviledged user now */
+ if( ( d->params.group_name != NULL ) && ( d->params.user_name != NULL ) ) {
+ errno = 0;
+ d->groupent = getgrnam( d->params.group_name );
+ if( d->groupent == NULL ) {
+ if( errno == 0 ) {
+ LOG( LOG_EMERG, "No group '%s' found", d->params.group_name );
+ } else {
+ LOG( LOG_EMERG, "Unable to retrieve group information for group '%s': %s (%d)",
+ d->params.group_name, strerror( errno ), errno );
+ }
+ return ERR_INTERNAL;
+ }
+
+ errno = 0;
+ d->userent = getpwnam( d->params.user_name );
+ if( d->userent == NULL ) {
+ if( errno == 0 ) {
+ LOG( LOG_EMERG, "No user '%s' found", d->params.user_name );
+ } else {
+ LOG( LOG_EMERG, "Unable to retrieve user information for user '%s': %s (%d)",
+ d->params.user_name, strerror( errno ), errno );
+ }
+ return ERR_INTERNAL;
+ }
+
+ if( setgid( d->userent->pw_gid ) < 0 ) {
+ LOG( LOG_EMERG, "Setting unprivileged group failed: %s (%d)",
+ strerror( errno ), errno );
+ return ERR_INTERNAL;
+ }
+
+ if( setuid( d->userent->pw_uid ) < 0 ) {
+ LOG( LOG_EMERG, "Setting unprivileged user failed: %s (%d)",
+ strerror( errno ), errno );
+ return ERR_INTERNAL;
+ }
+
+ /* TODO: setsid and setting all groups of the user */
+ /* TODO: also check the permissions of the home dir
+ * of the unpriviledged user */
+
+ LOG( LOG_DEBUG, "Switched to user '%s' (%d) and group '%s' (%d)",
+ d->params.user_name, d->userent->pw_uid,
+ d->params.group_name, d->userent->pw_gid );
+ }
+
+ /* assign stdin/stdout/stderr again to something harmless
+ * (/dev/null). Do this as late as possible, so somebody
+ * starting the daemon by hand still gets error messages
+ * on stderr.
+ */
+ if( ( error = daemon_close_standard_fds( ) ) != OK ) return error;
+ if( ( error = open_null_fd( STDIN_FILENO, O_RDONLY ) ) != OK ) return error;
+ if( ( error = open_null_fd( STDOUT_FILENO, O_WRONLY ) ) != OK ) return error;
+ if( ( error = open_null_fd( STDERR_FILENO, O_WRONLY ) ) != OK ) return error;
+
+ /* signal the grand-parent process, that he can return 0 to
+ * the calling shell/script
+ */
+ exit_code = EXIT_SUCCESS;
+ atomar_write( exit_code_pipe[1], &exit_code, sizeof( int ) );
+
+ return ( d->error = OK );
+}
+
+NORETURN void daemon_exit( daemon_p d ) {
+ int exit_code;
+ int pid_file_fd;
+
+ LOG( LOG_DEBUG, "daemon_exit called with error %d", d->error );
+
+ switch( d->error ) {
+ case TERMINATE_EXIT_CODE:
+ /* wait here for exit code in exit_code_pipe
+ * so we can return the correct exit code to
+ * the calling script or shell
+ */
+ LOG( LOG_DEBUG, "Waiting on exit_code pipe for exit code" );
+
+ atomar_read( exit_code_pipe[0], &exit_code, sizeof( int ) );
+
+ LOG( LOG_DEBUG, "Terminating grand-parent of daemon with code %d (PID: %lu)",
+ exit_code, getpid( ) );
+
+ signal_terminate( );
+
+ /* Do not close file descriptors of the child!
+ * We are the father or grand-father process
+ */
+ _exit( exit_code );
+
+ case TERMINATE_PARENT:
+ /* TODO: handle houskeeping here which needs
+ * root rights (log rotation?)
+ */
+
+ /* wait for termination signal */
+ LOG( LOG_DEBUG, "Waiting on parent pipe for termination signal" );
+
+ atomar_read( daemon_parent_pipe[0], &pid_file_fd, sizeof( int ) );
+ LOG( LOG_DEBUG, "Parent got termination (pidfile fd: %d).. cleaning up now (PID: %lu)",
+ pid_file_fd, getpid( ) );
+
+ /* we need root permissions for that! */
+ d->pidfile.fd = pid_file_fd;
+ (void)pidfile_remove( &d->pidfile );
+
+ signal_terminate( );
+
+ LOG( LOG_DEBUG, "Terminating parent of daemon pid file fd %d (PID %lu)",
+ pid_file_fd, getpid( ) );
+
+ /* exit code is irrelevant here.. */
+ exit( EXIT_SUCCESS );
+
+ case OK:
+ /* close and unlock the pidfile here */
+ (void)pidfile_release( &d->pidfile );
+
+ /* This is the daemon terminating, signal the
+ * parent that we are terminating
+ */
+ atomar_write( daemon_parent_pipe[1], &d->pidfile.fd, sizeof( int ) );
+
+ signal_terminate( );
+
+ LOG( LOG_DEBUG, "Terminating daemon (PID: %lu)", getpid( ) );
+
+ /* exit code is irrelevant here */
+ _exit( EXIT_SUCCESS );
+
+ default:
+ /* no exit_code_pipe exists yet, we are before the
+ * first fork, so terminate with proper exit code
+ */
+ if( exit_code_pipe[1] == -1 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ /* This is an error case, communicate exit code back
+ * to grand-parent
+ */
+ exit_code = EXIT_FAILURE;
+ atomar_write( exit_code_pipe[1], &exit_code, sizeof( int ) );
+
+ /* also terminate the parent of the daemon */
+ atomar_write( daemon_parent_pipe[1], &d->pidfile.fd, sizeof( int ) );
+
+ signal_terminate( );
+
+ LOG( LOG_DEBUG, "Terminating daemon (PID: %lu)", getpid( ) );
+
+ /* exit code is irrelevant here */
+ _exit( EXIT_SUCCESS );
+ }
+
+ /* silence up some versions of the GCC compiler ("noreturn function returns"),
+ * because they errornously tread _exit as a return, which is clearly wrong
+ * (experienced with gcc 3.3.5 on OpenBSD 4.3) */
+ exit( EXIT_SUCCESS );
+}
diff --git a/src/daemon.ggo b/src/daemon.ggo
new file mode 100644
index 0000000..08c83cb
--- /dev/null
+++ b/src/daemon.ggo
@@ -0,0 +1,70 @@
+package "testd"
+version "0.0.1"
+usage "testd [options]"
+description "tests daemonizing on Unix without any functionality\n"
+
+section "Main Options"
+ option "foreground" f
+ "Do not daemonize, run in foreground, write to stdout/stderr"
+ optional
+
+ option "config-file" c
+ "The location of the configuration file of the daemon"
+ string typestr="file"
+ optional
+
+ option "debug" d
+ "Increase debug level (option can be given many times)"
+ optional multiple hidden
+
+ option "test" t
+ "Test the configuration without running the daemon"
+ optional
+
+section "Query Options"
+ option "list-modules" -
+ "List loaded modules"
+ optional hidden
+
+section "Daemon Options"
+ option "user" u
+ "User the daemon should run as"
+ string typestr="user"
+ optional hidden
+
+ option "group" g
+ "Group the daemon should run as"
+ string typestr="group"
+ optional hidden
+
+ option "pidfile" -
+ "Location of the pidfile (explicitly, normaly the system knows where to store them)"
+ string typestr="file"
+ optional hidden
+
+ option "syslog-facility" -
+ "System log facility to use for logging to system log"
+ optional hidden
+ typestr="facility"
+ values="KERN","USER","MAIL","DAEMON","AUTH","SYSLOG","LPR","NEWS","UUCP","CRON","AUTHPRIV","FTP"
+ default="DAEMON"
+
+ option "syslog-level" -
+ "Level for logging to system log"
+ optional hidden
+ typestr="level"
+ values="EMERG","ALERT","CRIT","ERR","WARNING","NOTICE","INFO","DEBUG","DEBUG1","DEBUG2","DEBUG3","DEBUG4","DEBUG5"
+ default="NOTICE"
+
+ option "logfile" -
+ "Name of a log file where to log to"
+ string typestr="file"
+ optional hidden
+
+ option "logfile-level" -
+ "Level for logging to the logfile"
+ optional hidden
+ typestr="level"
+ values="EMERG","ALERT","CRIT","ERR","WARNING","NOTICE","INFO","DEBUG","DEBUG1","DEBUG2","DEBUG3","DEBUG4","DEBUG5"
+ default="NOTICE"
+
diff --git a/src/daemon.h b/src/daemon.h
new file mode 100644
index 0000000..d060cb8
--- /dev/null
+++ b/src/daemon.h
@@ -0,0 +1,36 @@
+#ifndef __DAEMON_H
+#define __DAEMON_H
+
+#include "errors.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int daemon_parent_pipe[2];
+extern int daemon_signal_pipe[2];
+
+typedef struct daemon_t *daemon_p;
+
+typedef struct daemon_params_t {
+ const char *daemon_name; /**< name of the daemon (used for setproctitle,
+ default name of the pidfile, logfile) */
+ const char *pid_filename; /**< name of the pid/lock file */
+ const char *group_name; /**< group of the effective user */
+ const char *user_name; /**< name of the effective user */
+} daemon_params_t;
+
+daemon_p daemon_new( daemon_params_t params,
+ error_t *error );
+
+void daemon_free( daemon_p demon );
+
+error_t daemon_start( daemon_p demon );
+
+void daemon_exit( daemon_p demon );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef __DAEMON_H */
diff --git a/src/errors.h b/src/errors.h
new file mode 100644
index 0000000..add5b3c
--- /dev/null
+++ b/src/errors.h
@@ -0,0 +1,25 @@
+#ifndef __ERRORS_H
+#define __ERRORS_H
+
+#include "port/sys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef signed int error_t;
+
+#define OK 0 /* no error, everything is fine */
+#define ERR_OUT_OF_MEMORY -1 /* out of memory in malloc */
+#define ERR_INVALID_STATE -2 /* the object is called in an illegal moment */
+#define ERR_INVALID_VALUE -3 /* invalid parameter to a function */
+#define ERR_INTERNAL -4 /* internal error of the object, usualy rare */
+#define ERR_PROGRAMMING -5 /* programming mistake, should not happen */
+#define ERR_NOT_IMPLEMENTED -6 /* not implemented yet */
+#define ERR_TIMEOUT -7 /* timeout */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef __ERRORS_H */
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..48b6210
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,160 @@
+#include "log.h"
+
+#include "port/stdio.h" /* for vsnprintf */
+#include "port/string.h" /* for strcmp */
+
+#include <stdarg.h> /* for variable arguments */
+
+const char *log_syslog_facility_to_str( int facility ) {
+ switch( facility ) {
+ case LOG_KERN: return "KERN";
+ case LOG_USER: return "USER";
+ case LOG_MAIL: return "MAIL";
+ case LOG_DAEMON: return "DAEMON";
+ case LOG_AUTH: return "AUTH";
+ case LOG_SYSLOG: return "SYSLOG";
+ case LOG_LPR: return "LPR";
+ case LOG_NEWS: return "NEWS";
+ case LOG_UUCP: return "UUCP";
+ case LOG_CRON: return "CRON";
+#if defined LOG_AUTHPRIV
+ case LOG_AUTHPRIV: return "AUTHPRIV";
+#endif
+#if defined LOG_FTP
+ case LOG_FTP: return "FTP";
+#endif
+ default: return "<unknown";
+ }
+}
+
+int log_str_to_syslog_facility( const char *facility ) {
+ if( strcmp( facility, "KERN" ) == 0 ) return LOG_KERN;
+ if( strcmp( facility, "USER" ) == 0 ) return LOG_USER;
+ if( strcmp( facility, "MAIL" ) == 0 ) return LOG_MAIL;
+ if( strcmp( facility, "DAEMON" ) == 0 ) return LOG_DAEMON;
+ if( strcmp( facility, "AUTH" ) == 0 ) return LOG_AUTH;
+ if( strcmp( facility, "SYSLOG" ) == 0 ) return LOG_SYSLOG;
+ if( strcmp( facility, "LPR" ) == 0 ) return LOG_LPR;
+ if( strcmp( facility, "NEWS" ) == 0 ) return LOG_NEWS;
+ if( strcmp( facility, "UUCP" ) == 0 ) return LOG_UUCP;
+ if( strcmp( facility, "CRON" ) == 0 ) return LOG_CRON;
+#if defined LOG_AUTHPRIV
+ if( strcmp( facility, "AUTHPRIV" ) == 0 ) return LOG_AUTHPRIV;
+#endif
+#if defined LOG_FTP
+ if( strcmp( facility, "FTP" ) == 0 ) return LOG_FTP;
+#endif
+
+ return LOG_DAEMON;
+}
+
+const char *log_level_to_str( int level ) {
+ switch( level ) {
+ case LOG_EMERG: return "EMERG";
+ case LOG_ALERT: return "ALERT";
+ case LOG_CRIT: return "CRIT";
+ case LOG_ERR: return "ERR";
+ case LOG_WARNING: return "WARNING";
+ case LOG_NOTICE: return "NOTICE";
+ case LOG_INFO: return "INFO";
+ case LOG_DEBUG: return "DEBUG";
+ case LOG_DEBUG1: return "DEBUG1";
+ case LOG_DEBUG2: return "DEBUG2";
+ case LOG_DEBUG3: return "DEBUG3";
+ case LOG_DEBUG4: return "DEBUG4";
+ case LOG_DEBUG5: return "DEBUG5";
+ default: return "<unknown>";
+ }
+}
+
+int log_str_to_level( const char *level ) {
+ if( strcmp( level, "EMERG" ) == 0 ) return LOG_EMERG;
+ if( strcmp( level, "ALERT" ) == 0 ) return LOG_ALERT;
+ if( strcmp( level, "CRIT" ) == 0 ) return LOG_CRIT;
+ if( strcmp( level, "ERR" ) == 0 ) return LOG_ERR;
+ if( strcmp( level, "WARNING" ) == 0 ) return LOG_WARNING;
+ if( strcmp( level, "NOTICE" ) == 0 ) return LOG_NOTICE;
+ if( strcmp( level, "INFO" ) == 0 ) return LOG_INFO;
+ if( strcmp( level, "DEBUG" ) == 0 ) return LOG_DEBUG;
+ if( strcmp( level, "DEBUG1" ) == 0 ) return LOG_DEBUG1;
+ if( strcmp( level, "DEBUG2" ) == 0 ) return LOG_DEBUG2;
+ if( strcmp( level, "DEBUG3" ) == 0 ) return LOG_DEBUG3;
+ if( strcmp( level, "DEBUG4" ) == 0 ) return LOG_DEBUG4;
+ if( strcmp( level, "DEBUG5" ) == 0 ) return LOG_DEBUG5;
+
+ return LOG_NOTICE;
+}
+
+static const char *log_logfile_filename;
+static FILE *log_file = NULL;
+static int log_logfile_level = -1;
+static int log_stderr_level = -1;
+static const char *syslog_ident = NULL;
+static int syslog_facility;
+static int syslog_level;
+
+void openlogtofile( const char *filename, int level ) {
+ if( log_file != NULL ) {
+ fclose( log_file );
+ }
+ log_file = fopen( filename, "a" );
+ log_logfile_filename = filename;
+ log_logfile_level = level;
+}
+
+void openlogtosyslog( const char *ident, int facility, int level ) {
+ openlog( ident, LOG_CONS | LOG_NDELAY, facility );
+ setlogmask( LOG_UPTO( level ) );
+ syslog_ident = ident;
+ syslog_facility = facility;
+ syslog_level = level;
+}
+
+void openlogtostderr( int level ) {
+ log_stderr_level = level;
+}
+
+void closelogtofile( void ) {
+ if( log_file != NULL ) {
+ fclose( log_file );
+ log_file = NULL;
+ }
+}
+
+void closelogtosyslog( void ) {
+ closelog( );
+}
+
+void closelogtostderr( void ) {
+ /* do nothing here */
+}
+
+void reopenlogtofile( void ) {
+ if( log_file != NULL ) {
+ fclose( log_file );
+ }
+ log_file = fopen( log_logfile_filename, "a" );
+}
+
+void reopenlogtosyslog( void ) {
+ openlog( syslog_ident, LOG_CONS | LOG_NDELAY, syslog_facility );
+ setlogmask( LOG_UPTO( syslog_level ) );
+}
+
+void LOG( int level, const char *format, ... ) {
+ va_list ap;
+ char s[1024];
+ va_start( ap, format );
+ vsnprintf( s, 1024, format, ap );
+ va_end( ap );
+
+ if( level <= log_stderr_level )
+ fprintf( stderr, "%s: %s\n", log_level_to_str( level ), s );
+
+ if( level <= log_logfile_level ) {
+ fprintf( log_file, "%s: %s\n", log_level_to_str( level ), s );
+ fflush( log_file );
+ }
+
+ syslog( level, "%s", s );
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..f6c0534
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,60 @@
+#ifndef __LOG_H
+#define __LOG_H
+
+#include "port/sys.h"
+
+#include <syslog.h> /* for syslog, closelog and levels */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if 0
+/* defined in syslog.h */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but significant condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#endif
+
+#define LOG_DEBUG1 ( LOG_DEBUG + 1 )
+#define LOG_DEBUG2 ( LOG_DEBUG + 2 )
+#define LOG_DEBUG3 ( LOG_DEBUG + 3 )
+#define LOG_DEBUG4 ( LOG_DEBUG + 4 )
+#define LOG_DEBUG5 ( LOG_DEBUG + 5 )
+
+const char *log_syslog_facility_to_str( int facility );
+
+int log_str_to_syslog_facility( const char *facility );
+
+const char *log_level_to_str( int level );
+
+int log_str_to_level( const char *level );
+
+void openlogtofile( const char *filename, int level );
+
+void openlogtosyslog( const char *ident, int facility, int level );
+
+void openlogtostderr( int level );
+
+void closelogtofile( void );
+
+void closelogtosyslog( void );
+
+void closelogtostderr( void );
+
+void reopenlogtofile( void );
+
+void reopenlogtosyslog( void );
+
+void LOG( int level, const char *format, ... );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/pidfile.c b/src/pidfile.c
new file mode 100644
index 0000000..07f7521
--- /dev/null
+++ b/src/pidfile.c
@@ -0,0 +1,245 @@
+#include "port/stdio.h" /* for snprintf */
+#include "port/string.h" /* for strdup, strcspn */
+#include "port/stdbool.h" /* for bool */
+#include "port/unistd.h" /* for getpid, open, write, read,
+ * unlink, lockf */
+
+#include "pidfile.h"
+#include "log.h"
+#include "errors.h"
+
+#include <stdlib.h> /* for strtol */
+#include <errno.h> /* for errno */
+#include <sys/types.h> /* for pid_t, ssize_t, off_t */
+#include <sys/stat.h> /* for umask */
+#include <fcntl.h> /* for O_RDWR */
+#include <signal.h> /* for kill */
+
+/* abstraction of the pid file handling in order to unclutter the
+ * daemon start and stop function
+ */
+
+#define VAR_RUN_DIR "/var/run"
+
+void pidfile_init( struct pidfile_t *pidfile ) {
+ pidfile->filename[0] = '\0';
+ pidfile->fd = -1;
+ pidfile->locked = false;
+ pidfile->running = false;
+}
+
+void pidfile_set_from_daemon_name( struct pidfile_t *pidfile, const char *daemon ) {
+ pidfile_init( pidfile );
+ snprintf( pidfile->filename, PATH_MAX, "%s/%s.pid", VAR_RUN_DIR, daemon );
+}
+
+void pidfile_set_from_filename( struct pidfile_t *pidfile, const char *filename ) {
+ pidfile_init( pidfile );
+ /* make sure the filename is shorter than the POSIX.1 length */
+ snprintf( pidfile->filename, PATH_MAX, "%s", filename );
+}
+
+bool is_daemon_running( struct pidfile_t *pidfile, pid_t *pid, error_t *error ) {
+ int res;
+ ssize_t bytes_read;
+ char buf[256];
+ char *end_ptr;
+
+ /* assume daemon is not running */
+ pidfile->running = false;
+
+ /* open pidfile with correct permissions */
+ pidfile->fd = open( pidfile->filename, O_RDWR, 0644 );
+ if( pidfile->fd < 0 ) {
+ if( errno == ENOENT ) {
+ /* this is good, pid file doesn't exist at all */
+ LOG( LOG_DEBUG, "No pidfile '%s' found, daemon is not running", pidfile->filename );
+ (void)close( pidfile->fd );
+ *error = OK;
+ return pidfile->running;
+ } else {
+ LOG( LOG_EMERG, "Unable to open pidfile '%s' for reading: %s", pidfile->filename, strerror( errno ) );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+ }
+
+ /* try to lock the pid file (non-blocking) */
+ res = lockf( pidfile->fd, F_TLOCK, (off_t)0 );
+ if( res < 0 ) {
+ if( errno == EAGAIN ) {
+ /* another process locks the file already */
+ LOG( LOG_DEBUG, "Another process locks the pidfile, daemon already running" );
+ *error = OK;
+ pidfile->locked = true;
+ pidfile->running = true;
+ } else {
+ LOG( LOG_EMERG, "Unable to lock pidfile '%s': %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ pidfile->running = false;
+ return pidfile->running;
+ }
+ } else {
+ pidfile->locked = false;
+ }
+
+ /* try to read the pid from the file */
+ bytes_read = read( pidfile->fd, buf, sizeof( buf ) - 1 );
+ if( bytes_read < 0 ) {
+ LOG( LOG_EMERG, "Unable to read pid from pidfile '%s': %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+
+ /* parse the file and see if we have a valid pid on the first line */
+ buf[bytes_read] = 0;
+ buf[strcspn( buf, "\n" )] = 0;
+
+ errno = 0;
+ *pid = (pid_t)strtol( buf, &end_ptr, 10 );
+ if( ( errno != 0 ) /* ERANGE or valid base */ ||
+ ( end_ptr == NULL ) /* pre-condition for check for '\0'! */ ||
+ ( end_ptr - buf < 1 ) /* too short */ ||
+ ( *pid < 2 ) /* too small value */ ) {
+ LOG( LOG_EMERG, "pidfile '%s' contains invalid data, can't read PID from it!", pidfile->filename );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+ LOG( LOG_DEBUG, "Found PID '%lu' in pidfile", *pid );
+
+ /* the pid is valid, but is there a process with the pid actually running?
+ * (this handles the case of kill -9!)
+ */
+ res = kill( *pid, 0 );
+ if( res < 0 ) {
+ if( errno == ESRCH ) {
+ /* this is fine, process doesn't exist with this PID */
+ LOG( LOG_EMERG, "Found PID '%lu' in pidfile '%s', but no such process is running. Check and manually delete the pidfile!", *pid, pidfile->filename );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ } else {
+ LOG( LOG_EMERG, "Can't check if processor with PID '%lu' is alive: %s", *pid, strerror( errno ) );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+ }
+ LOG( LOG_DEBUG, "A process with PID '%lu' is already running", *pid );
+
+ /* process successfuly signaled, so a process with this PID exists
+ * (worst case, we assume, it's the daemon)
+ */
+ (void)close( pidfile->fd );
+ *error = OK;
+ return pidfile->running;
+}
+
+error_t pidfile_create( struct pidfile_t *pidfile ) {
+ int res;
+ char pid_string[20];
+ ssize_t bytes_writen;
+
+ /* create or open pid file with correct permissions */
+ pidfile->fd = open( pidfile->filename, O_CREAT | O_WRONLY | O_EXCL, 0644 );
+ if( pidfile->fd < 0 ) {
+ LOG( LOG_EMERG, "Unable to open pidfile '%s' for writing: %s", pidfile->filename, strerror( errno ) );
+ return ERR_INTERNAL;
+ }
+
+ /* Try to lock the pid file (non-blocking) */
+ res = lockf( pidfile->fd, F_TLOCK, (off_t)0 );
+ if( res < 0 ) {
+ if( errno == EAGAIN ) {
+ /* another process locks the file already, this should not happen, maybe a
+ * race between to daemons started in parallel?
+ */
+ LOG( LOG_EMERG, "Unable to lock pidfile '%s' after creation, daemon started in parallel?", pidfile->filename );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INVALID_STATE;
+ } else {
+ LOG( LOG_EMERG, "Unable to lock pidfile '%s' after creation: %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INTERNAL;
+ }
+ }
+
+ /* Truncate the contents of the file, so we don't get funny values
+ * in the pid file
+ */
+ if( ftruncate( pidfile->fd, (off_t)0 ) < 0 ) {
+ LOG( LOG_EMERG, "Unable to truncate the pidfile '%s' before writing to it", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INTERNAL;
+ }
+
+ /* We remember the pid in the file for init scripts which rely on the pid
+ * to be stored here.
+ */
+ snprintf( pid_string, 20, "%lu\n", (unsigned long)getpid( ) );
+ bytes_writen = write( pidfile->fd, pid_string, strlen( pid_string ) );
+ if( bytes_writen < 0 ) {
+ LOG( LOG_EMERG, "Unable to write PID into the pidfile '%s': %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INTERNAL;
+ } else if( bytes_writen != (ssize_t)strlen( pid_string ) ) {
+ /* non-atomic write on files with so little data, strange, should never happen! */
+ LOG( LOG_EMERG, "Non-atomic write failed when storing the PID into the pidfile '%s'", pidfile->filename );
+ }
+ LOG( LOG_DEBUG, "Stored '%lu' into the pidfile '%s' and locked it", (unsigned long)getpid( ), pidfile->filename );
+
+ pidfile->locked = true;
+
+ return OK;
+}
+
+error_t pidfile_release( struct pidfile_t *pidfile ) {
+ error_t error = OK;
+
+ LOG( LOG_DEBUG, "Releasing (unlocking/closing) pidfile '%s' (fd: %d, locked: %d)",
+ pidfile->filename, pidfile->fd, pidfile->locked );
+
+ if( pidfile->locked ) {
+ if( lockf( pidfile->fd, F_ULOCK, (off_t)0 ) < 0 ) {
+ LOG( LOG_ALERT, "Unable to unlock the pidfile '%s': %s (%d)",
+ pidfile->filename, strerror( errno ), errno );
+ error = ERR_INTERNAL;
+ }
+ }
+
+ if( pidfile->fd >= 0 ) {
+ if( close( pidfile->fd ) < 0 ) {
+ LOG( LOG_ALERT, "Unable to close the pidfile '%s': %s (%d)",
+ pidfile->filename, strerror( errno ), errno );
+ error = ERR_INTERNAL;
+ }
+ pidfile->fd = -1;
+ }
+
+ return error;
+}
+
+error_t pidfile_remove( struct pidfile_t *pidfile ) {
+ error_t error = OK;
+
+ LOG( LOG_DEBUG, "Removing pidfile '%s' (fd: %d, locked: %d, running: %d)",
+ pidfile->filename, pidfile->fd, pidfile->locked, pidfile->running );
+
+
+ if( !pidfile->running && pidfile->fd != -1 ) {
+ if( unlink( pidfile->filename ) < 0 ) {
+ LOG( LOG_ALERT, "Unable to remove the pidfile '%s': %s (%d)",
+ pidfile->filename, strerror( errno ), errno );
+ error = ERR_INTERNAL;
+ }
+ }
+
+ return error;
+}
diff --git a/src/pidfile.h b/src/pidfile.h
new file mode 100644
index 0000000..25ddd48
--- /dev/null
+++ b/src/pidfile.h
@@ -0,0 +1,32 @@
+#ifndef __PIDFILE_H
+#define __PIDFILE_H
+
+#include "port/limits.h" /* for PATH_MAX */
+#include "port/stdbool.h" /* for bool */
+
+#include "errors.h"
+
+#include <sys/types.h> /* for pid_t */
+
+struct pidfile_t {
+ char filename[PATH_MAX]; /**< the filename */
+ int fd; /**< file descriptor */
+ bool locked; /**< is the pidfile locked? */
+ bool running; /**< is another process locking too? */
+};
+
+void pidfile_init( struct pidfile_t *pidfile );
+
+void pidfile_set_from_daemon_name( struct pidfile_t *pidfile, const char *daemon );
+
+void pidfile_set_from_filename( struct pidfile_t *pidfile, const char *filename );
+
+bool is_daemon_running( struct pidfile_t *pidfile, pid_t *pid, error_t *error );
+
+error_t pidfile_create( struct pidfile_t *pidfile );
+
+error_t pidfile_release( struct pidfile_t *pidfile );
+
+error_t pidfile_remove( struct pidfile_t *pidfile );
+
+#endif /* ifndef __PIDFILE_H */
diff --git a/src/port/limits.h b/src/port/limits.h
new file mode 100644
index 0000000..a9e13a7
--- /dev/null
+++ b/src/port/limits.h
@@ -0,0 +1,8 @@
+#ifndef __LIMITS_H
+#define __LIMITS_H
+
+#include "sys.h"
+
+#include <limits.h>
+
+#endif /* ifndef __LIMITS_H */
diff --git a/src/port/lockf.c b/src/port/lockf.c
new file mode 100644
index 0000000..09c613e
--- /dev/null
+++ b/src/port/lockf.c
@@ -0,0 +1,62 @@
+#include "lockf.h"
+
+#include "string.h" /* for memset */
+#include "unistd.h" /* for getpid */
+
+#if !defined HAVE_LOCKF
+
+#include <sys/types.h> /* for off_t */
+#include <errno.h> /* for errno */
+
+int lockf( int fd, int cmd, off_t len ) {
+ struct flock fl;
+
+ memset( (char *)&fl, 0, sizeof( fl ) );
+ /* lockf is always relative to the current file position. */
+ fl.l_whence = SEEK_CUR;
+ fl.l_start = 0;
+ fl.l_len = len;
+
+ errno = 0;
+
+ switch( cmd ) {
+ case F_TEST:
+ fl.l_type = F_RDLCK;
+ if( fcntl( fd, F_GETLK, &fl ) < 0 ) {
+ return -1;
+ }
+ if( fl.l_type == F_UNLCK ||
+ fl.l_pid == getpid( ) ) {
+ return 0;
+ }
+ errno = EACCES;
+ return -1;
+
+ case F_ULOCK:
+ fl.l_type = F_UNLCK;
+ cmd = F_SETLK;
+ break;
+
+ case F_LOCK:
+ fl.l_type = F_WRLCK;
+ cmd = F_SETLKW;
+ break;
+
+ case F_TLOCK:
+ fl.l_type = F_WRLCK;
+ cmd = F_SETLK;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return fcntl( fd, cmd, &fl );
+}
+
+#else
+
+extern int dummy; /* prevent ISO C forbids an empty source file' warning */
+
+#endif /* !defined HAVE_LOCKF */
diff --git a/src/port/lockf.h b/src/port/lockf.h
new file mode 100644
index 0000000..5ec99b3
--- /dev/null
+++ b/src/port/lockf.h
@@ -0,0 +1,20 @@
+#ifndef __LOCKF_H
+#define __LOCKF_H
+
+#include "sys.h"
+
+#if !defined HAVE_LOCKF
+
+/* Drop-in replacement for the simplified lockf interface (POSIX) */
+#include <fcntl.h> /* for fcntl and locking flags */
+
+#define F_ULOCK 0 /* Unlock a previously locked region */
+#define F_LOCK 1 /* Lock exclusively */
+#define F_TLOCK 2 /* Test and lock exclusively */
+#define F_TEST 3 /* Test for a locked region */
+
+extern int lockf( int fd, int cmd, off_t len );
+
+#endif /* !defined HAVE_LOCKF */
+
+#endif /* ifndef __LOCKF_H */
diff --git a/src/port/noreturn.h b/src/port/noreturn.h
new file mode 100644
index 0000000..afb4d1a
--- /dev/null
+++ b/src/port/noreturn.h
@@ -0,0 +1,10 @@
+#ifndef __NORETURN_H
+#define __NORETURN_H
+
+#if defined(__GNUC__)
+#define NORETURN __attribute__((noreturn))
+#else
+#define NORETURN
+#endif /* defined(__GNUC__) */
+
+#endif /* ifndef __NORETURN_H */
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
new file mode 100644
index 0000000..65d9602
--- /dev/null
+++ b/src/port/snprintf.c
@@ -0,0 +1,2109 @@
+/* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */
+
+/*
+ * Copyright (c) 1995 Patrick Powell.
+ *
+ * This code is based on code written by Patrick Powell <papowell@astart.com>.
+ * It may be used for any purpose as long as this notice remains intact on all
+ * source code distributions.
+ */
+
+/*
+ * Copyright (c) 2008 Holger Weiss.
+ *
+ * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
+ * My changes to the code may freely be used, modified and/or redistributed for
+ * any purpose. It would be nice if additions and fixes to this file (including
+ * trivial code cleanups) would be sent back in order to let me include them in
+ * the version available at <http://www.jhweiss.de/software/snprintf.html>.
+ * However, this is not a requirement for using or redistributing (possibly
+ * modified) versions of this file, nor is leaving this notice intact mandatory.
+ */
+
+/*
+ * History
+ *
+ * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
+ *
+ * Fixed the detection of infinite floating point values on IRIX (and
+ * possibly other systems) and applied another few minor cleanups.
+ *
+ * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
+ *
+ * Added a lot of new features, fixed many bugs, and incorporated various
+ * improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
+ * <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
+ * <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
+ * projects. The additions include: support the "e", "E", "g", "G", and
+ * "F" conversion specifiers (and use conversion style "f" or "F" for the
+ * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
+ * "t", and "z" length modifiers; support the "#" flag and the (non-C99)
+ * "'" flag; use localeconv(3) (if available) to get both the current
+ * locale's decimal point character and the separator between groups of
+ * digits; fix the handling of various corner cases of field width and
+ * precision specifications; fix various floating point conversion bugs;
+ * handle infinite and NaN floating point values; don't attempt to write to
+ * the output buffer (which may be NULL) if a size of zero was specified;
+ * check for integer overflow of the field width, precision, and return
+ * values and during the floating point conversion; use the OUTCHAR() macro
+ * instead of a function for better performance; provide asprintf(3) and
+ * vasprintf(3) functions; add new test cases. The replacement functions
+ * have been renamed to use an "rpl_" prefix, the function calls in the
+ * main project (and in this file) must be redefined accordingly for each
+ * replacement function which is needed (by using Autoconf or other means).
+ * Various other minor improvements have been applied and the coding style
+ * was cleaned up for consistency.
+ *
+ * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
+ *
+ * C99 compliant snprintf(3) and vsnprintf(3) functions return the number
+ * of characters that would have been written to a sufficiently sized
+ * buffer (excluding the '\0'). The original code simply returned the
+ * length of the resulting output string, so that's been fixed.
+ *
+ * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
+ *
+ * The original code assumed that both snprintf(3) and vsnprintf(3) were
+ * missing. Some systems only have snprintf(3) but not vsnprintf(3), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
+ *
+ * The PGP code was using unsigned hexadecimal formats. Unfortunately,
+ * unsigned formats simply didn't work.
+ *
+ * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
+ *
+ * Ok, added some minimal floating point support, which means this probably
+ * requires libm on most operating systems. Don't yet support the exponent
+ * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just
+ * wasn't being exercised in ways which showed it, so that's been fixed.
+ * Also, formatted the code to Mutt conventions, and removed dead code left
+ * over from the original. Also, there is now a builtin-test, run with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
+ *
+ * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
+ *
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything from the
+ * normal C string format, at least as far as I can tell from the Solaris
+ * 2.5 printf(3S) man page.
+ */
+
+/*
+ * ToDo
+ *
+ * - Add wide character support.
+ * - Add support for "%a" and "%A" conversions.
+ * - Create test routines which predefine the expected results. Our test cases
+ * usually expose bugs in system implementations rather than in ours :-)
+ */
+
+/*
+ * Usage
+ *
+ * 1) The following preprocessor macros should be defined to 1 if the feature or
+ * file in question is available on the target system (by using Autoconf or
+ * other means), though basic functionality should be available as long as
+ * HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
+ *
+ * HAVE_VSNPRINTF
+ * HAVE_SNPRINTF
+ * HAVE_VASPRINTF
+ * HAVE_ASPRINTF
+ * HAVE_STDARG_H
+ * HAVE_STDDEF_H
+ * HAVE_STDINT_H
+ * HAVE_STDLIB_H
+ * HAVE_INTTYPES_H
+ * HAVE_LOCALE_H
+ * HAVE_LOCALECONV
+ * HAVE_LCONV_DECIMAL_POINT
+ * HAVE_LCONV_THOUSANDS_SEP
+ * HAVE_LONG_DOUBLE
+ * HAVE_LONG_LONG_INT
+ * HAVE_UNSIGNED_LONG_LONG_INT
+ * HAVE_INTMAX_T
+ * HAVE_UINTMAX_T
+ * HAVE_UINTPTR_T
+ * HAVE_PTRDIFF_T
+ * HAVE_VA_COPY
+ * HAVE___VA_COPY
+ *
+ * 2) The calls to the functions which should be replaced must be redefined
+ * throughout the project files (by using Autoconf or other means):
+ *
+ * #define vsnprintf rpl_vsnprintf
+ * #define snprintf rpl_snprintf
+ * #define vasprintf rpl_vasprintf
+ * #define asprintf rpl_asprintf
+ *
+ * 3) The required replacement functions should be declared in some header file
+ * included throughout the project files:
+ *
+ * #if HAVE_CONFIG_H
+ * #include <config.h>
+ * #endif
+ * #if HAVE_STDARG_H
+ * #include <stdarg.h>
+ * #if !HAVE_VSNPRINTF
+ * int rpl_vsnprintf(char *, size_t, const char *, va_list);
+ * #endif
+ * #if !HAVE_SNPRINTF
+ * int rpl_snprintf(char *, size_t, const char *, ...);
+ * #endif
+ * #if !HAVE_VASPRINTF
+ * int rpl_vasprintf(char **, const char *, va_list);
+ * #endif
+ * #if !HAVE_ASPRINTF
+ * int rpl_asprintf(char **, const char *, ...);
+ * #endif
+ * #endif
+ *
+ * Autoconf macros for handling step 1 and step 2 are available at
+ * <http://www.jhweiss.de/software/snprintf.html>.
+ */
+
+#include "port/snprintf.h"
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#if TEST_SNPRINTF
+#include <math.h> /* For pow(3), NAN, and INFINITY. */
+#include <string.h> /* For strcmp(3). */
+#if defined(__NetBSD__) || \
+ defined(__FreeBSD__) || \
+ defined(__OpenBSD__) || \
+ defined(__NeXT__) || \
+ defined(__bsd__)
+#define OS_BSD 1
+#else
+#if defined(sgi) || defined(__sgi)
+#ifndef __c99
+#define __c99 /* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
+#endif /* !defined(__c99) */
+#define OS_IRIX 1
+#define OS_SYSV 1
+#else
+#if defined(__svr4__)
+#define OS_SYSV 1
+#else
+#if defined(__linux__)
+#define OS_LINUX 1
+#endif
+#endif
+#endif
+#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
+#if HAVE_CONFIG_H /* Undefine definitions possibly done in config.h. */
+#ifdef HAVE_SNPRINTF
+#undef HAVE_SNPRINTF
+#endif /* defined(HAVE_SNPRINTF) */
+#ifdef HAVE_VSNPRINTF
+#undef HAVE_VSNPRINTF
+#endif /* defined(HAVE_VSNPRINTF) */
+#ifdef HAVE_ASPRINTF
+#undef HAVE_ASPRINTF
+#endif /* defined(HAVE_ASPRINTF) */
+#ifdef HAVE_VASPRINTF
+#undef HAVE_VASPRINTF
+#endif /* defined(HAVE_VASPRINTF) */
+#ifdef snprintf
+#undef snprintf
+#endif /* defined(snprintf) */
+#ifdef vsnprintf
+#undef vsnprintf
+#endif /* defined(vsnprintf) */
+#ifdef asprintf
+#undef asprintf
+#endif /* defined(asprintf) */
+#ifdef vasprintf
+#undef vasprintf
+#endif /* defined(vasprintf) */
+#else /* By default, we assume a modern system for testing. */
+#ifndef HAVE_STDARG_H
+#define HAVE_STDARG_H 1
+#endif /* HAVE_STDARG_H */
+#ifndef HAVE_STDDEF_H
+#define HAVE_STDDEF_H 1
+#endif /* HAVE_STDDEF_H */
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H 1
+#endif /* HAVE_STDINT_H */
+#ifndef HAVE_STDLIB_H
+#define HAVE_STDLIB_H 1
+#endif /* HAVE_STDLIB_H */
+#ifndef HAVE_INTTYPES_H
+#define HAVE_INTTYPES_H 1
+#endif /* HAVE_INTTYPES_H */
+#ifndef HAVE_LOCALE_H
+#define HAVE_LOCALE_H 1
+#endif /* HAVE_LOCALE_H */
+#ifndef HAVE_LOCALECONV
+#define HAVE_LOCALECONV 1
+#endif /* !defined(HAVE_LOCALECONV) */
+#ifndef HAVE_LCONV_DECIMAL_POINT
+#define HAVE_LCONV_DECIMAL_POINT 1
+#endif /* HAVE_LCONV_DECIMAL_POINT */
+#ifndef HAVE_LCONV_THOUSANDS_SEP
+#define HAVE_LCONV_THOUSANDS_SEP 1
+#endif /* HAVE_LCONV_THOUSANDS_SEP */
+#ifndef HAVE_LONG_DOUBLE
+#define HAVE_LONG_DOUBLE 1
+#endif /* !defined(HAVE_LONG_DOUBLE) */
+#ifndef HAVE_LONG_LONG_INT
+#define HAVE_LONG_LONG_INT 1
+#endif /* !defined(HAVE_LONG_LONG_INT) */
+#ifndef HAVE_UNSIGNED_LONG_LONG_INT
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+#endif /* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
+#ifndef HAVE_INTMAX_T
+#define HAVE_INTMAX_T 1
+#endif /* !defined(HAVE_INTMAX_T) */
+#ifndef HAVE_UINTMAX_T
+#define HAVE_UINTMAX_T 1
+#endif /* !defined(HAVE_UINTMAX_T) */
+#ifndef HAVE_UINTPTR_T
+#define HAVE_UINTPTR_T 1
+#endif /* !defined(HAVE_UINTPTR_T) */
+#ifndef HAVE_PTRDIFF_T
+#define HAVE_PTRDIFF_T 1
+#endif /* !defined(HAVE_PTRDIFF_T) */
+#ifndef HAVE_VA_COPY
+#define HAVE_VA_COPY 1
+#endif /* !defined(HAVE_VA_COPY) */
+#ifndef HAVE___VA_COPY
+#define HAVE___VA_COPY 1
+#endif /* !defined(HAVE___VA_COPY) */
+#endif /* HAVE_CONFIG_H */
+#define snprintf rpl_snprintf
+#define vsnprintf rpl_vsnprintf
+#define asprintf rpl_asprintf
+#define vasprintf rpl_vasprintf
+#endif /* TEST_SNPRINTF */
+
+#if !defined( HAVE_SNPRINTF ) || !defined( HAVE_VSNPRINTF ) || !defined( HAVE_ASPRINTF ) || !defined( HAVE_VASPRINTF )
+#include <stdio.h> /* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
+#ifdef VA_START
+#undef VA_START
+#endif /* defined(VA_START) */
+#ifdef VA_SHIFT
+#undef VA_SHIFT
+#endif /* defined(VA_SHIFT) */
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#define VA_START(ap, last) va_start(ap, last)
+#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
+#else /* Assume <varargs.h> is available. */
+#include <varargs.h>
+#define VA_START(ap, last) va_start(ap) /* "last" is ignored. */
+#define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
+#endif /* HAVE_STDARG_H */
+
+#if !defined HAVE_VASPRINTF || !HAVE_VASPRINTF
+#if HAVE_STDLIB_H
+#include <stdlib.h> /* For malloc(3). */
+#endif /* HAVE_STDLIB_H */
+#ifdef VA_COPY
+#undef VA_COPY
+#endif /* defined(VA_COPY) */
+#ifdef VA_END_COPY
+#undef VA_END_COPY
+#endif /* defined(VA_END_COPY) */
+#if defined HAVE_VA_COPY && HAVE_VA_COPY
+#define VA_COPY(dest, src) va_copy(dest, src)
+#define VA_END_COPY(ap) va_end(ap)
+#else
+#if defined HAVE___VA_COPY && HAVE___VA_COPY
+#define VA_COPY(dest, src) __va_copy(dest, src)
+#define VA_END_COPY(ap) va_end(ap)
+#else
+#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
+#define VA_END_COPY(ap) /* No-op. */
+#define NEED_MYMEMCPY 1
+static void *mymemcpy(void *, void *, size_t);
+#endif /* HAVE___VA_COPY */
+#endif /* HAVE_VA_COPY */
+#endif /* !HAVE_VASPRINTF */
+
+#if !defined HAVE_VSNPRINTF
+#include <errno.h> /* For ERANGE and errno. */
+#include <limits.h> /* For *_MAX. */
+#if HAVE_INTTYPES_H
+#include <inttypes.h> /* For intmax_t (if not defined in <stdint.h>). */
+#endif /* HAVE_INTTYPES_H */
+#if HAVE_LOCALE_H
+#include <locale.h> /* For localeconv(3). */
+#endif /* HAVE_LOCALE_H */
+#if HAVE_STDDEF_H
+#include <stddef.h> /* For ptrdiff_t. */
+#endif /* HAVE_STDDEF_H */
+#if HAVE_STDINT_H
+#include <stdint.h> /* For intmax_t. */
+#endif /* HAVE_STDINT_H */
+
+/* Support for unsigned long long int. We may also need ULLONG_MAX. */
+#ifndef ULONG_MAX /* We may need ULONG_MAX as a fallback. */
+#ifdef UINT_MAX
+#define ULONG_MAX UINT_MAX
+#else
+#define ULONG_MAX INT_MAX
+#endif /* defined(UINT_MAX) */
+#endif /* !defined(ULONG_MAX) */
+#ifdef ULLONG
+#undef ULLONG
+#endif /* defined(ULLONG) */
+#if HAVE_UNSIGNED_LONG_LONG_INT
+#define ULLONG unsigned long long int
+#ifndef ULLONG_MAX
+#define ULLONG_MAX ULONG_MAX
+#endif /* !defined(ULLONG_MAX) */
+#else
+#define ULLONG unsigned long int
+#ifdef ULLONG_MAX
+#undef ULLONG_MAX
+#endif /* defined(ULLONG_MAX) */
+#define ULLONG_MAX ULONG_MAX
+#endif /* HAVE_LONG_LONG_INT */
+
+/* Support for uintmax_t. We also need UINTMAX_MAX. */
+#ifdef UINTMAX_T
+#undef UINTMAX_T
+#endif /* defined(UINTMAX_T) */
+#if HAVE_UINTMAX_T || defined(uintmax_t)
+#define UINTMAX_T uintmax_t
+#ifndef UINTMAX_MAX
+#define UINTMAX_MAX ULLONG_MAX
+#endif /* !defined(UINTMAX_MAX) */
+#else
+#define UINTMAX_T ULLONG
+#ifdef UINTMAX_MAX
+#undef UINTMAX_MAX
+#endif /* defined(UINTMAX_MAX) */
+#define UINTMAX_MAX ULLONG_MAX
+#endif /* HAVE_UINTMAX_T || defined(uintmax_t) */
+
+/* Support for long double. */
+#ifndef LDOUBLE
+#if HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif /* HAVE_LONG_DOUBLE */
+#endif /* !defined(LDOUBLE) */
+
+/* Support for long long int. */
+#ifndef LLONG
+#if HAVE_LONG_LONG_INT
+#define LLONG long long int
+#else
+#define LLONG long int
+#endif /* HAVE_LONG_LONG_INT */
+#endif /* !defined(LLONG) */
+
+/* Support for intmax_t. */
+#ifndef INTMAX_T
+#if HAVE_INTMAX_T || defined(intmax_t)
+#define INTMAX_T intmax_t
+#else
+#define INTMAX_T LLONG
+#endif /* HAVE_INTMAX_T || defined(intmax_t) */
+#endif /* !defined(INTMAX_T) */
+
+/* Support for uintptr_t. */
+#ifndef UINTPTR_T
+#if HAVE_UINTPTR_T || defined(uintptr_t)
+#define UINTPTR_T uintptr_t
+#else
+#define UINTPTR_T unsigned long int
+#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
+#endif /* !defined(UINTPTR_T) */
+
+/* Support for ptrdiff_t. */
+#ifndef PTRDIFF_T
+#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
+#define PTRDIFF_T ptrdiff_t
+#else
+#define PTRDIFF_T long int
+#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
+#endif /* !defined(PTRDIFF_T) */
+
+/*
+ * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
+ * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an
+ * unsigned type if necessary. This should work just fine in practice.
+ */
+#ifndef UPTRDIFF_T
+#define UPTRDIFF_T PTRDIFF_T
+#endif /* !defined(UPTRDIFF_T) */
+
+/*
+ * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
+ * However, we'll simply use size_t and convert it to a signed type if
+ * necessary. This should work just fine in practice.
+ */
+#ifndef SSIZE_T
+#define SSIZE_T size_t
+#endif /* !defined(SSIZE_T) */
+
+/* Either ERANGE or E2BIG should be available everywhere. */
+#ifndef ERANGE
+#define ERANGE E2BIG
+#endif /* !defined(ERANGE) */
+#ifndef EOVERFLOW
+#define EOVERFLOW ERANGE
+#endif /* !defined(EOVERFLOW) */
+
+/*
+ * Buffer size to hold the octal string representation of UINT128_MAX without
+ * nul-termination ("3777777777777777777777777777777777777777777").
+ */
+#ifdef MAX_CONVERT_LENGTH
+#undef MAX_CONVERT_LENGTH
+#endif /* defined(MAX_CONVERT_LENGTH) */
+#define MAX_CONVERT_LENGTH 43
+
+/* Format read states. */
+#define PRINT_S_DEFAULT 0
+#define PRINT_S_FLAGS 1
+#define PRINT_S_WIDTH 2
+#define PRINT_S_DOT 3
+#define PRINT_S_PRECISION 4
+#define PRINT_S_MOD 5
+#define PRINT_S_CONV 6
+
+/* Format flags. */
+#define PRINT_F_MINUS (1 << 0)
+#define PRINT_F_PLUS (1 << 1)
+#define PRINT_F_SPACE (1 << 2)
+#define PRINT_F_NUM (1 << 3)
+#define PRINT_F_ZERO (1 << 4)
+#define PRINT_F_QUOTE (1 << 5)
+#define PRINT_F_UP (1 << 6)
+#define PRINT_F_UNSIGNED (1 << 7)
+#define PRINT_F_TYPE_G (1 << 8)
+#define PRINT_F_TYPE_E (1 << 9)
+
+/* Conversion flags. */
+#define PRINT_C_CHAR 1
+#define PRINT_C_SHORT 2
+#define PRINT_C_LONG 3
+#define PRINT_C_LLONG 4
+#define PRINT_C_LDOUBLE 5
+#define PRINT_C_SIZE 6
+#define PRINT_C_PTRDIFF 7
+#define PRINT_C_INTMAX 8
+
+#ifndef MAX
+#define MAX(x, y) ((x >= y) ? x : y)
+#endif /* !defined(MAX) */
+#ifndef CHARTOINT
+#define CHARTOINT(ch) (ch - '0')
+#endif /* !defined(CHARTOINT) */
+#ifndef ISDIGIT
+#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
+#endif /* !defined(ISDIGIT) */
+#ifndef ISNAN
+#define ISNAN(x) (x != x)
+#endif /* !defined(ISNAN) */
+#ifndef ISINF
+#define ISINF(x) (x != 0.0 && x + x == x)
+#endif /* !defined(ISINF) */
+
+#ifdef OUTCHAR
+#undef OUTCHAR
+#endif /* defined(OUTCHAR) */
+#define OUTCHAR(str, len, size, ch) \
+do { \
+ if (len + 1 < size) \
+ str[len] = ch; \
+ (len)++; \
+} while (/* CONSTCOND */ 0)
+
+static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
+static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
+static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
+static void printsep(char *, size_t *, size_t);
+static int getnumsep(int);
+static int getexponent(LDOUBLE);
+static int convert(UINTMAX_T, char *, size_t, int, int);
+static UINTMAX_T cast(LDOUBLE);
+static UINTMAX_T myround(LDOUBLE);
+static LDOUBLE mypow10(int);
+
+extern int errno;
+
+int
+rpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+ LDOUBLE fvalue;
+ INTMAX_T value;
+ unsigned char cvalue;
+ const char *strvalue;
+ INTMAX_T *intmaxptr;
+ PTRDIFF_T *ptrdiffptr;
+ SSIZE_T *sizeptr;
+ LLONG *llongptr;
+ long int *longptr;
+ int *intptr;
+ short int *shortptr;
+ signed char *charptr;
+ size_t len = 0;
+ int overflow = 0;
+ int base = 0;
+ int cflags = 0;
+ int flags = 0;
+ int width = 0;
+ int precision = -1;
+ int state = PRINT_S_DEFAULT;
+ char ch = *format++;
+
+ /*
+ * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
+ * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer
+ * even if a size larger than zero was specified. At least NetBSD's
+ * snprintf(3) does the same, as well as other versions of this file.
+ * (Though some of these versions will write to a non-NULL buffer even
+ * if a size of zero was specified, which violates the standard.)
+ */
+ if (str == NULL && size != 0)
+ size = 0;
+
+ while (ch != '\0')
+ switch (state) {
+ case PRINT_S_DEFAULT:
+ if (ch == '%')
+ state = PRINT_S_FLAGS;
+ else
+ OUTCHAR(str, len, size, ch);
+ ch = *format++;
+ break;
+ case PRINT_S_FLAGS:
+ switch (ch) {
+ case '-':
+ flags |= PRINT_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= PRINT_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= PRINT_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= PRINT_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= PRINT_F_ZERO;
+ ch = *format++;
+ break;
+ case '\'': /* SUSv2 flag (not in C99). */
+ flags |= PRINT_F_QUOTE;
+ ch = *format++;
+ break;
+ default:
+ state = PRINT_S_WIDTH;
+ break;
+ }
+ break;
+ case PRINT_S_WIDTH:
+ if (ISDIGIT(ch)) {
+ ch = CHARTOINT(ch);
+ if (width > (INT_MAX - ch) / 10) {
+ overflow = 1;
+ goto out;
+ }
+ width = 10 * width + ch;
+ ch = *format++;
+ } else if (ch == '*') {
+ /*
+ * C99 says: "A negative field width argument is
+ * taken as a `-' flag followed by a positive
+ * field width." (7.19.6.1, 5)
+ */
+ if ((width = va_arg(args, int)) < 0) {
+ flags |= PRINT_F_MINUS;
+ width = -width;
+ }
+ ch = *format++;
+ state = PRINT_S_DOT;
+ } else
+ state = PRINT_S_DOT;
+ break;
+ case PRINT_S_DOT:
+ if (ch == '.') {
+ state = PRINT_S_PRECISION;
+ ch = *format++;
+ } else
+ state = PRINT_S_MOD;
+ break;
+ case PRINT_S_PRECISION:
+ if (precision == -1)
+ precision = 0;
+ if (ISDIGIT(ch)) {
+ ch = CHARTOINT(ch);
+ if (precision > (INT_MAX - ch) / 10) {
+ overflow = 1;
+ goto out;
+ }
+ precision = 10 * precision + ch;
+ ch = *format++;
+ } else if (ch == '*') {
+ /*
+ * C99 says: "A negative precision argument is
+ * taken as if the precision were omitted."
+ * (7.19.6.1, 5)
+ */
+ if ((precision = va_arg(args, int)) < 0)
+ precision = -1;
+ ch = *format++;
+ state = PRINT_S_MOD;
+ } else
+ state = PRINT_S_MOD;
+ break;
+ case PRINT_S_MOD:
+ switch (ch) {
+ case 'h':
+ ch = *format++;
+ if (ch == 'h') { /* It's a char. */
+ ch = *format++;
+ cflags = PRINT_C_CHAR;
+ } else
+ cflags = PRINT_C_SHORT;
+ break;
+ case 'l':
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long. */
+ ch = *format++;
+ cflags = PRINT_C_LLONG;
+ } else
+ cflags = PRINT_C_LONG;
+ break;
+ case 'L':
+ cflags = PRINT_C_LDOUBLE;
+ ch = *format++;
+ break;
+ case 'j':
+ cflags = PRINT_C_INTMAX;
+ ch = *format++;
+ break;
+ case 't':
+ cflags = PRINT_C_PTRDIFF;
+ ch = *format++;
+ break;
+ case 'z':
+ cflags = PRINT_C_SIZE;
+ ch = *format++;
+ break;
+ }
+ state = PRINT_S_CONV;
+ break;
+ case PRINT_S_CONV:
+ switch (ch) {
+ case 'd':
+ /* FALLTHROUGH */
+ case 'i':
+ switch (cflags) {
+ case PRINT_C_CHAR:
+ value = (signed char)va_arg(args, int);
+ break;
+ case PRINT_C_SHORT:
+ value = (short int)va_arg(args, int);
+ break;
+ case PRINT_C_LONG:
+ value = va_arg(args, long int);
+ break;
+ case PRINT_C_LLONG:
+ value = va_arg(args, LLONG);
+ break;
+ case PRINT_C_SIZE:
+ value = va_arg(args, SSIZE_T);
+ break;
+ case PRINT_C_INTMAX:
+ value = va_arg(args, INTMAX_T);
+ break;
+ case PRINT_C_PTRDIFF:
+ value = va_arg(args, PTRDIFF_T);
+ break;
+ default:
+ value = va_arg(args, int);
+ break;
+ }
+ fmtint(str, &len, size, value, 10, width,
+ precision, flags);
+ break;
+ case 'X':
+ flags |= PRINT_F_UP;
+ /* FALLTHROUGH */
+ case 'x':
+ base = 16;
+ /* FALLTHROUGH */
+ case 'o':
+ if (base == 0)
+ base = 8;
+ /* FALLTHROUGH */
+ case 'u':
+ if (base == 0)
+ base = 10;
+ flags |= PRINT_F_UNSIGNED;
+ switch (cflags) {
+ case PRINT_C_CHAR:
+ value = (unsigned char)va_arg(args,
+ unsigned int);
+ break;
+ case PRINT_C_SHORT:
+ value = (unsigned short int)va_arg(args,
+ unsigned int);
+ break;
+ case PRINT_C_LONG:
+ value = va_arg(args, unsigned long int);
+ break;
+ case PRINT_C_LLONG:
+ value = va_arg(args, ULLONG);
+ break;
+ case PRINT_C_SIZE:
+ value = va_arg(args, size_t);
+ break;
+ case PRINT_C_INTMAX:
+ value = va_arg(args, UINTMAX_T);
+ break;
+ case PRINT_C_PTRDIFF:
+ value = va_arg(args, UPTRDIFF_T);
+ break;
+ default:
+ value = va_arg(args, unsigned int);
+ break;
+ }
+ fmtint(str, &len, size, value, base, width,
+ precision, flags);
+ break;
+ case 'A':
+ /* Not yet supported, we'll use "%F". */
+ /* FALLTHROUGH */
+ case 'F':
+ flags |= PRINT_F_UP;
+ case 'a':
+ /* Not yet supported, we'll use "%f". */
+ /* FALLTHROUGH */
+ case 'f':
+ if (cflags == PRINT_C_LDOUBLE)
+ fvalue = va_arg(args, LDOUBLE);
+ else
+ fvalue = va_arg(args, double);
+ fmtflt(str, &len, size, fvalue, width,
+ precision, flags, &overflow);
+ if (overflow)
+ goto out;
+ break;
+ case 'E':
+ flags |= PRINT_F_UP;
+ /* FALLTHROUGH */
+ case 'e':
+ flags |= PRINT_F_TYPE_E;
+ if (cflags == PRINT_C_LDOUBLE)
+ fvalue = va_arg(args, LDOUBLE);
+ else
+ fvalue = va_arg(args, double);
+ fmtflt(str, &len, size, fvalue, width,
+ precision, flags, &overflow);
+ if (overflow)
+ goto out;
+ break;
+ case 'G':
+ flags |= PRINT_F_UP;
+ /* FALLTHROUGH */
+ case 'g':
+ flags |= PRINT_F_TYPE_G;
+ if (cflags == PRINT_C_LDOUBLE)
+ fvalue = va_arg(args, LDOUBLE);
+ else
+ fvalue = va_arg(args, double);
+ /*
+ * If the precision is zero, it is treated as
+ * one (cf. C99: 7.19.6.1, 8).
+ */
+ if (precision == 0)
+ precision = 1;
+ fmtflt(str, &len, size, fvalue, width,
+ precision, flags, &overflow);
+ if (overflow)
+ goto out;
+ break;
+ case 'c':
+ cvalue = va_arg(args, int);
+ OUTCHAR(str, len, size, cvalue);
+ break;
+ case 's':
+ strvalue = va_arg(args, char *);
+ fmtstr(str, &len, size, strvalue, width,
+ precision, flags);
+ break;
+ case 'p':
+ /*
+ * C99 says: "The value of the pointer is
+ * converted to a sequence of printing
+ * characters, in an implementation-defined
+ * manner." (C99: 7.19.6.1, 8)
+ */
+ if ((strvalue = va_arg(args, void *)) == NULL)
+ /*
+ * We use the glibc format. BSD prints
+ * "0x0", SysV "0".
+ */
+ fmtstr(str, &len, size, "(nil)", width,
+ -1, flags);
+ else {
+ /*
+ * We use the BSD/glibc format. SysV
+ * omits the "0x" prefix (which we emit
+ * using the PRINT_F_NUM flag).
+ */
+ flags |= PRINT_F_NUM;
+ flags |= PRINT_F_UNSIGNED;
+ fmtint(str, &len, size,
+ (UINTPTR_T)strvalue, 16, width,
+ precision, flags);
+ }
+ break;
+ case 'n':
+ switch (cflags) {
+ case PRINT_C_CHAR:
+ charptr = va_arg(args, signed char *);
+ *charptr = len;
+ break;
+ case PRINT_C_SHORT:
+ shortptr = va_arg(args, short int *);
+ *shortptr = len;
+ break;
+ case PRINT_C_LONG:
+ longptr = va_arg(args, long int *);
+ *longptr = len;
+ break;
+ case PRINT_C_LLONG:
+ llongptr = va_arg(args, LLONG *);
+ *llongptr = len;
+ break;
+ case PRINT_C_SIZE:
+ /*
+ * C99 says that with the "z" length
+ * modifier, "a following `n' conversion
+ * specifier applies to a pointer to a
+ * signed integer type corresponding to
+ * size_t argument." (7.19.6.1, 7)
+ */
+ sizeptr = va_arg(args, SSIZE_T *);
+ *sizeptr = len;
+ break;
+ case PRINT_C_INTMAX:
+ intmaxptr = va_arg(args, INTMAX_T *);
+ *intmaxptr = len;
+ break;
+ case PRINT_C_PTRDIFF:
+ ptrdiffptr = va_arg(args, PTRDIFF_T *);
+ *ptrdiffptr = len;
+ break;
+ default:
+ intptr = va_arg(args, int *);
+ *intptr = len;
+ break;
+ }
+ break;
+ case '%': /* Print a "%" character verbatim. */
+ OUTCHAR(str, len, size, ch);
+ break;
+ default: /* Skip other characters. */
+ break;
+ }
+ ch = *format++;
+ state = PRINT_S_DEFAULT;
+ base = cflags = flags = width = 0;
+ precision = -1;
+ break;
+ }
+out:
+ if (len < size)
+ str[len] = '\0';
+ else if (size > 0)
+ str[size - 1] = '\0';
+
+ if (overflow || len >= INT_MAX) {
+ errno = overflow ? EOVERFLOW : ERANGE;
+ return -1;
+ }
+ return (int)len;
+}
+
+static void
+fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
+ int precision, int flags)
+{
+ int padlen, strln; /* Amount to pad. */
+ int noprecision = (precision == -1);
+
+ if (value == NULL) /* We're forgiving. */
+ value = "(null)";
+
+ /* If a precision was specified, don't read the string past it. */
+ for (strln = 0; value[strln] != '\0' &&
+ (noprecision || strln < precision); strln++)
+ continue;
+
+ if ((padlen = width - strln) < 0)
+ padlen = 0;
+ if (flags & PRINT_F_MINUS) /* Left justify. */
+ padlen = -padlen;
+
+ while (padlen > 0) { /* Leading spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ padlen--;
+ }
+ while (*value != '\0' && (noprecision || precision-- > 0)) {
+ OUTCHAR(str, *len, size, *value);
+ value++;
+ }
+ while (padlen < 0) { /* Trailing spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ padlen++;
+ }
+}
+
+static void
+fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
+ int precision, int flags)
+{
+ UINTMAX_T uvalue;
+ char iconvert[MAX_CONVERT_LENGTH];
+ char sign = 0;
+ char hexprefix = 0;
+ int spadlen = 0; /* Amount to space pad. */
+ int zpadlen = 0; /* Amount to zero pad. */
+ int pos;
+ int separators = (flags & PRINT_F_QUOTE);
+ int noprecision = (precision == -1);
+
+ if (flags & PRINT_F_UNSIGNED)
+ uvalue = value;
+ else {
+ uvalue = (value >= 0) ? value : -value;
+ if (value < 0)
+ sign = '-';
+ else if (flags & PRINT_F_PLUS) /* Do a sign. */
+ sign = '+';
+ else if (flags & PRINT_F_SPACE)
+ sign = ' ';
+ }
+
+ pos = convert(uvalue, iconvert, sizeof(iconvert), base,
+ flags & PRINT_F_UP);
+
+ if (flags & PRINT_F_NUM && uvalue != 0) {
+ /*
+ * C99 says: "The result is converted to an `alternative form'.
+ * For `o' conversion, it increases the precision, if and only
+ * if necessary, to force the first digit of the result to be a
+ * zero (if the value and precision are both 0, a single 0 is
+ * printed). For `x' (or `X') conversion, a nonzero result has
+ * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
+ */
+ switch (base) {
+ case 8:
+ if (precision <= pos)
+ precision = pos + 1;
+ break;
+ case 16:
+ hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
+ break;
+ }
+ }
+
+ if (separators) /* Get the number of group separators we'll print. */
+ separators = getnumsep(pos);
+
+ zpadlen = precision - pos - separators;
+ spadlen = width /* Minimum field width. */
+ - separators /* Number of separators. */
+ - MAX(precision, pos) /* Number of integer digits. */
+ - ((sign != 0) ? 1 : 0) /* Will we print a sign? */
+ - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */
+
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (spadlen < 0)
+ spadlen = 0;
+
+ /*
+ * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+ * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a
+ * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
+ */
+ if (flags & PRINT_F_MINUS) /* Left justify. */
+ spadlen = -spadlen;
+ else if (flags & PRINT_F_ZERO && noprecision) {
+ zpadlen += spadlen;
+ spadlen = 0;
+ }
+ while (spadlen > 0) { /* Leading spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ spadlen--;
+ }
+ if (sign != 0) /* Sign. */
+ OUTCHAR(str, *len, size, sign);
+ if (hexprefix != 0) { /* A "0x" or "0X" prefix. */
+ OUTCHAR(str, *len, size, '0');
+ OUTCHAR(str, *len, size, hexprefix);
+ }
+ while (zpadlen > 0) { /* Leading zeros. */
+ OUTCHAR(str, *len, size, '0');
+ zpadlen--;
+ }
+ while (pos > 0) { /* The actual digits. */
+ pos--;
+ OUTCHAR(str, *len, size, iconvert[pos]);
+ if (separators > 0 && pos > 0 && pos % 3 == 0)
+ printsep(str, len, size);
+ }
+ while (spadlen < 0) { /* Trailing spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ spadlen++;
+ }
+}
+
+static void
+fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
+ int precision, int flags, int *overflow)
+{
+ LDOUBLE ufvalue;
+ UINTMAX_T intpart;
+ UINTMAX_T fracpart;
+ UINTMAX_T mask;
+ const char *infnan = NULL;
+ char iconvert[MAX_CONVERT_LENGTH];
+ char fconvert[MAX_CONVERT_LENGTH];
+ char econvert[4]; /* "e-12" (without nul-termination). */
+ char esign = 0;
+ char sign = 0;
+ int leadfraczeros = 0;
+ int exponent = 0;
+ int emitpoint = 0;
+ int omitzeros = 0;
+ int omitcount = 0;
+ int padlen = 0;
+ int epos = 0;
+ int fpos = 0;
+ int ipos = 0;
+ int separators = (flags & PRINT_F_QUOTE);
+ int estyle = (flags & PRINT_F_TYPE_E);
+#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
+ struct lconv *lc = localeconv();
+#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
+
+ /*
+ * AIX' man page says the default is 0, but C99 and at least Solaris'
+ * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
+ * defaults to 6.
+ */
+ if (precision == -1)
+ precision = 6;
+
+ if (fvalue < 0.0)
+ sign = '-';
+ else if (flags & PRINT_F_PLUS) /* Do a sign. */
+ sign = '+';
+ else if (flags & PRINT_F_SPACE)
+ sign = ' ';
+
+ if (ISNAN(fvalue))
+ infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
+ else if (ISINF(fvalue))
+ infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
+
+ if (infnan != NULL) {
+ if (sign != 0)
+ iconvert[ipos++] = sign;
+ while (*infnan != '\0')
+ iconvert[ipos++] = *infnan++;
+ fmtstr(str, len, size, iconvert, width, ipos, flags);
+ return;
+ }
+
+ /* "%e" (or "%E") or "%g" (or "%G") conversion. */
+ if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
+ if (flags & PRINT_F_TYPE_G) {
+ /*
+ * For "%g" (and "%G") conversions, the precision
+ * specifies the number of significant digits, which
+ * includes the digits in the integer part. The
+ * conversion will or will not be using "e-style" (like
+ * "%e" or "%E" conversions) depending on the precision
+ * and on the exponent. However, the exponent can be
+ * affected by rounding the converted value, so we'll
+ * leave this decision for later. Until then, we'll
+ * assume that we're going to do an "e-style" conversion
+ * (in order to get the exponent calculated). For
+ * "e-style", the precision must be decremented by one.
+ */
+ precision--;
+ /*
+ * For "%g" (and "%G") conversions, trailing zeros are
+ * removed from the fractional portion of the result
+ * unless the "#" flag was specified.
+ */
+ if (!(flags & PRINT_F_NUM))
+ omitzeros = 1;
+ }
+ exponent = getexponent(fvalue);
+ estyle = 1;
+ }
+
+again:
+ /*
+ * Sorry, we only support 9, 19, or 38 digits (that is, the number of
+ * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
+ * minus one) past the decimal point due to our conversion method.
+ */
+ switch (sizeof(UINTMAX_T)) {
+ case 16:
+ if (precision > 38)
+ precision = 38;
+ break;
+ case 8:
+ if (precision > 19)
+ precision = 19;
+ break;
+ default:
+ if (precision > 9)
+ precision = 9;
+ break;
+ }
+
+ ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
+ if (estyle) /* We want exactly one integer digit. */
+ ufvalue /= mypow10(exponent);
+
+ if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
+ *overflow = 1;
+ return;
+ }
+
+ /*
+ * Factor of ten with the number of digits needed for the fractional
+ * part. For example, if the precision is 3, the mask will be 1000.
+ */
+ mask = mypow10(precision);
+ /*
+ * We "cheat" by converting the fractional part to integer by
+ * multiplying by a factor of ten.
+ */
+ if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
+ /*
+ * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
+ * (because precision = 3). Now, myround(1000 * 0.99962) will
+ * return 1000. So, the integer part must be incremented by one
+ * and the fractional part must be set to zero.
+ */
+ intpart++;
+ fracpart = 0;
+ if (estyle && intpart == 10) {
+ /*
+ * The value was rounded up to ten, but we only want one
+ * integer digit if using "e-style". So, the integer
+ * part must be set to one and the exponent must be
+ * incremented by one.
+ */
+ intpart = 1;
+ exponent++;
+ }
+ }
+
+ /*
+ * Now that we know the real exponent, we can check whether or not to
+ * use "e-style" for "%g" (and "%G") conversions. If we don't need
+ * "e-style", the precision must be adjusted and the integer and
+ * fractional parts must be recalculated from the original value.
+ *
+ * C99 says: "Let P equal the precision if nonzero, 6 if the precision
+ * is omitted, or 1 if the precision is zero. Then, if a conversion
+ * with style `E' would have an exponent of X:
+ *
+ * - if P > X >= -4, the conversion is with style `f' (or `F') and
+ * precision P - (X + 1).
+ *
+ * - otherwise, the conversion is with style `e' (or `E') and precision
+ * P - 1." (7.19.6.1, 8)
+ *
+ * Note that we had decremented the precision by one.
+ */
+ if (flags & PRINT_F_TYPE_G && estyle &&
+ precision + 1 > exponent && exponent >= -4) {
+ precision -= exponent;
+ estyle = 0;
+ goto again;
+ }
+
+ if (estyle) {
+ if (exponent < 0) {
+ exponent = -exponent;
+ esign = '-';
+ } else
+ esign = '+';
+
+ /*
+ * Convert the exponent. The sizeof(econvert) is 4. So, the
+ * econvert buffer can hold e.g. "e+99" and "e-99". We don't
+ * support an exponent which contains more than two digits.
+ * Therefore, the following stores are safe.
+ */
+ epos = convert(exponent, econvert, 2, 10, 0);
+ /*
+ * C99 says: "The exponent always contains at least two digits,
+ * and only as many more digits as necessary to represent the
+ * exponent." (7.19.6.1, 8)
+ */
+ if (epos == 1)
+ econvert[epos++] = '0';
+ econvert[epos++] = esign;
+ econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
+ }
+
+ /* Convert the integer part and the fractional part. */
+ ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
+ if (fracpart != 0) /* convert() would return 1 if fracpart == 0. */
+ fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
+
+ leadfraczeros = precision - fpos;
+
+ if (omitzeros) {
+ if (fpos > 0) /* Omit trailing fractional part zeros. */
+ while (omitcount < fpos && fconvert[omitcount] == '0')
+ omitcount++;
+ else { /* The fractional part is zero, omit it completely. */
+ omitcount = precision;
+ leadfraczeros = 0;
+ }
+ precision -= omitcount;
+ }
+
+ /*
+ * Print a decimal point if either the fractional part is non-zero
+ * and/or the "#" flag was specified.
+ */
+ if (precision > 0 || flags & PRINT_F_NUM)
+ emitpoint = 1;
+ if (separators) /* Get the number of group separators we'll print. */
+ separators = getnumsep(ipos);
+
+ padlen = width /* Minimum field width. */
+ - ipos /* Number of integer digits. */
+ - epos /* Number of exponent characters. */
+ - precision /* Number of fractional digits. */
+ - separators /* Number of group separators. */
+ - (emitpoint ? 1 : 0) /* Will we print a decimal point? */
+ - ((sign != 0) ? 1 : 0); /* Will we print a sign character? */
+
+ if (padlen < 0)
+ padlen = 0;
+
+ /*
+ * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+ * ignored." (7.19.6.1, 6)
+ */
+ if (flags & PRINT_F_MINUS) /* Left justifty. */
+ padlen = -padlen;
+ else if (flags & PRINT_F_ZERO && padlen > 0) {
+ if (sign != 0) { /* Sign. */
+ OUTCHAR(str, *len, size, sign);
+ sign = 0;
+ }
+ while (padlen > 0) { /* Leading zeros. */
+ OUTCHAR(str, *len, size, '0');
+ padlen--;
+ }
+ }
+ while (padlen > 0) { /* Leading spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ padlen--;
+ }
+ if (sign != 0) /* Sign. */
+ OUTCHAR(str, *len, size, sign);
+ while (ipos > 0) { /* Integer part. */
+ ipos--;
+ OUTCHAR(str, *len, size, iconvert[ipos]);
+ if (separators > 0 && ipos > 0 && ipos % 3 == 0)
+ printsep(str, len, size);
+ }
+ if (emitpoint) { /* Decimal point. */
+#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
+ if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
+ OUTCHAR(str, *len, size, *lc->decimal_point);
+ else /* We'll always print some decimal point character. */
+#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
+ OUTCHAR(str, *len, size, '.');
+ }
+ while (leadfraczeros > 0) { /* Leading fractional part zeros. */
+ OUTCHAR(str, *len, size, '0');
+ leadfraczeros--;
+ }
+ while (fpos > omitcount) { /* The remaining fractional part. */
+ fpos--;
+ OUTCHAR(str, *len, size, fconvert[fpos]);
+ }
+ while (epos > 0) { /* Exponent. */
+ epos--;
+ OUTCHAR(str, *len, size, econvert[epos]);
+ }
+ while (padlen < 0) { /* Trailing spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ padlen++;
+ }
+}
+
+static void
+printsep(char *str, size_t *len, size_t size)
+{
+#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
+ struct lconv *lc = localeconv();
+ int i;
+
+ if (lc->thousands_sep != NULL)
+ for (i = 0; lc->thousands_sep[i] != '\0'; i++)
+ OUTCHAR(str, *len, size, lc->thousands_sep[i]);
+ else
+#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
+ OUTCHAR(str, *len, size, ',');
+}
+
+static int
+getnumsep(int digits)
+{
+ int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
+#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
+ int strln;
+ struct lconv *lc = localeconv();
+
+ /* We support an arbitrary separator length (including zero). */
+ if (lc->thousands_sep != NULL) {
+ for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
+ continue;
+ separators *= strln;
+ }
+#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
+ return separators;
+}
+
+static int
+getexponent(LDOUBLE value)
+{
+ LDOUBLE tmp = (value >= 0.0) ? value : -value;
+ int exponent = 0;
+
+ /*
+ * We check for 99 > exponent > -99 in order to work around possible
+ * endless loops which could happen (at least) in the second loop (at
+ * least) if we're called with an infinite value. However, we checked
+ * for infinity before calling this function using our ISINF() macro, so
+ * this might be somewhat paranoid.
+ */
+ while (tmp < 1.0 && tmp > 0.0 && --exponent > -99)
+ tmp *= 10;
+ while (tmp >= 10.0 && ++exponent < 99)
+ tmp /= 10;
+
+ return exponent;
+}
+
+static int
+convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
+{
+ const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
+ size_t pos = 0;
+
+ /* We return an unterminated buffer with the digits in reverse order. */
+ do {
+ buf[pos++] = digits[value % base];
+ value /= base;
+ } while (value != 0 && pos < size);
+
+ return (int)pos;
+}
+
+static UINTMAX_T
+cast(LDOUBLE value)
+{
+ UINTMAX_T result;
+
+ /*
+ * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
+ * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
+ * it may be increased to the nearest higher representable value for the
+ * comparison (cf. C99: 6.3.1.4, 2). It might then equal the LDOUBLE
+ * value although converting the latter to UINTMAX_T would overflow.
+ */
+ if (value >= UINTMAX_MAX)
+ return UINTMAX_MAX;
+
+ result = value;
+ /*
+ * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
+ * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
+ * the standard). Sigh.
+ */
+ return (result <= value) ? result : result - 1;
+}
+
+static UINTMAX_T
+myround(LDOUBLE value)
+{
+ UINTMAX_T intpart = cast(value);
+
+ return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
+}
+
+static LDOUBLE
+mypow10(int exponent)
+{
+ LDOUBLE result = 1;
+
+ while (exponent > 0) {
+ result *= 10;
+ exponent--;
+ }
+ while (exponent < 0) {
+ result /= 10;
+ exponent++;
+ }
+ return result;
+}
+#endif /* !HAVE_VSNPRINTF */
+
+#if !defined HAVE_VASPRINTF || !HAVE_VASPRINTF
+#if defined NEED_MYMEMCPY && NEED_MYMEMCPY
+void *
+mymemcpy(void *dst, void *src, size_t len)
+{
+ const char *from = src;
+ char *to = dst;
+
+ /* No need for optimization, we use this only to replace va_copy(3). */
+ while (len-- > 0)
+ *to++ = *from++;
+ return dst;
+}
+#endif /* NEED_MYMEMCPY */
+
+int
+rpl_vasprintf(char **ret, const char *format, va_list ap)
+{
+ size_t size;
+ int len;
+ va_list aq;
+
+ VA_COPY(aq, ap);
+ len = vsnprintf(NULL, 0, format, aq);
+ VA_END_COPY(aq);
+ if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
+ return -1;
+ return vsnprintf(*ret, size, format, ap);
+}
+#endif /* !HAVE_VASPRINTF */
+
+#if !defined HAVE_SNPRINTF
+#if HAVE_STDARG_H
+int
+rpl_snprintf(char *str, size_t size, const char *format, ...)
+#else
+int
+rpl_snprintf(va_alist) va_dcl
+#endif /* HAVE_STDARG_H */
+{
+#if !HAVE_STDARG_H
+ char *str;
+ size_t size;
+ char *format;
+#endif /* HAVE_STDARG_H */
+ va_list ap;
+ int len;
+
+ VA_START(ap, format);
+ VA_SHIFT(ap, str, char *);
+ VA_SHIFT(ap, size, size_t);
+ VA_SHIFT(ap, format, const char *);
+ len = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return len;
+}
+#endif /* !HAVE_SNPRINTF */
+
+#if !defined HAVE_ASPRINTF || !HAVE_ASPRINTF
+#if HAVE_STDARG_H
+int
+rpl_asprintf(char **ret, const char *format, ...)
+#else
+int
+rpl_asprintf(va_alist) va_dcl
+#endif /* HAVE_STDARG_H */
+{
+#if !HAVE_STDARG_H
+ char **ret;
+ char *format;
+#endif /* HAVE_STDARG_H */
+ va_list ap;
+ int len;
+
+ VA_START(ap, format);
+ VA_SHIFT(ap, ret, char **);
+ VA_SHIFT(ap, format, const char *);
+ len = vasprintf(ret, format, ap);
+ va_end(ap);
+ return len;
+}
+#endif /* !HAVE_ASPRINTF */
+#else /* Dummy declaration to avoid empty translation unit warnings. */
+int main(void);
+#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || [...] */
+
+#if TEST_SNPRINTF
+int
+main(void)
+{
+ const char *float_fmt[] = {
+ /* "%E" and "%e" formats. */
+#if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
+ "%.16e",
+ "%22.16e",
+ "%022.16e",
+ "%-22.16e",
+ "%#+'022.16e",
+#endif /* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
+ "foo|%#+0123.9E|bar",
+ "%-123.9e",
+ "%123.9e",
+ "%+23.9e",
+ "%+05.8e",
+ "%-05.8e",
+ "%05.8e",
+ "%+5.8e",
+ "%-5.8e",
+ "% 5.8e",
+ "%5.8e",
+ "%+4.9e",
+#if !OS_LINUX /* glibc sometimes gets these wrong. */
+ "%+#010.0e",
+ "%#10.1e",
+ "%10.5e",
+ "% 10.5e",
+ "%5.0e",
+ "%5.e",
+ "%#5.0e",
+ "%#5.e",
+ "%3.2e",
+ "%3.1e",
+ "%-1.5e",
+ "%1.5e",
+ "%01.3e",
+ "%1.e",
+ "%.1e",
+ "%#.0e",
+ "%+.0e",
+ "% .0e",
+ "%.0e",
+ "%#.e",
+ "%+.e",
+ "% .e",
+ "%.e",
+ "%4e",
+ "%e",
+ "%E",
+#endif /* !OS_LINUX */
+ /* "%F" and "%f" formats. */
+#if !OS_BSD && !OS_IRIX
+ "% '022f",
+ "%+'022f",
+ "%-'22f",
+ "%'22f",
+#if HAVE_LONG_LONG_INT
+ "%.16f",
+ "%22.16f",
+ "%022.16f",
+ "%-22.16f",
+ "%#+'022.16f",
+#endif /* HAVE_LONG_LONG_INT */
+#endif /* !OS_BSD && !OS_IRIX */
+ "foo|%#+0123.9F|bar",
+ "%-123.9f",
+ "%123.9f",
+ "%+23.9f",
+ "%+#010.0f",
+ "%#10.1f",
+ "%10.5f",
+ "% 10.5f",
+ "%+05.8f",
+ "%-05.8f",
+ "%05.8f",
+ "%+5.8f",
+ "%-5.8f",
+ "% 5.8f",
+ "%5.8f",
+ "%5.0f",
+ "%5.f",
+ "%#5.0f",
+ "%#5.f",
+ "%+4.9f",
+ "%3.2f",
+ "%3.1f",
+ "%-1.5f",
+ "%1.5f",
+ "%01.3f",
+ "%1.f",
+ "%.1f",
+ "%#.0f",
+ "%+.0f",
+ "% .0f",
+ "%.0f",
+ "%#.f",
+ "%+.f",
+ "% .f",
+ "%.f",
+ "%4f",
+ "%f",
+ "%F",
+ /* "%G" and "%g" formats. */
+#if !OS_BSD && !OS_IRIX && !OS_LINUX
+ "% '022g",
+ "%+'022g",
+ "%-'22g",
+ "%'22g",
+#if HAVE_LONG_LONG_INT
+ "%.16g",
+ "%22.16g",
+ "%022.16g",
+ "%-22.16g",
+ "%#+'022.16g",
+#endif /* HAVE_LONG_LONG_INT */
+#endif /* !OS_BSD && !OS_IRIX && !OS_LINUX */
+ "foo|%#+0123.9G|bar",
+ "%-123.9g",
+ "%123.9g",
+ "%+23.9g",
+ "%+05.8g",
+ "%-05.8g",
+ "%05.8g",
+ "%+5.8g",
+ "%-5.8g",
+ "% 5.8g",
+ "%5.8g",
+ "%+4.9g",
+#if !OS_LINUX /* glibc sometimes gets these wrong. */
+ "%+#010.0g",
+ "%#10.1g",
+ "%10.5g",
+ "% 10.5g",
+ "%5.0g",
+ "%5.g",
+ "%#5.0g",
+ "%#5.g",
+ "%3.2g",
+ "%3.1g",
+ "%-1.5g",
+ "%1.5g",
+ "%01.3g",
+ "%1.g",
+ "%.1g",
+ "%#.0g",
+ "%+.0g",
+ "% .0g",
+ "%.0g",
+ "%#.g",
+ "%+.g",
+ "% .g",
+ "%.g",
+ "%4g",
+ "%g",
+ "%G",
+#endif /* !OS_LINUX */
+ NULL
+ };
+ double float_val[] = {
+ -4.136,
+ -134.52,
+ -5.04030201,
+ -3410.01234,
+ -999999.999999,
+ -913450.29876,
+ -913450.2,
+ -91345.2,
+ -9134.2,
+ -913.2,
+ -91.2,
+ -9.2,
+ -9.9,
+ 4.136,
+ 134.52,
+ 5.04030201,
+ 3410.01234,
+ 999999.999999,
+ 913450.29876,
+ 913450.2,
+ 91345.2,
+ 9134.2,
+ 913.2,
+ 91.2,
+ 9.2,
+ 9.9,
+ 9.96,
+ 9.996,
+ 9.9996,
+ 9.99996,
+ 9.999996,
+ 9.9999996,
+ 9.99999996,
+ 0.99999996,
+ 0.99999999,
+ 0.09999999,
+ 0.00999999,
+ 0.00099999,
+ 0.00009999,
+ 0.00000999,
+ 0.00000099,
+ 0.00000009,
+ 0.00000001,
+ 0.0000001,
+ 0.000001,
+ 0.00001,
+ 0.0001,
+ 0.001,
+ 0.01,
+ 0.1,
+ 1.0,
+ 1.5,
+ -1.5,
+ -1.0,
+ -0.1,
+#if !OS_BSD /* BSD sometimes gets these wrong. */
+#ifdef INFINITY
+ INFINITY,
+ -INFINITY,
+#endif /* defined(INFINITY) */
+#ifdef NAN
+ NAN,
+#endif /* defined(NAN) */
+#endif /* !OS_BSD */
+ 0
+ };
+ const char *long_fmt[] = {
+ "foo|%0123ld|bar",
+#if !OS_IRIX
+ "% '0123ld",
+ "%+'0123ld",
+ "%-'123ld",
+ "%'123ld",
+#endif /* !OS_IRiX */
+ "%123.9ld",
+ "% 123.9ld",
+ "%+123.9ld",
+ "%-123.9ld",
+ "%0123ld",
+ "% 0123ld",
+ "%+0123ld",
+ "%-0123ld",
+ "%10.5ld",
+ "% 10.5ld",
+ "%+10.5ld",
+ "%-10.5ld",
+ "%010ld",
+ "% 010ld",
+ "%+010ld",
+ "%-010ld",
+ "%4.2ld",
+ "% 4.2ld",
+ "%+4.2ld",
+ "%-4.2ld",
+ "%04ld",
+ "% 04ld",
+ "%+04ld",
+ "%-04ld",
+ "%5.5ld",
+ "%+22.33ld",
+ "%01.3ld",
+ "%1.5ld",
+ "%-1.5ld",
+ "%44ld",
+ "%4ld",
+ "%4.0ld",
+ "%4.ld",
+ "%.44ld",
+ "%.4ld",
+ "%.0ld",
+ "%.ld",
+ "%ld",
+ NULL
+ };
+ long int long_val[] = {
+#ifdef LONG_MAX
+ LONG_MAX,
+#endif /* LONG_MAX */
+#ifdef LONG_MIN
+ LONG_MIN,
+#endif /* LONG_MIN */
+ -91340,
+ 91340,
+ 341,
+ 134,
+ 0203,
+ -1,
+ 1,
+ 0
+ };
+ const char *ulong_fmt[] = {
+ /* "%u" formats. */
+ "foo|%0123lu|bar",
+#if !OS_IRIX
+ "% '0123lu",
+ "%+'0123lu",
+ "%-'123lu",
+ "%'123lu",
+#endif /* !OS_IRiX */
+ "%123.9lu",
+ "% 123.9lu",
+ "%+123.9lu",
+ "%-123.9lu",
+ "%0123lu",
+ "% 0123lu",
+ "%+0123lu",
+ "%-0123lu",
+ "%5.5lu",
+ "%+22.33lu",
+ "%01.3lu",
+ "%1.5lu",
+ "%-1.5lu",
+ "%44lu",
+ "%lu",
+ /* "%o" formats. */
+ "foo|%#0123lo|bar",
+ "%#123.9lo",
+ "%# 123.9lo",
+ "%#+123.9lo",
+ "%#-123.9lo",
+ "%#0123lo",
+ "%# 0123lo",
+ "%#+0123lo",
+ "%#-0123lo",
+ "%#5.5lo",
+ "%#+22.33lo",
+ "%#01.3lo",
+ "%#1.5lo",
+ "%#-1.5lo",
+ "%#44lo",
+ "%#lo",
+ "%123.9lo",
+ "% 123.9lo",
+ "%+123.9lo",
+ "%-123.9lo",
+ "%0123lo",
+ "% 0123lo",
+ "%+0123lo",
+ "%-0123lo",
+ "%5.5lo",
+ "%+22.33lo",
+ "%01.3lo",
+ "%1.5lo",
+ "%-1.5lo",
+ "%44lo",
+ "%lo",
+ /* "%X" and "%x" formats. */
+ "foo|%#0123lX|bar",
+ "%#123.9lx",
+ "%# 123.9lx",
+ "%#+123.9lx",
+ "%#-123.9lx",
+ "%#0123lx",
+ "%# 0123lx",
+ "%#+0123lx",
+ "%#-0123lx",
+ "%#5.5lx",
+ "%#+22.33lx",
+ "%#01.3lx",
+ "%#1.5lx",
+ "%#-1.5lx",
+ "%#44lx",
+ "%#lx",
+ "%#lX",
+ "%123.9lx",
+ "% 123.9lx",
+ "%+123.9lx",
+ "%-123.9lx",
+ "%0123lx",
+ "% 0123lx",
+ "%+0123lx",
+ "%-0123lx",
+ "%5.5lx",
+ "%+22.33lx",
+ "%01.3lx",
+ "%1.5lx",
+ "%-1.5lx",
+ "%44lx",
+ "%lx",
+ "%lX",
+ NULL
+ };
+ unsigned long int ulong_val[] = {
+#ifdef ULONG_MAX
+ ULONG_MAX,
+#endif /* ULONG_MAX */
+ 91340,
+ 341,
+ 134,
+ 0203,
+ 1,
+ 0
+ };
+ const char *llong_fmt[] = {
+ "foo|%0123lld|bar",
+ "%123.9lld",
+ "% 123.9lld",
+ "%+123.9lld",
+ "%-123.9lld",
+ "%0123lld",
+ "% 0123lld",
+ "%+0123lld",
+ "%-0123lld",
+ "%5.5lld",
+ "%+22.33lld",
+ "%01.3lld",
+ "%1.5lld",
+ "%-1.5lld",
+ "%44lld",
+ "%lld",
+ NULL
+ };
+ LLONG llong_val[] = {
+#ifdef LLONG_MAX
+ LLONG_MAX,
+#endif /* LLONG_MAX */
+#ifdef LLONG_MIN
+ LLONG_MIN,
+#endif /* LLONG_MIN */
+ -91340,
+ 91340,
+ 341,
+ 134,
+ 0203,
+ -1,
+ 1,
+ 0
+ };
+ const char *string_fmt[] = {
+ "foo|%10.10s|bar",
+ "%-10.10s",
+ "%10.10s",
+ "%10.5s",
+ "%5.10s",
+ "%10.1s",
+ "%1.10s",
+ "%10.0s",
+ "%0.10s",
+ "%-42.5s",
+ "%2.s",
+ "%.10s",
+ "%.1s",
+ "%.0s",
+ "%.s",
+ "%4s",
+ "%s",
+ NULL
+ };
+ const char *string_val[] = {
+ "Hello",
+ "Hello, world!",
+ "Sound check: One, two, three.",
+ "This string is a little longer than the other strings.",
+ "1",
+ "",
+ NULL
+ };
+#if !OS_SYSV /* SysV uses a different format than we do. */
+ const char *pointer_fmt[] = {
+ "foo|%p|bar",
+ "%42p",
+ "%p",
+ NULL
+ };
+ const char *pointer_val[] = {
+ *pointer_fmt,
+ *string_fmt,
+ *string_val,
+ NULL
+ };
+#endif /* !OS_SYSV */
+ char buf1[1024], buf2[1024];
+ double value, digits = 9.123456789012345678901234567890123456789;
+ int i, j, r1, r2, failed = 0, num = 0;
+
+/*
+ * Use -DTEST_NILS in order to also test the conversion of nil values. Might
+ * segfault on systems which don't support converting a NULL pointer with "%s"
+ * and lets some test cases fail against BSD and glibc due to bugs in their
+ * implementations.
+ */
+#ifndef TEST_NILS
+#define TEST_NILS 0
+#else
+#if TEST_NILS
+#undef TEST_NILS
+#define TEST_NILS 1
+#endif /* TEST_NILS */
+#endif /* !defined(TEST_NILS) */
+#ifdef TEST
+#undef TEST
+#endif /* defined(TEST) */
+#define TEST(fmt, val) \
+do { \
+ for (i = 0; fmt[i] != NULL; i++) \
+ for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) { \
+ r1 = sprintf(buf1, fmt[i], val[j]); \
+ r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]); \
+ if (strcmp(buf1, buf2) != 0 || r1 != r2) { \
+ (void)printf("Results don't match, " \
+ "format string: %s\n" \
+ "\t sprintf(3): [%s] (%d)\n" \
+ "\tsnprintf(3): [%s] (%d)\n", \
+ fmt[i], buf1, r1, buf2, r2); \
+ failed++; \
+ } \
+ num++; \
+ } \
+} while (/* CONSTCOND */ 0)
+
+#if HAVE_LOCALE_H
+ (void)setlocale(LC_ALL, "");
+#endif /* HAVE_LOCALE_H */
+
+ (void)puts("Testing our snprintf(3) against your system's sprintf(3).");
+ TEST(float_fmt, float_val);
+ TEST(long_fmt, long_val);
+ TEST(ulong_fmt, ulong_val);
+ TEST(llong_fmt, llong_val);
+ TEST(string_fmt, string_val);
+#if !OS_SYSV /* SysV uses a different format than we do. */
+ TEST(pointer_fmt, pointer_val);
+#endif /* !OS_SYSV */
+ (void)printf("Result: %d out of %d tests failed.\n", failed, num);
+
+ (void)fputs("Checking how many digits we support: ", stdout);
+ for (i = 0; i < 100; i++) {
+ value = pow(10, i) * digits;
+ (void)sprintf(buf1, "%.1f", value);
+ (void)snprintf(buf2, sizeof(buf2), "%.1f", value);
+ if (strcmp(buf1, buf2) != 0) {
+ (void)printf("apparently %d.\n", i);
+ break;
+ }
+ }
+ return (failed == 0) ? 0 : 1;
+}
+#endif /* TEST_SNPRINTF */
+
+/* vim: set joinspaces textwidth=80: */
diff --git a/src/port/snprintf.h b/src/port/snprintf.h
new file mode 100644
index 0000000..158e5fb
--- /dev/null
+++ b/src/port/snprintf.h
@@ -0,0 +1,59 @@
+#ifndef __SNPRINTF_H
+#define __SNPRINTF_H
+
+#include "sys.h"
+
+#define HAVE_CONFIG_H 0
+#define TEST_SNPRINTF 0
+#define HAVE_STDARG_H 1
+#define HAVE_STDDEF_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_VA_COPY 1
+
+#if HAVE_STDARG_H
+#include <stdarg.h> /* for va_list */
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h> /* for size_t */
+#endif
+
+#if !defined HAVE_VSNPRINTF
+#define vsnprintf rpl_vsnprintf
+#if HAVE_STDARG_H
+extern int rpl_vsnprintf(char *str, size_t size, const char *format, va_list args);
+#else
+ #error va_list required for vsnprintf!
+#endif /* HAVE_STDARG_H */
+#endif /* !defined HAVE_VSNPRINTF */
+
+#if !defined HAVE_SNPRINTF
+#define snprintf rpl_snprintf
+#if HAVE_STDARG_H
+extern int
+rpl_snprintf(char *str, size_t size, const char *format, ...);
+#else
+extern int
+rpl_snprintf(va_alist) va_dcl;
+#endif /* HAVE_STDARG_H */
+#endif /* ! defined HAVE_SNPRINTF */
+
+#if !defined HAVE_VASPRINTF
+#define vasprintf rpl_vasprintf
+extern int
+rpl_vasprintf(char **ret, const char *format, va_list ap);
+#endif /* ! defined HAVE_VASPRINTF */
+
+#if !defined HAVE_ASPRINTF
+#define asprintf rpl_asprintf
+#if HAVE_STDARG_H
+extern int
+rpl_asprintf(char **ret, const char *format, ...);
+#else
+extern int
+rpl_asprintf(va_alist) va_dcl;
+#endif /* HAVE_STDARG_H */
+#endif /* ! defined HAVE_ASPRINTF */
+
+#endif /* ifndef __SNPRINTF_H */
diff --git a/src/port/stdbool.h b/src/port/stdbool.h
new file mode 100644
index 0000000..9c011e4
--- /dev/null
+++ b/src/port/stdbool.h
@@ -0,0 +1,35 @@
+#ifndef __STDBOOL_H
+#define __STDBOOL_H
+
+#include "sys.h"
+
+/* C99 Boolean types for compilers without C99 support, see
+ * http://www.opengroup.org/onlinepubs/009695399/basedefs/stdbool.h.html */
+
+/* don't define bool in C++, it exists already! */
+#ifndef __cplusplus
+
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+
+#if !defined HAVE_ENUM_BOOL
+typedef enum {
+ _Bool_must_promote_to_int = -1,
+ false = 0,
+ true = 1
+} _Bool;
+#endif /* !defined HAVE_ENUM_BOOL */
+
+#define bool _Bool
+
+#define true 1
+#define false 0
+
+#define __bool_true_false_are_defined 1
+
+#endif /* ifdef HAVE_STDBOOL_H */
+
+#endif /* ifndef __cplusplus */
+
+#endif /* ifndef STDBOOL_H */
diff --git a/src/port/stdint.h b/src/port/stdint.h
new file mode 100644
index 0000000..d5d830a
--- /dev/null
+++ b/src/port/stdint.h
@@ -0,0 +1,15 @@
+#ifndef __STDINT_H
+#define __STDINT_H
+
+#include "sys.h"
+
+#if defined HAVE_STDINT_H
+#include <stdint.h>
+#endif /* defined HAVE_STDINT_H */
+
+/* Solaris 8 weirdness */
+#if defined HAVE_LINK_H
+#include <link.h>
+#endif /* defined HAVE_LINK_H */
+
+#endif /* ifndef __STDINT_H */
diff --git a/src/port/stdio.h b/src/port/stdio.h
new file mode 100644
index 0000000..59a7d67
--- /dev/null
+++ b/src/port/stdio.h
@@ -0,0 +1,10 @@
+#ifndef __STDIO_H
+#define __STDIO_H
+
+#include "sys.h"
+
+#include <stdio.h>
+
+#include "snprintf.h"
+
+#endif /* ifndef __STDIO_H */
diff --git a/src/port/string.h b/src/port/string.h
new file mode 100644
index 0000000..8f96466
--- /dev/null
+++ b/src/port/string.h
@@ -0,0 +1,16 @@
+#ifndef __STRING_H
+#define __STRING_H
+
+#include "sys.h"
+
+#include <string.h>
+
+#include <strings.h>
+
+#ifdef HAVE_STRERROR_R
+#else
+/* TODO: port correctly! */
+#define strerror_r( errnum, buf, buflen ) strncpy( buf, strerror( errnum ), buflen )
+#endif
+
+#endif /* ifndef __STRING_H */
diff --git a/src/port/sys.h b/src/port/sys.h
new file mode 100644
index 0000000..65b68e2
--- /dev/null
+++ b/src/port/sys.h
@@ -0,0 +1,119 @@
+#if defined LINUX
+#if OS_MAJOR_VERSION == 2
+#if OS_MINOR_VERSION == 6
+#define _XOPEN_SOURCE 600
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_VSNPRINTF
+#define HAVE_SNPRINTF
+#define HAVE_VASPRINTF
+#define HAVE_ASPRINTF
+#define HAVE_STRDUP
+#define HAVE_LOCKF
+#else
+ #error unknown platform
+#endif /* defined OS_MINOR_VERSION == 6 */
+#else
+ #error unknown platform
+#endif /* defined OS_MAJOR_VERSION == 2 */
+#endif /* defined LINUX */
+
+#if defined FREEBSD
+#if OS_MAJOR_VERSION == 7
+#if OS_MINOR_VERSION == 0
+#define _XOPEN_SOURCE 600
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_VSNPRINTF
+#define HAVE_SNPRINTF
+#define HAVE_VASPRINTF
+#define HAVE_ASPRINTF
+#define HAVE_STRDUP
+#define HAVE_LOCKF
+#else
+ #error unknown platform
+#endif /* defined OS_MINOR_VERSION == 0 */
+#else
+#if OS_MAJOR_VERSION == 6
+#if OS_MINOR_VERSION == 2
+#define _XOPEN_SOURCE 600
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_VSNPRINTF
+#define HAVE_SNPRINTF
+#define HAVE_STRDUP
+#define HAVE_LOCKF
+#else
+ #error unknown platform
+#endif /* defined OS_MINOR_VERSION == 2 */
+#else
+ #error unknown platform
+#endif /* defined OS_MAJOR_VERSION == 6 */
+#endif /* defined OS_MAJOR_VERSION == 7 */
+#endif /* defined FREEBSD */
+
+#if defined OPENBSD
+#if OS_MAJOR_VERSION == 4
+#if OS_MINOR_VERSION >= 2 && OS_MINOR_VERSION <= 3
+#define _XOPEN_SOURCE 600
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_VSNPRINTF
+#define HAVE_SNPRINTF
+#define HAVE_VASPRINTF
+#define HAVE_ASPRINTF
+#define HAVE_STRDUP
+#define HAVE_LOCKF
+#else
+ #error unknown platform
+#endif /* defined OS_MINOR_VERSION >= 2 && OS_MINOR_VERSION <= 3 */
+#else
+ #error unknown platform
+#endif /* defined OS_MAJOR_VERSION == 4 */
+#endif /* defined OPENBSD */
+
+#if defined SUNOS
+#if OS_MAJOR_VERSION == 5
+#if OS_MINOR_VERSION == 8
+#if !defined __cplusplus
+#define _XOPEN_SOURCE 600
+#define __EXTENSIONS__
+#endif
+#define HAVE_SNPRINTF
+#define HAVE_VSNPRINTF
+#define HAVE_LOCKF
+#define HAVE_ENUM_BOOL
+#define HAVE_LINK_H
+#else
+#if OS_MINOR_VERSION == 10
+#if !defined __cplusplus
+#define _XOPEN_SOURCE 600
+#define __EXTENSIONS__
+#endif
+#define HAVE_SNPRINTF
+#define HAVE_VSNPRINTF
+#define HAVE_LOCKF
+#define HAVE_STDBOOL_H
+#define HAVE_STDINT_H
+#define HAVE_STRERROR_R
+#else
+ #error unknown platform
+#endif /* OS_MINOR_VERSION == 10 */
+#endif /* OS_MINOR_VERSION == 8 */
+#else
+ #error unknown platform
+#endif /* OS_MAJOR_VERSION == 5 */
+#endif /* defined SUNOS */
+
+#if defined CYGWIN
+#if OS_MAJOR_VERSION == 5
+#if OS_MINOR_VERSION == 0
+#define _XOPEN_SOURCE 600
+#define HAVE_ENUM_BOOL
+#else
+ #error unknown platform
+#endif /* OS_MINOR_VERSION == 0 */
+#else
+ #error unknown platform
+#endif /* OS_MAJOR_VERSION == 5 */
+#endif /* defined CYGWIN */
diff --git a/src/port/unistd.h b/src/port/unistd.h
new file mode 100644
index 0000000..712f851
--- /dev/null
+++ b/src/port/unistd.h
@@ -0,0 +1,12 @@
+#ifndef __UNISTD_H
+#define __UNISTD_H
+
+#include "sys.h"
+
+#include <unistd.h>
+
+#if !defined HAVE_LOCKF
+#include "port/lockf.h"
+#endif
+
+#endif /* ifndef __UNISTD_H */
diff --git a/src/port/unused.h b/src/port/unused.h
new file mode 100644
index 0000000..c7692f6
--- /dev/null
+++ b/src/port/unused.h
@@ -0,0 +1,8 @@
+#ifndef __UNUSED_H
+#define __UNUSED_H
+
+#include "port/sys.h"
+
+#define UNUSED( x ) if( 0 && (x) ) { }
+
+#endif /* ifndef __UNUSED_H */
diff --git a/src/signals.c b/src/signals.c
new file mode 100644
index 0000000..02cbb65
--- /dev/null
+++ b/src/signals.c
@@ -0,0 +1,560 @@
+#include "signals.h"
+#include "daemon.h"
+
+#include "port/string.h" /* for memset */
+
+#include <unistd.h> /* for getpid, pipe, write, read */
+#include <errno.h> /* for errno */
+#include <sys/types.h> /* for ssize_t */
+#include <sys/select.h> /* for FD_XX and select */
+
+#include "errors.h"
+#include "log.h"
+
+#include "port/unused.h"
+
+const char *signal_get_short_name( int sig ) {
+ switch( sig ) {
+ case SIGHUP: return "SIGHUP";
+ case SIGINT: return "SIGINT";
+ case SIGQUIT: return "SIGQUIT";
+ case SIGILL: return "SIGILL";
+ case SIGTRAP: return "SIGTRAP";
+ case SIGABRT: return "SIGABRT";
+#if defined( SIGIOT )
+#if SIGIOT != SIGABRT
+ case SIGIOT: return "SIGIOT";
+#endif
+#endif
+#if defined( SIGEMT )
+ case SIGEMT: return "SIGEMT";
+#endif
+ case SIGBUS: return "SIGBUS";
+ case SIGFPE: return "SIGFPE";
+ case SIGKILL: return "SIGKILL";
+ case SIGUSR1: return "SIGUSR1";
+ case SIGSEGV: return "SIGSEGV";
+ case SIGUSR2: return "SIGUSR2";
+ case SIGPIPE: return "SIGPIPE";
+ case SIGALRM: return "SIGALRM";
+ case SIGTERM: return "SIGTERM";
+#if defined( SIGSTKFLT )
+ case SIGSTKFLT: return "SIGSTKFLT";
+#endif
+#if defined( SIGCLD )
+#if SIGCLD != SIGCHLD
+ /* the SysV version */
+ case SIGCLD: return "SIGCLD";
+#endif
+#endif
+ /* the POSIX version */
+ case SIGCHLD: return "SIGCHLD";
+ case SIGCONT: return "SIGCONT";
+ case SIGSTOP: return "SIGSTOP";
+ case SIGTSTP: return "SIGTSTP";
+ case SIGTTIN: return "SIGTTIN";
+ case SIGTTOU: return "SIGTTOU";
+ case SIGURG: return "SIGURG";
+ case SIGXCPU: return "SIGXCPU";
+ case SIGXFSZ: return "SIGXFSZ";
+ case SIGVTALRM: return "SIGVTALRM";
+ case SIGPROF: return "SIGPROF";
+#if defined( SIGWINCH )
+ case SIGWINCH: return "SIGWINCH";
+#endif
+#if defined( SIGPOLL )
+#if SIGPOLL != SIGIO
+ /* SysV version of SIGIO */
+ case SIGPOLL: return "SIGPOLL";
+#endif
+#endif
+#if defined( SIGIO )
+ case SIGIO: return "SIGIO";
+#endif
+#if defined( SIGPWR )
+ case SIGPWR: return "SIGPWR";
+#endif
+#if defined( SIGSYS )
+ case SIGSYS: return "SIGSYS";
+#endif
+
+ default: return "<unknown>";
+ }
+}
+
+const char *signal_get_long_name( int sig ) {
+ /* we don't trust strsignal, needs _GNU_SOURCE,
+ * naming comes from bits/signum.h on Linux 2.6
+ */
+ switch( sig ) {
+ case SIGHUP: return "Hangup";
+ case SIGINT: return "Interrupt";
+ case SIGQUIT: return "Quit";
+ case SIGILL: return "Illegal instruction";
+ case SIGTRAP: return "Trace trap";
+ case SIGABRT: return "Abort";
+#if defined( SIGIOT )
+#if SIGIOT != SIGABRT
+ case SIGIOT: return "IOT trap";
+#endif
+#endif
+#if defined( SIGEMT )
+ case SIGEMT: return "EMT instruction";
+#endif
+ case SIGBUS: return "BUS error";
+ case SIGFPE: return "Floating-point exception";
+ case SIGKILL: return "Kill";
+ case SIGUSR1: return "User-defined signal 1";
+ case SIGSEGV: return "Segmentation violation";
+ case SIGUSR2: return "User-defined signal 2";
+ case SIGPIPE: return "Broken pipe";
+ case SIGALRM: return "Alarm clock";
+ case SIGTERM: return "Termination";
+#if defined( SIGSTKFLT )
+ case SIGSTKFLT: return "Stack fault";
+#endif
+#if defined( SIGCLD )
+#if SIGCLD != SIGCHLD
+ case SIGCLD: return "Child status has changed";
+#endif
+#endif
+ case SIGCHLD: return "Child status has changed";
+ case SIGCONT: return "Continue";
+ case SIGSTOP: return "Stop";
+ case SIGTSTP: return "Keyboard stop";
+ case SIGTTIN: return "Background read from tty";
+ case SIGTTOU: return "Background write to tty";
+ case SIGURG: return "Urgent condition on socket";
+ case SIGXCPU: return "CPU limit exceeded";
+ case SIGXFSZ: return "File size limit exceeded";
+ case SIGVTALRM: return "Virtual alarm clock";
+ case SIGPROF: return "Profiling alarm clock";
+#if defined( SIGWINCH )
+ case SIGWINCH: return "Window size change";
+#endif
+#if defined( SIGPOLL )
+#if SIGPOLL != SIGIO
+ case SIGPOLL: return "Pollable event occurred";
+#endif
+#endif
+#if defined( SIGIO )
+ case SIGIO: return "I/O now possible";
+#endif
+#if defined( SIGPWR )
+ case SIGPWR: return "Power failure restart";
+#endif
+#if defined( SIGSYS )
+ case SIGSYS: return "Bad system call";
+#endif
+
+ default: return "<unknown signal>";
+ }
+}
+
+static error_t vsignal_install_ignore( int sig, va_list ap ) {
+ struct sigaction sa;
+ char errbuf[1024];
+
+install_ignore_again:
+ memset( &sa, 0, sizeof( struct sigaction ) );
+ sa.sa_handler = SIG_IGN;
+
+ if( sigaction( sig, &sa, NULL ) < 0 ) {
+ (void)strerror_r( errno, errbuf, 1024 );
+ LOG( LOG_CRIT, "Can't ignore signal handler for signal '%s' (%s, %d): %s (errno: %d)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ),
+ sig,
+ errbuf,
+ errno );
+ return ERR_PROGRAMMING;
+ }
+ LOG( LOG_DEBUG, "Ignoring signal handler for signal '%s' (%s)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ) );
+ sig = va_arg( ap, int );
+ if( sig > 0 ) goto install_ignore_again;
+
+ return OK;
+}
+
+/* some signals we should not ignore, like SIGPIPE or SIGC(H)LD, because
+ * results of functions like write and some process data gets lost in
+ * forked children and waitpid.
+ */
+static void empty_handler( int sig ) {
+ UNUSED( sig );
+}
+
+static error_t vsignal_install_empty( int sig, va_list ap ) {
+ struct sigaction sa;
+ char errbuf[1024];
+
+install_empty_again:
+ memset( &sa, 0, sizeof( struct sigaction ) );
+ sa.sa_handler = empty_handler;
+
+ if( sigaction( sig, &sa, NULL ) < 0 ) {
+ (void)strerror_r( errno, errbuf, 1024 );
+ LOG( LOG_CRIT, "Can't install empty signal handler for signal '%s' (%s, %d): %s (errno: %d)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ),
+ sig,
+ errbuf,
+ errno );
+ return ERR_PROGRAMMING;
+ }
+ LOG( LOG_DEBUG, "Installed empty signal handler for signal '%s' (%s)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ) );
+ sig = va_arg( ap, int );
+ if( sig > 0 ) goto install_empty_again;
+
+ return OK;
+}
+
+static void fatal_handler( int sig ) {
+ struct sigaction sa;
+
+ /* reset default behaviour of the signal */
+ memset( &sa, 0, sizeof( struct sigaction ) );
+ sa.sa_handler = SIG_DFL;
+ (void)sigaction( sig, &sa, NULL );
+
+ /* log what happened */
+ LOG( LOG_ALERT, "Got signal '%s' (%s)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ) );
+
+ /* we are in one thread when we got the signal, now inform
+ * all other threads too (see Apache mpm_common.c for this
+ * trick)
+ */
+ kill( getpid( ), sig );
+}
+
+static error_t vsignal_install_func( signal_func_t func, int sig, va_list ap ) {
+ struct sigaction sa;
+ char errbuf[1024];
+
+install_func_again:
+ memset( &sa, 0, sizeof( struct sigaction ) );
+ sa.sa_handler = func;
+ sa.sa_flags = SA_RESTART;
+
+ if( sigaction( sig, &sa, NULL ) < 0 ) {
+ (void)strerror_r( errno, errbuf, 1024 );
+ LOG( LOG_CRIT, "Can't install signal handler for signal '%s' (%s, %d): %s (errno: %d)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ),
+ sig,
+ errbuf,
+ errno );
+ return ERR_PROGRAMMING;
+ }
+ LOG( LOG_DEBUG, "Installed signal handler for signal '%s' (%s)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ) );
+ sig = va_arg( ap, int );
+ if( sig > 0 ) goto install_func_again;
+
+ return OK;
+}
+
+error_t signal_install_func( signal_func_t func, int sig, ... ) {
+ va_list ap;
+ error_t error;
+ va_start( ap, sig );
+ error = vsignal_install_func( func, sig, ap );
+ va_end( ap );
+ return error;
+}
+
+#define INSTALL_SIGNAL( MODE ) \
+error_t signal_install_##MODE( int sig, ... ) { \
+ va_list ap; \
+ error_t error; \
+ va_start( ap, sig ); \
+ error = vsignal_install_##MODE( sig, ap ); \
+ va_end( ap ); \
+ return error; \
+}
+
+INSTALL_SIGNAL( ignore )
+INSTALL_SIGNAL( empty )
+
+#define INSTALL_SIGNAL_FUNC( MODE, FUNC ) \
+error_t signal_install_##MODE( int sig, ... ) { \
+ va_list ap; \
+ error_t error; \
+ va_start( ap, sig ); \
+ error = vsignal_install_func( FUNC, sig, ap ); \
+ va_end( ap ); \
+ return error; \
+}
+
+INSTALL_SIGNAL_FUNC( fatal, fatal_handler )
+
+int daemon_signal_pipe[2] = { -1, -1 };
+
+static void notify_parent_handler( int sig ) {
+ if( daemon_parent_pipe[1] != -1 )
+ (void)write( daemon_parent_pipe[1], &sig, sizeof( int ) );
+}
+
+static void notify_handler( int sig ) {
+ if( daemon_signal_pipe[1] != -1 )
+ (void)write( daemon_signal_pipe[1], &sig, sizeof( int ) );
+}
+
+INSTALL_SIGNAL_FUNC( notify, notify_handler )
+INSTALL_SIGNAL_FUNC( notify_parent, notify_parent_handler )
+
+error_t signal_initialize( void ) {
+ int res;
+ char errbuf[1024];
+
+ if( daemon_signal_pipe[0] != -1 ||
+ daemon_signal_pipe[1] != -1 ) {
+ return ERR_INVALID_STATE;
+ }
+
+ res = pipe( daemon_signal_pipe );
+ if( res < 0 ) {
+ (void)strerror_r( errno, errbuf, 1024 );
+ LOG( LOG_EMERG, "Unable to create signal pipe: %s (%d)",
+ errbuf, errno );
+ return ERR_INTERNAL;
+ }
+ LOG( LOG_DEBUG, "Created signal pipe (%d,%d)", daemon_signal_pipe[0], daemon_signal_pipe[1] );
+
+ return OK;
+}
+
+void signal_terminate( void ) {
+ (void)close( daemon_signal_pipe[0] );
+ (void)close( daemon_signal_pipe[1] );
+}
+
+int signal_suspend( int timeout, error_t *error ) {
+ struct timeval tv;
+ fd_set fds;
+ ssize_t res;
+ int sig;
+ char errbuf[1024];
+
+ if( daemon_signal_pipe[0] == -1 ) {
+ *error = ERR_INVALID_STATE;
+ return -1;
+ }
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ FD_ZERO( &fds );
+ FD_SET( daemon_signal_pipe[0], &fds );
+
+signal_select_again:
+ res = select( daemon_signal_pipe[0] + 1, &fds, 0, 0, &tv );
+ switch( res ) {
+ case 1: /* ready on pipe */
+ break;
+
+ case 0: /* timeout */
+ *error = ERR_TIMEOUT;
+ return 0;
+
+ case -1: /* error */
+ if( errno == EINTR ) goto signal_select_again;
+ /* gdb no-brainer when pressing Ctrl-C (at least Linux) */
+ if( errno == 514 ) goto signal_select_again;
+ (void)strerror_r( errno, errbuf, 1024 );
+ LOG( LOG_EMERG, "Error in select when waiting for signal from pipe: %s (%d)",
+ errbuf, errno );
+ *error = ERR_INTERNAL;
+ return -1;
+ }
+
+ res = read( daemon_signal_pipe[0], &sig, sizeof( int ) );
+ switch( res ) {
+ case sizeof( int ): /* got a signal */
+ break;
+
+ case -1: /* error */
+ (void)strerror_r( errno, errbuf, 1024 );
+ LOG( LOG_EMERG, "Error while reading a signal from the pipe: %s (%d)",
+ errbuf, errno );
+ *error = ERR_INTERNAL;
+ return -1;
+
+ default: /* unexpected result on atomic read */
+ LOG( LOG_EMERG, "Unexpected error in read: result is %d", res );
+ *error = ERR_PROGRAMMING;
+ return -1;
+ }
+
+ *error = OK;
+ return sig;
+}
+
+error_t signal_install_handlers_parent( void ) {
+ error_t error;
+
+ /* signals to ignore */
+ if( ( error = signal_install_ignore(
+ SIGPIPE,
+ SIGTSTP,
+ SIGTTIN,
+ SIGTTOU,
+ SIGURG,
+ SIGXCPU, /* later: throthling */
+ SIGXFSZ, /* later: close connections, reduce channels */
+ SIGVTALRM,
+ SIGPROF,
+#if defined( SIGWINCH )
+ SIGWINCH,
+#endif
+#if defined( SIGPOLL )
+ SIGPOLL,
+#endif
+#if defined( SIGIO )
+#if SIGIO != SIGPOLL
+ SIGIO,
+#endif
+#endif
+#if defined( SIGPWR )
+ SIGPWR,
+#endif
+ SIGTERM,
+ SIGINT,
+ SIGHUP,
+ SIGUSR1,
+ SIGUSR2,
+ 0 ) ) != OK ) return error;
+
+ /* signals for empty handlers */
+ if( ( error = signal_install_empty(
+#if defined( SIGCHLD ) && defined( SIGCLD )
+#if SIGCLD != SIGCHLD
+ SIGCHLD,
+#else
+#if defined( SIGCHLD )
+ SIGCHLD,
+#endif
+#if defined( SIGCLD )
+ SIGCLD,
+#endif
+#endif
+#endif
+ 0 ) ) != OK ) return error;
+
+ /* fatal signal handlers, make sure the parent can read the
+ * signal and clean up
+ */
+ if( ( error = signal_install_notify_parent(
+ SIGILL,
+ SIGABRT,
+#if defined( SIGIOT )
+#if SIGIOT != SIGABRT
+ SIGIOT,
+#endif
+#endif
+ SIGBUS,
+ SIGFPE,
+ SIGSEGV,
+ SIGVTALRM,
+#if defined( SIGSTKFLT )
+ SIGSTKFLT,
+#endif
+#if defined( SIGSYS )
+ SIGSYS,
+#endif
+ SIGALRM, /* we don't use it, but maybe a plugin */
+ 0 ) ) != OK ) return error;
+
+ return OK;
+}
+
+
+error_t signal_install_handlers_daemon( void ) {
+ error_t error;
+
+ /* signals to ignore */
+ if( ( error = signal_install_ignore(
+ SIGPIPE,
+ SIGTSTP,
+ SIGTTIN,
+ SIGTTOU,
+ SIGURG,
+ SIGXCPU, /* later: throthling */
+ SIGXFSZ, /* later: close connections, reduce channels */
+ SIGVTALRM,
+ SIGPROF,
+#if defined( SIGWINCH )
+ SIGWINCH,
+#endif
+#if defined( SIGPOLL )
+ SIGPOLL,
+#endif
+#if defined SIGIO
+#if SIGIO != SIGPOLL
+ SIGIO,
+#endif
+#endif
+#if defined( SIGPWR )
+ SIGPWR,
+#endif
+ 0 ) ) != OK ) return error;
+
+ /* signals for empty handlers */
+ if( ( error = signal_install_empty(
+#if defined( SIGCHLD ) && defined( SIGCLD )
+#if SIGCLD != SIGCHLD
+ SIGCHLD,
+#else
+#if defined( SIGCHLD )
+ SIGCHLD,
+#endif
+#if defined( SIGCLD )
+ SIGCLD,
+#endif
+#endif
+#endif
+ 0 ) ) != OK ) return error;
+
+ /* fatal signal handlers, log the exception and continue with
+ * default behaviour of the system
+ */
+ if( ( error = signal_install_fatal(
+ SIGILL,
+ SIGABRT,
+#if defined( SIGIOT )
+#if SIGIOT != SIGABRT
+ SIGIOT,
+#endif
+#endif
+ SIGBUS,
+ SIGFPE,
+ SIGSEGV,
+ SIGVTALRM,
+#if defined( SIGSTKFLT )
+ SIGSTKFLT,
+#endif
+#if defined( SIGSYS )
+ SIGSYS,
+#endif
+ SIGALRM, /* we don't use it, but maybe a plugin */
+ 0 ) ) != OK ) return error;
+
+ /* notify the following signals to the main loop */
+ if( ( error = signal_install_notify(
+ SIGTERM,
+ SIGINT,
+ SIGHUP,
+ SIGUSR1,
+ SIGUSR2,
+ 0 ) ) != OK ) return error;
+
+ return OK;
+}
+
diff --git a/src/signals.h b/src/signals.h
new file mode 100644
index 0000000..33f9dab
--- /dev/null
+++ b/src/signals.h
@@ -0,0 +1,46 @@
+#ifndef __SIGNALS_H
+#define __SIGNALS_H
+
+#include "errors.h"
+
+#include <stdarg.h> /* for variable arguments */
+#include <signal.h> /* for signal constants,
+ sigaction, kill, etc. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *signal_get_short_name( int sig );
+
+const char *signal_get_long_name( int sig );
+
+error_t signal_install_ignore( int sig, ... );
+
+error_t signal_install_empty( int sig, ... );
+
+typedef void (*signal_func_t)( int );
+
+error_t signal_install_func( signal_func_t func, int sig, ... );
+
+error_t signal_install_fatal( int sig, ... );
+
+error_t signal_install_notify( int sig, ... );
+
+error_t signal_install_notify_parent( int sig, ... );
+
+error_t signal_initialize( void );
+
+int signal_suspend( int timeout, error_t *error );
+
+void signal_terminate( void );
+
+error_t signal_install_handlers_parent( void );
+
+error_t signal_install_handlers_daemon( void );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef __SIGNALS_H */
diff --git a/src/testd.c b/src/testd.c
new file mode 100644
index 0000000..5138b12
--- /dev/null
+++ b/src/testd.c
@@ -0,0 +1,149 @@
+#include "port/stdbool.h" /* for bool */
+#include "port/string.h" /* for memset */
+
+#include <sys/types.h> /* for pid_t */
+#include <unistd.h> /* for exit, unistd, getuid, getppid */
+#include <stdlib.h> /* for EXIT_FAILURE */
+
+#include "errors.h" /* global error codes */
+#include "cmdline.h" /* for command line and option parsing (gengetopt) */
+#include "log.h" /* logging facility */
+#include "daemon.h" /* Unix daemonizing code */
+#include "signals.h" /* signal supension */
+
+#include "port/unused.h"
+
+#define DEFAULT_CONFIG_FILE "/etc/" CMDLINE_PARSER_PACKAGE ".conf"
+
+static int parse_options_and_arguments( int argc, char *argv[], struct gengetopt_args_info *args_info ) {
+ cmdline_parser_init( args_info );
+
+ if( cmdline_parser2( argc, argv, args_info, 1, 0, 1 ) != 0 ) {
+ cmdline_parser_free( args_info );
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int test_config( const char *filename ) {
+ UNUSED( filename );
+ return EXIT_SUCCESS;
+}
+
+static int read_config( const char *filename ) {
+ UNUSED( filename );
+ return EXIT_SUCCESS;
+}
+
+int main( int argc, char *argv[] ) {
+ struct gengetopt_args_info args_info;
+ error_t error;
+ daemon_params_t daemon_params;
+ daemon_p demon = NULL;
+ int sig = 0;
+
+ if( parse_options_and_arguments( argc, argv, &args_info ) == EXIT_FAILURE ) {
+ exit( EXIT_FAILURE );
+ }
+
+ if( read_config( args_info.config_file_given ?
+ args_info.config_file_arg : DEFAULT_CONFIG_FILE ) == EXIT_FAILURE ) {
+ exit( EXIT_FAILURE );
+ }
+
+ if( args_info.test_given ) {
+ cmdline_parser_free( &args_info );
+ exit( test_config( args_info.config_file_given ?
+ args_info.config_file_arg : DEFAULT_CONFIG_FILE ) );
+ }
+
+ openlogtostderr( LOG_DEBUG - 1 + (int)args_info.debug_given );
+ if( args_info.logfile_given )
+ openlogtofile( args_info.logfile_arg,
+ args_info.logfile_level_given ?
+ log_str_to_level( args_info.logfile_level_arg ) : LOG_NOTICE );
+
+ if( !args_info.foreground_given ) {
+ openlogtosyslog( CMDLINE_PARSER_PACKAGE,
+ args_info.syslog_facility_given ?
+ log_str_to_syslog_facility( args_info.syslog_facility_arg ) : LOG_DAEMON,
+ args_info.syslog_level_given ?
+ log_str_to_level( args_info.syslog_level_arg ) : LOG_NOTICE );
+
+ memset( &daemon_params, 0, sizeof( daemon_params ) );
+ daemon_params.daemon_name = CMDLINE_PARSER_PACKAGE;
+ daemon_params.pid_filename = args_info.pidfile_given ?
+ args_info.pidfile_arg : NULL;
+ daemon_params.group_name = args_info.group_given ?
+ args_info.group_arg : NULL;
+ daemon_params.user_name = args_info.user_given ?
+ args_info.user_arg : NULL;
+
+ demon = daemon_new( daemon_params, &error );
+ if( demon == NULL ) {
+ cmdline_parser_free( &args_info );
+ exit( EXIT_FAILURE );
+ }
+
+ if( ( error = daemon_start( demon ) ) != OK ) {
+ cmdline_parser_free( &args_info );
+ daemon_exit( demon );
+ }
+ } else {
+ if( ( error = signal_initialize( ) ) != OK ) {
+ cmdline_parser_free( &args_info );
+ exit( EXIT_FAILURE );
+ }
+ if( ( error = signal_install_handlers_daemon( ) ) != OK ) {
+ signal_terminate( );
+ cmdline_parser_free( &args_info );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ LOG( LOG_NOTICE, "Started %s daemon", CMDLINE_PARSER_PACKAGE );
+ while( ( sig != SIGTERM ) && ( sig != SIGINT ) && ( sig != -1 ) ) {
+ sig = signal_suspend( 60, &error );
+ switch( sig ) {
+ case 0: /* timeout */
+ break;
+
+ case -1: /* internal error */
+ break;
+
+ case SIGHUP:
+ LOG( LOG_NOTICE, "Rereading configuration" );
+ break;
+
+ case SIGTERM:
+ case SIGINT:
+ LOG( LOG_NOTICE, "Got termination signal, shutting down the daemon" );
+ break;
+
+ case SIGUSR1:
+ break;
+
+ case SIGUSR2:
+ break;
+
+ default:
+ LOG( LOG_ERR, "Unexpected signal '%s' (%s, %d)",
+ signal_get_long_name( sig ),
+ signal_get_short_name( sig ),
+ sig );
+ }
+ }
+ LOG( LOG_NOTICE, "Stopped %s daemon", CMDLINE_PARSER_PACKAGE );
+
+ if( !args_info.foreground_given ) {
+ cmdline_parser_free( &args_info );
+ daemon_exit( demon );
+ } else {
+ cmdline_parser_free( &args_info );
+ signal_terminate( );
+ exit( EXIT_SUCCESS );
+ }
+
+ exit( EXIT_FAILURE );
+}
diff --git a/tests/GNUmakefile b/tests/GNUmakefile
new file mode 100644
index 0000000..caf20e7
--- /dev/null
+++ b/tests/GNUmakefile
@@ -0,0 +1,31 @@
+TOPDIR = ..
+
+SUBDIRS =
+
+INCLUDE_DIRS = -I.
+
+BINS = \
+ stdargs_for_signal_functions$(EXE)
+
+OBJS =
+
+-include $(TOPDIR)/makefiles/sub.mk
+
+test: all
+ @./stdargs_for_signal_functions
+
+# TODO:still a little bit unrealiable with fakeroot
+# @fakeroot $(TOPDIR)/src/testd -d --pidfile /tmp/testd.pid && \
+# sleep 3 && \
+# ls -altr /tmp/testd* && \
+# cat /tmp/testd.pid
+# -@ps -alef | grep test | grep -v grep
+# @sleep 1
+# @pkill testd
+# -@ls -altr /tmp/testd*
+
+local_all:
+
+local_clean:
+
+local_distclean:
diff --git a/tests/stdargs_for_signal_functions.c b/tests/stdargs_for_signal_functions.c
new file mode 100644
index 0000000..f752630
--- /dev/null
+++ b/tests/stdargs_for_signal_functions.c
@@ -0,0 +1,33 @@
+#include <stdarg.h>
+#include <stdio.h>
+
+static void vf2( const char *s, int a, va_list ap ) {
+ int v;
+
+ printf( "ap: %s %d", s, a );
+ v = va_arg( ap, int );
+ while( v != 0 ) {
+ printf( "%d ", v );
+ v = va_arg( ap, int );
+ }
+ puts( "" );
+}
+
+static void f2( const char *s, int a, ... ) {
+ va_list ap;
+ va_start( ap, a );
+ vf2( s, a, ap );
+ va_end( ap );
+}
+
+static void f( int a, ... ) {
+ va_list ap;
+ va_start( ap, a );
+ vf2( "f", a, ap );
+ va_end( ap );
+}
+
+int main( void ) {
+ f2( "f2", 1, 2, 3, 4, 0 );
+ f( 1, 2, 3, 4, 0 );
+}