diff --git a/Makefile b/Makefile
index 4571affc6f865f7c8b251b54fd4f124fbd236454..94bfa0d32a6bfae0a19ef60c26a31fd1aa5e866b 100644
--- a/Makefile
+++ b/Makefile
@@ -262,6 +262,7 @@ DISTRIB_FILES:=\
       share/analysis-scripts/frama_c_results.py                         \
       share/analysis-scripts/git_utils.py                               \
       share/analysis-scripts/list_files.py                              \
+      share/analysis-scripts/make_wrapper.py                            \
       share/analysis-scripts/parse-coverage.sh                          \
       share/analysis-scripts/README.md                                  \
       share/analysis-scripts/results_display.py                         \
@@ -1915,6 +1916,7 @@ install:: install-lib
 	  share/analysis-scripts/frama_c_results.py \
 	  share/analysis-scripts/git_utils.py \
 	  share/analysis-scripts/list_files.py \
+	  share/analysis-scripts/make_wrapper.py \
 	  share/analysis-scripts/parse-coverage.sh \
 	  share/analysis-scripts/README.md \
 	  share/analysis-scripts/results_display.py \
diff --git a/bin/frama-c-script b/bin/frama-c-script
index b797957eeb5db40710aaf6eb46af17c2ca03c0f0..e3dafca01d88506b4c9402155b7c5b0f84f1b29e 100755
--- a/bin/frama-c-script
+++ b/bin/frama-c-script
@@ -59,6 +59,21 @@ if [ $# -lt 1 ]; then
    echo "      Monitors and summarizes multiple analyses dispatched by a Makefile"
    echo "      in the current PWD."
    echo "      Use $0 summary --help for more informations."
+   echo ""
+   echo "  - configure <machdep>"
+   echo "      Runs an existing configure script to only consider files"
+   echo "      in Frama-C's libc; this will hopefully disable non-essential"
+   echo "      and non-POSIX external libraries."
+   echo "      <machdep> is necessary to define a required preprocessor symbol"
+   echo "      (run 'frama-c -machdep' help to get the list of machdeps)."
+   echo ""
+   echo "  - make-wrapper <target> <args>"
+   echo "      Runs 'make <target> <args>', parsing the output to suggest"
+   echo "      useful commands in case of failure."
+   echo ""
+   echo "  - normalize-jcdb [path/to/compile_commands.json]"
+   echo "      Applies some transformations to an existing compile_commands.json"
+   echo "      (such as relativizing paths) to improve portability"
    exit
 fi
 
@@ -105,6 +120,7 @@ make_template() {
         dir="."
     fi
     path="$dir/GNUmakefile"
+    path_tmp="${path}.tmp"
     check_path_exists "$path"
     read -p "Main target name: " main
     if [[ ! ( "$main" =~ ^[a-zA-Z_0-9]+$ ) ]]; then
@@ -115,9 +131,41 @@ make_template() {
     if [ "$sources" = "" ]; then
         sources="*.c"
     fi
-    sed "s/^MAIN_TARGET :=/MAIN_TARGET := ${main}/" "${FRAMAC_SHARE}/analysis-scripts/template.mk" > "${path}.tmp"
-    sed "s|\$(MAIN_TARGET).parse:|\$(MAIN_TARGET).parse: $sources|" "${path}.tmp" | sed "/# Remove these lines after defining the main target/{N;N;N;N;d;}" > "$path"
-    rm "${path}.tmp"
+
+    add_main_stub=0
+    read -p "Add stub for function main (only needed if it uses command-line arguments)? [y/N] " yn
+    case $yn in
+        [Yy])
+            add_main_stub=1
+            sources="fc_stubs.c $sources"
+            ;;
+        *)
+            ;;
+    esac
+
+    sed "s/^MAIN_TARGET :=/MAIN_TARGET := ${main}/" "${FRAMAC_SHARE}/analysis-scripts/template.mk" > "$path"
+    sed "s|\$(MAIN_TARGET).parse:|\$(MAIN_TARGET).parse: $sources|" "$path" | sed "/# Remove these lines after defining the main target/{N;N;N;N;d;}" > "$path_tmp"; mv "$path_tmp" "$path"
+    if [ -e "compile_commands.json" ]; then
+        read -p "compile_commands.json exists, add option -json-compilation-database? [Y/n] " yn
+        case $yn in
+            [Nn])
+                echo "Option not added; you can later add it to FCFLAGS."
+                ;;
+            *)
+                # the command below inserts a line after matching a given
+                # pattern; compatible with macOS sed
+                sed -e '/^FCFLAGS[[:space:]]\++=/a\
+\ \ -json-compilation-database .\\' "$path" > "$path_tmp"; mv "$path_tmp" "$path"
+                ;;
+        esac
+    fi
+    if [ $add_main_stub -eq 1 ]; then
+        check_path_exists "fc_stubs.c"
+        cp "${FRAMAC_SHARE}/analysis-scripts/fc_stubs.c" "./"
+        echo "Created stub for main function: fc_stubs.c"
+        sed -e '/^FCFLAGS[[:space:]]\++=/a\
+\ \ -main eva_main \\' "$path" > "$path_tmp"; mv "$path_tmp" "$path"
+    fi
     echo "Template created: $path"
 }
 
@@ -185,6 +233,48 @@ EOF
     fi
 }
 
+configure_for_frama_c() {
+    if [ "$#" -eq 0 ]; then
+        echo "error: 'configure' command requires a machdep";
+        exit 1
+    fi
+    MACHDEP="$(echo $1 | tr a-z A-Z)"
+    shift
+    CPP="gcc -E -nostdinc -fno-builtin -I${FRAMAC_SHARE}/libc -D__FC_MACHDEP_${MACHDEP}" ./configure "$@"
+}
+
+normalize_jcdb() {
+    path=""
+    if [ "$#" -eq 0 ]; then
+        path="./compile_commands.json"
+    else
+        path="$1"
+    fi
+    if [ ! -e "$path" ]; then
+        echo "error: cannot find file: $path";
+        exit 1
+    fi
+    sed "s|$PWD/||g" "$path" > "${path}.tmp"
+    cmp -s "$path" "${path}.tmp"
+    if [ $? -eq 0 ]; then
+        echo "No changes to be applied to $path"
+        rm "${path}.tmp"
+    else
+        echo "Differences to be applied to $path:"
+        diff -u0 "$path" "${path}.tmp"
+        read -p "Normalize $path? [y/N] " yn
+        case $yn in
+            [Yy])
+                mv "${path}.tmp" "$path"
+                echo "Normalization applied to $path"
+            ;;
+            *)
+                echo "Exiting without overwriting."
+                exit 0;;
+        esac
+    fi
+}
+
 case "$command" in
     "make-template")
         shift;
@@ -210,6 +300,18 @@ case "$command" in
         shift;
         ${FRAMAC_SHARE}/analysis-scripts/summary.py "$@";
         ;;
+    "configure")
+        shift;
+        configure_for_frama_c "$@";
+        ;;
+    "make-wrapper")
+        shift;
+        ${FRAMAC_SHARE}/analysis-scripts/make_wrapper.py "$0" "$@";
+        ;;
+    "normalize-jcdb")
+        shift;
+        normalize_jcdb "$@";
+        ;;
     *)
         echo "error: unrecognized command: $command"
 esac
diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 8400bc0b75b7c5909a1637aec9185fdd562ded33..52cb9fa3d6b427f8d8d608cb230b39e6c589eb9e 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -128,6 +128,7 @@ share/analysis-scripts/find_fun.py: .ignore
 share/analysis-scripts/flamegraph.pl: CDDL
 share/analysis-scripts/git_utils.py: .ignore
 share/analysis-scripts/list_files.py: .ignore
+share/analysis-scripts/make_wrapper.py: .ignore
 share/analysis-scripts/parse-coverage.sh: .ignore
 share/analysis-scripts/README.md: .ignore
 share/analysis-scripts/results_display.py: .ignore
diff --git a/share/analysis-scripts/fc_stubs.c b/share/analysis-scripts/fc_stubs.c
new file mode 100644
index 0000000000000000000000000000000000000000..ca1ed39a3ba9e6bf25975698f1f058fd08548dc6
--- /dev/null
+++ b/share/analysis-scripts/fc_stubs.c
@@ -0,0 +1,23 @@
+// Stub for a main function which reads arguments from the command line, to be
+// used by the Eva plug-in.
+// This stub emulates non-deterministic input of up to 5 arguments, each up
+// to 256 characters long. This is sufficient to ensure arbitrary input in
+// virtually every case.
+// Do not forget to add option '-main eva_main' in order to use this stub.
+
+#ifdef __FRAMAC__
+# include "__fc_builtin.h"
+int main(int, char **);
+static volatile int nondet;
+int eva_main() {
+  int argc = Frama_C_interval(0, 5);
+  char argv0[256], argv1[256], argv2[256], argv3[256], argv4[256];
+  char *argv[5] = {argv0, argv1, argv2, argv3, argv4};
+  //@ loop unroll 5;
+  for (int i = 0; i < 5; i++) {
+    Frama_C_make_unknown(argv[i], 255);
+    argv[i][255] = 0;
+  }
+  return main(argc, argv);
+}
+#endif // __FRAMAC__
diff --git a/share/analysis-scripts/find_fun.py b/share/analysis-scripts/find_fun.py
index b8a3565da0bad21178f815a28b18749f3405d46d..5ef92a69b6287eeb95687c4cc6c443bec8bf503b 100755
--- a/share/analysis-scripts/find_fun.py
+++ b/share/analysis-scripts/find_fun.py
@@ -101,8 +101,8 @@ else:
    if possible_declarators != []:
       print("Possible declarations for function '%s' in the following file(s):"
             % fname)
-      print("  " + "\n  ".join(possible_declarators))
+      print("  " + "\n  ".join(map(os.path.relpath, possible_declarators)))
    if possible_definers != []:
       print("Possible definitions for function '%s' in the following file(s):"
             % fname)
-      print("  " + "\n  ".join(possible_definers))
+      print("  " + "\n  ".join(map(os.path.relpath, possible_definers)))
diff --git a/share/analysis-scripts/make_wrapper.py b/share/analysis-scripts/make_wrapper.py
new file mode 100755
index 0000000000000000000000000000000000000000..050643df89c5c074cb25515f9f3da3cbe96ab254
--- /dev/null
+++ b/share/analysis-scripts/make_wrapper.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+#-*- coding: utf-8 -*-
+##########################################################################
+#                                                                        #
+#  This file is part of Frama-C.                                         #
+#                                                                        #
+#  Copyright (C) 2007-2018                                               #
+#    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 serves as wrapper to 'make' (when using the analysis-scripts
+# GNUmakefile template): it parses the output and suggests useful commands
+# whenever it can, by calling frama-c-script itself.
+
+import subprocess
+import sys
+import os
+import re
+from functools import partial
+
+if len(sys.argv) < 3:
+   print("usage: %s path-to-frama-c-script target" % sys.argv[0])
+   print("       Builds the specified target, parsing the output to")
+   print("       identify and recommend actions in case of failure.")
+   print("       The first argument must be the path to the frama-c-script")
+   print("       binary.")
+   sys.exit(1)
+
+framac_script = sys.argv[1]
+target = sys.argv[2]
+args = sys.argv[3:]
+
+out = subprocess.Popen(['make', target] + args,
+                       stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+output = out.communicate()[0].decode('utf-8')
+
+re_missing_spec = re.compile("Neither code nor specification for function ([^,]+),")
+re_recursive_call_start = re.compile("detected recursive call")
+re_recursive_call_end = re.compile("Use -eva-ignore-recursive-calls to ignore")
+
+tips = []
+
+lines = iter(output.splitlines())
+for line in lines:
+    print(line)
+    match = re_missing_spec.search(line)
+    if match:
+       fname = match.group(1)
+       def action(fname):
+           out = subprocess.Popen([framac_script, "find-fun", fname],
+                                  stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+           output = out.communicate()[0].decode('utf-8')
+           re_possible_definers = re.compile("Possible definitions for function")
+           find_fun_lines = iter(output.splitlines())
+           for find_fun_line in find_fun_lines:
+              if re_possible_definers.match(find_fun_line):
+                 found_files = [next(find_fun_lines)]
+                 while True:
+                    try:
+                       found_files.append(next(find_fun_lines))
+                    except StopIteration:
+                       if len(found_files) > 1:
+                          print("Found several files defining function '"
+                                + fname + "', cannot recommend automatically.")
+                          print("Check which one is appropriate and add it " +
+                                "to the list of sources to be parsed:")
+                          print("\n".join(found_files))
+                       else:
+                          print("Add the following file to the list of "
+                                + "sources to be parsed:\n" + found_files[0])
+                       return
+           print("Could not find any files defining " + fname + ".")
+           print("Find the sources defining it and add them, " +
+                 "or provide a stub.")
+       tip = {"message": "Found function with missing spec: " + fname + "\n" +
+              "   Looking for files defining it...",
+              "action":partial(action, fname)
+       }
+       tips.append(tip)
+    else:
+       match = re_recursive_call_start.search(line)
+       if match:
+          def action():
+             print("Consider patching or stubbing the recursive call, " +
+                   "then re-run the analysis.")
+          msg_lines = []
+          line = next(lines)
+          while True:
+             match = re_recursive_call_end.search(line)
+             if match:
+                tip = {"message": "Found recursive call at:\n" +
+                       "\n".join(msg_lines),
+                       "action":action
+                       }
+                tips.append(tip)
+                break
+             else:
+                msg_lines.append(line)
+                try:
+                   line = next(lines)
+                except StopIteration:
+                   print("** Error: EOF without ending recursive call stack?")
+                   assert False
+
+if tips != []:
+   print("")
+   print("***** make-wrapper recommendations *****")
+   print("")
+   counter = 1
+   print("*** recommendation #" + str(counter) + " ***")
+   print("")
+   for tip in tips:
+      if counter > 1:
+         print("")
+         print("*** recommendation #" + str(counter) + " ***")
+      print(str(counter) + ". " + tip["message"])
+      counter += 1
+      tip["action"]()
diff --git a/src/kernel_services/ast_queries/json_compilation_database.ok.ml b/src/kernel_services/ast_queries/json_compilation_database.ok.ml
index 2442e726b6a3dd8583bed2415b238e1fd64af819..8d31f5b6709a8bedd67c52e864f04b4076b00881 100644
--- a/src/kernel_services/ast_queries/json_compilation_database.ok.ml
+++ b/src/kernel_services/ast_queries/json_compilation_database.ok.ml
@@ -81,6 +81,10 @@ let split_command_args s =
       let c = String.get s i in
       let new_state, new_acc =
         match state, prev_c, c with
+        | Outside_quote, '\\', c when c = '\"' || c = '\'' ->
+          (* escaped quote, continue with previous arg *)
+          Buffer.add_char buf c;
+          state, acc
         | Outside_quote, _, q when q = '\'' || q = '\"' ->
           (* continue previous arg with q *)
           Buffer.add_char buf q;
diff --git a/tests/jcdb/jcdb.c b/tests/jcdb/jcdb.c
index ddf00afcd0acb406597062b30cab1a821f769faf..f2389fe2e8ada1276a640ceb2689b7ff4f5a6f76 100644
--- a/tests/jcdb/jcdb.c
+++ b/tests/jcdb/jcdb.c
@@ -1,7 +1,7 @@
 /* run.config
 EXECNOW: make -s @PTEST_DIR@/@PTEST_NAME@.cmxs
 OPT: -json-compilation-database @PTEST_DIR@ -print
-OPT: -json-compilation-database @PTEST_DIR@/with_arguments.json -print
+OPT: @PTEST_DIR@/jcdb2.c -json-compilation-database @PTEST_DIR@/with_arguments.json -print
 OPT: -json-compilation-database @PTEST_DIR@/with_arguments.json -no-autoload-plugins -load-module @PTEST_DIR@/@PTEST_NAME@.cmxs
 EXECNOW: LOG list_files.res LOG list_files.err share/analysis-scripts/list_files.py @PTEST_DIR@/compile_commands_working.json > @PTEST_DIR@/result/list_files.res 2> @PTEST_DIR@/result/list_files.err
 */
diff --git a/tests/jcdb/oracle/jcdb.1.res.oracle b/tests/jcdb/oracle/jcdb.1.res.oracle
index 87d1d04f76ffe2da596bb596c8ebbb67ea0e9d88..3fbf07641f32aa75dd444c39500a1a58a7fe4029 100644
--- a/tests/jcdb/oracle/jcdb.1.res.oracle
+++ b/tests/jcdb/oracle/jcdb.1.res.oracle
@@ -1,4 +1,5 @@
 [kernel] Parsing tests/jcdb/jcdb.c (with preprocessing)
+[kernel] Parsing tests/jcdb/jcdb2.c (with preprocessing)
 /* Generated by Frama-C */
 #include "errno.h"
 #include "stdarg.h"
diff --git a/tests/jcdb/with_arguments.json b/tests/jcdb/with_arguments.json
index 49b11f5d572af2f851c3795ef79d706217af6881..4217869b5eafd76539298bf3156a9d8d7a1df764 100644
--- a/tests/jcdb/with_arguments.json
+++ b/tests/jcdb/with_arguments.json
@@ -16,5 +16,9 @@
           "-UTOUNDEF"
       ],
       "file": "jcdb.c"
+    },
+    { "directory": "tests/jcdb",
+      "command": "/usr/bin/cc -DONION_VERSION=\\\"0.1.tr\\\" -Ibla -o jcdb2.o -c jcdb2.c jcdb.c",
+      "file": "jcdb2.c"
     }
 ]