minimal skeleton
authorLaurent Mazet <laurent.mazet@thalesgroup.com>
Mon, 24 Apr 2023 09:19:33 +0000 (11:19 +0200)
committerLaurent Mazet <laurent.mazet@thalesgroup.com>
Mon, 24 Apr 2023 09:19:33 +0000 (11:19 +0200)
color.c [new file with mode: 0644]
color.h [new file with mode: 0644]
debug.c [new file with mode: 0644]
debug.h [new file with mode: 0644]
getcomments.pl [new file with mode: 0644]
makefile [new file with mode: 0644]
webserver.c [new file with mode: 0644]

diff --git a/color.c b/color.c
new file mode 100644 (file)
index 0000000..7aec785
--- /dev/null
+++ b/color.c
@@ -0,0 +1,96 @@
+#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: */
diff --git a/color.h b/color.h
new file mode 100644 (file)
index 0000000..6a2e8dc
--- /dev/null
+++ b/color.h
@@ -0,0 +1,41 @@
+#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: */
diff --git a/debug.c b/debug.c
new file mode 100644 (file)
index 0000000..151e615
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,5 @@
+#include "debug.h"
+
+int verbose = 1;
+
+/* vim: set ts=4 sw=4 et: */
diff --git a/debug.h b/debug.h
new file mode 100644 (file)
index 0000000..46442d4
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,32 @@
+#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: */
diff --git a/getcomments.pl b/getcomments.pl
new file mode 100644 (file)
index 0000000..47edf67
--- /dev/null
@@ -0,0 +1,85 @@
+#!/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";
+    }
+  }
+}
diff --git a/makefile b/makefile
new file mode 100644 (file)
index 0000000..b8a856e
--- /dev/null
+++ b/makefile
@@ -0,0 +1,144 @@
+# 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
diff --git a/webserver.c b/webserver.c
new file mode 100644 (file)
index 0000000..c5d2199
--- /dev/null
@@ -0,0 +1,95 @@
+/* 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: */