diff --git a/Makefile b/Makefile
index 7a62602f3aef3a28b0419d968f24f9bf2d31f629..81cce48e4319a4f780159c3f53c6d3c9c442931a 100644
--- a/Makefile
+++ b/Makefile
@@ -27,16 +27,14 @@ MAKECONFIG_DIR=share
 include $(MAKECONFIG_DIR)/Makefile.common
 
 ##############################################################################
-# PTESTS SRC
+# TOOLS CONFIG
 ################################
 
-FRAMAC_PTESTS_SRC:=tools/ptests
-
-##############################################################################
-# HDRCK SRC
-################################
+IN_FRAMAC:=yes
 
+FRAMAC_PTESTS_SRC:=tools/ptests
 FRAMAC_HDRCK_SRC:=tools/hdrck
+FRAMAC_LINTCK_SRC:=tools/lint
 
 ##############################################################################
 # Frama-C
@@ -80,9 +78,6 @@ include ivette/Makefile.installation
 # HEADER MANAGEMENT
 ################################
 
-# HDRCK is internal
-FRAMAC_HDRCK:=headers/hdrck.exe
-
 # Part that can be shared for external plugins
 include share/Makefile.headers
 
@@ -112,8 +107,6 @@ include share/Makefile.testing
 # Linters
 ################################
 
-###############################################################################
-
 # Code prettyfication and lint
 include share/Makefile.linting
 
diff --git a/dev/check-files.sh b/dev/check-files.sh
new file mode 100755
index 0000000000000000000000000000000000000000..60cb4899acf926f7457a17a351b32eb3506d4a19
--- /dev/null
+++ b/dev/check-files.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+##########################################################################
+#                                                                        #
+#  This file is part of Frama-C.                                         #
+#                                                                        #
+#  Copyright (C) 2007-2022                                               #
+#    CEA (Commissariat à l'énergie atomique et aux énergies              #
+#         alternatives)                                                  #
+#                                                                        #
+#  you can redistribute it and/or modify it under the terms of the GNU   #
+#  Lesser General Public License as published by the Free Software       #
+#  Foundation, version 2.1.                                              #
+#                                                                        #
+#  It is distributed in the hope that it will be useful,                 #
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
+#  GNU Lesser General Public License for more details.                   #
+#                                                                        #
+#  See the GNU Lesser General Public License version 2.1                 #
+#  for more details (enclosed in the file licenses/LGPLv2.1).            #
+#                                                                        #
+##########################################################################
+
+THIS_SCRIPT="$0"
+LINT=check-lint
+HDRCK=check-headers
+DO_LINT="yes"
+DO_HDRCK="yes"
+REFERENCE=
+MODE="all"
+
+while [ "$1" != "" ]
+do
+    case "$1" in
+        "-h"|"-help"|"--help")
+            echo "${THIS_SCRIPT} [OPTIONS]"
+            echo "OPTIONS"
+            echo "  -h|-help|--help    print this message and exit"
+            echo "  -f|--fix           fix files"
+            echo "  -c|--commit        check modified files to be commited only"
+            echo "  -p|--push          check modified files to be pushed only"
+            echo "  --no-headers       do not check headers"
+            echo "  --no-lint          do not check lint"
+            exit 0
+            ;;
+        "-f"|"--fix")
+            LINT=lint
+            HDRCK=headers
+            ;;
+        "-p"|"--push")
+            REFERENCE="origin/$(git rev-parse --abbrev-ref HEAD)"
+            MODE="push"
+            ;;
+        "-c"|"--commit")
+            MODE="commit"
+            ;;
+        "--no-headers")
+            DO_HDRCK="no"
+            ;;
+        "--no-lint")
+            DO_LINT="no"
+            ;;
+        *)
+            echo "Unknown option '$1'"
+            exit 2
+            ;;
+    esac
+    shift
+done
+
+if [ "$MODE" = "all" ]; then
+  if [ $DO_LINT = "yes" ] ; then
+    make $LINT || exit 1
+  fi
+  if [ $DO_HDRCK = "yes" ] ; then
+    make $HDRCK HDRCK_EXTRA="-quiet" || exit 1
+  fi
+else
+  STAGED=$(git diff --diff-filter ACMR --name-only --cached $REFERENCE | sort)
+  UNSTAGED=$(git diff --diff-filter DMR --name-only | sort)
+
+  if [ "$STAGED" = "" ];
+  then
+    echo "No staged modification since last $MODE, nothing to do."
+    exit 0
+  fi
+
+  if [ "$UNSTAGED" != "" ];
+  then
+    INTER=$(comm -12 <(ls $STAGED) <(ls $UNSTAGED))
+
+    if [ "$INTER" != "" ];
+    then
+      echo "Cannot validate push."
+      echo "The following staged files have been modified, renamed or deleted."
+      for file in $INTER ; do
+        echo "- $file"
+      done
+      exit 1
+    fi
+  fi
+
+  STAGED=$(echo $STAGED | tr '\n' ' ')
+
+  TMP=$(mktemp)
+
+  cleanup () {
+    rm "$TMP"
+  }
+  trap cleanup exit
+
+  git check-attr -za $STAGED > "$TMP"
+  if [ $DO_LINT = "yes" ] ; then
+    make $LINT LINTCK_FILES_INPUT="$TMP" || exit 1
+  fi
+  git check-attr -z header_spec $STAGED > "$TMP"
+  if [ $DO_HDRCK = "yes" ] ; then
+    make $HDRCK HDRCK_FILES_INPUT="$TMP" HDRCK_EXTRA="-quiet" || exit 1
+  fi
+fi
diff --git a/dev/git-hooks/pre-commit.sh b/dev/git-hooks/pre-commit.sh
index 3f376ce71e3bb85da9bb1c3ca551bfd4905875f3..0f460288e035b37a0005579ac86f16db4524ea5f 100755
--- a/dev/git-hooks/pre-commit.sh
+++ b/dev/git-hooks/pre-commit.sh
@@ -21,26 +21,20 @@
 #                                                                        #
 ##########################################################################
 
-# Examples of installation of this pre-commit hook (client side):
-# - cp ./dev/git-hooks/pre-commit.sh .git/hooks/pre-commit
+# Example of installation of this pre-commit hook (client side):
 # - (cd .git/hooks/ && ln -s ../../dev/git-hooks/pre-commit.sh pre-commit)
-
-# Note:
-# - that checks the unstaged version of the files and these files are
-#   only commited with a `git commit -a` command.
-# - so, a `git commit` command may  checks the wrong version of a file.
+# Note: if you decide to copy the file, the `SCRIPT_DIR` variable must be
+# fixed accordingly.
 
 echo "Pre-commit Hook..."
 
-# Extract the files that have both an unstaged version and a staged one.
-UNSTAGED="git diff --name-status"
-STAGED="git diff --name-status --cached"
-(($UNSTAGED ; $STAGED) | sed "s:^.::" | sort -u) | diff - <(($UNSTAGED ; $STAGED) | sed "s:^.::" | sort)
-if [ "$?" != "0" ]; then
-    echo "WARNING: These previous files are both unstaged and in the index."
-    echo "         They will be verified only for a 'git commit -a' command."
+STAGED=$(git diff --diff-filter ACMR --name-only --cached | sort)
+
+if [ "$STAGED" = "" ];
+then
+  echo "Empty commit, nothing to do"
+  exit 0
 fi
 
-# Verifies the current version of the files
-make lint.before-commit-a || exit 1
-make check-headers.before-commit-a || exit 1
+SCRIPT_DIR=$(dirname -- "$( readlink -f -- "$0"; )")
+"$SCRIPT_DIR/../check-files.sh" -c || exit 1
diff --git a/dev/git-hooks/pre-push.sh b/dev/git-hooks/pre-push.sh
new file mode 100755
index 0000000000000000000000000000000000000000..d06723672d1f370c970b72816d144b073419231d
--- /dev/null
+++ b/dev/git-hooks/pre-push.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+##########################################################################
+#                                                                        #
+#  This file is part of Frama-C.                                         #
+#                                                                        #
+#  Copyright (C) 2007-2022                                               #
+#    CEA (Commissariat à l'énergie atomique et aux énergies              #
+#         alternatives)                                                  #
+#                                                                        #
+#  you can redistribute it and/or modify it under the terms of the GNU   #
+#  Lesser General Public License as published by the Free Software       #
+#  Foundation, version 2.1.                                              #
+#                                                                        #
+#  It is distributed in the hope that it will be useful,                 #
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
+#  GNU Lesser General Public License for more details.                   #
+#                                                                        #
+#  See the GNU Lesser General Public License version 2.1                 #
+#  for more details (enclosed in the file licenses/LGPLv2.1).            #
+#                                                                        #
+##########################################################################
+
+# Example of installation of this pre-push hook (client side):
+# - (cd .git/hooks/ && ln -s ../../dev/git-hooks/pre-push.sh pre-push)
+# Note: if you decide to copy the file, the `SCRIPT_DIR` variable must be
+# fixed accordingly.
+
+echo "Pre-push Hook..."
+
+STAGED=$(git diff --diff-filter ACMR --name-only --cached origin/$(git rev-parse --abbrev-ref HEAD) | sort)
+
+if [ "$STAGED" = "" ];
+then
+  echo "No diff since last push, nothing to do"
+  exit 0
+fi
+
+SCRIPT_DIR=$(dirname -- "$( readlink -f -- "$0"; )")
+"$SCRIPT_DIR/../check-files.sh" -p || exit 1
diff --git a/nix/frama-c-checkers-shell.nix b/nix/frama-c-checkers-shell.nix
index de0483335050f1ba782ec032815c3c65804e1f3b..40a0406a77685de48266658d19caeb00488df77f 100644
--- a/nix/frama-c-checkers-shell.nix
+++ b/nix/frama-c-checkers-shell.nix
@@ -2,6 +2,7 @@
 , stdenv
 , clang_10
 , frama-c-hdrck
+, frama-c-lint
 , git
 , git-lfs
 , gnumake
@@ -13,6 +14,7 @@ stdenv.mkDerivation rec {
   buildInputs = [
     clang_10
     frama-c-hdrck
+    frama-c-lint
     git
     git-lfs
     gnumake
diff --git a/nix/frama-c-lint.nix b/nix/frama-c-lint.nix
new file mode 100644
index 0000000000000000000000000000000000000000..d33b25870079d5d24d2ba1a685ac8b1b3a203d82
--- /dev/null
+++ b/nix/frama-c-lint.nix
@@ -0,0 +1,40 @@
+{ lib
+, stdenv
+, camomile
+, dune_3
+, findlib
+, gitignoreSource
+, ocaml
+, ocp-indent
+} :
+
+stdenv.mkDerivation rec {
+  pname = "frama-c-lint";
+  version =
+    lib.strings.replaceStrings ["~"] ["-"]
+      (lib.strings.removeSuffix "\n"
+        (builtins.readFile ../VERSION));
+  slang = lib.strings.removeSuffix "\n" (builtins.readFile ../VERSION_CODENAME);
+
+  src = gitignoreSource ./../tools/lint ;
+
+  buildInputs = [
+    camomile
+    dune_3
+    findlib
+    ocaml
+    ocp-indent
+  ];
+
+  configurePhase = ''
+    true
+  '';
+
+  buildPhase = ''
+    dune build --root .
+  '';
+
+  installPhase = ''
+    dune install --prefix $out --root .
+  '';
+}
diff --git a/nix/frama-c.nix b/nix/frama-c.nix
index ac981c7f92d2b247dec972d682a52d2ee7086a9d..29f6fac278d81b7afa32f749aeee2d3f91a3694f 100644
--- a/nix/frama-c.nix
+++ b/nix/frama-c.nix
@@ -12,6 +12,7 @@
 # Frama-C build
 , apron
 , camlzip
+, camomile
 , dune_3
 , dune-configurator
 , dune-site
@@ -25,6 +26,7 @@
 , ocaml
 , ocamlgraph
 , ocamlgraph_gtk
+, ocp-indent
 , ppx_deriving
 , ppx_deriving_yojson
 , ppx_import
@@ -63,6 +65,7 @@ stdenvNoCC.mkDerivation rec {
   buildInputs = [
     apron
     camlzip
+    camomile
     dune_3
     dune-configurator
     dune-site
@@ -77,6 +80,7 @@ stdenvNoCC.mkDerivation rec {
     ocaml
     ocamlgraph
     ocamlgraph_gtk
+    ocp-indent
     ppx_deriving
     ppx_deriving_yojson
     ppx_import
diff --git a/nix/ocp-indent.nix b/nix/ocp-indent.nix
index 089d6ac9b6dc8049477d520e8fbfc8a5fba391e9..395c663ae31eda896f4d25b91ddd035d071b5134 100644
--- a/nix/ocp-indent.nix
+++ b/nix/ocp-indent.nix
@@ -1,5 +1,6 @@
 { lib
 , fetchFromGitHub
+, findlib
 , buildDunePackage
 , cmdliner
 }:
@@ -18,7 +19,7 @@ buildDunePackage rec {
   minimumOCamlVersion = "4.02";
   useDune2 = true;
 
-  buildInputs = [ cmdliner ];
+  buildInputs = [ cmdliner findlib ];
 
   meta = with lib; {
     homepage = http://typerex.ocamlpro.com/ocp-indent.html;
diff --git a/nix/pkgs.nix b/nix/pkgs.nix
index 522be707e117795dfd820ffac5c6b68f8cbf030d..113c31eac7b511a4c92b08d322069ab4042b8940 100644
--- a/nix/pkgs.nix
+++ b/nix/pkgs.nix
@@ -28,6 +28,7 @@ let
     # Builds
     frama-c = oself.callPackage ./frama-c.nix {};
     frama-c-hdrck = oself.callPackage ./frama-c-hdrck.nix {};
+    frama-c-lint = oself.callPackage ./frama-c-lint.nix {};
     lint = oself.callPackage ./lint.nix {};
 
     # Tests
diff --git a/share/Makefile.headers b/share/Makefile.headers
index 7e0beb06fc8fee2a3626bc86f3c1c9e892fc600a..5afbe5a533a97eba7aaee60396db746ed39b46ab 100644
--- a/share/Makefile.headers
+++ b/share/Makefile.headers
@@ -28,7 +28,7 @@
 # Parameters
 
 # Must be unset (for external plugins) and set (for FRAMA-C kernel)
-FRAMAC_HDRCK?=
+IN_FRAMAC?=
 
 HDRCK?=frama-c-hdrck
 
@@ -50,22 +50,15 @@ HEADER_OPEN_SOURCE?=yes
 # - <dir>: directory containing the files to manage
 HEADER_REPO?=
 
-# Can be set to a file
-# - <file>: file containing the list of the files to manage
-HEADER_DISTRIB_FILE?=
+# Can be set to a
+# - <file> containing files to check/update and their attributes, with 0
+#   separators (like 'git ls-files -z | git check-attr --stdin -z header_spec').
+HDRCK_FILES_INPUT?=
 
 # Can be set to a file
 # - <file>: file containing the list of the files to ignore any way
 HEADER_EXCEPTIONS?=
 
-# Can be set to a file
-# - <files>: input file containing, for each file to manage, the "header_spec" attribute value
-HEADER_SPEC?=
-
-# Can be set to a file
-# - yes: save the  "header_spec" attribute specification into the file "HEADER_SPEC"
-HEADER_SAVE_SPEC?=no
-
 # Extra parameters for "frama-c-hdrck"
 HDRCK_EXTRA?=
 
@@ -74,82 +67,19 @@ HDRCK_EXTRA?=
 
 GIT  ?= git
 DUNE ?= dune
-TEE  ?= tee
 
 ##########################################################################
 ## Command used to execute hdrck
 
-# Note: the public name of hdrck.exe is frama-c-hdrck
-ifeq ($(FRAMAC_HDRCK),)
-
-# HDRCK is external
+ifneq ($(IN_FRAMAC),yes)
 HDRCK.CMD:= $(HDRCK)
-
 else
-
-# HDRCK is internal
-HDRCK.CMD:= $(DUNE) exec --root $(FRAMAC_HDRCK_SRC) -- $(HDRCK)
-
-$(FRAMAC_HDRCK): tools/hdrck/hdrck.ml
-	$(DUNE) build --root $(FRAMAC_HDRCK_SRC) hdrck.exe
-
+HDRCK.CMD:= $(DUNE) exec --no-print-directory --root $(FRAMAC_HDRCK_SRC) -- $(HDRCK)
 endif
 
-HDRCK.OPTS:= --stdin
-
-######################################################################
-## Identifies how to get the header specification (from git/find/cat)
-## and updates HDRCK.OPTS variable
-
-HDRCK.FILE_LIST=$(GIT) ls-files -z
-HDRCK.FILE_ATTR=$(GIT) check-attr --stdin -z header_spec
-
-ifeq ($(HEADER_SPEC),)
-
-HDRCK.HAS_GIT:=$(shell git rev-parse --is-inside-work-tree 2> /dev/null)
-ifeq ($(HDRCK.HAS_GIT),true)
-
-# From git (including git work-trees)
-HDRCK.SPEC:=$(HDRCK.FILE_LIST) | $(HDRCK.FILE_ATTR)
-HDRCK.OPTS+= -z
-
-else
+HDRCK.OPTS:= --stdin -z -spec-format 3-zeros
 
-# Note: in such a case, GIT is required...
-HDRCK.SPEC:=
-
-endif # HDRCK.HAS_GIT
-
-else # HEADER_SPEC
-
-ifeq ($(HEADER_SAVE_SPEC),yes)
-
-HDRCK.HAS_GIT:=$(shell git rev-parse --is-inside-work-tree 2> /dev/null)
-ifeq ($(HDRCK.HAS_GIT),true)
-
-# From git (including git work-trees)
-HDRCK.SPEC:= $(HDRCK.FILE_LIST) | $(HDRCK.FILE_ATTR) | $(TEE) $(HEADER_SPEC)
-HDRCK.OPTS+= -z
-
-else
-
-# Note: in such a case, GIT is required...
-HDRCK.SPEC:=
-
-endif # HDRCK.HAS_GIT
-
-else # HEADER_SAVE_SPEC
-
-# From a file
-HDRCK.SPEC:= cat $(HEADER_SPEC)
-
-# The file format can be overloaded in using the var HDRCK_EXTRA
-HDRCK.OPTS+= -spec-format 3-zeros
-
-endif # HEADER_SAVE_SPEC
-endif # HEADER_SPEC
-
-################################
+##########################################################################
 ## Updates HDRCK.OPTS variable
 
 ifneq ($(HEADER_DISTRIB_FILE),)
@@ -172,9 +102,8 @@ ifeq ($(HEADER_STRICT),yes)
 HDRCK.OPTS+= -exit-on-warning
 endif
 
-#####################################
-## Identifies where are the headers
-## and updates HDRCK.OPTS variable
+##########################################################################
+## Identifies where are the headers and updates HDRCK.OPTS variable
 
 ifeq ($(HEADER_OPEN_SOURCE),yes)
 HDRCK.OPTS+= $(addprefix -header-dirs=,$(addsuffix /open-source,$(HEADER_DIRS)))
@@ -190,134 +119,61 @@ endif
 endif
 endif
 
-#################################
-
-ifeq ($(HDRCK.SPEC),)
-
-.PHONY: check-headers
-check-headers: $(FRAMAC_HDRCK)
-	$(error "Please set HEADER_SPEC variable to a file containing the header specification.")
+##########################################################################
+## HDRCK
 
+HDRCK.HAS_GIT:=$(shell git rev-parse --is-inside-work-tree 2> /dev/null)
+HDRCK.ENABLED=yes
+ifneq ($(HDRCK.HAS_GIT),true)
+ifeq ($(HDRCK_FILES_INPUT),)
+HDRCK.ENABLED=no
+endif
+endif
 
-.PHONY: headers
-headers: $(FRAMAC_HDRCK)
-	$(error "Please set HEADER_SPEC variable to a file containing the header specification.")
+ifeq ($(HDRCK_FILES_INPUT),)
+HDRCK.COLLECT=$(GIT) ls-files -z | $(GIT) check-attr --stdin -z header_spec
 else
+HDRCK.COLLECT=cat $(HDRCK_FILES_INPUT)
+endif
 
-ifneq ($(HDRCK.HAS_GIT),)
-
-define HDRCK.file-list-from-git-diff-info
-$(GIT) diff --name-status $(1) | $(GREP) -v "^D" | $(SED) -e "s/^[CR][0-9]\+[ \t]\+[^ \t]\+[ \t]\+//" -e "s/^[AM][ \t]*//"
-endef
-
-define HDRCK.file-list-from-git-diff
- $(call HDRCK.file-list-from-git-diff-info,$(1)) | $(TR) '\n' '\000' | $(HDRCK.FILE_ATTR)
-endef
-
-HDRCK.main-targets=check-headers headers
-define HDRCK.file-list-filter-with-git-diff
-HDRCK.$(1)-filter-targets=$$(addsuffix .$(1),$$(HDRCK.main-targets))
-.PHONY: $(LINT.$(1)-filter-targets)
-$$(HDRCK.$(1)-filter-targets):: HDRCK.SPEC=$$(call HDRCK.file-list-from-git-diff,$(2))
-$$(HDRCK.$(1)-filter-targets):: %.$(1) : $(3) %
-	@echo [HDRCK] Done: HDRCK_DIFF=\"$(2)\" make $$(basename $$@)
-	@echo [HDRCK] checked file list:
-	$$(call HDRCK.file-list-from-git-diff-info,$(2))
-endef
-
-ifneq ($(HEADER_DIFF),)
-
-$(info [HDRCK] Looking at files modified from branch/commit: '$(HEADER_DIFF)')
-HDRCK.SPEC=$(call LINT.file-list-from-git-diff,$(HEADER_DIFF))
-$(info $(HEADER.FILE_LIST))
-
-endif # $(HEADER_DIFF)
-
-#### rules for make <headers-target>.before-push
-##   looks at files modified from origin/$(git rev-parse --abbrev-ref HEAD) and checks that there is no staged nor unstaged files
-
-PHONY: HDRCK.checked-unmodified-ok
-HDRCK.checked-unmodified-ok:
-	$(eval HDRCK.checked-unmodified := $(shell $(GIT) diff --name-status HEAD | $(WC) -l))
-	if [ 0 != $(HDRCK.checked-unmodified) ]; then \
-           echo "[HDRCK] Staged or unstaged files=$(HDRCK.checked-unmodified):"; \
-            $(GIT) diff --name-status HEAD; \
-           echo "[HDRCK] Error: may check some staged or unstaged file versions" ; \
-           exit 1; \
-        fi;
-
-$(eval $(call HDRCK.file-list-filter-with-git-diff,before-push,--cached origin/$(shell git rev-parse --abbrev-ref HEAD),HDRCK.checked-unmodified-ok))
-
-#### rules for make <headers-target>.unstaged
-##   looks at unstaged files
-
-$(eval $(call HDRCK.file-list-filter-with-git-diff,unstaged, ))
-
-#### rules for make <headers-target>.before-commit
-##   looks at staged files and checks that there is no unstaged files
-
-PHONY: HDRCK.checked-unstaged-ok
-HDRCK.checked-unstaged-ok:
-	$(eval HDRCK.checked-unstaged := $(shell $(GIT) diff --name-status | $(WC) -l))
-	if [ 0 != $(HDRCK.checked-unstaged) ]; then \
-	   echo "[HDRCK] Unstaged files=$(HDRCK.checked-unstaged):"; \
-           $(GIT) diff --name-status ; \
-           echo "[HDRCK] Error: may check some unstaged version files version." ; \
-           exit 1; \
-        fi;
-
-$(eval $(call HDRCK.file-list-filter-with-git-diff,before-commit,--cached HEAD,HDRCK.checked-unstaged-ok))
-
-#### rules for make <headers-target>.before-commit-a
-## looks at unstaged and staged files
-
-$(eval $(call HDRCK.file-list-filter-with-git-diff,before-commit-a,HEAD,))
-
-#### rules for make <headers-target>.before-commit-a--ammend
-##   looks at unstaged, staged and previously committed files
-
-$(eval $(call HDRCK.file-list-filter-with-git-diff,before-commit-a--ammend,HEAD~1,))
-
-#### rules for make <headers-target>.before-commit--amend
-##   looks at staged and previously committed files and checks that there is no unstaged files
-
-$(eval $(call HDRCK.file-list-filter-with-git-diff,before-commit--ammend,HEAD~1,HDRCK.checked-unstaged-ok))
+ifeq ($(HDRCK.ENABLED),yes)
 
-#### rules for make <headers-target>.previous-commit
-## looks at files of the last commit and checks that there is no staged nor unstaged files
+.PHONY: check-headers
+check-headers:
+	$(HDRCK.COLLECT) | $(HDRCK.CMD) $(HDRCK.OPTS) $(HDRCK_EXTRA)
 
-$(eval $(call HDRCK.file-list-filter-with-git-diff,previous-commit,HEAD~1,HDRCK.checked-unmodified-ok))
+.PHONY: headers
+headers:
+	$(HDRCK.COLLECT) | $(HDRCK.CMD) -update $(HDRCK.OPTS) $(HDRCK_EXTRA)
 
-endif # $(HDRCK.HAS_GIT)
+else
 
 .PHONY: check-headers
-check-headers: $(FRAMAC_HDRCK)
-	$(HDRCK.SPEC) | $(HDRCK.CMD) $(HDRCK.OPTS) $(HDRCK_EXTRA)
+check-headers:
+	$(error "Cannot check-headers: not a Git repository, \
+	   			 you should specify HDRCK_FILES_INPUT")
 
 .PHONY: headers
-headers: $(FRAMAC_HDRCK)
-	$(HDRCK.SPEC) | $(HDRCK.CMD) -update $(HDRCK.OPTS) $(HDRCK_EXTRA)
+headers:
+	$(error "Cannot make headers: not a Git repository, \
+	   			 you should specify HDRCK_FILES_INPUT")
 
-endif # $(HDRCK.SPEC)
+endif
 
 #################################
 
 .PHONY: headers.info
 headers.info:
-	echo "FRAMAC_HDRCK='$(FRAMAC_HDRCK)'"
 	echo "HEADER_OPEN_SOURCE='$(HEADER_OPEN_SOURCE)'"
 	echo "HEADER_STRICT='$(HEADER_STRICT)'"
 	echo "HEADER_DIRS='$(HEADER_STRICT)'"
-	echo "HEADER_DISTRIB_FILE='$(HEADER_DISTRIB_FILE)'"
 	echo "HEADER_EXCEPTIONS='$(HEADER_EXCEPTIONS)'"
 	echo "HEADER_REPO='$(HEADER_REPO)'"
-	echo "HEADER_SPEC='$(HEADER_SPEC)'"
+	echo "HDRCK_FILES_INPUT='$(HDRCK_FILES_INPUT)'"
 	echo "HDRCK.HAS_GIT='$(HDRCK.HAS_GIT)'"
 	echo "HDRCK.CMD='$(HDRCK.CMD)'"
-	echo "HDRCK.SPEC='$(HDRCK.SPEC)'"
 	echo "HDRCK.OPTS='$(HDRCK.OPTS)'"
 
-
 ###############################################################################
 # Local Variables:
 # compile-command: "make"
diff --git a/share/Makefile.linting b/share/Makefile.linting
index 7b64a553d86b2040061ef647d4e27c4f61184df0..caf1c36f0dfb1f26a98c7e106231251652d35b66 100644
--- a/share/Makefile.linting
+++ b/share/Makefile.linting
@@ -20,555 +20,88 @@
 #                                                                        #
 ##########################################################################
 
+##########################################################################
+# Parameters
 
-################################
-# Code prettyfication and lint #
-################################
-
-# make lint includes:
-# - make check-utf8: valid UTF-8 encoding
-# - make check-eoleof: EOF preceded by an EOL
-# - make check-syntax: EOF preceded by an EOL + no TAB + no BLANK at the end
-# - make check-indent: valid indentation
-#
-# For all these targets (and the corresponding fix-XXX), it is possible to restrict the search to a sub-directory:
-# - make LINT_DIR=<subdir> <lint-target>
-# It is possible to force the action to given files:
-# - make LINT_FILE=<file> <lint-target>
-# or to the files modified between HEAD and a specific branch/commit:
-# - make LINT_DIFF=<branch/commit> <lint-target>
-#
-# Notes:
-# - when LINT_FILE is given, .gitattributes, LINT_DIFF and LINT_DIR are ignored;
-# - when LINT_DIFF is given, LINT_DIR is ignored (but .gitattributes is used);
-# - to use fix-utf8 target, the variable LINT_FROM_ENCODING=<from-encoding-name>
-#   has to be set.
-#
-# Advanced targets when LINT_FILE is undefined
-# - make <lint-target>.unstaged
-#   looks at unstaged files
-# - make <lint-target>.before-commit-a
-#   looks at unstaged and staged files
-# - make <lint-target>.before-commit-a--ammend
-#   looks at unstaged, staged and previously committed files
-# - make <lint-target>.before-commit--ammend
-#   looks at staged and previously committed files and checks that there is no unstaged files
-# - make <lint-target>.before-commit
-#   looks at staged files and checks that there is no unstaged files
-# - make <lint-target>.previous-commit
-#   looks at files of the last commit and checks that there is no staged nor unstaged files
-# - make <lint-target>.before-push:
-#   looks at files modified from origin/$(git rev-parse --abbrev-ref HEAD) and checks that there is no staged nor unstaged files
+# Must be unset (for external plugins) and set (for FRAMA-C kernel)
+IN_FRAMAC?=
 
-# make clean-lint (removing linting targets) includes
-# - make clean-utf8
-# - make clean-eoleof
-# - make clean-syntax
-# - make clean-indent
+# Note: DO NOT rename this variable "LINT", this name is defined in GNU Makefile
+LINTCK?=frama-c-lint
 
-# make clean-lint (removing linting targets) includes
-# - make clean-utf8
-# - make clean-eoleof
-# - make clean-syntax
-# - make clean-indent
+# Can be set to a
+# - <file> containing files to check/update and their attributes, with 0
+#   separators (like 'git ls-files -z | git check-attr --stdin -za').
+LINTCK_FILES_INPUT?=
 
-################################
-## Default variables
+# Extra parameters for the lint command
+LINTCK_EXTRA?=
 
-IS_UTF8 ?= iconv -f UTF-8 -t UTF-8
-TO_UTF8 ?= iconv -t UTF-8 -f
+##########################################################################
+## Tools
 
-OCP_INDENT   ?= ocp-indent
-CLANG_FORMAT ?= clang-format
-# wc, tail, head, cmp
+GIT  ?= git
+DUNE ?= dune
 
-# Default values necessary for
-#   LINT_MAKEFILE=<this-makefile> make -f <this-makefile> <lint-target>`
-# Otherwise theses variables are defined into share/Makefile.common
-GREP    ?= grep
-TR      ?= tr
-SED     ?= LC_ALL=C sed
-ISED    ?= sed -i
-FIND    ?= find
-GIT     ?= git
-MKDIR   ?= mkdir -p
-MV      ?= mv
-RM      ?= rm -f
-RMDIR   ?= rm -rf
-TOUCH   ?= touch
-WC      ?= wc
+##########################################################################
+## Command used to execute lint
 
-ifeq ($(PLATFORM),Darwin)
-XARGS   ?= xargs
+ifneq ($(IN_FRAMAC),yes)
+LINTCK.CMD:= $(LINTCK)
 else
-# Unix, Cygwin
-XARGS   ?= xargs --no-run-if-empty
+LINTCK.CMD:= $(DUNE) exec --no-print-directory --root $(FRAMAC_LINTCK_SRC) -- $(LINTCK)
 endif
 
-############
-
-# Can be set to the path of that makefile
-LINT_MAKEFILE ?=
-
-# Can be set to a sub-directory to restrict the checking
-LINT_DIR ?= .
-
-################################
-
-LINT.makefile=$(wildcard $(LINT_MAKEFILE))
-ifeq ($(LINT.makefile),)
-
-LINT.make?=$(MAKE) --quiet
-else
+##########################################################################
+## LINT
 
-LINT.make?=$(MAKE) --quiet -f $(LINT.makefile)
+LINTCK.HAS_GIT:=$(shell git rev-parse --is-inside-work-tree 2> /dev/null)
+LINTCK.ENABLED=yes
+ifneq ($(LINTCK.HAS_GIT),true)
+ifeq ($(LINTCK_FILES_INPUT),)
+LINTCK.ENABLED=no
 endif
-
-################################
-## CHECK ocp-indent VERSION
-
-.lint/check-ocp-indent-version:
-	if command -v $(OCP_INDENT) >/dev/null; then \
-          if [ -z "$(shell $(OCP_INDENT) --version)" ]; then echo "warning: $(OCP_INDENT) returned an empty string, assuming it is the correct version"; \
-          else \
-            $(eval ocp_version_major := $(shell $(OCP_INDENT) --version | $(SED) -E "s/^([0-9]+)\.[0-9]+\..*/\1/")) \
-            $(eval ocp_version_minor := $(shell $(OCP_INDENT) --version | $(SED) -E "s/^[0-9]+\.([0-9]+)\..*/\1/")) \
-            if [ "$(ocp_version_major)" -lt 1 -o "$(ocp_version_minor)" -lt 7 ]; then \
-              echo "error: $(OCP_INDENT) 1.7.0 required for linting (got $(ocp_version_major).$(ocp_version_minor))"; \
-              exit 1; \
-            fi; \
-          fi; \
-        else \
-          exit 1; \
-        fi;
-	$(MKDIR) $(dir $@)
-	$(TOUCH) $@
-
-.lint/check-clang-format-installed:
-	if ! command -v $(CLANG_FORMAT) >/dev/null; then \
-          echo "error: $(CLANG_FORMAT) must be installed"; \
-          exit 1; \
-        fi;
-	$(MKDIR) $(dir $@)
-	$(TOUCH) $@
-
-###############
-## Main target
-
-LINT.check-targets= \
-        check-syntax check-indent check-eoleof check-utf8
-
-LINT.fix-targets=$(subst check-,fix-,$(LINT.check-targets))
-LINT.main-targets=lint $(LINT.check-targets) $(LINT.fix-targets)
-
-LINT.HAS_GIT:=$(shell git rev-parse --is-inside-work-tree 2> /dev/null)
-ifneq ($(LINT.HAS_GIT),true)
-
-.PHONY: lint
-lint:
-	echo "'make lint' requires a git repository but, that is not"
-	echo "the case for example with 'make LINT_FILE=<file> check-syntax'"
-	git rev-parse --is-inside-work-tree
-
-else
-
-.PHONY: lint
-lint: $(LINT.check-targets)
-
 endif
 
-###############################
-## Cleaning
-
-# Targets are generated into .lint/ directories
-
-.PHONY: clean-lint
-clean-lint:
-	echo "Cleaning LINT targets..."
-	$(RMDIR) .lint
-
-clean:: clean-lint
-
-#### clean-check-XXX targets
-
-LINT.clean-targets=$(addprefix clean-,$(LINT.check-targets))
-
-.PHONY: $(LINT.clean-targets)
-# Generic rule
-$(LINT.clean-targets):
-	echo "[LINT] Cleaning $(patsubst clean-%,%,$@) targets..."
-	$(FIND) .lint -type f -name \*.$(patsubst clean-%,%,$@) | $(XARGS) -n 10 $(RM)
-
-###############################
-
-ifeq ($(LINT_FILE),) ############# No lint file given
-
-######## LINT_DIR filter
-LINT.dir=$(wildcard $(LINT_DIR))
-
-ifeq ($(LINT.HAS_GIT),true)  ############# For Git repository
-
-######## LINT_DIFF filter
-
-define LINT.file-list-from-git-diff-info
-$(GIT) diff --name-status $(1) | $(GREP) -v "^D" | $(SED) -e "s/^[CR][0-9]\+[ \t]\+[^ \t]\+[ \t]\+//" -e "s/^[AM][ \t]*//"
-endef
-
-define LINT.file-list-from-git-diff
-$(call LINT.file-list-from-git-diff-info,$(1)) | $(TR) '\n' '\000'
-endef
-
-define LINT.file-list-filter-with-git-diff
-LINT.$(1)-filter-targets=$$(addsuffix .$(1),$$(LINT.main-targets))
-.PHONY: $(LINT.$(1)-filter-targets)
-$$(LINT.$(1)-filter-targets):: LINT.FILE_LIST=$$(call LINT.file-list-from-git-diff,$(2))
-$$(LINT.$(1)-filter-targets):: %.$(1) : $(3) %
-	@echo [LINT] Done: LINT_DIFF=\"$(2)\" make $$(basename $$@)
-	@echo [LINT] checked file list:
-	$$(call LINT.file-list-from-git-diff-info,$(2))
-endef
-
-ifeq ($(LINT_DIFF),)
-LINT.FILE_LIST=$(GIT) ls-files $(LINT.dir) -z
+ifeq ($(LINTCK_FILES_INPUT),)
+LINTCK.COLLECT=$(GIT) ls-files -z | $(GIT) check-attr --stdin -z -a
 else
-$(info [LINT] Looking at files modified from branch/commit: '$(LINT_DIFF)')
-LINT.FILE_LIST=$(call LINT.file-list-from-git-diff,$(LINT_DIFF))
-$(info $(LINT.FILE_LIST))
-endif
-
-#### rules for make <lint-target>.before-push
-##   looks at files modified from origin/$(git rev-parse --abbrev-ref HEAD) and checks that there is no staged nor unstaged files
-
-PHONY: LINT.checked-unmodified-ok
-LINT.checked-unmodified-ok:
-	$(eval LINT.checked-unmodified := $(shell $(GIT) diff --name-status HEAD | $(WC) -l))
-	if [ 0 != $(LINT.checked-unmodified) ]; then \
-           echo "[LINT] Staged or unstaged files=$(LINT.checked-unmodified):"; \
-            $(GIT) diff --name-status HEAD; \
-           echo "[LINT] Error: may check some staged or unstaged file versions" ; \
-           exit 1; \
-        fi;
-
-$(eval $(call LINT.file-list-filter-with-git-diff,before-push,--cached origin/$(shell git rev-parse --abbrev-ref HEAD),LINT.checked-unmodified-ok))
-
-#### rules for make <lint-target>.unstaged
-##   looks at unstaged files
-
-$(eval $(call LINT.file-list-filter-with-git-diff,unstaged, ))
-
-#### rules for make <lint-target>.before-commit
-##   looks at staged files and checks that there is no unstaged files
-
-PHONY: LINT.checked-unstaged-ok
-LINT.checked-unstaged-ok:
-	$(eval LINT.checked-unstaged := $(shell $(GIT) diff --name-status | $(WC) -l))
-	if [ 0 != $(LINT.checked-unstaged) ]; then \
-	   echo "[LINT] Unstaged files=$(LINT.checked-unstaged):"; \
-           $(GIT) diff --name-status ; \
-           echo "[LINT] Error: may check some unstaged version files version." ; \
-           exit 1; \
-        fi;
-
-$(eval $(call LINT.file-list-filter-with-git-diff,before-commit,--cached HEAD,LINT.checked-unstaged-ok))
-
-#### rules for make <lint-target>.before-commit-a
-## looks at unstaged and staged files
-
-$(eval $(call LINT.file-list-filter-with-git-diff,before-commit-a,HEAD,))
-
-#### rules for make <lint-target>.before-commit-a--ammend
-##   looks at unstaged, staged and previously committed files
-
-$(eval $(call LINT.file-list-filter-with-git-diff,before-commit-a--ammend,HEAD~1,))
-
-#### rules for make <lint-target>.before-commit--amend
-##   looks at staged and previously committed files and checks that there is no unstaged files
-
-$(eval $(call LINT.file-list-filter-with-git-diff,before-commit--ammend,HEAD~1,LINT.checked-unstaged-ok))
-
-#### rules for make <lint-target>.previous-commit
-## looks at files of the last commit and checks that there is no staged nor unstaged files
-
-$(eval $(call LINT.file-list-filter-with-git-diff,previous-commit,HEAD~1,LINT.checked-unmodified-ok))
-
-#### get attr / filter set attr
-
-LINT.GET_ATTR=$(GIT) check-attr --stdin -z
-LINT.FILTER_FILE_ATTR=$(SED) -zne 'x;n;n;s/^set$$//;t print;b;:print;x;p'
-
-#### check-XXX targets
-
-.PHONY: $(LINT.check-targets)
-# Generic rule
-$(LINT.check-targets):
-	echo "[LINT] Checking from GIT attribute $@..."
-	$(LINT.FILE_LIST) \
-        | $(LINT.GET_ATTR) $@ \
-        | $(LINT.FILTER_FILE_ATTR) \
-        | $(XARGS) -0 -IXX sh -c '$(LINT.make) LINT_FILE="XX" $@ || exit 255'
-
-#### fix-XXX targets
-
-# Generic rule
-$(LINT.fix-targets):
-	echo "[LINT] Fixing from GIT attribute $(patsubst fix-%,check-%,$@)..."
-	$(LINT.FILE_LIST) \
-        | $(LINT.GET_ATTR) $(patsubst fix-%,check-%,$@) \
-        | $(LINT.FILTER_FILE_ATTR) \
-        | $(XARGS) -0 -IXX sh -c '$(LINT.make) LINT_FILE="XX" $@ || exit 255'
-
-endif ############  For Git repository
-
-else #############  LINT_FILE are given
-
-## Internal targets from the recursive make
-
-LINT_FILE.list=$(addprefix .lint/,$(wildcard $(LINT_FILE)))
-
-LINT_FILE.check-utf8= $(addsuffix .check-utf8,$(LINT_FILE.list))
-LINT_FILE.check-eoleof= $(addsuffix .check-eoleof,$(LINT_FILE.list))
-LINT_FILE.check-syntax= $(addsuffix .check-syntax,$(LINT_FILE.list))
-LINT_FILE.check-indent= $(addsuffix .check-indent,$(LINT_FILE.list))
-
-LINT_FILE.fix-utf8= $(addsuffix .fix-utf8,$(LINT_FILE.list))
-LINT_FILE.fix-eoleof= $(addsuffix .fix-eoleof,$(LINT_FILE.list))
-LINT_FILE.fix-syntax= $(addsuffix .fix-syntax,$(LINT_FILE.list))
-LINT_FILE.fix-indent= $(addsuffix .fix-indent,$(LINT_FILE.list))
-
-## Filters for the extension for check/fix-indent
-
-# ML filter
-LINT_FILE.ml-indent = $(filter $(addsuffix .ml,$(basename $(LINT_FILE.list))),$(LINT_FILE.list))
-LINT_FILE.ml-indent+= $(filter $(addsuffix .mli,$(basename $(LINT_FILE.list))),$(LINT_FILE.list))
-LINT_FILE.check-ml-indent= $(addsuffix .check-indent,$(LINT_FILE.ml-indent))
-LINT_FILE.fix-ml-indent= $(addsuffix .fix-indent,$(LINT_FILE.ml-indent))
-
-# C filter
-LINT_FILE.c-indent =$(filter $(addsuffix .c,$(basename $(LINT_FILE.list))),$(LINT_FILE.list))
-LINT_FILE.c-indent+= $(filter $(addsuffix .h,$(basename $(LINT_FILE.list))),$(LINT_FILE.list))
-LINT_FILE.check-c-indent= $(addsuffix .check-indent,$(LINT_FILE.c-indent))
-LINT_FILE.fix-c-indent= $(addsuffix .fix-indent,$(LINT_FILE.c-indent))
-
-
-LINT_FILE.check-unknown-indent=$(filter-out $(LINT_FILE.check-ml-indent) \
-                                            $(LINT_FILE.check-c-indent),$(LINT_FILE.check-indent))
-LINT_FILE.fix-unknown-indent=$(patsusbt %.check-indent,%.fix-indent,LINT_FILE.check-unknown-indent)
-
-## check-XXX targets
-
-.PHONY: check-utf8
-check-utf8: $(LINT_FILE.check-utf8)
-
-.PHONY: check-eoleof
-check-eoleof: $(LINT_FILE.check-eoleof)
-
-.PHONY: check-syntax
-check-syntax: $(LINT_FILE.check-syntax)
-
-# ML and C format
-.PHONY: check-indent
-check-indent: $(LINT_FILE.check-indent)
-
-## fix-XXX targets
-
-.PHONY: fix-utf8
-fix-utf8: $(LINT_FILE.fix-utf8)
-
-.PHONY: fix-eoleof
-fix-eoleof: $(LINT_FILE.fix-eoleof)
-
-.PHONY: fix-syntax
-fix-syntax: $(LINT_FILE.fix-syntax)
-
-# ML and C format
-.PHONY: fix-indent
-fix-indent: $(LINT_FILE.fix-indent)
-
+LINTCK.COLLECT=cat $(LINTCK_FILES_INPUT)
 endif
 
-###############################
-## UTF8 ASPECT
-
-$(LINT_FILE.check-utf8): .lint/%.check-utf8: %
-	echo "Check UTF8: $<"
-	if ! $(IS_UTF8) $< > /dev/null; \
-        then \
-          echo "File $< uses an invalid UTF-8 encoding."; \
-          echo "Please fixe it manualy or in running: make LINT_FROM_ENCODING=<ENCODING-NAME> LINT_FILE=$< fix-utf8";\
-          echo "The next command may help you to find that <ENCODING-NAME>: file $<";\
-          exit 1; \
-        fi
-	$(MKDIR) $(dir $@)
-	$(TOUCH) $@
+ifeq ($(LINTCK.ENABLED),yes)
 
-ifneq ($(LINT_FROM_ENCODING),)
+.PHONY: check-lint
+check-lint:
+	$(LINTCK.COLLECT) | $(LINTCK.CMD) $(LINTCK_EXTRA)
 
-.PHONY: $(LINT_FILE.fix-utf8)
-$(LINT_FILE.fix-utf8): .lint/%.fix-utf8: %
-	if ! $(IS_UTF8) $< > /dev/null 2> /dev/null; \
-        then \
-          echo "Fixes UTF8 (from $(LINT_FROM_ENCODING)): $<"; \
-          if ! $(TO_UTF8) $(LINT_FROM_ENCODING) $< > $<.tmp; then exit 1; fi; \
-           $(MV) $<.tmp $<; \
-        fi
-	$(MKDIR) $(dir $@)
-	$(TOUCH) .lint/$<.check-utf8 # no more need of check-indent
+.PHONY: lint
+lint:
+	$(LINTCK.COLLECT) | $(LINTCK.CMD) -u $(LINTCK_EXTRA)
 
 else
 
-$(LINT_FILE.fix-utf8): .lint/%.fix-utf8: % .lint/check-ocp-indent-version
-	$(error "Target fix-utf8 requires to define LINT_FROM_ENCODING variable")
-
-endif # LINT_FROM_ENCODING
+.PHONY: check-lint
+check-lint:
+	$(error "Cannot lint: not a Git repository, \
+	   			 you should specify LINTCK_FILES_INPUT")
 
-###############################
-## UNKNOWN INDENTATION FORMATER
-
-ifneq ($(LINT_FILE.check-unknown-indent),)
-.PHONY: $(LINT_FILE.check-unknown-indent)
-$(LINT_FILE.check-unknown-indent): .lint/%.check-indent: %
-	$(error "Check indent with an unknown formater:  $<")
-endif
+.PHONY: lint
+lint:
+	$(error "Cannot fix-lint: not a Git repository, \
+	   			 you should specify LINTCK_FILES_INPUT")
 
-ifneq ($(LINT_FILE.fix-unknown-indent),)
-.PHONY: $(LINT_FILE.fix-unknown-indent)
-$(LINT_FILE.check-unknown-indent): .lint/%.fix-indent: %
-	$(error "Check indent with an unknown formater:  $<")
 endif
 
-###############################
-## ML INDENTATION ASPECT
-
-$(LINT_FILE.check-ml-indent): .lint/%.check-indent: % .lint/check-ocp-indent-version
-	echo "Check ML indent: $<"
-	$(OCP_INDENT) $< > $<.tmp
-	if cmp -s $< $<.tmp; \
-        then $(RM) $<.tmp; \
-        else \
-          echo "File $< is not indented correctly."; \
-          echo "Please run: make LINT_FILE=$< fix-indent";\
-          $(RM) $<.tmp; \
-          exit 1; \
-        fi
-	$(MKDIR) $(dir $@)
-	$(TOUCH) $@
-
-.PHONY: $(LINT_FILE.fix-ml-indent)
-$(LINT_FILE.fix-ml-indent): .lint/%.fix-indent: % .lint/check-ocp-indent-version
-	echo "Fixes ML indent: $<"
-	$(OCP_INDENT) -i $<
-	$(MKDIR) $(dir $@)
-	$(TOUCH) .lint/$<.check-indent # no more need of check-indent
-
-###############################
-## C INDENTATION ASPECT
-
-$(LINT_FILE.check-c-indent): .lint/%.check-indent: % .lint/check-clang-format-installed
-	echo "Check C indent: $<"
-	$(CLANG_FORMAT) --dry-run -Werror $<
-	$(MKDIR) $(dir $@)
-	$(TOUCH) $@
-
-.PHONY: $(LINT_FILE.fix-c-indent)
-$(LINT_FILE.fix-c-indent): .lint/%.fix-indent: % .lint/check-clang-format-installed
-	echo "Fixes C indent: $<"
-	$(CLANG_FORMAT) -i $<
-	$(MKDIR) $(dir $@)
-	$(TOUCH) .lint/$<.check-indent # no more need of check-indent
-
-###############################
-## EOL EOF ASPECT (included by check-syntax target)
-
-$(LINT_FILE.check-eoleof): .lint/%.check-eoleof: %
-	# See SO 1825552 on mixing grep and \t (and cry)
-	# For OK_NL, we have three cases:
-	# - for empty files, the computation boils down to 0 - 0 == 0
-	# - for non-empty files with a proper \n at the end, to 1 - 1 == 0
-	# - for empty files without \n, to 1 - 0 == 1 that will be catched
-	echo "Check EOL EOF: $<"
-	OK_NL=$$(($$(tail -c -1 $< | wc -c) - $$(tail -n -1 $< | wc -l))) ; \
-        if test $$OK_NL -gt 0; then \
-          echo "File $< does not pass syntactic checks:"; \
-          echo "No newline at end of file"; \
-          echo "Please run: make LINT_FILE=$< fix-eoleof"; \
-          exit 1 ; \
-        fi
-	$(MKDIR) $(dir $@)
-	$(TOUCH) $@
-
-# The real target is a check-XXX one
-.PHONY: $(LINT_FILE.fix-eoleof)
-$(LINT_FILE.fix-eoleof): .lint/%.fix-eoleof: %
-	echo "Fixes EOL EOF: $<"
-	$(ISED) -e '$$a\' $<
-	$(MKDIR) $(dir $@)
-	$(TOUCH) .lint/$<.check-eoleof # no more need of this checking
-
-%.fix-eoleof: LC_ALL = C
-
-###############################
-## SYNTACTICAL ASPECT
-
-$(LINT_FILE.check-syntax): .lint/%.check-syntax: %
-	# See SO 1825552 on mixing grep and \t (and cry)
-	# For OK_NL, we have three cases:
-	# - for empty files, the computation boils down to 0 - 0 == 0
-	# - for non-empty files with a proper \n at the end, to 1 - 1 == 0
-	# - for empty files without \n, to 1 - 0 == 1 that will be catched
-	echo "Check syntax: $<"
-	OK_TAB=$$(grep -c -e "$$(printf '^ *\t')" $<) ; \
-        OK_SPACE=$$(grep -c -e '[[:blank:]]$$' $<) ; \
-        OK_NL=$$(($$(tail -c -1 $< | wc -c) - $$(tail -n -1 $< | wc -l))) ; \
-        OK_EMPTY=$$(tail -n -1 $< | grep -c -e '^[[:blank:]]*$$') ; \
-        ERROR=$$(($$OK_TAB + $$OK_SPACE + $$OK_NL + $$OK_EMPTY)) ; \
-        if test $$ERROR -gt 0; then \
-          echo "File $< does not pass syntactic checks:"; \
-          echo "$$OK_TAB lines indented with tabulation instead of spaces"; \
-          echo "$$OK_SPACE lines with spaces at end of line"; \
-          test $$OK_NL -eq 0 || echo "No newline at end of file"; \
-          test $$OK_EMPTY -eq 0 || echo "Empty line(s) at end of file"; \
-          echo "Please run: make LINT_FILE=$< fix-syntax"; \
-          exit 1 ; \
-        fi
-	$(MKDIR) $(dir $@)
-	$(TOUCH) $@
-
-# The real target is a check-XXX one
-.PHONY: $(LINT_FILE.fix-syntax)
-$(LINT_FILE.fix-syntax): .lint/%.fix-syntax: %
-	echo "Fixes syntax: $<"
-	$(ISED) -e 's/^ *\t\+/ /' $<
-	$(ISED) -e 's/\(.*[^[:blank:]]\)[[:blank:]]\+$$/\1/' $<
-	$(ISED) -e 's/^[ \t]\+$$//' $<
-	if test \( $$(tail -n -1 $< | wc -l) -eq 0 \) -a \( $$(wc -c $< | cut -d " " -f 1) -gt 0 \) ; then \
-          echo "" >> $<; \
-        else \
-          while tail -n -1 $< | grep -l -e '^[ \t]*$$'; do \
-            head -n -1 $< > $<.tmp; \
-            $(MV) $<.tmp $<; \
-          done; \
-        fi
-	$(MKDIR) $(dir $@)
-	$(TOUCH) .lint/$<.check-syntax # no more need of check-syntax
-
-# Avoid a UTF-8 locale at all cost: in such setting, sed does not work
-# reliably if you happen to have latin-1 encoding somewhere,
-# which is still unfortunately the case in some dark corners of the platform
-%.fix-syntax: LC_ALL = C
-
-###############################
-## Info
+##########################################################################
 
+.PHONY: lint.info
 lint.info:
-	echo "LINT.HAS_GIT='$(LINT.HAS_GIT)'"
-	echo "LINT.make='$(LINT.make)'"
-	echo "LINT_FILE.list='$(LINT_FILE.list)'"
-	echo "LINT_FILE.check-syntax='$(LINT_FILE.check-syntax)'"
-	echo "LINT_FILE.check-indent='$(LINT_FILE.check-indent)'"
-	echo "LINT_FILE.fix-syntax='$(LINT_FILE.fix-syntax)'"
-	echo "LINT_FILE.fix-indent='$(LINT_FILE.fix-indent)'"
+	echo "LINTCK_FILES_INPUT='$(LINTCK_FILES_INPUT)'"
+	echo "LINTCK.HAS_GIT='$(LINTCK.HAS_GIT)'"
+	echo "LINTCK.CMD='$(LINTCK.CMD)'"
 
-###############################################################################
+##########################################################################
 # Local Variables:
 # compile-command: "make"
 # End:
diff --git a/src/kernel_services/ast_data/property_status.ml b/src/kernel_services/ast_data/property_status.ml
index 46c82aeca51a4f89ebbc231346f44927c49b4da1..26eba1a5c8a2c268a1dde6583886f0e9582e12e7 100644
--- a/src/kernel_services/ast_data/property_status.ml
+++ b/src/kernel_services/ast_data/property_status.ml
@@ -1142,43 +1142,43 @@ module Consolidation = struct
            consolidate_of_local_when_cycle ppt
            (* [JS 2011/11/04] use the following code (to be tested) as soon as WP uses the
               new function [legal_dependency_cycle] *)
-           (*	  match e with
-             | Not_yet -> assert false
-             | Single e ->
-             if Valid_cycles.mem e path then
-             consolidate_of_local_when_cycle ppt
-             else
-             Kernel.fatal
-             "illegal dependency cycle for emitter %a"
-             Usable_emitter.pretty e
-             | Several ->
-             (* cycle because the proof of [ppt] with emitter [E1] depends
-             on another [ppt'] which is proven with another emitter [E2]
-             by using [ppt] itself: it is not inconsistent by itself, but we
-             cannot use it as a proof. *)
-             consolidate ppt
-             (fun _ ->
-             Unknown
-             (Usable_emitter.Map.add
-             usable_kernel_emitter
-             (Usable_emitter.Map.add
-             usable_kernel_emitter
-             (List.fold_left
-             (fun acc p -> Property.Set.add p acc)
-             Property.Set.empty
-             path)
-             Usable_emitter.Map.empty)
-             Usable_emitter.Map.empty))*)
+           (* match e with
+              | Not_yet -> assert false
+              | Single e ->
+              if Valid_cycles.mem e path then
+              consolidate_of_local_when_cycle ppt
+              else
+              Kernel.fatal
+              "illegal dependency cycle for emitter %a"
+              Usable_emitter.pretty e
+              | Several ->
+              (* cycle because the proof of [ppt] with emitter [E1] depends
+              on another [ppt'] which is proven with another emitter [E2]
+              by using [ppt] itself: it is not inconsistent by itself, but we
+              cannot use it as a proof. *)
+              consolidate ppt
+              (fun _ ->
+              Unknown
+              (Usable_emitter.Map.add
+              usable_kernel_emitter
+              (Usable_emitter.Map.add
+              usable_kernel_emitter
+              (List.fold_left
+              (fun acc p -> Property.Set.add p acc)
+              Property.Set.empty
+              path)
+              Usable_emitter.Map.empty)
+              Usable_emitter.Map.empty))*)
          end else begin
            Property.Hashtbl.add visited_ppt ppt ();
            consolidate ppt (consolidated_emitters ppt e (ppt :: path))
            (* [JS 2011/11/04] think about that when uncommenting the code above *)
-           (*	  try
-             (* was previously added during its own calculus
-             in case of inconsistent mutual dependency *)
-             Consolidated_status.find ppt
-             with Not_found ->*)
-           (*	    consolidated_status*)
+           (* try
+              (* was previously added during its own calculus
+              in case of inconsistent mutual dependency *)
+              Consolidated_status.find ppt
+              with Not_found ->*)
+           (* consolidated_status*)
          end)
       ppt
 
@@ -1432,15 +1432,15 @@ module Consolidation_graph = struct
              (distinct_tuning_parameters emitter)
              g
          in
-         (*	let g =
-           (* adding the correctness parameters *)
-           Datatype.String.Set.fold
-           (fun p g ->
-           let s = get_parameter_string ~tuning:false emitter p in
-           G.add_edge g v_e (Correctness_parameter s); g)
-           (distinct_correctness_parameters emitter)
-           g
-           in*)
+         (* let g =
+            (* adding the correctness parameters *)
+            Datatype.String.Set.fold
+            (fun p g ->
+            let s = get_parameter_string ~tuning:false emitter p in
+            G.add_edge g v_e (Correctness_parameter s); g)
+            (distinct_correctness_parameters emitter)
+            g
+            in*)
          (* adding the hypotheses *)
          let g, truncated =
            List.fold_left
diff --git a/src/kernel_services/ast_queries/cil.ml b/src/kernel_services/ast_queries/cil.ml
index df68d08f1229c4946c3e9aa197e3e30028c4ce9b..c727f7c578c8468876beca8ae4492ef585e01a2e 100644
--- a/src/kernel_services/ast_queries/cil.ml
+++ b/src/kernel_services/ast_queries/cil.ml
@@ -5289,7 +5289,7 @@ let rec constFoldTermNodeAtTop = function
     end
   | TSizeOfE { term_type= Ctype typ } -> constFoldTermNodeAtTop (TSizeOf typ)
   | TAlignOfE { term_type= Ctype typ }
-    -> 	constFoldTermNodeAtTop (TAlignOf typ)
+    -> constFoldTermNodeAtTop (TAlignOf typ)
   | TSizeOfE _ | TAlignOfE _ ->
     assert false (* sizeof/alignof of logic types are rejected
                     by typing anyway. *)
diff --git a/src/kernel_services/ast_queries/cil.mli b/src/kernel_services/ast_queries/cil.mli
index 05129394522bc88c3537442082d1ca52890276dc..005bc7a063469332ff0ab80f24dbf8ad3778d501 100644
--- a/src/kernel_services/ast_queries/cil.mli
+++ b/src/kernel_services/ast_queries/cil.mli
@@ -1752,14 +1752,14 @@ class type cilVisitor = object
 
   method vfrees:
     identified_term list -> identified_term list visitAction
-  (**	@since Oxygen-20120901 *)
+  (**   @since Oxygen-20120901 *)
 
   method vallocates:
     identified_term list -> identified_term list visitAction
-  (**	@since Oxygen-20120901 *)
+  (**   @since Oxygen-20120901 *)
 
   method vallocation: allocation -> allocation visitAction
-  (**	@since Oxygen-20120901 *)
+  (**   @since Oxygen-20120901 *)
 
   method vloop_pragma: loop_pragma -> loop_pragma visitAction
   method vslice_pragma: slice_pragma -> slice_pragma visitAction
@@ -1950,18 +1950,15 @@ val visitCilFrom: cilVisitor -> from -> from
 
 val visitCilAssigns: cilVisitor -> assigns -> assigns
 
-(** @since Oxygen-20120901
-*)
+(** @since Oxygen-20120901 *)
 val visitCilFrees:
   cilVisitor -> identified_term list -> identified_term list
 
-(** @since Oxygen-20120901
-*)
+(** @since Oxygen-20120901 *)
 val visitCilAllocates:
   cilVisitor -> identified_term list -> identified_term list
 
-(** @since Oxygen-20120901
-*)
+(** @since Oxygen-20120901 *)
 val visitCilAllocation: cilVisitor -> allocation -> allocation
 
 val visitCilFunspec: cilVisitor -> funspec -> funspec
diff --git a/src/kernel_services/ast_queries/logic_typing.ml b/src/kernel_services/ast_queries/logic_typing.ml
index 9de89b3bbd52faf2f891fa3e459652c61145fa1e..d91eced84818a70794632819757dfae6d9a80ee9 100644
--- a/src/kernel_services/ast_queries/logic_typing.ml
+++ b/src/kernel_services/ast_queries/logic_typing.ml
@@ -600,7 +600,7 @@ let type_of_array_elem =
 
 let rec ctype_of_array_elem t =
   match unroll_type t with
-  |	Ctype ty when isArrayType ty -> Cil.typeOf_array_elem ty
+  | Ctype ty when isArrayType ty -> Cil.typeOf_array_elem ty
   | Ltype ({lt_name = "set"},[t]) -> ctype_of_array_elem t
   | _ ->
     Kernel.fatal ~current:true "type %a is not a pointer type"
@@ -2726,7 +2726,7 @@ struct
       let ty1 = t1.term_type in
       let t2 = term env t2 in
       let ty2 = t2.term_type in
-      let binop op tr =	TBinOp (op, mk_cast t1 tr, mk_cast t2 tr),
+      let binop op tr = TBinOp (op, mk_cast t1 tr, mk_cast t2 tr),
                         logic_arithmetic_promotion tr
       in
       begin match op with
diff --git a/src/libraries/utils/hptmap.ml b/src/libraries/utils/hptmap.ml
index 15622103b2b1eb7dffe642f3b5f38e8bae4f9c60..b1fff81fcd0702622895d473cd5717a959edf906 100644
--- a/src/libraries/utils/hptmap.ml
+++ b/src/libraries/utils/hptmap.ml
@@ -999,7 +999,7 @@ struct
         | Branch(p, m, s0, s1, _) ->
           try
             let result = Hashtbl.find table t in
-            (*		Format.printf "find %s %d@." name !counter; *)
+            (* Format.printf "find %s %d@." name !counter; *)
             result
           with Not_found ->
             let result0 = traverse s0 in
@@ -1008,11 +1008,11 @@ struct
             incr counter;
             if !counter >= cache
             then begin
-              (*	    Format.printf "Clearing %s fold table@." name;*)
+              (* Format.printf "Clearing %s fold table@." name;*)
               Hashtbl.clear table;
               counter := 0;
             end;
-            (*		Format.printf "add  %s %d@." name !counter; *)
+            (* Format.printf "add  %s %d@." name !counter; *)
             Hashtbl.add table t result;
             result
       in
diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c
index 9e5dd8f5f57cc7ae8befa78281fae1ec6a13849a..57d0faa6e0ce920640004927792c79e94747facd 100644
--- a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c
+++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c
@@ -1086,7 +1086,7 @@ void print_heap_shadows(uintptr_t addr) {
   DLOG(" | === Block Shadow ======================================\n");
   DLOG(" | Access addr:    %a\n", addr);
   DLOG(" | Block Shadow:   %a\n", block_shadow);
-  DLOG(" | Init	 Shadow:   %a\n", init_shadow);
+  DLOG(" | Init  Shadow:   %a\n", init_shadow);
   DLOG(" | Segments:       %lu\n", segments);
   DLOG(" | Actual size:    %lu bytes\n", alloc_size);
   DLOG(" | Tracked Length: %lu bytes\n", length);
diff --git a/src/plugins/e-acsl/src/analyses/memory_tracking.ml b/src/plugins/e-acsl/src/analyses/memory_tracking.ml
index 1b0830126927676e793b79ea589dc27bde37cdb4..d6fec703a3d1f0e02084364627157e058fbd3c66 100644
--- a/src/plugins/e-acsl/src/analyses/memory_tracking.ml
+++ b/src/plugins/e-acsl/src/analyses/memory_tracking.ml
@@ -364,7 +364,7 @@ module rec Transfer
       | Pvalid(_, t) | Pvalid_read(_, t)
       | Pobject_pointer(_, t) | Pvalid_function t
       | Pinitialized(_, t) | Pfreeable(_, t) ->
-        (*	Options.feedback "REGISTER %a" Cil.d_term t;*)
+        (* Options.feedback "REGISTER %a" Cil.d_term t;*)
         state_ref := register_term kf !state_ref t;
         Cil.DoChildren
       | Pseparated tlist ->
diff --git a/src/plugins/eva/utils/eva_perf.ml b/src/plugins/eva/utils/eva_perf.ml
index 84655c882771e48a75fb4a3583555b72d37441ae..7db82e63fe48003fa5b5a2b4dff1d81bf200e2ef 100644
--- a/src/plugins/eva/utils/eva_perf.ml
+++ b/src/plugins/eva/utils/eva_perf.ml
@@ -94,7 +94,7 @@ module Call_info = struct
     let list = ref [] in
     iter (fun elt ->
         let ci = get_ci elt in
-        if total_duration current_time ci	> threshold
+        if total_duration current_time ci > threshold
         then list := elt::!list);
     let sorted_list = List.fast_sort
         (fun elt1 elt2 -> (cmp current_time) (get_ci elt1) (get_ci elt2)) !list
diff --git a/tools/hdrck/dune-project b/tools/hdrck/dune-project
index c96c6067e4897c2cefb0a05d9d8219bca7602eb9..a7877e55264bccb1fe5bc7d216b0395c59971b38 100644
--- a/tools/hdrck/dune-project
+++ b/tools/hdrck/dune-project
@@ -22,5 +22,5 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (package
-  (name frama-c-hdchk)
+  (name frama-c-hdrck)
 )
diff --git a/tools/hdrck/hdrck.ml b/tools/hdrck/hdrck.ml
index e8fd61024ec531b223e662b26ccf70d4aac4ae5d..c65802c97ed37153ee18d8e752ff96f0cab20b44 100644
--- a/tools/hdrck/hdrck.ml
+++ b/tools/hdrck/hdrck.ml
@@ -60,7 +60,7 @@ and headache_config_file = ref [] (* empty -> headache_config_file_default *)
 and headache_config_file_default = "headers/headache_config.txt"
 and exit_on_warning = ref false
 and exit_on_error = ref true (* only settable to false for debugging purposes *)
-
+and quiet = ref false
 
 type mode =
   | Check
@@ -84,7 +84,8 @@ let job_head fmt =
   Format.printf fmt
 
 let job_done () =
-  Format.printf "done@."
+  if not !quiet then
+    Format.printf "done@."
 
 let pp_job_first_line () =
   if !is_first_job_line then
@@ -258,7 +259,8 @@ let add_spec_entry (ignored_files: StringSet.t ref) (spec_tab: (string, string)
 let read_specs spec_format (ignored_files: StringSet.t ref) (spec_tab: (string, string) Hashtbl.t) (spec_file : string option) =
   let spec_fname = match spec_file with None -> "--stdin" | Some filename -> filename in
   debug "Specification file: %s@." spec_fname ;
-  job_head "Checking format of specification file %s... @?" spec_fname;
+  if not !quiet then
+    job_head "Checking format of specification file %s... @?" spec_fname;
   let sub_dir = extract_sub_dir spec_fname in
   let add_spec, get_line =
     let add_spec_item i ~file_name ~license_name =
@@ -337,7 +339,8 @@ let get_header_files ?directories:(dirs=(get_header_dirs ())) () :
   let license_path_tbl = Hashtbl.create 23 in
   List.iter
     (fun dir ->
-       job_head "Reading license header definition files from directory %s... @?" dir;
+       if not !quiet then
+         job_head "Reading license header definition files from directory %s... @?" dir;
        if Sys.file_exists dir && Sys.is_directory dir then begin
          Array.iter
            (fun filename ->
@@ -373,7 +376,8 @@ let get_header_files ?directories:(dirs=(get_header_dirs ())) () :
    @requires ignored files have been filtered out the specifications
 *)
 let check_declared_headers specification headers =
-  job_head "Checking license specifications are defined... @?" ;
+  if not !quiet then
+    job_head "Checking license specifications are defined... @?" ;
   Hashtbl.iter
     (fun file header_type ->
        if not (Hashtbl.mem headers header_type) then begin
@@ -429,7 +433,8 @@ let check_spec_discrepancies
     if ret <> 0 && !debug_flag then extract_header orig_file template_hdr ;
     ret = 0
   in
-  job_head "Checking specification discrepancies... @?";
+  if not !quiet then
+    job_head "Checking specification discrepancies... @?";
   let n = ref 0 in
   let discrepancies = ref [] in
   Hashtbl.iter
@@ -459,7 +464,8 @@ let check_spec_discrepancies
 
 let check_forbidden_headers (forbidden_headers:StringSet.t) header_specifications (distributed_files:StringSet.t) =
   if not (StringSet.is_empty forbidden_headers) then begin
-    job_head "Checking that all distributed files have no forbidden header specification... @?";
+    if not !quiet then
+      job_head "Checking that all distributed files have no forbidden header specification... @?";
     let forbidden = ref [] in
     let n = ref 0 in
     StringSet.iter
@@ -492,7 +498,8 @@ let check_forbidden_headers (forbidden_headers:StringSet.t) header_specification
  * @param exceptions a set of files distributed but that should not be checked
 *)
 let check files_ignored header_specifications distributed_files exceptions =
-  job_head "Checking that all distributed files do exist... @?";
+  if not !quiet then
+    job_head "Checking that all distributed files do exist... @?";
   let nonexistent_files =
     StringSet.filter (fun f -> not (Sys.file_exists f)) distributed_files
   in
@@ -501,7 +508,8 @@ let check files_ignored header_specifications distributed_files exceptions =
       "@[<v 2># Non-existing files listed as distributed:@ %a@]@."
       StringSet.pp nonexistent_files;
   job_done ();
-  job_head "Checking that distributed exception files have no license header specification... @?";
+  if not !quiet then
+    job_head "Checking that distributed exception files have no license header specification... @?";
   let files_licencied =
     Hashtbl.fold
       (fun file _ set -> StringSet.add file set)
@@ -518,7 +526,8 @@ let check files_ignored header_specifications distributed_files exceptions =
       "@[<v 2># Files distributed with an header exception (even having to be ignored):@ %a@]@."
       StringSet.pp ignored_exceptions;
   job_done ();
-  job_head "Checking that other distributed files have a license header specification... @?";
+  if not !quiet then
+    job_head "Checking that other distributed files have a license header specification... @?";
   let files_to_check = StringSet.diff distributed_files exceptions in
   let files_specified = StringSet.union files_licencied files_ignored in
   let distributed_unspecified = StringSet.diff files_to_check files_specified in
@@ -527,7 +536,8 @@ let check files_ignored header_specifications distributed_files exceptions =
       "@[<v 2># Files distributed without specified header:@ %a@]@."
       StringSet.pp distributed_unspecified;
   job_done ();
-  job_head "Checking presence of source files having an header specification... @?" ;
+  if not !quiet then
+    job_head "Checking presence of source files having an header specification... @?" ;
   StringSet.iter
     (fun filename ->
        if not (Sys.file_exists filename) then
@@ -565,7 +575,8 @@ let update_headers ~config_file_opts header_specifications =
       else
         debug "%s : error updating header" filename
   in
-  job_head "Updating header files ... @?";
+  if not !quiet then
+    job_head "Updating header files ... @?";
   Hashtbl.iter
     (fun filename header_name ->
        if Sys.file_exists filename then begin
@@ -620,6 +631,8 @@ let rec argspec = [
   " print this option list and exits";
   "-debug", Arg.Set debug_flag,
   " enable debug messages";
+  "-quiet", Arg.Set quiet,
+  "disable most messages";
 
   "-forbidden-headers", Arg.String (fun set -> set_cumulative ~name:"-forbidden-headers" forbidden_headers ~set) ,
   "<license name>,... \t none of the checked files may have one of the <license name> []";
@@ -679,7 +692,8 @@ let _ =
       let ignored_files = ref StringSet.empty in
       if !from_stdin then read_specs (if !zero_stdin then Zero3 else !spec_format) ignored_files specified_files None;
       List.iter (fun f -> read_specs !spec_format ignored_files specified_files (Some f)) spec_files;
-      Format.printf "- ignored=%d@.- specified=%d@." (StringSet.cardinal !ignored_files) (Hashtbl.length specified_files);
+      if not !quiet then
+        Format.printf "- ignored=%d@.- specified=%d@." (StringSet.cardinal !ignored_files) (Hashtbl.length specified_files);
       match !mode with
       | Check ->
         let stringset_from_opt_file = function
@@ -692,7 +706,8 @@ let _ =
         in
         let distributed_files = stringset_from_opt_file distrib_file_opt in
         let header_exception_files = stringset_from_opt_file header_except_opt in
-        Format.printf "- excepted=%d@.- distributed=%d@." (StringSet.cardinal header_exception_files) (StringSet.cardinal distributed_files);
+        if not !quiet then
+          Format.printf "- excepted=%d@.- distributed=%d@." (StringSet.cardinal header_exception_files) (StringSet.cardinal distributed_files);
         check ~config_file_opts !ignored_files specified_files distributed_files header_exception_files
       | Update ->
         update_headers ~config_file_opts specified_files;
diff --git a/tools/lint/dune b/tools/lint/dune
new file mode 100644
index 0000000000000000000000000000000000000000..9240fe33ae0b3204e2c656aab00ac80e037a1518
--- /dev/null
+++ b/tools/lint/dune
@@ -0,0 +1,28 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                                                        ;;
+;;  This file is part of Frama-C.                                         ;;
+;;                                                                        ;;
+;;  Copyright (C) 2007-2022                                               ;;
+;;    CEA (Commissariat à l'énergie atomique et aux énergies              ;;
+;;         alternatives)                                                  ;;
+;;                                                                        ;;
+;;  you can redistribute it and/or modify it under the terms of the GNU   ;;
+;;  Lesser General Public License as published by the Free Software       ;;
+;;  Foundation, version 2.1.                                              ;;
+;;                                                                        ;;
+;;  It is distributed in the hope that it will be useful,                 ;;
+;;  but WITHOUT ANY WARRANTY; without even the implied warranty of        ;;
+;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ;;
+;;  GNU Lesser General Public License for more details.                   ;;
+;;                                                                        ;;
+;;  See the GNU Lesser General Public License version 2.1                 ;;
+;;  for more details (enclosed in the file licenses/LGPLv2.1).            ;;
+;;                                                                        ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(executable
+ (public_name frama-c-lint)
+ (name lint)
+ (modules lint)
+ (libraries unix camomile ocp-indent.lexer ocp-indent.lib ocp-indent.dynlink)
+)
diff --git a/tools/lint/dune-project b/tools/lint/dune-project
new file mode 100644
index 0000000000000000000000000000000000000000..485c7a1837dfaa1e25cf4340dc66c5d64d82bdb9
--- /dev/null
+++ b/tools/lint/dune-project
@@ -0,0 +1,26 @@
+(lang dune 3.2)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                                                        ;;
+;;  This file is part of Frama-C.                                         ;;
+;;                                                                        ;;
+;;  Copyright (C) 2007-2022                                               ;;
+;;    CEA (Commissariat à l'énergie atomique et aux énergies              ;;
+;;         alternatives)                                                  ;;
+;;                                                                        ;;
+;;  you can redistribute it and/or modify it under the terms of the GNU   ;;
+;;  Lesser General Public License as published by the Free Software       ;;
+;;  Foundation, version 2.1.                                              ;;
+;;                                                                        ;;
+;;  It is distributed in the hope that it will be useful,                 ;;
+;;  but WITHOUT ANY WARRANTY; without even the implied warranty of        ;;
+;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ;;
+;;  GNU Lesser General Public License for more details.                   ;;
+;;                                                                        ;;
+;;  See the GNU Lesser General Public License version 2.1                 ;;
+;;  for more details (enclosed in the file licenses/LGPLv2.1).            ;;
+;;                                                                        ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(package
+  (name frama-c-lint)
+)
diff --git a/tools/lint/lint.ml b/tools/lint/lint.ml
new file mode 100644
index 0000000000000000000000000000000000000000..645fdf437ee74eedc963d2940d30fcc81cea7c3a
--- /dev/null
+++ b/tools/lint/lint.ml
@@ -0,0 +1,335 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2022                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open CamomileLibrary
+
+(**************************************************************************)
+(* Utils *)
+
+let read_buffered channel =
+  let buffer = Buffer.create 0 in
+  let size = 4096 in
+  let load = Bytes.create size in
+  let read = ref @@ input channel load 0 size in
+  while !read <> 0 do
+    Buffer.add_subbytes buffer load 0 !read ;
+    read := input channel load 0 size
+  done ;
+  Buffer.to_bytes buffer
+
+let rec lines_from_buffer acc buffer start =
+  if start = Bytes.length buffer then acc
+  else
+    let line_end = Bytes.index_from buffer start '\000' in
+    let len = line_end - start in
+    let line = Bytes.sub_string buffer start len in
+    lines_from_buffer (line :: acc) buffer (line_end + 1)
+
+let lines_from_in channel =
+  let content = read_buffered channel in
+  let acc = lines_from_buffer [] content 0 in
+  List.rev acc
+
+(**************************************************************************)
+(* Available Checks and corresponding attributes *)
+
+type checks =
+  { eoleof : bool
+  ; indent : bool
+  ; syntax : bool
+  ; utf8 : bool
+  }
+
+let no_checks =
+  { eoleof = false
+  ; indent = false
+  ; syntax = false
+  ; utf8 = false
+  }
+
+let add_attr checks attr value =
+  let value = value = "set" in
+  match attr with
+  | "check-eoleof" -> { checks with eoleof = value }
+  | "check-indent" -> { checks with indent = value }
+  | "check-syntax" -> { checks with syntax = value }
+  | "check-utf8"   -> { checks with utf8 = value }
+  | _ -> failwith (Printf.sprintf "Unknown attr %s" attr)
+
+let handled_attr s =
+  s = "check-eoleof" || s = "check-indent" ||
+  s = "check-syntax" || s = "check-utf8"
+
+let ignored_attr s =
+  not (handled_attr s)
+
+(**************************************************************************)
+(* Table of the files to control *)
+
+let table = Hashtbl.create 1031
+
+let get file =
+  try Hashtbl.find table file
+  with Not_found -> no_checks
+
+let rec collect = function
+  | _file :: attr :: _value :: tl when ignored_attr attr ->
+    collect tl
+  | file :: attr :: value :: tl ->
+    let checks = get file in
+    Hashtbl.replace table file (add_attr checks attr value) ;
+    collect tl
+  | [] -> ()
+  | l -> List.iter (Printf.eprintf "Could not load file list %s\n") l
+
+(**************************************************************************)
+(* Functions used to check lint *)
+
+(* UTF8 *)
+
+let is_utf8 content =
+  try UTF8.validate (Bytes.to_string content) ; true
+  with UTF8.Malformed_code -> false
+
+(* Syntax *)
+
+let check_syntax ~update content =
+  let size = Bytes.length content in
+  let out = Buffer.create 0 in
+  let exception Bad_syntax in
+  try
+    let i = ref 0 in
+    let blank = ref (-1) in
+    while !i < size do
+      let byte = Bytes.get content !i in
+      if byte = '\t' then begin
+        if not update then raise Bad_syntax ;
+        if !blank = -1 then blank := Buffer.length out ;
+        Buffer.add_string out "  "
+      end
+      else if byte = ' ' then begin
+        if !blank = -1 then blank := Buffer.length out ;
+        Buffer.add_char out ' '
+      end
+      else if byte = '\n' && !blank <> -1 then begin
+        if not update then raise Bad_syntax ;
+        Buffer.truncate out !blank ;
+        Buffer.add_char out '\n' ;
+        blank := -1
+      end
+      else begin
+        Buffer.add_char out byte ;
+        blank := -1
+      end ;
+      incr i
+    done ;
+    if !blank <> -1 then
+      Buffer.truncate out !blank ;
+    let out = Buffer.to_bytes out in
+    if not @@ Bytes.equal out content
+    then out, false
+    else content, true
+  with Bad_syntax ->
+    content, false
+
+(* EOL/EOF *)
+
+let check_eoleof ~update content =
+  let length = Bytes.length content in
+  if length = 0 then content, true
+  else if '\n' = Bytes.get content (length - 1) then content, true
+  else if update then begin
+    let new_content = Bytes.extend content 0 1 in
+    Bytes.set new_content length '\n' ;
+    new_content, false
+  end else
+    content,false
+
+(* Indentation *)
+
+(* ML(I) *)
+
+(* Basically this is OCP-Indent main where all elements related to options have
+   been removed and the printer changed so that it prints into a buffer and not
+   a file.
+*)
+
+let global_config = ref None
+let config () =
+  match !global_config with
+  | None ->
+    let config, syntaxes, dlink = IndentConfig.local_default () in
+    IndentLoader.load ~debug:false dlink ;
+    Approx_lexer.disable_extensions ();
+    List.iter
+      (fun stx ->
+         try Approx_lexer.enable_extension stx
+         with IndentExtend.Syntax_not_found name ->
+           Format.eprintf "Warning: unknown syntax extension %S@." name)
+      syntaxes ;
+    global_config := Some config ;
+    config
+  | Some config -> config
+
+let ocp_indent channel =
+  let config = config () in
+  let buffer = Buffer.create 0 in
+  let out = IndentPrinter.{
+      debug = false; config = config; indent_empty = false; adaptive = true;
+      in_lines = (fun _ -> true);
+      kind = Print (fun s b -> Buffer.add_string b s ; b);
+    }
+  in
+  let stream = Nstream.of_channel channel in
+  Buffer.to_bytes (IndentPrinter.proceed out stream IndentBlock.empty buffer)
+
+let check_ml_indent ~update file =
+  let input = open_in file in
+  let original = read_buffered input in
+  seek_in input 0 ;
+  let modified = ocp_indent input in
+  close_in input ;
+  let result = Bytes.equal original modified in
+  if update && not result then
+    let output = open_out file in
+    output_bytes output modified ;
+    close_out output ;
+    true
+  else
+    result
+
+(* C/H *)
+
+let clang_format_available = ref None
+let clang_format_available () =
+  match !clang_format_available with
+  | None ->
+    clang_format_available :=
+      Some (0 = Sys.command "clang-format --version > /dev/null") ;
+    Option.get !clang_format_available
+  | Some available -> available
+
+let check_c_indent ~update file =
+  if not @@ clang_format_available () then true
+  else
+    let opt = if update then "-i" else "--dry-run -Werror" in
+    0 = Sys.command (Printf.sprintf "clang-format %s \"%s\"" opt file)
+
+exception Bad_ext
+
+let check_indent ~update file =
+  match Filename.extension file with
+  | ".c" | ".h" -> check_c_indent ~update file
+  | ".ml" | ".mli" -> check_ml_indent ~update file
+  | _ -> raise Bad_ext
+
+let res = ref true
+
+(* Main checks *)
+
+let check ~verbose ~update file params =
+  if verbose then
+    Printf.printf "Checking %s\n" file ;
+  if Sys.is_directory file then ()
+  else begin
+    let in_chan = open_in file in
+    let content = read_buffered in_chan in
+    close_in in_chan ;
+    (* UTF8 *)
+    if params.utf8 then
+      if not @@ is_utf8 content then begin
+        Printf.eprintf "Bad encoding (not UTF8) for %s\n" file ;
+        res := false
+      end ;
+    (* Blanks *)
+    let rewrite = ref false in
+    let syntactic_check checker content message  =
+      let new_content, was_ok = checker ~update content in
+      if update && not was_ok
+      then begin rewrite := true ; new_content end
+      else if not was_ok then begin
+        Printf.eprintf "%s for %s\n" message file ;
+        res := false ; new_content
+      end
+      else new_content
+    in
+    let content =
+      if params.syntax
+      then syntactic_check check_syntax content "Bad syntax"
+      else content
+    in
+    let content =
+      if params.eoleof || params.syntax
+      then syntactic_check check_eoleof content "Bad EOF"
+      else content
+    in
+    if !rewrite then begin
+      let out_chan = open_out file in
+      output_bytes out_chan content ;
+      close_out out_chan
+    end ;
+    (* Indentation *)
+    try
+      if params.indent then
+        if not @@ check_indent ~update file then begin
+          Printf.eprintf "Bad indentation for %s\n" file ;
+          res := false
+        end ;
+    with Bad_ext ->
+      Printf.eprintf "Don't know how to (check) indent %s\n" file ;
+      res := false
+  end
+
+(**************************************************************************)
+(* Options *)
+
+let exec_name = Sys.argv.(0)
+let update = ref false
+let verbose = ref false
+
+let rec argspec = [
+  "--help", Arg.Unit print_usage, "print this option list and exits" ;
+  "-u", Arg.Set update, "update ill-formed files (does not handle UTF8 update)" ;
+  "-v", Arg.Set verbose, "verbose mode" ;
+]
+and sort argspec =
+  List.sort (fun (name1, _, _) (name2, _, _) -> String.compare name1 name2)
+    argspec
+and print_usage () =
+  Arg.usage
+    (Arg.align (sort argspec))
+    (Printf.sprintf "Usage: %s [-u]" exec_name) ;
+  exit 0
+
+(**************************************************************************)
+(* Main *)
+
+let () =
+  if not @@ clang_format_available () then
+    Printf.eprintf "clang-format unavailable, I will not check C files" ;
+  Arg.parse
+    (Arg.align (sort argspec))
+    (fun s -> Printf.eprintf "Unknown argument: %s" s)
+    "";
+  collect @@ lines_from_in stdin ;
+  Hashtbl.iter (check ~verbose:!verbose ~update:!update) table ;
+  if not !res then exit 1