diff --git a/bin/migration_scripts/titanium2vanadium.sh b/bin/migration_scripts/titanium2vanadium.sh
index 7c1b304029c0cd6c2c7dea0ef5dd0839405707c4..4288a6b24f61cb03d2e82b11beb154e826b1be86 100755
--- a/bin/migration_scripts/titanium2vanadium.sh
+++ b/bin/migration_scripts/titanium2vanadium.sh
@@ -106,7 +106,9 @@ process_file ()
    -e 's/Extlib\.the \([^~]\)/Option.get \1/g' \
    -e 's/Extlib\.opt_equal/Option.equal/g' \
    -e 's/Extlib\.opt_compare/Option.compare/g' \
-   -e 's/Extlib\.array_exists/Array.exists/g'
+   -e 's/Extlib\.array_exists/Array.exists/g' \
+   -e 's/Filepath\.Normalized\.is_unknown/Filepath.Normalized.is_empty/g' \
+   -e 's/Filepath\.Normalized\.unknown/Filepath.Normalized.empty/g'
 }
 
 apply_one_dir ()
diff --git a/share/analysis-scripts/list_functions.ml b/share/analysis-scripts/list_functions.ml
index 96511af9c7140a248de0dd7fbe157e2f70f05887..4bf75dc70a1bb9763f12f18e9c7c03d695cebd2d 100644
--- a/share/analysis-scripts/list_functions.ml
+++ b/share/analysis-scripts/list_functions.ml
@@ -282,7 +282,7 @@ let run () =
       (fun fi1 fi2 -> Extlib.compare_ignore_case fi1.name fi2.name) funinfos
   in
   let outfp = Output.get () in
-  if Filepath.Normalized.is_unknown outfp then
+  if Filepath.Normalized.is_empty outfp then
     print_text funinfos
   else
     let funinfos_json = `List (List.map (fun fi ->
diff --git a/src/kernel_internals/runtime/special_hooks.ml b/src/kernel_internals/runtime/special_hooks.ml
index fd23b4bec48a74b72bd789d3b2f5df3bce4af279..e0edf8abfab073a2744df8bb1db580eae191c358 100644
--- a/src/kernel_internals/runtime/special_hooks.ml
+++ b/src/kernel_internals/runtime/special_hooks.ml
@@ -87,8 +87,8 @@ let () =
 
 (* Load Frama-c from disk if required *)
 let load_binary () =
-  let filepath = Kernel.LoadState.get () in
-  if filepath <> Filepath.Normalized.unknown then begin
+  if not (Kernel.LoadState.is_empty ()) then begin
+    let filepath = Kernel.LoadState.get () in
     try
       Project.load_all filepath
     with Project.IOError s ->
@@ -133,8 +133,8 @@ let () = Extlib.safe_at_exit time
 
 (* Save Frama-c on disk if required *)
 let save_binary error_extension =
-  let filename = Kernel.SaveState.get () in
-  if not (Filepath.Normalized.is_unknown filename) then begin
+  if not (Kernel.SaveState.is_empty ()) then begin
+    let filename = Kernel.SaveState.get () in
     Kernel.SaveState.clear ();
     let realname =
       match error_extension with
diff --git a/src/kernel_services/ast_printing/cil_types_debug.ml b/src/kernel_services/ast_printing/cil_types_debug.ml
index e818857a279b7482830c58d6473fd2d73b6b910b..60f93f7bb1b8c480cf391f4ef18f14c4ae95bd1f 100644
--- a/src/kernel_services/ast_printing/cil_types_debug.ml
+++ b/src/kernel_services/ast_printing/cil_types_debug.ml
@@ -606,7 +606,7 @@ and pp_location fmt (pos_start,pos_end) =
 
 and pp_if_loc_known prefix suffix fmt loc =
   if print_locations &&
-     not (Filepath.Normalized.is_unknown (fst loc).Filepath.pos_path)
+     not (Filepath.Normalized.is_empty (fst loc).Filepath.pos_path)
   then Format.fprintf fmt "%s%a%s" prefix pp_location loc suffix
   else ()
 
diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml
index 6cd0cc0cecf3212337ddc80ec4ea9b66baa0ffb8..5198bfd3bd84f2e329605ac89e0f03fe86a9b46a 100644
--- a/src/kernel_services/ast_queries/file.ml
+++ b/src/kernel_services/ast_queries/file.ml
@@ -53,9 +53,9 @@ module D =
       type t = file
       let name = "File"
       let reprs =
-        [ NeedCPP(Filepath.Normalized.unknown, "", [], Unknown);
-          NoCPP Filepath.Normalized.unknown;
-          External(Filepath.Normalized.unknown, "")
+        [ NeedCPP(Filepath.Normalized.empty, "", [], Unknown);
+          NoCPP Filepath.Normalized.empty;
+          External(Filepath.Normalized.empty, "")
         ]
       let structural_descr = Structural_descr.t_abstract
       let mem_project = Datatype.never_any_project
@@ -1744,17 +1744,17 @@ let prepare_from_c_files () =
   let files = Files.get () in (* Allow pre-registration of prolog files *)
   let cpp_commands = List.map (fun f -> (f, build_cpp_cmd f)) files in
   if Kernel.PrintCppCommands.get () then print_and_exit cpp_commands;
-  let audit_check_path = Kernel.AuditCheck.get () in
-  if not (Filepath.Normalized.is_unknown audit_check_path) then begin
+  if not (Kernel.AuditCheck.is_empty ()) then begin
     let all_sources_tbl = compute_sources_table cpp_commands in
-    let expected_hashes = source_hashes_of_json audit_check_path in
+    let expected_hashes = source_hashes_of_json (Kernel.AuditCheck.get ()) in
     check_source_hashes expected_hashes all_sources_tbl
   end;
-  let audit_path = Kernel.AuditPrepare.get () in
-  if not (Filepath.Normalized.is_unknown audit_path) then begin
+  if not (Kernel.AuditPrepare.is_empty ()) then begin
     let all_sources_tbl = compute_sources_table cpp_commands in
+    let audit_path = Kernel.AuditPrepare.get () in
     print_all_sources audit_path all_sources_tbl;
-    if not (Filepath.Normalized.is_special_stdout audit_path) then
+    if not (Filepath.Normalized.is_special_stdout audit_path)
+    then
       Kernel.feedback "Audit: sources list written to: %a@."
         Filepath.Normalized.pretty audit_path;
   end;
diff --git a/src/kernel_services/ast_queries/json_compilation_database.ml b/src/kernel_services/ast_queries/json_compilation_database.ml
index 895e3e49c46f2d187dfa05300cd47e3dd1fb63a3..11c2802451cd21e4e895e0d32639d3a8ce2e1a34 100644
--- a/src/kernel_services/ast_queries/json_compilation_database.ml
+++ b/src/kernel_services/ast_queries/json_compilation_database.ml
@@ -270,7 +270,7 @@ let compute_flags_from_file () =
   Flags.mark_as_computed ()
 
 let get_flags f =
-  if not (Filepath.Normalized.is_unknown (Kernel.JsonCompilationDatabase.get ())) then begin
+  if not (Kernel.JsonCompilationDatabase.is_empty ()) then begin
     if not (Flags.is_computed ()) then compute_flags_from_file ();
     try
       let flags = Flags.find f in
diff --git a/src/kernel_services/cmdline_parameters/parameter_builder.ml b/src/kernel_services/cmdline_parameters/parameter_builder.ml
index e3b4f91162884ed8c60721538f0894870123e7db..53e2eb4f2c981701a6a7956b3942232c94a0a8b6 100644
--- a/src/kernel_services/cmdline_parameters/parameter_builder.ml
+++ b/src/kernel_services/cmdline_parameters/parameter_builder.ml
@@ -480,7 +480,7 @@ struct
         (struct
           include Datatype.Filepath
           include X
-          let default () = Filepath.Normalized.unknown
+          let default () = Filepath.Normalized.empty
           let functor_name = "Filepath"
         end)
 
@@ -532,6 +532,7 @@ struct
       else
         p
 
+    let is_empty () = Filepath.Normalized.is_empty (get ())
   end
 
   (* ************************************************************************ *)
diff --git a/src/kernel_services/cmdline_parameters/parameter_sig.mli b/src/kernel_services/cmdline_parameters/parameter_sig.mli
index f4ce284ef09a4f137820680f724dc74bd7445433..4d16f3101e799b36e4bad457557a6fbf44c31db1 100644
--- a/src/kernel_services/cmdline_parameters/parameter_sig.mli
+++ b/src/kernel_services/cmdline_parameters/parameter_sig.mli
@@ -293,7 +293,16 @@ module type With_output = sig
 end
 
 (** signature for normalized pathnames. *)
-module type Filepath = S with type t = Filepath.Normalized.t
+module type Filepath = sig
+  include S with type t = Filepath.Normalized.t
+
+  (**
+     Whether the Filepath is empty.
+
+     @since Frama-C+dev
+  *)
+  val is_empty: unit -> bool
+end
 
 (** signature for searching files in a specific directory. *)
 module type Specific_dir = sig
diff --git a/src/libraries/datatype/datatype.ml b/src/libraries/datatype/datatype.ml
index 774bdb8c79216b030a31e7f4f69f65aa0eaa0a41..2ed442511ec751e1cf1ab471985351979b19a40f 100644
--- a/src/libraries/datatype/datatype.ml
+++ b/src/libraries/datatype/datatype.ml
@@ -1946,7 +1946,7 @@ module Filepath = struct
         let pretty = Filepath.Normalized.pretty
         let varname _ = "p"
       end)
-  let dummy = Filepath.Normalized.unknown
+  let dummy = Filepath.Normalized.empty
   let of_string ?existence ?base_name s =
     Filepath.Normalized.of_string ?existence ?base_name s
   let concat ?existence t s = Filepath.Normalized.concat ?existence t s
diff --git a/src/libraries/project/project.ml b/src/libraries/project/project.ml
index f68bcdc4bcac40f3e2341aa53322808397f3c22a..b9400c45d0cc06b881dde52b477fbfd984fc0187 100644
--- a/src/libraries/project/project.ml
+++ b/src/libraries/project/project.ml
@@ -829,7 +829,7 @@ let create_by_copy
 module Undo = struct
 
   let short_filename = "frama_c_undo_restore"
-  let filename = ref Filepath.Normalized.unknown
+  let filename = ref Filepath.Normalized.empty
 
   let clear_breakpoint () = Extlib.safe_remove (!filename:>string)
 
diff --git a/src/libraries/utils/filepath.ml b/src/libraries/utils/filepath.ml
index 567261e3b48b51525b1a55d9ff5cf9b4b9b86c2e..bbd3f32cad03a385d96ae915dd67eec499489429 100644
--- a/src/libraries/utils/filepath.ml
+++ b/src/libraries/utils/filepath.ml
@@ -269,8 +269,10 @@ module Normalized = struct
     if case_sensitive then String.compare s1 s2
     else Extlib.compare_ignore_case s1 s2
 
-  let unknown = normalize ""
-  let is_unknown fp = equal fp unknown
+  let empty = normalize ""
+  let unknown = empty
+  let is_empty fp = equal fp empty
+  let is_unknown = is_empty
   let special_stdout = normalize "-"
   let is_special_stdout fp = equal fp special_stdout
 
diff --git a/src/libraries/utils/filepath.mli b/src/libraries/utils/filepath.mli
index 72577230acf4a7e9924fda47ada63639a2d0fda3..935d89611afc2294cc10c10d25836f3c5414681b 100644
--- a/src/libraries/utils/filepath.mli
+++ b/src/libraries/utils/filepath.mli
@@ -154,11 +154,25 @@ module Normalized: sig
   (** Pretty-prints the normalized (absolute) path. *)
   val pp_abs: Format.formatter -> t -> unit
 
-  (** Unknown filepath, used as 'dummy' for [Datatype.Filepath]. *)
+  (** Unknown filepath, used as 'dummy' for [Datatype.Filepath].
+      @deprecated Frama-C+dev use 'empty' instead
+  *)
   val unknown: t
+  [@@alert deprecated "Use Filepath.Normalized.empty instead"]
 
-  (** @since 20.0-Calcium *)
+  (** @since 20.0-Calcium
+      @deprecated Frama-C+dev use 'is_empty' instead
+  *)
   val is_unknown: t -> bool
+  [@@alert deprecated "Use Filepath.Normalized.is_empty instead"]
+
+  (** Empty filepath, used as 'dummy' for [Datatype.Filepath].
+      @since Frama-C+dev.
+  *)
+  val empty: t
+
+  (** @since Frama-C+dev *)
+  val is_empty: t -> bool
 
   (** [is_special_stdout f] returns [true] iff [f] is '-' (a single dash),
       which is a special notation for 'stdout'.
diff --git a/src/plugins/aorai/aorai_option.ml b/src/plugins/aorai/aorai_option.ml
index 02127aa546d10d2cbffa57589840ca4a46c0f47b..c047a144ab9d47b2b9749e6c6681933971d9c277 100644
--- a/src/plugins/aorai/aorai_option.ml
+++ b/src/plugins/aorai/aorai_option.ml
@@ -173,7 +173,7 @@ let is_on () =
        Buchi.is_default ()    && Ya.is_default () )
 
 let promela_file () =
-  if Fc_Filepath.Normalized.is_unknown (Buchi.get ()) then To_Buchi.get () else Buchi.get ()
+  if not (Buchi.is_empty ()) then Buchi.get () else To_Buchi.get ()
 
 let advance_abstract_interpretation () =
   not (AbstractInterpretationOff.get ()) && not (AbstractInterpretation.get ())
diff --git a/src/plugins/aorai/aorai_register.ml b/src/plugins/aorai/aorai_register.ml
index ec4f9e123cd8b6f1056b5e56e023becf30c9b68e..90965ec6c8d0c7b65f453ead13e1aaa07f536169 100644
--- a/src/plugins/aorai/aorai_register.ml
+++ b/src/plugins/aorai/aorai_register.ml
@@ -27,12 +27,12 @@ open Logic_ptree
 open Promelaast
 
 (* [VP] Need to get rid of those global references at some point. *)
-let promela_file = ref Filepath.Normalized.unknown
-let c_file = ref Filepath.Normalized.unknown
-let output_c_file = ref Filepath.Normalized.unknown
-let ltl_tmp_file = ref Filepath.Normalized.unknown
-let ltl_file = ref Filepath.Normalized.unknown
-let dot_file = ref Filepath.Normalized.unknown
+let promela_file = ref Filepath.Normalized.empty
+let c_file = ref Filepath.Normalized.empty
+let output_c_file = ref Filepath.Normalized.empty
+let ltl_tmp_file = ref Filepath.Normalized.empty
+let ltl_file = ref Filepath.Normalized.empty
+let dot_file = ref Filepath.Normalized.empty
 let ltl2ba_params = " -l -p -o "
 
 let ltl_to_promela = Hashtbl.create 7
@@ -178,14 +178,14 @@ let init_file_names () =
     in
     Filepath.Normalized.of_string name
   in
-  if Filepath.Normalized.is_unknown (Aorai_option.Ya.get ()) then
-    if Filepath.Normalized.is_unknown (Aorai_option.Buchi.get ()) then begin
+  if Aorai_option.Ya.is_empty () then
+    if Aorai_option.Buchi.is_empty () then begin
       (* ltl_file name is given and has to point out a valid file. *)
       ltl_file := Aorai_option.Ltl_File.get ();
       if Aorai_option.Dot.get() then dot_file := freshname !ltl_file ".dot";
       (* The LTL file is always used. *)
       (* The promela file can be given or not. *)
-      if not (Filepath.Normalized.is_unknown (Aorai_option.To_Buchi.get ())) then begin
+      if not (Aorai_option.To_Buchi.is_empty ()) then begin
         ltl_tmp_file:=
           freshname (Aorai_option.promela_file ()) ".ltl";
         promela_file:= Aorai_option.promela_file ();
@@ -203,8 +203,7 @@ let init_file_names () =
         Extlib.cleanup_at_exit (!promela_file :> string);
       end
     end else begin
-      if not (Filepath.Normalized.is_unknown (Aorai_option.To_Buchi.get ())) &&
-         not (Filepath.Normalized.is_unknown (Aorai_option.Ltl_File.get ()))
+      if not (Aorai_option.To_Buchi.is_empty ()) && not (Aorai_option.Ltl_File.is_empty ())
       then begin
         Aorai_option.abort
           "Error. '-buchi' option is incompatible with '-to-buchi' and '-ltl' \
@@ -217,12 +216,10 @@ let init_file_names () =
         dot_file := freshname !promela_file ".dot";
     end
   else begin
-    let ya_file = Aorai_option.Ya.get () in
-    if (Filepath.Normalized.is_unknown ya_file) then
-      Aorai_option.abort
-        "invalid Ya file name %a" Filepath.Normalized.pretty ya_file;
+    if Aorai_option.Ya.is_empty () then
+      Aorai_option.abort "empty Ya file name";
     if Aorai_option.Dot.get() then
-      dot_file := freshname ya_file ".dot"
+      dot_file := freshname (Aorai_option.Ya.get ()) ".dot"
   end;
   display_status ()
 
@@ -243,8 +240,8 @@ let work () =
   let file = Ast.get () in
   Aorai_utils.initFile file;
   printverb "C file loading         : done\n";
-  if Filepath.Normalized.is_unknown (Aorai_option.Ya.get ()) then
-    if Filepath.Normalized.is_unknown (Aorai_option.Buchi.get ()) then begin
+  if Aorai_option.Ya.is_empty () then
+    if Aorai_option.Buchi.is_empty () then begin
       ltl_to_ltlLight !ltl_file !ltl_tmp_file;
       printverb "LTL loading            : done\n";
       let cmd = Format.asprintf "ltl2ba %s -F %a > %a"
@@ -255,16 +252,16 @@ let work () =
         Aorai_option.abort "failed to run: %s" cmd ;
       printverb "LTL ~> Promela (ltl2ba): done\n"
     end;
-  if not (Filepath.Normalized.is_unknown (Aorai_option.To_Buchi.get ())) then
+  if not (Aorai_option.To_Buchi.is_empty ()) then
     printverb ("Finished.\nGenerated file: '"^(Filepath.Normalized.to_pretty_string !promela_file)^"'\n")
   else
     begin
       (* Step 3 : Loading promela_file and checking the consistency between informations from C code and LTL property *)
       (*          Such as functions name and global variables. *)
 
-      if not (Filepath.Normalized.is_unknown (Aorai_option.Buchi.get ())) then
+      if not (Aorai_option.Buchi.is_empty ()) then
         load_promela_file_withexps !promela_file
-      else if not (Filepath.Normalized.is_unknown (Aorai_option.Ya.get ())) then
+      else if not (Aorai_option.Ya.is_empty ()) then
         load_ya_file (Aorai_option.Ya.get ())
       else
         load_promela_file !promela_file;
diff --git a/src/plugins/callgraph/Callgraph.mli b/src/plugins/callgraph/Callgraph.mli
index 0d649136073dd5b2302303f3db77b9f2bae279aa..e7ba902325e6c84043bec088f00613e614853015 100644
--- a/src/plugins/callgraph/Callgraph.mli
+++ b/src/plugins/callgraph/Callgraph.mli
@@ -23,7 +23,7 @@
 (** Callgraph plugin. *)
 
 module Options: sig
-  module Filename: Parameter_sig.String
+  module Filename: Parameter_sig.Filepath
   module Service_roots: Parameter_sig.Kernel_function_set
   module Uncalled: Parameter_sig.Bool
   module Uncalled_leaf: Parameter_sig.Bool
diff --git a/src/plugins/callgraph/options.ml b/src/plugins/callgraph/options.ml
index d42856664a9fe2f3e6bc2d0124c99ae90e729a68..04158a6a507d274ce7cedc76f220e6da4170279f 100644
--- a/src/plugins/callgraph/options.ml
+++ b/src/plugins/callgraph/options.ml
@@ -32,10 +32,12 @@ include
     end)
 
 module Filename =
-  Empty_string
+  Filepath
     (struct
       let option_name = "-cg"
       let arg_name = "filename"
+      let file_kind = "DOT"
+      let existence = Fc_Filepath.Indifferent
       let help = "dump the callgraph to the file \
                   <filename> in dot format"
     end)
@@ -97,9 +99,10 @@ module Uncalled_leaf =
 
 let dump output g =
   let file = Filename.get () in
-  feedback ~level:2 "dumping the graph into file %s" file;
+  feedback ~level:2 "dumping the graph into file %a"
+    Fc_Filepath.Normalized.pretty file;
   try
-    let cout = open_out file in
+    let cout = open_out (file:>string) in
     output cout g;
     close_out cout
   with e ->
diff --git a/src/plugins/callgraph/options.mli b/src/plugins/callgraph/options.mli
index aa6fd2e5026a828c84f95be2cbca523154793c72..ab238a90b57d79c2387e9f5e9e688c2779d2d35c 100644
--- a/src/plugins/callgraph/options.mli
+++ b/src/plugins/callgraph/options.mli
@@ -23,7 +23,7 @@
 include Plugin.S
 
 val name: string
-module Filename: Parameter_sig.String
+module Filename: Parameter_sig.Filepath
 module Roots: Parameter_sig.Kernel_function_set
 module Service_roots: Parameter_sig.Kernel_function_set
 module Function_pointers: Parameter_sig.Bool
diff --git a/src/plugins/callgraph/register.ml b/src/plugins/callgraph/register.ml
index d7da576b50f9ee45167f7e3404d27e0c179facd8..487f6d51055e43009da0df956a6e071653fd135d 100644
--- a/src/plugins/callgraph/register.ml
+++ b/src/plugins/callgraph/register.ml
@@ -21,7 +21,7 @@
 (**************************************************************************)
 
 let main () =
-  if Options.Filename.get () <> "" then
+  if not (Options.Filename.is_empty ()) then
     if Options.Services.get () then begin
       if not (Services.is_computed ()) then Services.dump ()
     end else
diff --git a/src/plugins/dive/main.ml b/src/plugins/dive/main.ml
index 7387399121640d369c24df0f3323af284fe4c709..10d3da3b178ed82ae91cff195a264c333b5072ef 100644
--- a/src/plugins/dive/main.ml
+++ b/src/plugins/dive/main.ml
@@ -22,13 +22,13 @@
 
 type format = Dot | Json
 
-let output format context basename =
-  let filename, output_function = match format with
-    | Dot -> basename ^ ".dot", Dive_graph.ouptput_to_dot
-    | Json -> basename ^ ".json", Dive_graph.ouptput_to_json
+let output format context filename =
+  let output_function = match format with
+    | Dot -> Dive_graph.ouptput_to_dot
+    | Json -> Dive_graph.ouptput_to_json
   in
-  Self.result "output to %s" filename;
-  let out_channel = open_out filename in
+  Self.result "output to %a" Filepath.Normalized.pretty filename;
+  let out_channel = open_out (filename:>string) in
   output_function out_channel (Context.get_graph context);
   close_out out_channel
 
@@ -57,9 +57,9 @@ let main () =
     if not (Self.FromFunctionAlarms.is_empty ()) then
       Alarms.iter add_alarm;
     (* Output it *)
-    if Self.OutputDot.get () <> "" then
+    if not (Self.OutputDot.is_empty ()) then
       output Dot context (Self.OutputDot.get ());
-    if Self.OutputJson.get () <> "" then
+    if not (Self.OutputJson.is_empty ()) then
       output Json context (Self.OutputJson.get ());
   end
 
diff --git a/src/plugins/dive/self.ml b/src/plugins/dive/self.ml
index 26a925a3f5475acf84c842e7a8cb0b1ae10672ff..ba3fd755c8f3bf66b9fe0aa6d90141d7dbb9b181 100644
--- a/src/plugins/dive/self.ml
+++ b/src/plugins/dive/self.ml
@@ -29,18 +29,22 @@ include Plugin.Register
       let help = "An interactive imprecision graph generator for Eva."
     end)
 
-module OutputDot = Empty_string
+module OutputDot = Filepath
     (struct
       let option_name = "-dive-output-dot"
-      let help = "Outputs the built graph into a dot file with this basename."
-      let arg_name = "basename"
+      let arg_name = "output.dot"
+      let file_kind = "DOT"
+      let existence = Fc_Filepath.Indifferent
+      let help = "Outputs the built graph in DOT format to the specified file."
     end)
 
-module OutputJson = Empty_string
+module OutputJson = Filepath
     (struct
       let option_name = "-dive-output-json"
-      let help = "Outputs the built graph into a json file with this basename."
-      let arg_name = "basename"
+      let arg_name = "output.json"
+      let file_kind = "JSON"
+      let existence = Fc_Filepath.Indifferent
+      let help = "Outputs the built graph in JSON format to the specified."
     end)
 
 module DepthLimit = Int
diff --git a/src/plugins/dive/self.mli b/src/plugins/dive/self.mli
index cb5014982463f0f11c2adc18224bcc0c5ef1ea47..664546c1cc6887e6ac3a71182c88731e1a6e8eec 100644
--- a/src/plugins/dive/self.mli
+++ b/src/plugins/dive/self.mli
@@ -26,8 +26,8 @@ module type Varinfo_set = Parameter_sig.Set
   with type elt = Cil_types.varinfo
    and type t = Cil_datatype.Varinfo.Set.t
 
-module OutputDot : Parameter_sig.String
-module OutputJson : Parameter_sig.String
+module OutputDot : Parameter_sig.Filepath
+module OutputJson : Parameter_sig.Filepath
 module DepthLimit : Parameter_sig.Int
 module FromFunctionAlarms : Parameter_sig.Kernel_function_set
 module FromBases : Varinfo_set
diff --git a/src/plugins/dive/tests/test_config b/src/plugins/dive/tests/test_config
index 9ff9387337545ed97c88920ed5c79a722351eaa8..b5de8cfbedf81997ee828999794f2b0c93025bed 100644
--- a/src/plugins/dive/tests/test_config
+++ b/src/plugins/dive/tests/test_config
@@ -1,3 +1,3 @@
 LOG: @PTEST_NAME@.dot
-OPT: -no-autoload-plugins -load-module from,inout,eva,variadic,scope,dive -check -eva-msg-key=-initial-state -eva-no-show-progress -dive-output-dot @PTEST_RESULT@/@PTEST_NAME@
+OPT: -no-autoload-plugins -load-module from,inout,eva,variadic,scope,dive -check -eva-msg-key=-initial-state -eva-no-show-progress -dive-output-dot @PTEST_RESULT@/@PTEST_NAME@.dot
 FILTER: perl -0777 -pe 's/\[server[^[]*//g'
diff --git a/src/plugins/markdown-report/md_gen.ml b/src/plugins/markdown-report/md_gen.ml
index dc212069f0d533bf8513d4311ccaf8ef7f9daaa7..b8a09ed3b99df3c6f9b02ba4b5bb1b5bb427c4a4 100644
--- a/src/plugins/markdown-report/md_gen.ml
+++ b/src/plugins/markdown-report/md_gen.ml
@@ -554,13 +554,14 @@ let gen_alarms env =
   gen_section_postlude env
 
 let mk_remarks is_draft =
-  let f = Mdr_params.Remarks.get () in
-  if f <> "" then Parse_remarks.get_remarks f
+  if not (Mdr_params.Remarks.is_empty ()) then
+    Parse_remarks.get_remarks (Mdr_params.Remarks.get ())
   else if is_draft then begin
     let f = Mdr_params.Output.get() in
-    if Sys.file_exists f then begin
+    if Sys.file_exists (f:>string) then begin
       Mdr_params.feedback
-        "Re-using pre-existing remarks in draft file %s" f;
+        "Re-using pre-existing remarks in draft file %a"
+        Filepath.Normalized.pretty f;
       Parse_remarks.get_remarks f
     end else Datatype.String.Map.empty
   end else  Datatype.String.Map.empty
@@ -609,8 +610,9 @@ let gen_report ~draft:is_draft () =
   let doc = Markdown.pandoc ~title ~authors ?date elements in
   let file = Mdr_params.Output.get() in
   try
-    Command.print_file file (fun fmt -> Markdown.pp_pandoc fmt doc) ;
-    Mdr_params.result "Report %s generated" file
+    Command.print_file (file:>string) (fun fmt -> Markdown.pp_pandoc fmt doc) ;
+    Mdr_params.result "Report %a generated" Filepath.Normalized.pretty file
   with Sys_error s ->
     Mdr_params.warning
-      "Unable to open %s for writing (%s). No report generated" file s
+      "Unable to open %a for writing (%s). No report generated"
+      Filepath.Normalized.pretty file s
diff --git a/src/plugins/markdown-report/mdr_params.ml b/src/plugins/markdown-report/mdr_params.ml
index 3b799faccaad2e5e4349c5ebe73993683abf2dbb..12d8cfbf603b1ea4f869c8aa011538b5f254462c 100644
--- a/src/plugins/markdown-report/mdr_params.ml
+++ b/src/plugins/markdown-report/mdr_params.ml
@@ -39,33 +39,36 @@ module Generate = String(
        none (default), md, draft and sarif"
   end)
 
-module Output : Parameter_sig.String =
+module Output : Parameter_sig.Filepath =
 struct
-  include String(
+  include Filepath(
     struct
       let option_name = "-mdr-out"
       let arg_name = "f"
-      let default = "report"
+      let file_kind = "Report"
+      let existence = Fc_Filepath.Indifferent
       let help = "sets the name of the output file to <f>. \
                   If <f> has no extension, it is chosen automatically based on \
                   the report kind"
     end)
   let get () =
     let s = get () in
-    if Pervasives_string.contains (Filename.basename s) '.' then s
+    if Pervasives_string.contains (Filename.basename (s:>string)) '.' then s
     else
       let kind = Generate.get () in
       let ext = if kind = "sarif" then ".sarif" else ".md" in
-      s ^ ext
+      Fc_Filepath.Normalized.concat s ext
 end
 
 let () =
   Generate.set_possible_values [ "none"; "md"; "draft"; "sarif" ]
 
-module Remarks = Empty_string(
+module Remarks = Filepath(
   struct
     let option_name = "-mdr-remarks"
     let arg_name = "f"
+    let file_kind = "Remarks file"
+    let existence = Fc_Filepath.Must_exist
     let help =
       "reads file <f> to add additional remarks to various sections of the report. \
        Must be in a format compatible with the file produced by -mdr-gen-draft. \
diff --git a/src/plugins/markdown-report/mdr_params.mli b/src/plugins/markdown-report/mdr_params.mli
index b7c5b06901b78d086f8389fa5fe8adf149d07112..f40ba97f1b52665c59872985be6bfd728725fa80 100644
--- a/src/plugins/markdown-report/mdr_params.mli
+++ b/src/plugins/markdown-report/mdr_params.mli
@@ -23,13 +23,13 @@
 include Plugin.S
 
 (** Value of [-mdr-out]. *)
-module Output: Parameter_sig.String
+module Output: Parameter_sig.Filepath
 
 (** Value of [-mdr-gen]. *)
 module Generate: Parameter_sig.String
 
 (** Value of [-mdr-remarks]. *)
-module Remarks: Parameter_sig.String
+module Remarks: Parameter_sig.Filepath
 
 (** Value of [-mdr-flamegraph]. *)
 module FlameGraph: Parameter_sig.String
diff --git a/src/plugins/markdown-report/parse_remarks.ml b/src/plugins/markdown-report/parse_remarks.ml
index d2e4860e7c455fb1f4fc6b477c3882b2ffc653da..10fc83a0da5af95687e7ec085acff31c9de2337e 100644
--- a/src/plugins/markdown-report/parse_remarks.ml
+++ b/src/plugins/markdown-report/parse_remarks.ml
@@ -112,13 +112,15 @@ let parse_remarks env chan =
     env
 
 let get_remarks f =
-  Mdr_params.debug ~dkey "Using remarks file %s" f;
+  Mdr_params.debug ~dkey "Using remarks file %a"
+    Filepath.Normalized.pretty f;
   try
-    let chan = open_in f in
+    let chan = open_in (f:>string) in
     let { remarks } = parse_remarks (empty_env ()) chan in
     remarks
   with Sys_error err ->
     Mdr_params.error
-      "Unable to open remarks file %s (%s). \
-       No additional remarks will be included in the report." f err;
+      "Unable to open remarks file %a (%s). \
+       No additional remarks will be included in the report."
+      Filepath.Normalized.pretty f err;
     Datatype.String.Map.empty
diff --git a/src/plugins/markdown-report/parse_remarks.mli b/src/plugins/markdown-report/parse_remarks.mli
index 2491fe94a4998fe29fed358596b97815f98da233..48728e02729fbad4a37dc3647d8153d729cfdc69 100644
--- a/src/plugins/markdown-report/parse_remarks.mli
+++ b/src/plugins/markdown-report/parse_remarks.mli
@@ -25,4 +25,4 @@
 
 (** [get_remarks f] retrieves the elements associated to various sections
     of the report, referenced by their anchor. *)
-val get_remarks: string -> Markdown.element list Datatype.String.Map.t
+val get_remarks: Filepath.Normalized.t -> Markdown.element list Datatype.String.Map.t
diff --git a/src/plugins/markdown-report/sarif_gen.ml b/src/plugins/markdown-report/sarif_gen.ml
index 12d64ad6e94bf30041c359bf8bb4d705e75a29f7..62ee563dbe9f07043c858439d9daab6353bd2304 100644
--- a/src/plugins/markdown-report/sarif_gen.ml
+++ b/src/plugins/markdown-report/sarif_gen.ml
@@ -38,8 +38,8 @@ let frama_c_sarif () =
        ~informationUri ())
 
 let get_remarks () =
-  let f = Mdr_params.Remarks.get () in
-  if f <> "" then Parse_remarks.get_remarks f
+  if not (Mdr_params.Remarks.is_empty ()) then
+    Parse_remarks.get_remarks (Mdr_params.Remarks.get ())
   else Datatype.String.Map.empty
 
 let get_remark remarks label =
@@ -273,13 +273,14 @@ let generate () =
   let remarks = get_remarks () in
   let runs = [ gen_run remarks ] in
   let json = Schema.create ~runs () |> Schema.to_yojson in
-  let file = Mdr_params.Output.get () in
-  if file = "" then
-    Log.print_on_output (fun fmt -> Yojson.Safe.pretty_print fmt json)
-  else
+  if not (Mdr_params.Output.is_empty ()) then
+    let file = Mdr_params.Output.get () in
     try
-      Command.write_file file
+      Command.write_file (file:>string)
         (fun out -> Yojson.Safe.pretty_to_channel ~std:true out json) ;
-      Mdr_params.result "Report %s generated" file
+      Mdr_params.result "Report %a generated" Filepath.Normalized.pretty file
     with Sys_error s ->
-      Mdr_params.abort "Unable to generate %s (%s)" file s
+      Mdr_params.abort "Unable to generate %a (%s)"
+        Filepath.Normalized.pretty file s
+  else
+    Log.print_on_output (fun fmt -> Yojson.Safe.pretty_print fmt json)
diff --git a/src/plugins/metrics/metrics_acsl.ml b/src/plugins/metrics/metrics_acsl.ml
index 75acb89eb4d1ed53cd438c23c2e0af51e3e6fcd8..9800d450ab5c59d5bb1a1c992edcd52403c8d4b6 100644
--- a/src/plugins/metrics/metrics_acsl.ml
+++ b/src/plugins/metrics/metrics_acsl.ml
@@ -308,10 +308,10 @@ let dump_acsl_stats_html fmt =
     
 
 let dump () =
-  let out = Metrics_parameters.OutputFile.get () in
-  if out <> "" then begin
+  if not (Metrics_parameters.OutputFile.is_empty ()) then begin
+    let out = Metrics_parameters.OutputFile.get () in
     try
-      let chan = open_out out in
+      let chan = open_out (out:>string) in
       let fmt = Format.formatter_of_out_channel chan in
       (match Metrics_base.get_file_type out with
             | Metrics_base.Html -> dump_acsl_stats_html fmt
@@ -322,6 +322,7 @@ let dump () =
       );
       close_out chan
     with Sys_error s ->
-      Metrics_parameters.abort "Cannot open file %s (%s)" out s
+      Metrics_parameters.abort "Cannot open file %a (%s)"
+        Filepath.Normalized.pretty out s
   end else Metrics_parameters.result "%t" dump_acsl_stats
 
diff --git a/src/plugins/metrics/metrics_base.ml b/src/plugins/metrics/metrics_base.ml
index a29fdada1c39483d090761c6af07185d31df7d24..4555837a8811f08bce4a738427e8625889174f41 100644
--- a/src/plugins/metrics/metrics_base.ml
+++ b/src/plugins/metrics/metrics_base.ml
@@ -251,9 +251,9 @@ type output_type =
   | Json
 ;;
 
-let get_file_type filename =
+let get_file_type (filename : Filepath.Normalized.t) =
   try
-    match get_suffix filename with
+    match get_suffix (filename:>string) with
     | "html" | "htm" -> Html
     | "txt" | "text" -> Text
     | "json" -> Json
@@ -263,7 +263,8 @@ let get_file_type filename =
   with
   | No_suffix ->
     Metrics_parameters.abort
-      "File %s has no suffix. Cannot produce output.@." filename
+      "File %a has no suffix. Cannot produce output.@."
+      Filepath.Normalized.pretty filename
 
 module VarinfoByName = struct
   type t = Cil_types.varinfo
diff --git a/src/plugins/metrics/metrics_base.mli b/src/plugins/metrics/metrics_base.mli
index f4c84845ca1c4ca177dd1c2fbd555e0cec255d88..a93c078c04ecda52d352be849d92e39e9da8174a 100644
--- a/src/plugins/metrics/metrics_base.mli
+++ b/src/plugins/metrics/metrics_base.mli
@@ -146,7 +146,7 @@ type output_type =
 (** get_file_type [extension] sets the output type according to [extension].
     Raises an error if [extension] is not among supported extensions or is empty.
 *)
-val get_file_type: string -> output_type;;
+val get_file_type: Filepath.Normalized.t -> output_type;;
 
 (** consider_function [vinfo] returns false if the varinfo is not a function we
     are interested in.
diff --git a/src/plugins/metrics/metrics_cilast.ml b/src/plugins/metrics/metrics_cilast.ml
index 962ed38461e91ff9f4f4e810d6cd14f4b026b7bb..5abd0b83e04bf242ddf98095cc3dcf365be1f7f2 100644
--- a/src/plugins/metrics/metrics_cilast.ml
+++ b/src/plugins/metrics/metrics_cilast.ml
@@ -734,25 +734,25 @@ let compute_on_cilast ~libc =
       "@[<v 0>Cil AST@ %t@]" cil_visitor#pp_detailed_text_metrics;
   (*  let r =  metrics_to_result cil_visitor in *)
   (* Print the result to file if required *)
-  let out_fname = Metrics_parameters.OutputFile.get () in
-  begin
-    if out_fname <> "" then
-      try
-        let oc = open_out_bin out_fname in
-        let fmt = Format.formatter_of_out_channel oc in
-        (match Metrics_base.get_file_type out_fname with
-         | Html -> dump_html fmt cil_visitor
-         | Text -> pp_with_funinfo fmt cil_visitor
-         | Json ->
-           let json = json_of_funinfo cil_visitor in
-           Yojson.pretty_print fmt json;
-           Format.fprintf fmt "@." (* ensure the file ends with a newline *)
-        );
-        close_out oc;
-      with Sys_error _ ->
-        Metrics_parameters.failure "Cannot open file %s.@." out_fname
-    else Metrics_parameters.result "%a" pp_with_funinfo cil_visitor
+  if not (Metrics_parameters.OutputFile.is_empty ()) then begin
+    let out_fname = Metrics_parameters.OutputFile.get () in
+    try
+      let oc = open_out_bin (out_fname:>string) in
+      let fmt = Format.formatter_of_out_channel oc in
+      (match Metrics_base.get_file_type out_fname with
+       | Html -> dump_html fmt cil_visitor
+       | Text -> pp_with_funinfo fmt cil_visitor
+       | Json ->
+         let json = json_of_funinfo cil_visitor in
+         Yojson.pretty_print fmt json;
+         Format.fprintf fmt "@." (* ensure the file ends with a newline *)
+      );
+      close_out oc;
+    with Sys_error _ ->
+      Metrics_parameters.failure "Cannot open file %a.@."
+        Filepath.Normalized.pretty out_fname
   end
+  else Metrics_parameters.result "%a" pp_with_funinfo cil_visitor
 
 (* Visitor for the recursive estimation of a stack size.
    Its arguments are the function currently being visited and the current
diff --git a/src/plugins/metrics/metrics_parameters.ml b/src/plugins/metrics/metrics_parameters.ml
index 30ae2ef4d389e1a3db3a9e0f5e8fe3e35c7ac1bd..5aeae5dc6886cb3b151c7a285471a7421f72e207 100644
--- a/src/plugins/metrics/metrics_parameters.ml
+++ b/src/plugins/metrics/metrics_parameters.ml
@@ -44,10 +44,12 @@ module ByFunction =
      end)
 
 module OutputFile =
-  Empty_string
+  Filepath
     (struct
       let option_name = "-metrics-output"
       let arg_name = "filename"
+      let file_kind = "Text, HTML or JSON"
+      let existence = Fc_Filepath.Indifferent
       let help = "print some metrics into the specified file; \
                   the output format is recognized through the extension: \
                   .text/.txt for text, .html/.htm for HTML, or .json for JSON."
diff --git a/src/plugins/metrics/metrics_parameters.mli b/src/plugins/metrics/metrics_parameters.mli
index bc9a5e556236fb042b721a5f4bed701c44b00db8..990c31c6751adcd6cd7eced29131fc2f796fb0ae 100644
--- a/src/plugins/metrics/metrics_parameters.mli
+++ b/src/plugins/metrics/metrics_parameters.mli
@@ -35,7 +35,7 @@ module ValueCoverage: Parameter_sig.With_output
 module AstType: Parameter_sig.String
 (** Set the ASTs on which the metrics should be computed *)
 
-module OutputFile: Parameter_sig.String
+module OutputFile: Parameter_sig.Filepath
 (** Pretty print metrics to the given file.
     The output format will be recognized through the extension.
     Supported extensions are:
diff --git a/src/plugins/report/classify.ml b/src/plugins/report/classify.ml
index 1c6c22f3d644838f5ab6dbbe6e833784f446642e..eab82120d48a99be2b60ec2b7575f82e4fb2f4fa 100644
--- a/src/plugins/report/classify.ml
+++ b/src/plugins/report/classify.ml
@@ -520,14 +520,14 @@ let report_dump fmt =
   end
 
 let report_output file =
-  R.feedback "Output %s@." file ;
-  Command.print_file file report_dump
+  R.feedback "Output %a@." Filepath.Normalized.pretty file;
+  Command.print_file (file:>string) report_dump
 
 let report_number name nb opt =
   if nb > 0 then R.feedback "%s%4d" name nb ;
   let file = opt () in
-  if file <> "" then
-    let out = open_out file in
+  if not (Filepath.Normalized.is_empty file) then
+    let out = open_out (file:>string) in
     output_string out (string_of_int nb) ;
     flush out ; close_out out
 
@@ -540,8 +540,7 @@ let classify () =
        (not (R.Stderr.get ()) &&
         not (R.Output.is_set ()))
     then report_console () ;
-    let file = R.Output.get () in
-    if file <> "" then report_output file ;
+    if not (R.Output.is_empty ()) then report_output (R.Output.get ());
     report_number "Reviews     : " !nb_reviews R.OutputReviews.get ;
     report_number "Errors      : " !nb_errors R.OutputErrors.get ;
     report_number "Unclassified: " !nb_unclassified R.OutputUnclassified.get ;
diff --git a/src/plugins/report/csv.ml b/src/plugins/report/csv.ml
index bd6becb61a8ef9dbb6b4dde3387827fcfec7fa71..b4120daeb835084b1bb6c905e8390731424bbec9 100644
--- a/src/plugins/report/csv.ml
+++ b/src/plugins/report/csv.ml
@@ -95,8 +95,9 @@ let print_csv =
 
 let print_csv_once () =
   let file = Report_parameters.CSVFile.get () in
-  Report_parameters.feedback "Dumping properties in '%s'" file;
-  print_csv file
+  Report_parameters.feedback "Dumping properties in '%a'"
+    Filepath.Normalized.pretty file;
+  print_csv (file:>string)
 
 let print_csv, _ =
   State_builder.apply_once
@@ -107,7 +108,8 @@ let print_csv, _ =
       Property_status.self ]
     print_csv_once
 
-let main () = if Report_parameters.CSVFile.get () <> "" then print_csv ()
+let main () =
+  if not (Report_parameters.CSVFile.is_empty ()) then print_csv ()
 
 let () = Db.Main.extend main
 
diff --git a/src/plugins/report/report_parameters.ml b/src/plugins/report/report_parameters.ml
index b7f24343af6f4afc0dc5e609f2d6e535c0e3e42a..da18d1ca076be5d29c04d093f6f9ca169dfc57e4 100644
--- a/src/plugins/report/report_parameters.ml
+++ b/src/plugins/report/report_parameters.ml
@@ -73,11 +73,12 @@ module Proven =
 
 let () = Parameter_customize.set_group printing
 module CSVFile =
-  String
+  Filepath
     (struct
       let option_name = "-report-csv"
       let arg_name = "name"
-      let default = ""
+      let file_kind = "CSV"
+      let existence = Fc_Filepath.Indifferent
       let help = "if set, output properties as a csv file of the given name"
     end)
 
@@ -166,12 +167,13 @@ module InvalidStatus =
 
 let () = Parameter_customize.set_group monitoring
 module Output =
-  String
+  Filepath
     (struct
       let option_name = "-report-output"
       let arg_name = "*.json"
+      let file_kind = "JSON"
+      let existence = Fc_Filepath.Indifferent
       let help = "Output -report-classify in JSON format"
-      let default = ""
     end)
 
 let () = Parameter_customize.set_group monitoring
@@ -184,32 +186,35 @@ module AbsolutePath =
 
 let () = Parameter_customize.set_group monitoring
 module OutputReviews =
-  String
+  Filepath
     (struct
       let option_name = "-report-output-reviews"
       let arg_name = "file"
+      let file_kind = "Text"
+      let existence = Fc_Filepath.Indifferent
       let help = "Output number of reviews to <file>"
-      let default = ""
     end)
 
 let () = Parameter_customize.set_group monitoring
 module OutputErrors =
-  String
+  Filepath
     (struct
       let option_name = "-report-output-errors"
       let arg_name = "file"
+      let file_kind = "Text"
+      let existence = Fc_Filepath.Indifferent
       let help = "Output number of errors to <file>"
-      let default = ""
     end)
 
 let () = Parameter_customize.set_group monitoring
 module OutputUnclassified =
-  String
+  Filepath
     (struct
       let option_name = "-report-output-unclassified"
       let arg_name = "file"
+      let file_kind = "Text"
+      let existence = Fc_Filepath.Indifferent
       let help = "Output number of unclassified to <file>"
-      let default = ""
     end)
 
 let () = Parameter_customize.set_group monitoring
diff --git a/src/plugins/report/report_parameters.mli b/src/plugins/report/report_parameters.mli
index 0f806eeed28e8f2741737097c5079fa667404ac9..80602986b49cf3ff4669859c0bc49a1e9fa1ae84 100644
--- a/src/plugins/report/report_parameters.mli
+++ b/src/plugins/report/report_parameters.mli
@@ -29,7 +29,7 @@ module Untried: Parameter_sig.Bool
 module Specialized: Parameter_sig.Bool
 module Proven: Parameter_sig.Bool
 
-module CSVFile: Parameter_sig.String
+module CSVFile: Parameter_sig.Filepath
 module Classify: Parameter_sig.Bool
 module Rules: Parameter_sig.String_list
 module Warning: Parameter_sig.String
@@ -39,10 +39,10 @@ module UntriedStatus: Parameter_sig.String
 module UnknownStatus: Parameter_sig.String
 module InvalidStatus: Parameter_sig.String
 
-module Output: Parameter_sig.String
-module OutputReviews: Parameter_sig.String
-module OutputErrors: Parameter_sig.String
-module OutputUnclassified: Parameter_sig.String
+module Output: Parameter_sig.Filepath
+module OutputReviews: Parameter_sig.Filepath
+module OutputErrors: Parameter_sig.Filepath
+module OutputUnclassified: Parameter_sig.Filepath
 module AbsolutePath: Parameter_sig.Bool
 module Stdout: Parameter_sig.Bool
 module Stderr: Parameter_sig.Bool
diff --git a/src/plugins/server/server_doc.ml b/src/plugins/server/server_doc.ml
index f38f08705f929dc2d66c7b02a8acd4678acee133..206ebd4ca538956d506237e164f49b170773b16e 100644
--- a/src/plugins/server/server_doc.ml
+++ b/src/plugins/server/server_doc.ml
@@ -307,16 +307,17 @@ let metadata page : json =
 (* -------------------------------------------------------------------------- *)
 
 let pp_one_page ~root ~page ~title body =
-  let full_path = Filepath.normalize (root ^ "/" ^ page) in
-  let dir = Filename.dirname full_path in
+  let full_path = Filepath.Normalized.concat root page in
+  let dir = Filename.dirname (full_path:>string) in
   if not (Sys.file_exists dir) then Extlib.mkdir ~parents:true dir 0o755;
   try
-    let chan = open_out full_path in
+    let chan = open_out (full_path:>string) in
     let fmt = Format.formatter_of_out_channel chan in
     let title = Md.plain title in
     Markdown.(pp_pandoc ~page fmt (pandoc ~title body))
   with Sys_error e ->
-    Senv.fatal "Could not open file %s for writing: %s" full_path e
+    Senv.fatal "Could not open file %a for writing: %s"
+      Filepath.Normalized.pretty full_path e
 
 (* Build section contents in reverse order *)
 let build d s = List.fold_left (fun d s -> s() :: d) d s
@@ -338,13 +339,13 @@ let dump ~root ?(meta=true) () =
          let body = Markdown.subsections page.descr (build [] page.sections) in
          pp_one_page ~root ~page:path ~title (intro @ body) ;
          if meta then
-           let path = Printf.sprintf "%s/%s.json" root path in
-           Yojson.Basic.to_file path (metadata page) ;
+           let path = Filepath.Normalized.concat root (path ^ ".json") in
+           Yojson.Basic.to_file (path:>string) (metadata page) ;
       ) !pages ;
     Senv.feedback "[doc] Page: 'readme.md'" ;
     if meta then
-      let path = Printf.sprintf "%s/readme.md.json" root in
-      Yojson.Basic.to_file path maindata ;
+      let path = Filepath.Normalized.concat root "readme.md.json" in
+      Yojson.Basic.to_file (path:>string) maindata ;
       let body =
         [ Md.H1 (Md.plain "Presentation", None);
           Md.Block (Md.text (Md.format "Version %s" Fc_config.version))]
@@ -362,16 +363,17 @@ let dump ~root ?(meta=true) () =
 let () =
   Db.Main.extend begin
     fun () ->
-      let root = Senv.Doc.get () in
-      if root <> "" then
-        if Sys.file_exists root && Sys.is_directory root then
+      if not (Senv.Doc.is_empty ()) then
+        let root = Senv.Doc.get () in
+        if Sys.is_directory (root:>string) then
           begin
-            Senv.feedback "[doc] Root: '%s'" root ;
+            Senv.feedback "[doc] Root: '%a'" Filepath.Normalized.pretty root ;
             Package.iter package ;
             dump ~root () ;
           end
         else
-          Senv.error "[doc] File '%s' is not a directory" root
+          Senv.error "[doc] File '%a' is not a directory"
+            Filepath.Normalized.pretty root
   end
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/server_doc.mli b/src/plugins/server/server_doc.mli
index 6bb0462554ea7348d1133a53bd925ed4f62098ab..d86aaa97d5be52ac58a8cbb44ce264eb18c52ae8 100644
--- a/src/plugins/server/server_doc.mli
+++ b/src/plugins/server/server_doc.mli
@@ -72,6 +72,6 @@ val package : Package.packageInfo -> unit
 
 (** Dumps all published pages of documentations. Unless [~meta:false], also
     generates METADATA for each page in [<filename>.json] for each page. *)
-val dump : root:string -> ?meta:bool -> unit -> unit
+val dump : root:Filepath.Normalized.t -> ?meta:bool -> unit -> unit
 
 (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/server/server_parameters.ml b/src/plugins/server/server_parameters.ml
index 3eedff1c7280142bea5b0bdd536b36eb50a384d6..c7086902c978644aa8f1d127c3bc115d0d378654 100644
--- a/src/plugins/server/server_parameters.ml
+++ b/src/plugins/server/server_parameters.ml
@@ -60,11 +60,12 @@ let server_doc = add_group "Server Doc Generation"
 let () = Parameter_customize.set_group server_doc
 let () = Parameter_customize.do_not_save ()
 
-module Doc = P.String
+module Doc = P.Filepath
     (struct
       let option_name = "-server-doc"
       let arg_name = "dir"
-      let default = ""
+      let file_kind = "Directory"
+      let existence = Fc_Filepath.Must_exist
       let help = "Output a markdown documentation of the server in <dir>"
     end)
 
diff --git a/src/plugins/server/server_parameters.mli b/src/plugins/server/server_parameters.mli
index 4186a397421ee6135487e34a06ad7b1bc90b3f13..74d29a8d0c1a1575ff25bbb0906f97b80905516d 100644
--- a/src/plugins/server/server_parameters.mli
+++ b/src/plugins/server/server_parameters.mli
@@ -24,7 +24,7 @@
 
 include Plugin.General_services
 
-module Doc : Parameter_sig.String (** Generate documentation *)
+module Doc : Parameter_sig.Filepath (** Generate documentation *)
 module Polling : Parameter_sig.Int (** Idle waiting time (in ms) *)
 module AutoLog : Parameter_sig.Bool (** Monitor logs *)
 
diff --git a/src/plugins/value/domains/numerors/numerors_domain.ml b/src/plugins/value/domains/numerors/numerors_domain.ml
index cafa9a5f1da9f06c0be6fedbffbec0f6e4117543..e2afa36684c692128b90b61eed91623cce461df9 100644
--- a/src/plugins/value/domains/numerors/numerors_domain.ml
+++ b/src/plugins/value/domains/numerors/numerors_domain.ml
@@ -86,14 +86,14 @@ module Domain = struct
   include Simple_memory.Make_Domain (Name) (Numerors_Value)
 
   let post_analysis f =
-    match f, Value_parameters.NumerorsLogFile.get () with
-    | _, s when s = "" -> ()
-    | `Value _, s ->
-      let log = open_out s in
-      let fmt = Format.formatter_of_out_channel log in
-      List.iter (fun f -> f fmt ()) !Numerors_Value.dprint_callstack ;
-      close_out log
-    | _, _ -> ()
+    if not (Value_parameters.NumerorsLogFile.is_empty ()) then
+      match f with
+      | `Value _ ->
+        let log = open_out (Value_parameters.NumerorsLogFile.get ():>string) in
+        let fmt = Format.formatter_of_out_channel log in
+        List.iter (fun f -> f fmt ()) !Numerors_Value.dprint_callstack ;
+        close_out log
+      | _ -> ()
 end
 
 (* Reduced product between the cvalue values and the numerors values. *)
diff --git a/src/plugins/value/domains/traces_domain.ml b/src/plugins/value/domains/traces_domain.ml
index ce718b03ee17ad5b2c30afd9a4d08658f3b5f7ee..408114d15682d5121126bb154111f216bf67c8f6 100644
--- a/src/plugins/value/domains/traces_domain.ml
+++ b/src/plugins/value/domains/traces_domain.ml
@@ -1278,7 +1278,7 @@ module D = struct
         Value_parameters.failure "The trace is TOP can't generate code"
       | `Value state ->
         if not (Value_parameters.TracesDot.is_default ())
-        then output_dot (Value_parameters.TracesDot.get ()) state;
+        then output_dot (Value_parameters.TracesDot.get ():>string) state;
         if Value_parameters.TracesProject.get ()
         then project_of_cfg return_exp state
 end
diff --git a/src/plugins/value/engine/analysis.ml b/src/plugins/value/engine/analysis.ml
index 93fd504680369faf45e68100017c4acf5552ea39..33c7c3687c564d51e19b87ceca850b0687a751a2 100644
--- a/src/plugins/value/engine/analysis.ml
+++ b/src/plugins/value/engine/analysis.ml
@@ -173,10 +173,10 @@ let reset_analyzer () =
 let force_compute () =
   Ast.compute ();
   Value_parameters.configure_precision ();
-  if not (Filepath.Normalized.is_unknown (Kernel.AuditCheck.get ()))
-  then Eva_audit.check_configuration (Kernel.AuditCheck.get ());
-  if not (Filepath.Normalized.is_unknown (Kernel.AuditPrepare.get ()))
-  then Eva_audit.print_configuration (Kernel.AuditPrepare.get ());
+  if not (Kernel.AuditCheck.is_empty ()) then
+    Eva_audit.check_configuration (Kernel.AuditCheck.get ());
+  if not (Kernel.AuditPrepare.is_empty ()) then
+    Eva_audit.print_configuration (Kernel.AuditPrepare.get ());
   let kf, lib_entry = Globals.entry_point () in
   reset_analyzer ();
   let module Analyzer = (val snd !ref_analyzer) in
diff --git a/src/plugins/value/utils/red_statuses.ml b/src/plugins/value/utils/red_statuses.ml
index 8657a9dae7c99b77cd9bb5e7c11f0b9d33a708bb..cd194c77d7a2ef18a4cc3bb67e224939e2e56e04 100644
--- a/src/plugins/value/utils/red_statuses.ml
+++ b/src/plugins/value/utils/red_statuses.ml
@@ -175,8 +175,9 @@ let print_information fmt { loc; kf; alarm; kind; text; status; contexts } =
     dir file lnum kf alarm kind contexts status text
 
 let output file =
-  Value_parameters.feedback "Listing red statuses in file %s" file;
-  let channel = open_out file in
+  Value_parameters.feedback "Listing red statuses in file %a"
+    Filepath.Normalized.pretty file;
+  let channel = open_out (file:>string) in
   let fmt = Format.formatter_of_out_channel channel in
   Format.pp_set_margin fmt 1000000;
   Format.fprintf fmt "@[<v>";
@@ -189,5 +190,4 @@ let output file =
   Format.fprintf fmt "@]%!"
 
 let report () =
-  let file = Value_parameters.ReportRedStatuses.get () in
-  if file <> "" then output file
+  Value_parameters.ReportRedStatuses.(if not (is_empty ()) then output (get ()))
diff --git a/src/plugins/value/utils/value_perf.ml b/src/plugins/value/utils/value_perf.ml
index e8f0be51619f58a5a7003874680638b01b43d8a4..b4c455f6db589faaba2a24fbf63646b29526febf 100644
--- a/src/plugins/value/utils/value_perf.ml
+++ b/src/plugins/value/utils/value_perf.ml
@@ -395,11 +395,11 @@ let start_doing_flamegraph callstack =
   | [] -> assert false
   | [_] ->
     (* Analysis of main *)
-    let file = Value_parameters.ValPerfFlamegraphs.get () in
-    if file <> "" then begin
+    if not (Value_parameters.ValPerfFlamegraphs.is_empty ()) then begin
+      let file = Value_parameters.ValPerfFlamegraphs.get () in
       try
         (* Flamegraphs must be computed. Set up the stack and the output file *)
-        let oc = open_out file in
+        let oc = open_out (file:>string) in
         oc_flamegraph := Some oc;
         stack_flamegraph := [ (Sys.time (), 0.) ]
       with e ->
diff --git a/src/plugins/value/value_parameters.ml b/src/plugins/value/value_parameters.ml
index ec4e77fd3ed01fec6b323a0129a6a1f580db8bd0..494a55fc98f21b9986e5d52116d7f3aacecf67aa 100644
--- a/src/plugins/value/value_parameters.ml
+++ b/src/plugins/value/value_parameters.ml
@@ -351,11 +351,13 @@ module TracesUnifyLoop =
 let () = add_precision_dep TracesUnifyLoop.parameter
 
 let () = Parameter_customize.set_group domains
-module TracesDot = Empty_string
+module TracesDot = Filepath
     (struct
       let option_name = "-eva-traces-dot"
-      let help = "Output to the given filename the Cfg in dot format."
       let arg_name = "FILENAME"
+      let file_kind = "DOT"
+      let existence = Fc_Filepath.Indifferent
+      let help = "Output to the given filename the Cfg in dot format."
     end)
 
 let () = Parameter_customize.set_group domains
@@ -1070,14 +1072,15 @@ module ValShowPerf =
 
 let () = Parameter_customize.set_group messages
 module ValPerfFlamegraphs =
-  String
+  Filepath
     (struct
       let option_name = "-eva-flamegraph"
+      let arg_name = "file"
+      let file_kind = "Text for flamegraph"
+      let existence = Fc_Filepath.Indifferent
       let help = "Dump a summary of the time spent analyzing function calls \
                   in a format suitable for the Flamegraph tool \
                   (http://www.brendangregg.com/flamegraphs.html)"
-      let arg_name = "file"
-      let default = ""
     end)
 
 
@@ -1103,11 +1106,12 @@ module PrintCallstacks =
 
 let () = Parameter_customize.set_group messages
 module ReportRedStatuses =
-  String
+  Filepath
     (struct
       let option_name = "-eva-report-red-statuses"
       let arg_name = "filename"
-      let default = ""
+      let file_kind = "CSV"
+      let existence = Fc_Filepath.Indifferent
       let help = "Output the list of \"red properties\" in a csv file of the \
                   given name. These are the properties which were invalid for \
                   some states. Their consolidated status may not be invalid, \
@@ -1116,13 +1120,14 @@ module ReportRedStatuses =
 
 let () = Parameter_customize.set_group messages
 module NumerorsLogFile =
-  String
+  Filepath
     (struct
       let option_name = "-eva-numerors-log-file"
+      let arg_name = "file"
+      let file_kind = "Text"
+      let existence = Fc_Filepath.Indifferent
       let help = "The Numerors domain will save each call to the DPRINT \
                   function in the given file"
-      let arg_name = "file"
-      let default = ""
     end)
 
 (* ------------------------------------------------------------------------- *)
diff --git a/src/plugins/value/value_parameters.mli b/src/plugins/value/value_parameters.mli
index 3f50d6efaa7990c3c68289b0043e4259bab34ba0..9a6a7172cc20cf89b2a178a6bf40c59cf1e91779 100644
--- a/src/plugins/value/value_parameters.mli
+++ b/src/plugins/value/value_parameters.mli
@@ -42,7 +42,7 @@ module OctagonCall: Parameter_sig.Bool
 
 module TracesUnrollLoop: Parameter_sig.Bool
 module TracesUnifyLoop: Parameter_sig.Bool
-module TracesDot: Parameter_sig.String
+module TracesDot: Parameter_sig.Filepath
 module TracesProject: Parameter_sig.Bool
 
 module EqualityStorage: Parameter_sig.Bool
@@ -122,11 +122,11 @@ module SplitGlobalStrategy: State_builder.Ref with type data = Split_strategy.t
 
 module ValShowProgress: Parameter_sig.Bool
 module ValShowPerf: Parameter_sig.Bool
-module ValPerfFlamegraphs: Parameter_sig.String
+module ValPerfFlamegraphs: Parameter_sig.Filepath
 module ShowSlevel: Parameter_sig.Int
 module PrintCallstacks: Parameter_sig.Bool
-module ReportRedStatuses: Parameter_sig.String
-module NumerorsLogFile: Parameter_sig.String
+module ReportRedStatuses: Parameter_sig.Filepath
+module NumerorsLogFile: Parameter_sig.Filepath
 
 module MemExecAll: Parameter_sig.Bool
 
diff --git a/tests/crowbar/test_ghost_cfg.ml b/tests/crowbar/test_ghost_cfg.ml
index cc91a8af05d2d6259b9edb867296fe76f2cd96e9..5f67349aee7aec87e003b56d33b1dac317de6964 100644
--- a/tests/crowbar/test_ghost_cfg.ml
+++ b/tests/crowbar/test_ghost_cfg.ml
@@ -460,7 +460,7 @@ let gen_file =
       f.svar.vdefined <- true;
       f.sbody <- body;
       (env,
-       { fileName = Filepath.Normalized.unknown;
+       { fileName = Filepath.Normalized.empty;
          globals = [
              GVarDecl (x,Cil_datatype.Location.unknown);
              GVarDecl (y,Cil_datatype.Location.unknown);