--- /dev/null
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <stdio.h>
+#endif
+
+#include "color.h"
+
+#ifdef _WIN32 /* Windows */
+
+void color_set (FILE *fid, color_e color)
+{
+ /* save default configuration */
+ static WORD init = 0;
+ if (init == 0) {
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
+ init = info.wAttributes;
+ }
+
+ /* default attribute */
+ WORD attr = 0;
+
+ /* define foreground color */
+ if (FG_DEFAULT == (color & FG_MASK)) {
+ attr |= init & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ } else {
+ attr |= (color & FG_BRIGHT) ? FOREGROUND_INTENSITY : 0;
+ switch (color & FG_MASK) {
+ case FG_RED: attr |= FOREGROUND_RED; break;
+ case FG_GREEN: attr |= FOREGROUND_GREEN; break;
+ case FG_BLUE: attr |= FOREGROUND_BLUE; break;
+ case FG_YELLOW: attr |= FOREGROUND_RED | FOREGROUND_GREEN; break;
+ case FG_MAGENTA: attr |= FOREGROUND_RED | FOREGROUND_BLUE; break;
+ case FG_CYAN: attr |= FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ case FG_WHITE: attr |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+ }
+ }
+
+ /* define background color */
+ if (BG_DEFAULT == (color & BG_MASK)) {
+ attr |= init & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
+ } else {
+ attr |= (color & BG_BRIGHT) ? BACKGROUND_INTENSITY : 0;
+ switch (color&BG_MASK) {
+ case BG_RED: attr |= BACKGROUND_RED; break;
+ case BG_GREEN: attr |= BACKGROUND_GREEN; break;
+ case BG_BLUE: attr |= BACKGROUND_BLUE; break;
+ case BG_YELLOW: attr |= BACKGROUND_RED | BACKGROUND_GREEN; break;
+ case BG_MAGENTA: attr |= BACKGROUND_RED | BACKGROUND_BLUE; break;
+ case BG_CYAN: attr |= BACKGROUND_GREEN | BACKGROUND_BLUE; break;
+ case BG_WHITE: attr |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; break;
+ }
+ }
+
+ /* define underline state */
+ if (color & UNDERLINE) {
+ attr |= COMMON_LVB_UNDERSCORE;
+ }
+
+ /* set text attribute */
+ SetConsoleTextAttribute (GetStdHandle ((fid == stdout) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE), attr);
+}
+
+#else /* Linux */
+
+void color_set (FILE *fid, color_e color)
+{
+ /* check if it's a terminal */
+ if (!isatty (fileno (fid))) {
+ return;
+ }
+
+ /* default color */
+ fprintf (fid, "\033[m");
+
+ /* set foreground color */
+ if (FG_DEFAULT != (color & FG_MASK)) {
+ fprintf (fid, "\033[%dm", 29 + (color & FG_MASK) + ((color & FG_BRIGHT) ? 60 : 0));
+ }
+
+ /* set background color */
+ if (BG_DEFAULT != (color & BG_MASK)) {
+ fprintf (fid, "\033[%dm", 39 + ((color & BG_MASK ) >> 8) + ((color & BG_BRIGHT) ? 60 : 0));
+ }
+
+ /* set underline state */
+ if (color & UNDERLINE) {
+ fprintf (fid, "\033[4m");
+ }
+}
+
+#endif /* _WIN32 */
+
+/* vim: set ts=4 sw=4 et: */
--- /dev/null
+#ifndef __COLOR_H__
+#define __COLOR_H__
+
+#include "stdio.h"
+
+typedef enum {
+ FG_DEFAULT = 0x00,
+ FG_BLACK = 0x01,
+ FG_RED = 0x02,
+ FG_GREEN = 0x03,
+ FG_YELLOW = 0x04,
+ FG_BLUE = 0x05,
+ FG_MAGENTA = 0x06,
+ FG_CYAN = 0x07,
+ FG_WHITE = 0x08,
+ FG_BRIGHT = 0x80,
+ FG_MASK = 0x7F,
+
+ BG_DEFAULT = 0x0000,
+ BG_BLACK = 0x0100,
+ BG_RED = 0x0200,
+ BG_GREEN = 0x0300,
+ BG_YELLOW = 0x0400,
+ BG_BLUE = 0x0500,
+ BG_MAGENTA = 0x0600,
+ BG_CYAN = 0x0700,
+ BG_WHITE = 0x0800,
+ BG_BRIGHT = 0x8000,
+ BG_MASK = 0x7F00,
+
+ UNDERLINE = 0x10000,
+
+ COLOR_DEFAULT = FG_DEFAULT | BG_DEFAULT
+} color_e;
+
+/* Set text color, COLOR_DEFAULT will revert to default setting */
+void color_set (FILE *fid, color_e color);
+
+#endif /* __COLOR_H__ */
+
+/* vim: set ts=4 sw=4 et: */
--- /dev/null
+#include "debug.h"
+
+int verbose = 1;
+
+/* vim: set ts=4 sw=4 et: */
--- /dev/null
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+#include "color.h"
+
+/* constants */
+
+#define DEBUG 3
+#define INFO 2
+#define WARNING 1
+#define ERROR 0
+
+/* macros */
+
+#define VERBOSE(level, statement...) \
+ do { if (level <= verbose) { CHANGE_COLOR(level); statement; CHANGE_COLOR(-1); } } while(0)
+
+#define CHANGE_COLOR(level) \
+ color_set ((level == ERROR) ? stderr : stdout, \
+ (level == ERROR) ? FG_RED : \
+ (level == WARNING) ? FG_YELLOW : \
+ (level == INFO) ? FG_BLUE : \
+ (level == DEBUG) ? FG_GREEN : COLOR_DEFAULT)
+
+/* vim: set ts=4 sw=4 et: */
+/* gobal variables */
+
+extern int verbose;
+
+#endif /* __DEBUG_H__ */
+
+/* vim: set ts=4 sw=4 et: */
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+
+# default value
+my $format = "%";
+my $pattern = "";
+
+# help message
+sub usage() {
+
+ print <<EOF;
+usage: getcomments [-f string] [-h] [-p regex] file...
+ -f|--format string: format string for output printing [%]
+ -h|--help: help message
+ -p|--pattern regex: pattern matching on block []
+
+ Extract C/C++ block of comments
+
+Example: getcomments.pl -p='test:\\s' -f='./%' random.c
+EOF
+
+ exit 1;
+}
+
+usage() if ($#ARGV < 0);
+
+# process argument
+foreach my $arg (@ARGV) {
+ use vars qw/$caif $caip $naif $naip/;
+
+ # analyse format argument
+ ($caif, $_) = ($arg =~ /^(-f|--format)=(.*)/);
+ ($caif, $_) = (1, $arg) if ($naif);
+ next if ($naif = ($arg =~ /^(-f|--format)$/));
+ if ($caif) { $format = $_; next }
+
+ # check for help message
+ usage() if ($arg =~ /^(-h|--help)$/);
+
+ # analyse pattern argument
+ ($caip, $_) = ($arg =~ /^(-p|--pattern)=(.*)/);
+ ($caip, $_) = (1, $arg) if ($naip);
+ next if ($naip = ($arg =~ /^(-p|--pattern)$/));
+ if ($caip) { $pattern = $_; next }
+
+ # no more argument, only file
+ my $filename = $arg;
+
+ # open file
+ if (!open (IN, "<", $filename)) {
+ print "Can not open $filename\n";
+ }
+
+ # init table of comments
+ my @comments;
+ $#comments = -1;
+
+ # read all the file
+ while ($_ .= <IN>) {
+ my $cmt;
+
+ # process c++ comments
+ ($cmt, $_) = m{//\s*(.*?)\s*$()} if (m{//} && !m{/\*.*//});
+
+ # process standard c comments
+ ($cmt, $_) = m{^.*?/\*\s*(.*?)\s*\*/(.*)}s if (m{/\*.*\*/}s);
+
+ push(@comments, $cmt) if ($cmt);
+
+ # empty buffer if no comment is present
+ undef($_) if (!m{/[/*]});
+ }
+
+ # close file
+ close (IN);
+
+ # display comment blocks
+ foreach my $block (@comments) {
+ if (($block) = ($block =~ /$pattern(.*)/s)) {
+ ($_ = $format) =~ s/%/$block/gs;
+ print "$_\n";
+ }
+ }
+}
--- /dev/null
+# Default flags
+
+CC = gcc
+
+INCLUDES = -I../debug -D__MEMORY_ALLOCATION__
+OFLAGS = -O4 -Os
+#OFLAGS = -O4 -ffast-math -finline-functions
+#OFLAGS = -O4 -finline-functions
+#OFLAGS += -mtune=pentium3 -mmmx -msse -msse2 -m3dnow
+#OFLAGS += -minline-all-stringops -fsingle-precision-constant
+#OFLAGS += -malign-double
+CFLAGS += -W -Wall -Wextra -g
+CFLAGS += -std=c99 -D_XOPEN_SOURCE=500
+CFLAGS += $(OFLAGS) $(INCLUDES) $(GCOV)
+LDFLAGS += -g $(GCOV)
+
+# Targets
+
+ALLEXE =
+ALLEXE += webserver
+#ALLEXE += skel
+
+SHELL = bash
+
+#MAKE = mingw32-make
+MAKEFLAGS += -s
+include $(wildcard .makefile)
+
+# Functions
+
+TITLE = echo -en "\033[0;1m$(strip $(1))\033[0;0m:\t"
+PASS = echo -e "\033[1;32m$(strip $(1))\033[0;0m"
+WARN = echo -e "\033[1;33m$(strip $(1))\033[0;0m"
+FAIL = echo -e "\033[1;31m$(strip $(1))\033[0;0m"
+
+MKDIR = mkdir -p $(1) && chmod a+rx,go-w $(1)
+
+INSTALL = test -d `dirname $(2)` || $(call MKDIR, `dirname $(2)`) && cp -pa $(1) $(2) && chmod a+rX,go-w $(2)
+
+VALID = $(call TITLE, $(1)) && $(2) && $(call PASS, SUCCESS) || { $(call FAIL, FAILED); test; }
+
+## Generic rules
+
+all: depends
+ $(MAKE) $(ALLEXE:%=%.exe)
+
+count:
+ wc $(wildcard *.c *.h) $(MAKEFILE_LIST)
+
+clean:
+ $(call TITLE, "Cleaning")
+ touch clean
+ rm -f clean $(wildcard *.d *.ld *.log *.o *.test *~ .exec_*)
+ $(call PASS, SUCCESS)
+
+depends: $(patsubst %.c, %.d, $(wildcard *.c)) $(patsubst %, %.ld, $(ALLEXE))
+
+gcovs:
+ $(MAKE) $(addprefix gcov_,$(ALLEXE))
+
+purge: clean
+ $(call TITLE, "Purging")
+ touch purge
+ rm -f purge $(ALLEXE:%=%.exe)
+ $(call PASS, SUCCESS)
+
+valgrinds:
+ $(MAKE) $(addprefix valgrind_,$(ALLEXE))
+
+wipe: purge
+ $(call TITLE, "Wiping")
+ touch wipe
+ rm -f wipe $(wildcard *.gcda *.gcno *.gcov)
+ $(call PASS, SUCCESS)
+
+tests: all
+ $(MAKE) $(addprefix test_,$(ALLEXE))
+
+## Main rules
+
+include $(wildcard *.d)
+include $(wildcard *.ld)
+
+gcov_%:
+ $(MAKE) purge
+ GCOV="-coverage -O0" $(MAKE)
+ $(MAKE) test_$(@:gcov_%=%)
+ gcov `sed -e 's/\.exe:/.c/;s/\.o/.c/g' $(@:gcov_%=%.ld)`
+ touch gcov
+ rm -f gcov $(wildcard *.gcda *.gcno)
+ $(MAKE) purge
+ grep '#####' *.c.gcov || true
+
+%.test: %.c
+ $(call TITLE, "Building $@")
+# awk '/\/\* *test:.*\*\// { sub(/^.*\/\* *test: */, ""); sub(/ *\*\/.*$$/, ""); print }' $< > $@
+ perl -- getcomments.pl -p='test:\s' -f='%' $< > $@
+ $(call PASS, SUCCESS)
+
+test_%: %.test %.exe
+ IFS=$$'\n'; RC=0; \
+ for test in `cat $< | sed 's,${<:.test=.exe},$(VALGRIND) ./${<:.test=.exe},g'`; do \
+ echo "=== $$test ==="; \
+ eval $$test; \
+ [ $$? -eq 0 ] && echo -e "\033[1;32mSUCCESS\033[0;0m" \
+ || { echo -e "\033[1;31mFAILED\033[0;0m"; RC=1; }; \
+ done; \
+ test "$$RC" -ne 1
+
+valgrind_%: %.exe
+ VALGRIND="valgrind -v --leak-check=full --log-fd=3"; \
+ export VALGRIND; \
+ $(MAKE) $(@:valgrind_%=test_%) 3>$@.log
+
+%.d: %.c
+ $(call TITLE, "Building $@")
+ $(CC) $(INCLUDES) -MM $< -o $@~
+ echo ${<:.c=.o}: $(shell perl -- getcomments.pl -p='depend:\s' -f='%' $<) >> $@~
+ mv $@~ $@
+ $(call PASS, SUCCESS)
+
+%.ld: %.c
+ $(call TITLE, "Building $@")
+ echo ${<:.c=.exe}: $(shell perl -- getcomments.pl -p='linker:\s' -f='%' $< | awk '{for (i=1;i<=NF;i++) if ($$(i) ~ /.o$$/) printf " %s", $$(i)}') > $@
+ $(call PASS, SUCCESS)
+
+%.o: %.c
+ $(call TITLE, "Building $@")
+ $(CC) $(CFLAGS) $(INCLUDES) $(shell perl -- getcomments.pl -p='cflags:\s' -f='%' $<) -c $< -o $@
+ $(call PASS, SUCCESS)
+
+
+%.exe: %.o %.d
+ $(call TITLE, "Building $@")
+ $(CC) $(LDFLAGS) $< $(shell perl -- getcomments.pl -p='linker:\s' -f='%' ${<:.o=.c}) -o $@
+ $(call PASS, SUCCESS)
+
+## Phony
+
+.PHONY: all clean count depends gcovs purge tests
+
+## Precious
+
+.PRECIOUS: %.d %.o
--- /dev/null
+/* depend: */
+/* cflags: */
+/* linker: color.o debug.o */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "debug.h"
+
+/* constants */
+
+#define BUFFER_SIZE 4096
+
+/* macros */
+
+/* gobal variables */
+
+char *progname = NULL;
+
+/* help function */
+
+int usage (int ret)
+{
+ FILE *fid = ret ? stderr : stdout;
+ fprintf (fid, "usage: %s\n", progname);
+ fprintf (fid, " -h : help message\n");
+ fprintf (fid, " -v : verbose level (%d)\n", verbose);
+
+ return ret;
+}
+
+/* main function */
+
+int main (int argc, char *argv[])
+{
+ int i = 0;
+ int ret = 0;
+
+ /* program name */
+
+ progname = argv[0];
+ while (progname[i] != '\0') {
+ if ((progname[i] == '/') || (progname[i] == '\\')) {
+ progname += i + 1;
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+
+ /* argument processing */
+
+ while (argc-- > 1) {
+ char *arg = *(++argv);
+ if (arg[0] != '-') {
+ VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- '%s'\n", progname, arg); usage (1));
+ return 1;
+ }
+ char c = arg[1];
+ switch (c) {
+/*
+ case 'i':
+ arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+ if (arg == NULL) {
+ VERBOSE (ERROR, fprintf (stderr, "%s: missing input prompt\n", progname); usage (1));
+ return 1;
+ }
+ iprompt = arg;
+ break;
+*/
+ case 'v':
+ arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL;
+ if (arg == NULL) {
+ VERBOSE (ERROR, fprintf (stderr, "%s: missing verbose level\n", progname); usage (1));
+ return 1;
+ }
+ verbose = atoi (arg);
+ break;
+ case 'h':
+ default:
+ return usage (c != 'h');
+ }
+ }
+
+ return ret;
+}
+
+// test: webserver.exe -h
+// test: webserver.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
+// test: echo 1 | webserver.exe -v3 | grep -q value
+// test: webserver.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
+// test: webserver.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
+// test: webserver.exe error 2>&1 | grep -q 'invalid option'
+
+/* vim: set ts=4 sw=4 et: */