diff --git a/gdb/NEWS b/gdb/NEWS
index 962eae4..ce9f7b0 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,12 @@
 
 *** Changes since GDB 7.10
 
+* GDB's printf command now has a format specifier (%q) to display the
+  textual label (enumerator) of an enumeration value.  For example:
+
+    (gdb) printf "Visiting node of type %q\n", node
+    Visiting node of type NODE_INTEGER
+
 * GDB now supports debugging kernel-based threads on FreeBSD.
 
 * Per-inferior thread numbers
diff --git a/gdb/common/format.c b/gdb/common/format.c
index fbb82fd..f90893e 100644
--- a/gdb/common/format.c
+++ b/gdb/common/format.c
@@ -294,6 +294,10 @@ parse_format_string (const char **arg)
 	  case '\0':
 	    error (_("Incomplete format specifier at end of format string"));
 
+	  case 'q':
+	    this_argclass = enum_arg;
+	    break;
+
 	  default:
 	    error (_("Unrecognized format specifier '%c' in printf"), *f);
 	  }
diff --git a/gdb/common/format.h b/gdb/common/format.h
index cb56a2c..884403e 100644
--- a/gdb/common/format.h
+++ b/gdb/common/format.h
@@ -35,7 +35,7 @@ enum argclass
     literal_piece,
     int_arg, long_arg, long_long_arg, ptr_arg,
     string_arg, wide_string_arg, wide_char_arg,
-    double_arg, long_double_arg, decfloat_arg
+    double_arg, long_double_arg, decfloat_arg, enum_arg
   };
 
 /* A format piece is a section of the format string that may include a
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2d09d13..183bd9e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24343,6 +24343,9 @@ sequences, such as @code{\n}, @samp{\t}, @samp{\\}, @samp{\"},
 single character.  Octal and hexadecimal escape sequences are not
 supported.
 
+@code{printf} supports the @samp{q} conversion letter to print the textual
+label (enumerator) of a C enumeration value.
+
 Additionally, @code{printf} supports conversion specifications for DFP
 (@dfn{Decimal Floating Point}) types using the following length modifiers
 together with a floating point specifier.
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index f5c4211..e4637df 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2258,6 +2258,23 @@ printf_pointer (struct ui_file *stream, const char *format,
     }
 }
 
+static void
+printf_enum (struct ui_file *stream, struct value *value)
+{
+  struct type *type = check_typedef (value_type (value));
+
+  if (TYPE_CODE (type) == TYPE_CODE_ENUM)
+    {
+      val_print_enum_label (type, value_contents_for_printing (value),
+			    value_embedded_offset (value), stream);
+    }
+  else
+    {
+      error (_("\
+Value given for enum format specifier (%%q) is not of an enum type."));
+    }
+}
+
 /* printf "printf format string" ARG to STREAM.  */
 
 static void
@@ -2446,6 +2463,9 @@ ui_printf (const char *arg, struct ui_file *stream)
 	  case ptr_arg:
 	    printf_pointer (stream, current_substring, val_args[i]);
 	    break;
+	  case enum_arg:
+	    printf_enum (stream, val_args[i]);
+	    break;
 	  case literal_piece:
 	    /* Print a portion of the format string that has no
 	       directives.  Note that this will not include any
diff --git a/gdb/testsuite/gdb.base/printcmds.c b/gdb/testsuite/gdb.base/printcmds.c
index 57e04e6..bc7c981 100644
--- a/gdb/testsuite/gdb.base/printcmds.c
+++ b/gdb/testsuite/gdb.base/printcmds.c
@@ -90,7 +90,7 @@ struct some_arrays {
 
 struct some_arrays *parrays = &arrays;
 
-enum some_volatile_enum { enumvolval1, enumvolval2 };
+enum some_volatile_enum { enumvolval1, enumvolval2, enumvolval3, enumvolval4 };
 
 /* A volatile enum variable whose name is the same as the enumeration
    name.  See PR11827.  */
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index 3c78a53..4254581d 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -763,6 +763,16 @@ proc test_printf {} {
 	"" \
 	"create hibob command"
     gdb_test "hibob" "hi bob zzz.*y" "run hibob command"
+
+    # Test for enum printing (%q) in printf
+    gdb_test "printf \"%q\\n\", some_volatile_enum" "enumvolval1"
+    gdb_test "printf \"%q\\n\", enumvolval2" "enumvolval2"
+    gdb_test "printf \"%q\\n\", (enum some_volatile_enum) 1" "enumvolval2"
+    gdb_test "printf \"%q\\n\", (enum some_volatile_enum) 111" "111"
+    gdb_test "printf \"%q\\n\", three" "\\(ONE | TWO\\)"
+    gdb_test "printf \"%q\\n\", (enum flag_enum) 8" "\\(unknown: 8\\)"
+
+    gdb_test "printf \"%q\\n\", 2" "Value given for enum format specifier \\(%q\\) is not of an enum type."
 }
 
 #Test printing DFP values with printf
diff --git a/gdb/valprint.c b/gdb/valprint.c
index a9b03ec..fde48b4 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -526,35 +526,23 @@ generic_val_print_ref (struct type *type, const gdb_byte *valaddr,
     }
 }
 
-/* generic_val_print helper for TYPE_CODE_ENUM.  */
-
-static void
-generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
-			int embedded_offset, struct ui_file *stream,
-			const struct value *original_value,
-			const struct value_print_options *options)
+void
+val_print_enum_label (struct type *type, const gdb_byte *valaddr,
+		      int embedded_offset, struct ui_file *stream)
 {
-  unsigned int i;
-  unsigned int len;
-  LONGEST val;
+  int len = TYPE_NFIELDS (type);
+  int i;
   struct gdbarch *gdbarch = get_type_arch (type);
   int unit_size = gdbarch_addressable_memory_unit_size (gdbarch);
+  LONGEST val = unpack_long (type, valaddr + embedded_offset * unit_size);
 
-  if (options->format)
-    {
-      val_print_scalar_formatted (type, valaddr, embedded_offset,
-				  original_value, options, 0, stream);
-      return;
-    }
-  len = TYPE_NFIELDS (type);
-  val = unpack_long (type, valaddr + embedded_offset * unit_size);
   for (i = 0; i < len; i++)
     {
       QUIT;
       if (val == TYPE_FIELD_ENUMVAL (type, i))
-	{
-	  break;
-	}
+      {
+	break;
+      }
     }
   if (i < len)
     {
@@ -565,31 +553,31 @@ generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
       int first = 1;
 
       /* We have a "flag" enum, so we try to decompose it into
-	 pieces as appropriate.  A flag enum has disjoint
-	 constants by definition.  */
+       pieces as appropriate.  A flag enum has disjoint
+       constants by definition.  */
       fputs_filtered ("(", stream);
       for (i = 0; i < len; ++i)
-	{
-	  QUIT;
+      {
+	QUIT;
 
-	  if ((val & TYPE_FIELD_ENUMVAL (type, i)) != 0)
-	    {
-	      if (!first)
-		fputs_filtered (" | ", stream);
-	      first = 0;
+	if ((val & TYPE_FIELD_ENUMVAL (type, i)) != 0)
+	  {
+	    if (!first)
+	      fputs_filtered (" | ", stream);
+	    first = 0;
 
-	      val &= ~TYPE_FIELD_ENUMVAL (type, i);
-	      fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
-	    }
-	}
+	    val &= ~TYPE_FIELD_ENUMVAL (type, i);
+	    fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+	  }
+      }
 
       if (first || val != 0)
-	{
-	  if (!first)
-	    fputs_filtered (" | ", stream);
-	  fputs_filtered ("unknown: ", stream);
-	  print_longest (stream, 'd', 0, val);
-	}
+      {
+	if (!first)
+	  fputs_filtered (" | ", stream);
+	fputs_filtered ("unknown: ", stream);
+	print_longest (stream, 'd', 0, val);
+      }
 
       fputs_filtered (")", stream);
     }
@@ -597,6 +585,25 @@ generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
     print_longest (stream, 'd', 0, val);
 }
 
+/* generic_val_print helper for TYPE_CODE_ENUM.  */
+
+static void
+generic_val_print_enum (struct type *type, const gdb_byte *valaddr,
+			int embedded_offset, struct ui_file *stream,
+			const struct value *original_value,
+			const struct value_print_options *options)
+{
+  if (options->format)
+    {
+      val_print_scalar_formatted (type, valaddr, embedded_offset,
+				  original_value, options, 0, stream);
+    }
+  else
+    {
+      val_print_enum_label (type, valaddr, embedded_offset, stream);
+    }
+}
+
 /* generic_val_print helper for TYPE_CODE_FLAGS.  */
 
 static void
diff --git a/gdb/valprint.h b/gdb/valprint.h
index bb03024..97a22d5 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -170,6 +170,9 @@ extern void val_print_unavailable (struct ui_file *stream);
 
 extern void val_print_invalid_address (struct ui_file *stream);
 
+extern void val_print_enum_label (struct type *type, const gdb_byte *valaddr,
+				  int embedded_offset, struct ui_file *stream);
+
 /* An instance of this is passed to generic_val_print and describes
    some language-specific ways to print things.  */
 
