diff --git a/gdb/NEWS b/gdb/NEWS
index 9e1462b6bf..9d6a2de661 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -98,6 +98,13 @@ apropos [-v] REGEXP
   of matching commands and to use the highlight style to mark
   the documentation parts matching REGEXP.
 
+printf
+eval
+  The GDB printf and eval commands can now print C-style and Ada-style
+  convenience variables without calling functions in the program.
+  This allows to do formatted printing of strings without having
+  an inferior, or when debugging a core dump.
+
 show style
   The "show style" and its subcommands are now styling
   a style name in their output using its own style, to help
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 9e84594fe6..d7b8b9a1c1 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -23,6 +23,7 @@
 #include "gdbtypes.h"
 #include "value.h"
 #include "language.h"
+#include "c-lang.h"
 #include "expression.h"
 #include "gdbcore.h"
 #include "gdbcmd.h"
@@ -2200,91 +2201,127 @@ print_variable_and_value (const char *name, struct symbol *var,
 
 /* Subroutine of ui_printf to simplify it.
    Print VALUE to STREAM using FORMAT.
-   VALUE is a C-style string on the target.  */
+   VALUE is a C-style string on the target or a C-style string
+   in a GDB internal variable.  */
 
 static void
 printf_c_string (struct ui_file *stream, const char *format,
 		 struct value *value)
 {
-  gdb_byte *str;
-  CORE_ADDR tem;
-  int j;
+  const gdb_byte *str;
 
-  tem = value_as_address (value);
-  if (tem == 0)
+  if (VALUE_LVAL (value) == lval_internalvar
+      && c_is_string_type_p (value_type (value)))
     {
-      DIAGNOSTIC_PUSH
-      DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
-      fprintf_filtered (stream, format, "(null)");
-      DIAGNOSTIC_POP
-      return;
-    }
+      gdb_byte *tem_str;
+      size_t len  = TYPE_LENGTH (value_type (value));
 
-  /* This is a %s argument.  Find the length of the string.  */
-  for (j = 0;; j++)
+      /* Copy the internal var value to tem_str and append a terminating null
+	 character.  This protects against corrupted C-style strings that lacks
+	 the terminating null char.  It also allows Ada style strings (not not
+	 null terminated) to be printed without problems.  */
+      tem_str = (gdb_byte *) alloca (len + 1);
+      memcpy (tem_str, value_contents (value), len);
+      tem_str [len] = 0;
+      str = tem_str;
+    }
+  else
     {
-      gdb_byte c;
+      int len;
+      CORE_ADDR tem;
+      gdb_byte *tem_str;
 
-      QUIT;
-      read_memory (tem + j, &c, 1);
-      if (c == 0)
-	break;
-    }
+      tem = value_as_address (value);
+      if (tem == 0)
+	{
+	  DIAGNOSTIC_PUSH
+	    DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+	    fprintf_filtered (stream, format, "(null)");
+	  DIAGNOSTIC_POP
+	    return;
+	}
 
-  /* Copy the string contents into a string inside GDB.  */
-  str = (gdb_byte *) alloca (j + 1);
-  if (j != 0)
-    read_memory (tem, str, j);
-  str[j] = 0;
+      /* This is a %s argument.  Find the length of the string.  */
+      for (len = 0;; len++)
+	{
+	  gdb_byte c;
+
+	  QUIT;
+	  read_memory (tem + len, &c, 1);
+	  if (c == 0)
+	    break;
+	}
+
+      /* Copy the string contents into a string inside GDB.  */
+      tem_str = (gdb_byte *) alloca (len + 1);
+      if (len != 0)
+	read_memory (tem, tem_str, len);
+      tem_str[len] = 0;
+      str = tem_str;
+    }
 
   DIAGNOSTIC_PUSH
-  DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
-  fprintf_filtered (stream, format, (char *) str);
+    DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+    fprintf_filtered (stream, format, (char *) str);
   DIAGNOSTIC_POP
-}
+    }
 
 /* Subroutine of ui_printf to simplify it.
    Print VALUE to STREAM using FORMAT.
-   VALUE is a wide C-style string on the target.  */
+   VALUE is a wide C-style string on the target or a wide C-style string
+   in a GDB internal variable.  */
 
 static void
 printf_wide_c_string (struct ui_file *stream, const char *format,
 		      struct value *value)
 {
-  gdb_byte *str;
-  CORE_ADDR tem;
+  const gdb_byte *str;
   int j;
   struct gdbarch *gdbarch = get_type_arch (value_type (value));
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   struct type *wctype = lookup_typename (current_language, gdbarch,
 					 "wchar_t", NULL, 0);
   int wcwidth = TYPE_LENGTH (wctype);
-  gdb_byte *buf = (gdb_byte *) alloca (wcwidth);
 
-  tem = value_as_address (value);
-  if (tem == 0)
+  if (VALUE_LVAL (value) == lval_internalvar
+      && c_is_string_type_p (value_type (value)))
     {
-      DIAGNOSTIC_PUSH
-      DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
-      fprintf_filtered (stream, format, "(null)");
-      DIAGNOSTIC_POP
-      return;
+      str = value_contents (value);
+      j = TYPE_LENGTH (value_type (value));
     }
-
-  /* This is a %s argument.  Find the length of the string.  */
-  for (j = 0;; j += wcwidth)
+  else
     {
-      QUIT;
-      read_memory (tem + j, buf, wcwidth);
-      if (extract_unsigned_integer (buf, wcwidth, byte_order) == 0)
-	break;
-    }
+      gdb_byte *tem_str;
+      CORE_ADDR tem;
+      gdb_byte *buf = (gdb_byte *) alloca (wcwidth);
 
-  /* Copy the string contents into a string inside GDB.  */
-  str = (gdb_byte *) alloca (j + wcwidth);
-  if (j != 0)
-    read_memory (tem, str, j);
-  memset (&str[j], 0, wcwidth);
+      enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+      tem = value_as_address (value);
+      if (tem == 0)
+	{
+	  DIAGNOSTIC_PUSH
+	    DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+	    fprintf_filtered (stream, format, "(null)");
+	  DIAGNOSTIC_POP
+	    return;
+	}
+
+      /* This is a %s argument.  Find the length of the string.  */
+      for (j = 0;; j += wcwidth)
+	{
+	  QUIT;
+	  read_memory (tem + j, buf, wcwidth);
+	  if (extract_unsigned_integer (buf, wcwidth, byte_order) == 0)
+	    break;
+	}
+
+      /* Copy the string contents into a string inside GDB.  */
+      tem_str = (gdb_byte *) alloca (j + wcwidth);
+      if (j != 0)
+	read_memory (tem, tem_str, j);
+      memset (&tem_str[j], 0, wcwidth);
+      str = tem_str;
+    }
 
   auto_obstack output;
 
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index f2d6ee229d..3b6562426e 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -932,6 +932,32 @@ proc test_repeat_bytes {} {
     }
 }
 
+proc test_printf_convenience_var {prefix do_wstring} {
+
+    with_test_prefix $prefix {
+	gdb_test_no_output "set var \$cstr = \"abcde\"" "set \$cstr, conv var"
+	gdb_test "printf \"cstr val = %s\\n\", \$cstr" "cstr val = abcde" \
+	    "printf \$cstr, conv var"
+	gdb_test_no_output "set var \$abcde = \"ABCDE\"" "set \$abcde, conv var"
+	gdb_test "eval \"print \$%s\\n\", \$cstr" "= \"ABCDE\"" \
+	    "indirect print abcde"
+	gdb_test "set language ada" ".*" "set language ada, conv var"
+	gdb_test_no_output "set var \$astr := \"fghij\"" "set \$astr, conv var"
+	gdb_test "printf \"astr val = %s\\n\", \$astr" "astr val = fghij" \
+	    "printf \$astr, conv var"
+	gdb_test "set language auto" ".*" "set language auto, conv var"
+	gdb_test "printf \"astr val = %s\\n\", \$astr" "astr val = fghij" \
+	    "printf \$astr, conv var, auto language"
+	if ($do_wstring) {
+	    gdb_test_no_output "set var \$wstr = L\"facile\"" \
+		"set \$wstr, conv var"
+	    gdb_test "printf \"wstr val = %ls\\n\", \$wstr" \
+		"wstr val = facile" "printf \$wstr, conv var"
+	}
+    }
+}
+
+
 # Start with a fresh gdb.
 
 gdb_exit
@@ -948,6 +974,11 @@ gdb_test "ptype \"abc\"" " = char \\\[4\\\]"
 gdb_test "print \$cvar = \"abc\"" " = \"abc\""
 gdb_test "print sizeof (\$cvar)" " = 4"
 
+# Similarly, printf of convenience var should work without a target.
+# At this point, we cannot create wide strings convenience var, as the
+# type wchar_t is not yet known, so skip the wide string tests.
+test_printf_convenience_var "no target" 0
+
 # GDB used to complete the explicit location options even when
 # printing expressions.
 gdb_test_no_output "complete p -function"
@@ -977,6 +1008,14 @@ if ![runto_main] then {
     return 0
 }
 
+# With a target, printf convenience var should of course work.
+test_printf_convenience_var "with target" 1
+
+# But it should also work when inferior function calls are forbidden.
+gdb_test_no_output "set may-call-functions off"
+test_printf_convenience_var "with target, may-call-functions off" 1
+gdb_test_no_output "set may-call-functions on"
+
 test_integer_literals_accepted
 test_integer_literals_rejected
 test_float_accepted
