diff --git a/nix/frama-c-lint.nix b/nix/frama-c-lint.nix
index d33b25870079d5d24d2ba1a685ac8b1b3a203d82..dfb9e89512a7e6ac4d8908c4709f27593cf355da 100644
--- a/nix/frama-c-lint.nix
+++ b/nix/frama-c-lint.nix
@@ -6,6 +6,8 @@
 , gitignoreSource
 , ocaml
 , ocp-indent
+, ppx_deriving_yojson
+, yojson
 } :
 
 stdenv.mkDerivation rec {
@@ -24,6 +26,8 @@ stdenv.mkDerivation rec {
     findlib
     ocaml
     ocp-indent
+    ppx_deriving_yojson
+    yojson
   ];
 
   configurePhase = ''
diff --git a/tools/lint/README.md b/tools/lint/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b5ab4cfe03061fe8370eb9f4cc9a59bd2f25c2b9
--- /dev/null
+++ b/tools/lint/README.md
@@ -0,0 +1,83 @@
+## frama-c-lint tool
+
+## Usage
+
+
+```
+> frama-c-lint -help
+Usage: git ls-files -z | git check-attr --stdin -z -a | _build/install/default/bin/frama-c-lint [options]
+
+Checks or updates files in relation to lint constraints specified by these git attributes:
+  check-eoleof, check-syntax, check-utf8 and check-indent.
+
+Options:
+  --version        Prints tool version
+  -c <json-config-file> Reads the JSON configuration file (allows to overload the default configuration)
+  -e               Prints default JSON configuration
+  -s               Considers warnings as errors for the exit value
+  -u               Update ill-formed files (does not handle UTF8 update)
+  -v               Verbose mode
+  -help            Display this list of options
+  --help           Display this list of options
+```
+
+## Managed Git Attributes
+
+The tool manage the following `git` attributes:
+`check-eoleof`, `check-syntax`, `check-utf8`, `check-indent`.
+
+All of them can be `set` or `unset`, but a tool name can also be assignable to the `check-indent` attribute.
+
+The check/update commands related to `check-eoleof`, `check-syntax` and `check-utf8` attributes are not overloadable and cannot be parametrized.
+Only the commands related to `check-indent` attribute can be parametrized.
+
+## The Parametrizable Configuration
+
+The command `frama-c-lint -e` pretty prints the JSON description equivalent to the default parametrizable configuration related to `check-indent` attribute.
+
+```
+> frama-c-lint -e
+Default JSON configuration:
+[
+  {
+    "kind": "C",
+    "extensions": [ ".c", ".h" ],
+    "name": "clang-format",
+    "available_cmd": "clang-format --version > /dev/null 2> /dev/null",
+    "check_cmd": "clang-format --dry-run -Werror",
+    "update_cmd": "clang-format -i"
+  },
+  {
+    "kind": "Python",
+    "extensions": [ ".py" ],
+    "name": "black",
+    "available_cmd": "black --version > /dev/null 2> /dev/null",
+    "check_cmd": "black --quiet --line-length 100 --check",
+    "update_cmd": "black --quiet --line-length 100"
+  }
+]
+```
+
+This description defines `black` and `clang-format` as the assignable values of the `git` attribute `check-indent`. It also specifies the system command to use for checking the availability of the tool to use for checking/updating the indentation. The check and update commands are also specified.
+
+When the `check-indent` attribute is set without a value, the description specifies the tool to use from the extension of the file to check/update.
+
+There is also a built-in configuration for `.ml` and `.mli` files based of `ocp-indent` tool (from a directly use of `ocp-indent` library to improve the efficiency of the tool) that can be overloaded if necessary.
+That means there is an implicit overloadable JSON description:
+```
+[
+  {
+    "kind": "OCaml",
+    "extensions": [ ".ml", ".mli" ],
+    "name": "ocp-indent",
+    "available_cmd": "...",
+    "check_cmd": "...",
+    "update_cmd": "..."
+  }
+]
+```
+
+The option  `-c <json-confi g-file>` allows to extend and/or overload the default configuration.
+
+When the `available_cmd` field is set to an empty string, that disable the check/update with the related tool.
+An empty string can also be set to the field `check_cmd` (resp. `update_cmd`) when the related tool does not offer check (resp. update) command.
diff --git a/tools/lint/dune b/tools/lint/dune
index 1d2b1c4f05f25e66ac27035c716f10657484b5a2..cff48d122fdfd851d113eb40f14a7f5b1bc38f09 100644
--- a/tools/lint/dune
+++ b/tools/lint/dune
@@ -24,5 +24,6 @@
  (public_name frama-c-lint)
  (name lint)
  (modules lint UTF8)
- (libraries unix ocp-indent.lexer ocp-indent.lib ocp-indent.dynlink)
+ (preprocess (pps ppx_deriving_yojson))
+ (libraries unix yojson ocp-indent.lexer ocp-indent.lib ocp-indent.dynlink)
 )
diff --git a/tools/lint/lint.ml b/tools/lint/lint.ml
index 3b6a33d13dd0ccd080933768d44ce8712d05794f..e08be5eb7821545c3530ad9ad365b7a1300c489f 100644
--- a/tools/lint/lint.ml
+++ b/tools/lint/lint.ml
@@ -20,6 +20,38 @@
 (*                                                                        *)
 (**************************************************************************)
 
+type tool_cmds =
+  { kind: string ;
+    extensions: string list ;
+    name: string ;
+    available_cmd: string ; (* leaves it empty to set it as unavailable *)
+    check_cmd: string ; (* leaves it empty if there is no check command *)
+    update_cmd: string (* leaves it empty if there is no updating command *)
+  }
+[@@deriving yojson]
+
+(**************************************************************************)
+(** The only part to modify for adding a new external formatters *)
+
+(** Supported indent formatters *)
+let external_formatters = [
+  { kind = "C";
+    extensions = [ ".c" ; ".h" ];
+    name = "clang-format";
+    available_cmd = "clang-format --version > /dev/null 2> /dev/null";
+    check_cmd = "clang-format --dry-run -Werror" ;
+    update_cmd = "clang-format -i"
+  }
+  ;
+  { kind = "Python";
+    extensions = [ ".py" ];
+    name = "black";
+    available_cmd = "black --version > /dev/null 2> /dev/null";
+    check_cmd = "black --quiet --line-length 100 --check" ;
+    update_cmd = "black --quiet --line-length 100"
+  }
+]
+
 (**************************************************************************)
 (* Warning/Error *)
 
@@ -65,36 +97,46 @@ let lines_from_in channel =
   List.rev acc
 
 (**************************************************************************)
-(* Supported indent formatter *)
-
-type formatter_cmds =
+type available_tools =
   { mutable is_available: bool option ;
-    kind: string ;
-    name: string ;
-    available_cmd: string ;
-    check_cmd: string ;
-    update_cmd: string (* leaves it empty if there is no updating command *)
+    tool_cmds: tool_cmds
   }
 
-let c_indent_formatter =
-  { is_available = None ;
-    kind = "C";
-    name = "clang-format";
-    available_cmd = "clang-format --version > /dev/null 2> /dev/null";
-    check_cmd = "clang-format --dry-run -Werror" ;
-    update_cmd = "clang-format -i"
-  }
+type indent_formatter = Ocp_indent | Tool of available_tools
 
-let python_indent_formatter =
-  { is_available = None ;
-    kind = "Python";
-    name = "black";
-    available_cmd = "black --version > /dev/null 2> /dev/null";
-    check_cmd = "black --quiet --line-length 100 --check" ;
-    update_cmd = "black --quiet --line-length 100"
-  }
+(* from formatter name *)
+let external_tbl = Hashtbl.create 13
+
+(* from file extension *)
+let default_tbl = Hashtbl.create 13
 
-type indent_formatter = Ocp_indent | Tool of  formatter_cmds
+let updates_tbl external_tools =
+  List.iter (fun formatter ->
+      let tool = Tool { is_available = None; tool_cmds = formatter } in
+      List.iter (fun extension ->
+          Hashtbl.replace default_tbl extension tool)
+        formatter.extensions;
+      Hashtbl.add external_tbl formatter.name tool)
+    external_tools
+
+let () = updates_tbl external_formatters
+
+type tools = tool_cmds list
+[@@deriving yojson]
+
+let parse_config config_file =
+  if config_file <> "" then
+    let config_tools =
+      try
+        tools_of_yojson (Yojson.Safe.from_file config_file)
+      with Yojson.Json_error txt ->
+        Error txt
+    in match config_tools with
+    | Result.Ok external_tools -> updates_tbl external_tools
+    | Result.Error txt ->
+      warn "Parse error:%s:%s@." config_file txt
+
+(************************)
 
 let ml_indent_formatter = Ocp_indent
 
@@ -103,12 +145,16 @@ type indent_check = NoCheck | Check of indent_formatter option
 let parse_indent_formatter ~file ~attr ~value = match value with
   | "unset" -> NoCheck
   | "set"   -> Check None (* use the default formatter *)
-  | "ocp-indent" -> Check (Some ml_indent_formatter)
-  | "clang-format" -> Check (Some (Tool c_indent_formatter))
-  | "black" -> Check (Some (Tool python_indent_formatter))
-  | _ -> warn "Unsupported indent formatter: %s %s=%s@."
-           file attr value;
-    NoCheck
+  | _ ->
+    match Hashtbl.find_opt external_tbl value with
+    | None ->
+      if value = "ocp-indent" then
+        (* "ocp-indent" is not overloaded: using the built-in configuration *)
+        Check (Some ml_indent_formatter)
+      else (warn "Unsupported indent formatter: %s %s=%s@."
+              file attr value;
+            NoCheck)
+    | res -> Check res
 
 (**************************************************************************)
 (* Available Checks and corresponding attributes *)
@@ -131,7 +177,7 @@ let add_attr ~file ~attr ~value checks =
   let is_set = function
     | "set" -> true
     | "unset" -> false
-    | _ -> warn "Invalid attribute value: %s %s=%s" file attr value ; false
+    | _ -> warn "Invalid attribute value: %s %s=%s@." file attr value ; false
   in
   match attr with
   | "check-eoleof" -> { checks with eoleof = is_set value }
@@ -139,7 +185,7 @@ let add_attr ~file ~attr ~value checks =
   | "check-utf8"   -> { checks with utf8 = is_set value }
   | "check-indent" -> { checks with
                         indent = parse_indent_formatter ~file ~attr ~value }
-  | _ -> warn "Unknown attribute: %s %s=%s" file attr value;
+  | _ -> warn "Unknown attribute: %s %s=%s@." file attr value;
     checks
 
 let handled_attr s =
@@ -284,11 +330,13 @@ let check_ml_indent ~update file =
 let is_formatter_available ~file indent_formatter =
   match indent_formatter.is_available with
   | None ->
-    let is_available = (0 = Sys.command indent_formatter.available_cmd) in
+    let is_available =
+      let cmd = indent_formatter.tool_cmds.available_cmd in
+      (cmd <> "") && (0 = Sys.command cmd) in
     indent_formatter.is_available <- Some is_available ;
     if not is_available then
       warn "%s is unavailable for checking indentation of some %s files (i.e. %s)@."
-        indent_formatter.name indent_formatter.kind file;
+        indent_formatter.tool_cmds.name indent_formatter.tool_cmds.kind file;
     is_available
   | Some is_available -> is_available
 
@@ -298,20 +346,23 @@ let check_indent ~indent_formatter ~update file =
   let tool = match indent_formatter with
     | Some tool -> tool
     | None -> (* uses the default formatter *)
-      match Filename.extension file with
-      | ".c" | ".h" -> Tool c_indent_formatter
-      | ".ml" | ".mli" -> ml_indent_formatter
-      | ".py" -> Tool python_indent_formatter
-      | _ -> raise Bad_ext
+      let extension = Filename.extension file in
+      match Hashtbl.find_opt default_tbl extension with
+      | Some tool -> tool
+      | None -> match extension with
+        | ".ml" | ".mli" -> ml_indent_formatter
+        | _ -> raise Bad_ext
   in match tool with
   | Ocp_indent -> check_ml_indent ~update file
   | Tool indent_formatter ->
+    let do_cmd cmd =
+      (cmd = "") || (0 = Sys.command (Format.sprintf "%s \"%s\"" cmd file))
+    in
     if not @@ is_formatter_available ~file indent_formatter then true
     else if not update then
-      0 = Sys.command (Format.sprintf "%s \"%s\"" indent_formatter.check_cmd file)
-    else if indent_formatter.update_cmd <> "" then
-      0 = Sys.command (Format.sprintf "%s \"%s\"" indent_formatter.update_cmd file)
-    else true (* there no updating command *)
+      do_cmd indent_formatter.tool_cmds.check_cmd
+    else
+      do_cmd indent_formatter.tool_cmds.update_cmd
 
 (* Main checks *)
 
@@ -371,13 +422,25 @@ let check ~verbose ~update file params =
 (* Options *)
 
 let exec_name = Sys.argv.(0)
+
+let version= "1.0"
+
+let version () =
+  Format.printf "%s version %s@." (Filename.basename exec_name) version;
+  exit 0
+
 let update = ref false
 let verbose = ref false
+let config_file = ref ""
+let extract_config = ref false
 
 let argspec = [
-  "-u", Arg.Set update, " update ill-formed files (does not handle UTF8 update)" ;
-  "-v", Arg.Set verbose, " verbose mode" ;
-  "-s", Arg.Set strict, " considers warnings as errors for the exit value" ;
+  "-u", Arg.Set update, " Update ill-formed files (does not handle UTF8 update)" ;
+  "-v", Arg.Set verbose, " Verbose mode" ;
+  "-s", Arg.Set strict, " Considers warnings as errors for the exit value" ;
+  "-c", Arg.String (fun s -> config_file := s), "<json-config-file> Reads the JSON configuration file (allows to overload the default configuration)" ;
+  "-e", Arg.Set extract_config, " Prints default JSON configuration" ;
+  "--version", Arg.Unit version, " Prints tool version" ;
 ]
 let sort argspec =
   List.sort (fun (name1, _, _) (name2, _, _) -> String.compare name1 name2)
@@ -390,7 +453,17 @@ let () =
   Arg.parse
     (Arg.align (sort argspec))
     (fun s -> warn "Unknown argument: %s@." s)
-    ("Usage: git ls-files -z | git check-attr --stdin -z -a | " ^ exec_name ^ " [options]");
-  collect @@ lines_from_in stdin ;
-  Hashtbl.iter (check ~verbose:!verbose ~update:!update) table ;
-  if not !res then exit 1
+    ("Usage: git ls-files -z | git check-attr --stdin -z -a | " ^ exec_name ^ " [options]\n"
+     ^"\nChecks or updates files in relation to lint constraints specified by these git attributes:\n"
+     ^"  check-eoleof, check-syntax, check-utf8 and check-indent.\n"
+     ^"\nOptions:");
+  if !extract_config then
+    Format.printf "Default JSON configuration:@.%a@."
+      (Yojson.Safe.pretty_print ~std:false) (tools_to_yojson external_formatters)
+  else begin
+    updates_tbl external_formatters ;
+    parse_config !config_file;
+    collect @@ lines_from_in stdin ;
+    Hashtbl.iter (check ~verbose:!verbose ~update:!update) table ;
+    if not !res then exit 1
+  end