Imported Upstream version 2.4.3
[platform/upstream/gpg2.git] / g10 / keylist.c
index cc92d1a..8b7c597 100644 (file)
@@ -16,7 +16,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #ifdef HAVE_DOSISH_SYSTEM
-#include <fcntl.h>             /* for setmode() */
+# include <fcntl.h>            /* for setmode() */
 #endif
 
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "status.h"
+#include "../common/status.h"
 #include "keydb.h"
 #include "photoid.h"
-#include "util.h"
-#include "ttyio.h"
+#include "../common/util.h"
+#include "../common/ttyio.h"
 #include "trustdb.h"
 #include "main.h"
-#include "i18n.h"
-#include "status.h"
+#include "../common/i18n.h"
+#include "../common/status.h"
 #include "call-agent.h"
-#include "mbox-util.h"
-
-
-static void list_all (int, int);
-static void list_one (strlist_t names, int secret, int mark_secret);
-static void locate_one (ctrl_t ctrl, strlist_t names);
+#include "../common/mbox-util.h"
+#include "../common/zb32.h"
+#include "tofu.h"
+#include "../common/init.h"
+#include "../common/recsel.h"
+#include "../common/compliance.h"
+#include "../common/pkscreening.h"
+
+
+static void list_all (ctrl_t, int, int);
+static void list_one (ctrl_t ctrl,
+                      strlist_t names, int secret, int mark_secret);
+static void locate_one (ctrl_t ctrl, strlist_t names, int no_local);
 static void print_card_serialno (const char *serialno);
 
 struct keylist_context
@@ -57,17 +63,29 @@ struct keylist_context
   int inv_sigs;    /* Counter used if CHECK_SIGS is set.  */
   int no_key;      /* Counter used if CHECK_SIGS is set.  */
   int oth_err;     /* Counter used if CHECK_SIGS is set.  */
+  int no_validity; /* Do not show validity.  */
 };
 
-
-static void list_keyblock (kbnode_t keyblock, int secret, int has_secret,
-                           int fpr, struct keylist_context *listctx);
+/* An object and a global instance to store selectors created from
+ * --list-filter select=EXPR.
+ */
+struct list_filter_s
+{
+  recsel_expr_t selkey;
+};
+struct list_filter_s list_filter;
 
 
 /* The stream used to write attribute packets to.  */
 static estream_t attrib_fp;
 
 
+
+\f
+static void list_keyblock (ctrl_t ctrl,
+                           kbnode_t keyblock, int secret, int has_secret,
+                           int fpr, struct keylist_context *listctx);
+
 /* Release resources from a keylist context.  */
 static void
 keylist_context_release (struct keylist_context *listctx)
@@ -76,11 +94,55 @@ keylist_context_release (struct keylist_context *listctx)
 }
 
 
+static void
+release_list_filter (struct list_filter_s *filt)
+{
+  recsel_release (filt->selkey);
+  filt->selkey = NULL;
+}
+
+
+static void
+cleanup_keylist_globals (void)
+{
+  release_list_filter (&list_filter);
+}
+
+
+/* Parse and set an list filter from string.  STRING has the format
+ * "NAME=EXPR" with NAME being the name of the filter.  Spaces before
+ * and after NAME are not allowed.  If this function is all called
+ * several times all expressions for the same NAME are concatenated.
+ * Supported filter names are:
+ *
+ *  - select :: If the expression evaluates to true for a certain key
+ *              this key will be listed.  The expression may use any
+ *              variable defined for the export and import filters.
+ *
+ */
+gpg_error_t
+parse_and_set_list_filter (const char *string)
+{
+  gpg_error_t err;
+
+  /* Auto register the cleanup function.  */
+  register_mem_cleanup_func (cleanup_keylist_globals);
+
+  if (!strncmp (string, "select=", 7))
+    err = recsel_parse_expr (&list_filter.selkey, string+7);
+  else
+    err = gpg_error (GPG_ERR_INV_NAME);
+
+  return err;
+}
+
+
 /* List the keys.  If list is NULL, all available keys are listed.
-   With LOCATE_MODE set the locate algorithm is used to find a
-   key.  */
+ * With LOCATE_MODE set the locate algorithm is used to find a key; if
+ * in addition NO_LOCAL is set the locate does not look into the local
+ * keyring.  */
 void
-public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
+public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode, int no_local)
 {
 #ifndef NO_TRUST_MODELS
   if (opt.with_colons)
@@ -88,7 +150,7 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
       byte trust_model, marginals, completes, cert_depth, min_cert_level;
       ulong created, nextcheck;
 
-      read_trust_options (&trust_model, &created, &nextcheck,
+      read_trust_options (ctrl, &trust_model, &created, &nextcheck,
                          &marginals, &completes, &cert_depth, &min_cert_level);
 
       es_fprintf (es_stdout, "tru:");
@@ -97,7 +159,8 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
        es_fprintf (es_stdout, "o");
       if (trust_model != opt.trust_model)
        es_fprintf (es_stdout, "t");
-      if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC)
+      if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC
+         || opt.trust_model == TM_TOFU_PGP)
        {
          if (marginals != opt.marginals_needed)
            es_fprintf (es_stdout, "m");
@@ -126,14 +189,22 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
      is very bad for W32 because of a sharing violation. For real OSes
      it might lead to false results if we are later listing a keyring
      which is associated with the inode of a deleted file.  */
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
+
+#ifdef USE_TOFU
+  tofu_begin_batch_update (ctrl);
+#endif
 
   if (locate_mode)
-    locate_one (ctrl, list);
+    locate_one (ctrl, list, no_local);
   else if (!list)
-    list_all (0, opt.with_secret);
+    list_all (ctrl, 0, opt.with_secret);
   else
-    list_one (list, 0, opt.with_secret);
+    list_one (ctrl, list, 0, opt.with_secret);
+
+#ifdef USE_TOFU
+  tofu_end_batch_update (ctrl);
+#endif
 }
 
 
@@ -142,57 +213,86 @@ secret_key_list (ctrl_t ctrl, strlist_t list)
 {
   (void)ctrl;
 
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 
   if (!list)
-    list_all (1, 0);
+    list_all (ctrl, 1, 0);
   else                         /* List by user id */
-    list_one (list, 1, 0);
+    list_one (ctrl, list, 1, 0);
 }
 
-void
-print_seckey_info (PKT_public_key *pk)
+
+/* Helper for print_key_info and print_key_info_log.  */
+static char *
+format_key_info (ctrl_t ctrl, PKT_public_key *pk, int secret)
 {
   u32 keyid[2];
   char *p;
   char pkstrbuf[PUBKEY_STRING_SIZE];
+  char *result;
 
   keyid_from_pk (pk, keyid);
-  p = get_user_id_native (keyid);
 
-  tty_printf ("\nsec  %s/%s %s %s\n",
-              pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-             keystr (keyid), datestr_from_pk (pk), p);
+  /* If the pk was chosen by a particular user ID, that is the one to
+     print.  */
+  if (pk->user_id)
+    p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0);
+  else
+    p = get_user_id_native (ctrl, keyid);
 
+  result = xtryasprintf ("%s  %s/%s %s %s",
+                         secret? (pk->flags.primary? "sec":"ssb")
+                         /* */ : (pk->flags.primary? "pub":"sub"),
+                         pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
+                         keystr (keyid), datestr_from_pk (pk), p);
   xfree (p);
+  return result;
 }
 
-/* Print information about the public key.  With FP passed as NULL,
-   the tty output interface is used, otherwise output is directted to
-   the given stream.  */
+
+/* Print basic information about a public or secret key.  With FP
+ * passed as NULL, the tty output interface is used, otherwise output
+ * is directed to the given stream.  INDENT gives the requested
+ * indentation; if that is a negative value indentation is suppressed
+ * for the first line.  SECRET tells that the PK has a secret part.
+ * FIXME: This is similar in use to print_key_line and thus both
+ * functions should eventually be united.
+ */
 void
-print_pubkey_info (estream_t fp, PKT_public_key *pk)
+print_key_info (ctrl_t ctrl, estream_t fp,
+                int indent, PKT_public_key *pk, int secret)
 {
-  u32 keyid[2];
-  char *p;
-  char pkstrbuf[PUBKEY_STRING_SIZE];
+  int indentabs = indent >= 0? indent : -indent;
+  char *info;
 
-  keyid_from_pk (pk, keyid);
+  /* Note: Negative values for INDENT are not yet needed. */
 
-  /* If the pk was chosen by a particular user ID, that is the one to
-     print.  */
-  if (pk->user_id)
-    p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0);
-  else
-    p = get_user_id_native (keyid);
-
-  if (fp)
-    tty_printf ("\n");
-  tty_fprintf (fp, "%s  %s/%s %s %s\n",
-               pk->flags.primary? "pub":"sub",
-               pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-               keystr (keyid), datestr_from_pk (pk), p);
-  xfree (p);
+  info = format_key_info (ctrl, pk, secret);
+
+  if (!fp && indent >= 0)
+    tty_printf ("\n");  /* (Backward compatibility to old code) */
+  tty_fprintf (fp, "%*s%s\n", indentabs, "",
+               info? info : "[Ooops - out of core]");
+
+  xfree (info);
+}
+
+
+/* Same as print_key_info put print using the log functions at
+ * LOGLEVEL.  */
+void
+print_key_info_log (ctrl_t ctrl, int loglevel,
+                    int indent, PKT_public_key *pk, int secret)
+{
+  int indentabs = indent >= 0? indent : -indent;
+  char *info;
+
+  info = format_key_info (ctrl, pk, secret);
+
+  log_log (loglevel, "%*s%s\n", indentabs, "",
+           info? info : "[Ooops - out of core]");
+
+  xfree (info);
 }
 
 
@@ -224,7 +324,7 @@ print_card_key_info (estream_t fp, kbnode_t keyblock)
               log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
               s2k_char = '?';
             }
-          else if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
+          else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
             s2k_char = serialno? '>':' ';
           else
             s2k_char = '#';  /* Key not found.  */
@@ -261,6 +361,173 @@ print_card_key_info (estream_t fp, kbnode_t keyblock)
 #endif /*ENABLE_CARD_SUPPORT*/
 
 
+/* Print the preferences line.  Allowed values for MODE are:
+ *  -1 - print to the TTY
+ *   0 - print to stdout.
+ *   1 - use log_info
+ */
+void
+show_preferences (PKT_user_id *uid, int indent, int mode, int verbose)
+{
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
+  const prefitem_t fake = { 0, 0 };
+  const prefitem_t *prefs;
+  int i;
+
+  if (!uid)
+    return;
+
+  if (uid->prefs)
+    prefs = uid->prefs;
+  else if (verbose)
+    prefs = &fake;
+  else
+    return;
+
+  if (verbose)
+    {
+      int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0;
+
+      tty_fprintf (fp, "%*s %s", indent, "", _("Cipher: "));
+      for (i = any = 0; prefs[i].type; i++)
+       {
+         if (prefs[i].type == PREFTYPE_SYM)
+           {
+             if (any)
+               tty_fprintf (fp, ", ");
+             any = 1;
+             /* We don't want to display strings for experimental algos */
+             if (!openpgp_cipher_test_algo (prefs[i].value)
+                 && prefs[i].value < 100)
+               tty_fprintf (fp, "%s", openpgp_cipher_algo_name (prefs[i].value));
+             else
+               tty_fprintf (fp, "[%d]", prefs[i].value);
+             if (prefs[i].value == CIPHER_ALGO_3DES)
+               des_seen = 1;
+           }
+       }
+      if (!des_seen)
+       {
+         if (any)
+           tty_fprintf (fp, ", ");
+         tty_fprintf (fp, "%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES));
+       }
+      tty_fprintf (fp, "\n%*s %s", indent, "", _("AEAD: "));
+      for (i = any = 0; prefs[i].type; i++)
+       {
+         if (prefs[i].type == PREFTYPE_AEAD)
+           {
+             if (any)
+               tty_fprintf (fp, ", ");
+             any = 1;
+             /* We don't want to display strings for experimental algos */
+             if (!openpgp_aead_test_algo (prefs[i].value)
+                 && prefs[i].value < 100)
+               tty_fprintf (fp, "%s", openpgp_aead_algo_name (prefs[i].value));
+             else
+               tty_fprintf (fp, "[%d]", prefs[i].value);
+           }
+       }
+      tty_fprintf (fp, "\n%*s %s", indent, "", _("Digest: "));
+      for (i = any = 0; prefs[i].type; i++)
+       {
+         if (prefs[i].type == PREFTYPE_HASH)
+           {
+             if (any)
+               tty_fprintf (fp, ", ");
+             any = 1;
+             /* We don't want to display strings for experimental algos */
+             if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100)
+               tty_fprintf (fp, "%s", gcry_md_algo_name (prefs[i].value));
+             else
+               tty_fprintf (fp, "[%d]", prefs[i].value);
+             if (prefs[i].value == DIGEST_ALGO_SHA1)
+               sha1_seen = 1;
+           }
+       }
+      if (!sha1_seen)
+       {
+         if (any)
+           tty_fprintf (fp, ", ");
+         tty_fprintf (fp, "%s", gcry_md_algo_name (DIGEST_ALGO_SHA1));
+       }
+      tty_fprintf (fp, "\n%*s %s", indent, "", _("Compression: "));
+      for (i = any = 0; prefs[i].type; i++)
+       {
+         if (prefs[i].type == PREFTYPE_ZIP)
+           {
+             const char *s = compress_algo_to_string (prefs[i].value);
+
+             if (any)
+               tty_fprintf (fp, ", ");
+             any = 1;
+             /* We don't want to display strings for experimental algos */
+             if (s && prefs[i].value < 100)
+               tty_fprintf (fp, "%s", s);
+             else
+               tty_fprintf (fp, "[%d]", prefs[i].value);
+             if (prefs[i].value == COMPRESS_ALGO_NONE)
+               uncomp_seen = 1;
+           }
+       }
+      if (!uncomp_seen)
+       {
+         if (any)
+           tty_fprintf (fp, ", ");
+         else
+           {
+             tty_fprintf (fp, "%s",
+                           compress_algo_to_string (COMPRESS_ALGO_ZIP));
+             tty_fprintf (fp, ", ");
+           }
+         tty_fprintf (fp, "%s", compress_algo_to_string (COMPRESS_ALGO_NONE));
+       }
+      if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify)
+       {
+          tty_fprintf (fp, "\n%*s %s", indent, "", _("Features: "));
+         any = 0;
+         if (uid->flags.mdc)
+           {
+             tty_fprintf (fp, "MDC");
+             any = 1;
+           }
+         if (uid->flags.aead)
+           {
+             if (any)
+               tty_fprintf (fp, ", ");
+             tty_fprintf (fp, "AEAD");
+           }
+         if (!uid->flags.ks_modify)
+           {
+             if (any)
+               tty_fprintf (fp, ", ");
+             tty_fprintf (fp, _("Keyserver no-modify"));
+           }
+       }
+      tty_fprintf (fp, "\n");
+    }
+  else
+    {
+      tty_fprintf (fp, "%*s", indent, "");
+      for (i = 0; prefs[i].type; i++)
+        {
+          tty_fprintf (fp, " %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' :
+                       prefs[i].type == PREFTYPE_AEAD ? 'A' :
+                       prefs[i].type == PREFTYPE_HASH ? 'H' :
+                       prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?',
+                       prefs[i].value);
+        }
+      if (uid->flags.mdc)
+        tty_fprintf (fp, " [mdc]");
+      if (uid->flags.aead)
+        tty_fprintf (fp, " [aead]");
+      if (!uid->flags.ks_modify)
+        tty_fprintf (fp, " [no-ks-modify]");
+      tty_fprintf (fp, "\n");
+    }
+}
+
+
 /* Flags = 0x01 hashed 0x02 critical.  */
 static void
 status_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
@@ -280,6 +547,7 @@ status_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
 
 
 /* Print a policy URL.  Allowed values for MODE are:
+ *  -1 - print to the TTY
  *   0 - print to stdout.
  *   1 - use log_info and emit status messages.
  *   2 - emit only status messages.
@@ -290,95 +558,90 @@ show_policy_url (PKT_signature * sig, int indent, int mode)
   const byte *p;
   size_t len;
   int seq = 0, crit;
-  estream_t fp = mode ? log_get_stream () : es_stdout;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
 
-  while ((p =
-         enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit)))
+  while ((p = enum_sig_subpkt (sig, 1, SIGSUBPKT_POLICY, &len, &seq, &crit)))
     {
       if (mode != 2)
        {
-         int i;
          const char *str;
 
-         for (i = 0; i < indent; i++)
-           es_putc (' ', fp);
+          tty_fprintf (fp, "%*s", indent, "");
 
          if (crit)
            str = _("Critical signature policy: ");
          else
            str = _("Signature policy: ");
-         if (mode)
+         if (mode > 0)
            log_info ("%s", str);
          else
-           es_fprintf (fp, "%s", str);
-         print_utf8_buffer (fp, p, len);
-         es_fprintf (fp, "\n");
+           tty_fprintf (fp, "%s", str);
+         tty_print_utf8_string2 (fp, p, len, 0);
+         tty_fprintf (fp, "\n");
        }
 
-      if (mode)
+      if (mode > 0)
        write_status_buffer (STATUS_POLICY_URL, p, len, 0);
     }
 }
 
 
-/*
-  mode=0 for stdout.
-  mode=1 for log_info + status messages
-  mode=2 for status messages only
-*/
-/* TODO: use this */
+/* Print a keyserver URL.  Allowed values for MODE are:
+ *  -1 - print to the TTY
+ *   0 - print to stdout.
+ *   1 - use log_info and emit status messages.
+ *   2 - emit only status messages.
+ */
 void
 show_keyserver_url (PKT_signature * sig, int indent, int mode)
 {
   const byte *p;
   size_t len;
   int seq = 0, crit;
-  estream_t fp = mode ? log_get_stream () : es_stdout;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
 
-  while ((p =
-         enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq,
-                          &crit)))
+  while ((p = enum_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, &len, &seq, &crit)))
     {
       if (mode != 2)
        {
-         int i;
          const char *str;
 
-         for (i = 0; i < indent; i++)
-           es_putc (' ', es_stdout);
+          tty_fprintf (fp, "%*s", indent, "");
 
          if (crit)
            str = _("Critical preferred keyserver: ");
          else
            str = _("Preferred keyserver: ");
-         if (mode)
+         if (mode > 0)
            log_info ("%s", str);
          else
-           es_fprintf (es_stdout, "%s", str);
-         print_utf8_buffer (fp, p, len);
-         es_fprintf (fp, "\n");
+           tty_fprintf (fp, "%s", str);
+         tty_print_utf8_string2 (fp, p, len, 0);
+         tty_fprintf (fp, "\n");
        }
 
-      if (mode)
+      if (mode > 0)
        status_one_subpacket (SIGSUBPKT_PREF_KS, len,
                              (crit ? 0x02 : 0) | 0x01, p);
     }
 }
 
-/*
-  mode=0 for stdout.
-  mode=1 for log_info + status messages
-  mode=2 for status messages only
-
-  Defined bits in WHICH:
-    1 == standard notations
-    2 == user notations
-*/
+
+/* Print notation data.  Allowed values for MODE are:
+ *  -1 - print to the TTY
+ *   0 - print to stdout.
+ *   1 - use log_info and emit status messages.
+ *   2 - emit only status messages.
+ *
+ * Defined bits in WHICH:
+ *   1 - standard notations
+ *   2 - user notations
+ */
 void
 show_notation (PKT_signature * sig, int indent, int mode, int which)
 {
-  estream_t fp = mode ? log_get_stream () : es_stdout;
-  struct notation *nd, *notations;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
+  notation_t nd, notations;
 
   if (which == 0)
     which = 3;
@@ -394,39 +657,45 @@ show_notation (PKT_signature * sig, int indent, int mode, int which)
 
          if ((which & 1 && !has_at) || (which & 2 && has_at))
            {
-             int i;
              const char *str;
 
-             for (i = 0; i < indent; i++)
-               es_putc (' ', es_stdout);
+              tty_fprintf (fp, "%*s", indent, "");
 
              if (nd->flags.critical)
                str = _("Critical signature notation: ");
              else
                str = _("Signature notation: ");
-             if (mode)
+             if (mode > 0)
                log_info ("%s", str);
              else
-               es_fprintf (es_stdout, "%s", str);
+               tty_fprintf (fp, "%s", str);
              /* This is all UTF8 */
-             print_utf8_buffer (fp, nd->name, strlen (nd->name));
-             es_fprintf (fp, "=");
-             print_utf8_buffer (fp, nd->value, strlen (nd->value));
+             tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0);
+             tty_fprintf (fp, "=");
+             tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0);
               /* (We need to use log_printf so that the next call to a
                   log function does not insert an extra LF.)  */
-              if (mode)
+              if (mode > 0)
                 log_printf ("\n");
               else
-                es_putc ('\n', fp);
+                tty_fprintf (fp, "\n");
            }
        }
 
-      if (mode)
+      if (mode > 0)
        {
          write_status_buffer (STATUS_NOTATION_NAME,
                               nd->name, strlen (nd->name), 0);
-         write_status_buffer (STATUS_NOTATION_DATA,
-                              nd->value, strlen (nd->value), 50);
+          if (nd->flags.critical || nd->flags.human)
+            write_status_text (STATUS_NOTATION_FLAGS,
+                               nd->flags.critical && nd->flags.human? "1 1" :
+                               nd->flags.critical? "1 0" : "0 1");
+          if (!nd->flags.human && nd->bdat && nd->blen)
+            write_status_buffer (STATUS_NOTATION_DATA,
+                                 nd->bdat, nd->blen, 250);
+          else
+            write_status_buffer (STATUS_NOTATION_DATA,
+                                 nd->value, strlen (nd->value), 50);
        }
     }
 
@@ -440,25 +709,27 @@ print_signature_stats (struct keylist_context *s)
   if (!s->check_sigs)
     return;  /* Signature checking was not requested.  */
 
-  if (s->good_sigs == 1)
-    log_info (_("1 good signature\n"));
-  else if (s->good_sigs)
-    log_info (_("%d good signatures\n"), s->good_sigs);
-
-  if (s->inv_sigs == 1)
-    log_info (_("1 bad signature\n"));
-  else if (s->inv_sigs)
-    log_info (_("%d bad signatures\n"), s->inv_sigs);
-
-  if (s->no_key == 1)
-    log_info (_("1 signature not checked due to a missing key\n"));
-  else if (s->no_key)
-    log_info (_("%d signatures not checked due to missing keys\n"), s->no_key);
-
-  if (s->oth_err == 1)
-    log_info (_("1 signature not checked due to an error\n"));
-  else if (s->oth_err)
-    log_info (_("%d signatures not checked due to errors\n"), s->oth_err);
+  /* Better flush stdout so that the stats are always printed after
+   * the output.  */
+  es_fflush (es_stdout);
+
+  if (s->good_sigs)
+    log_info (ngettext("%d good signature\n",
+                       "%d good signatures\n", s->good_sigs), s->good_sigs);
+
+  if (s->inv_sigs)
+    log_info (ngettext("%d bad signature\n",
+                       "%d bad signatures\n", s->inv_sigs), s->inv_sigs);
+
+  if (s->no_key)
+    log_info (ngettext("%d signature not checked due to a missing key\n",
+                       "%d signatures not checked due to missing keys\n",
+                       s->no_key), s->no_key);
+
+  if (s->oth_err)
+    log_info (ngettext("%d signature not checked due to an error\n",
+                       "%d signatures not checked due to errors\n",
+                       s->oth_err), s->oth_err);
 }
 
 
@@ -466,7 +737,7 @@ print_signature_stats (struct keylist_context *s)
    MARK_SECRET is true secret keys are indicated in a public key
    listing.  */
 static void
-list_all (int secret, int mark_secret)
+list_all (ctrl_t ctrl, int secret, int mark_secret)
 {
   KEYDB_HANDLE hd;
   KBNODE keyblock = NULL;
@@ -479,9 +750,9 @@ list_all (int secret, int mark_secret)
   if (opt.check_sigs)
     listctx.check_sigs = 1;
 
-  hd = keydb_new ();
+  hd = keydb_new (ctrl);
   if (!hd)
-    rc = gpg_error (GPG_ERR_GENERAL);
+    rc = gpg_error_from_syserror ();
   else
     rc = keydb_search_first (hd);
   if (rc)
@@ -494,7 +765,11 @@ list_all (int secret, int mark_secret)
   lastresname = NULL;
   do
     {
+      if (secret)
+        glo_ctrl.silence_parse_warnings++;
       rc = keydb_get_keyblock (hd, &keyblock);
+      if (secret)
+        glo_ctrl.silence_parse_warnings--;
       if (rc)
        {
           if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY)
@@ -504,7 +779,7 @@ list_all (int secret, int mark_secret)
        }
 
       if (secret || mark_secret)
-        any_secret = !agent_probe_any_secret_key (NULL, keyblock);
+        any_secret = !agent_probe_any_secret_key (ctrl, keyblock);
       else
         any_secret = 0;
 
@@ -512,7 +787,7 @@ list_all (int secret, int mark_secret)
         ; /* Secret key listing requested but this isn't one.  */
       else
         {
-          if (!opt.with_colons)
+          if (!opt.with_colons && !(opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
             {
               resname = keydb_get_resource_name (hd);
               if (lastresname != resname)
@@ -526,8 +801,8 @@ list_all (int secret, int mark_secret)
                   lastresname = resname;
                 }
             }
-          merge_keys_and_selfsig (keyblock);
-          list_keyblock (keyblock, secret, any_secret, opt.fingerprint,
+          merge_keys_and_selfsig (ctrl, keyblock);
+          list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint,
                          &listctx);
         }
       release_kbnode (keyblock);
@@ -538,7 +813,9 @@ list_all (int secret, int mark_secret)
   if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
     log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
   if (keydb_get_skipped_counter (hd))
-    log_info (_("Warning: %lu key(s) skipped due to their large size\n"),
+    log_info (ngettext("Warning: %lu key skipped due to its large size\n",
+                       "Warning: %lu keys skipped due to their large sizes\n",
+                       keydb_get_skipped_counter (hd)),
               keydb_get_skipped_counter (hd));
 
   if (opt.check_sigs && !opt.with_colons)
@@ -552,11 +829,12 @@ list_all (int secret, int mark_secret)
 
 
 static void
-list_one (strlist_t names, int secret, int mark_secret)
+list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret)
 {
   int rc = 0;
   KBNODE keyblock = NULL;
   GETKEY_CTX ctx;
+  int any_secret;
   const char *resname;
   const char *keyring_str = _("Keyring");
   int i;
@@ -567,7 +845,7 @@ list_one (strlist_t names, int secret, int mark_secret)
     listctx.check_sigs = 1;
 
   /* fixme: using the bynames function has the disadvantage that we
-   * don't know wether one of the names given was not found.  OTOH,
+   * don't know whether one of the names given was not found.  OTOH,
    * this function has the advantage to list the names in the
    * sequence as defined by the keyDB and does not duplicate
    * outputs.  A solution could be do test whether all given have
@@ -575,29 +853,47 @@ list_one (strlist_t names, int secret, int mark_secret)
    * functions) or to have the search function return indicators for
    * found names.  Yet another way is to use the keydb search
    * facilities directly. */
-  rc = getkey_bynames (&ctx, NULL, names, secret, &keyblock);
+  rc = getkey_bynames (ctrl, &ctx, NULL, names, secret, &keyblock);
   if (rc)
     {
       log_error ("error reading key: %s\n", gpg_strerror (rc));
-      get_pubkey_end (ctx);
+      getkey_end (ctrl, ctx);
+      write_status_error ("keylist.getkey", rc);
       return;
     }
 
   do
     {
-      if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons)
+      /* getkey_bynames makes sure that only secret keys are returned
+       * if requested, thus we do not need to test again.  With
+       * MARK_SECRET set (ie. option --with-secret) we have to test
+       * for a secret key, though.  */
+      if (secret)
+        any_secret = 1;
+      else if (mark_secret)
+        any_secret = !agent_probe_any_secret_key (ctrl, keyblock);
+      else
+        any_secret = 0;
+
+      if (secret && !any_secret)
+        ;/* Secret key listing requested but getkey_bynames failed.  */
+      else
         {
-          resname = keydb_get_resource_name (get_ctx_handle (ctx));
-          es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname);
-          for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--)
-            es_putc ('-', es_stdout);
-          es_putc ('\n', es_stdout);
+          if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons)
+            {
+              resname = keydb_get_resource_name (get_ctx_handle (ctx));
+              es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname);
+              for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--)
+                es_putc ('-', es_stdout);
+              es_putc ('\n', es_stdout);
+            }
+          list_keyblock (ctrl, keyblock, secret, any_secret,
+                         opt.fingerprint, &listctx);
         }
-      list_keyblock (keyblock, secret, mark_secret, opt.fingerprint, &listctx);
       release_kbnode (keyblock);
     }
-  while (!getkey_next (ctx, NULL, &keyblock));
-  getkey_end (ctx);
+  while (!getkey_next (ctrl, ctx, NULL, &keyblock));
+  getkey_end (ctrl, ctx);
 
   if (opt.check_sigs && !opt.with_colons)
     print_signature_stats (&listctx);
@@ -607,7 +903,7 @@ list_one (strlist_t names, int secret, int mark_secret)
 
 
 static void
-locate_one (ctrl_t ctrl, strlist_t names)
+locate_one (ctrl_t ctrl, strlist_t names, int no_local)
 {
   int rc = 0;
   strlist_t sl;
@@ -621,7 +917,10 @@ locate_one (ctrl_t ctrl, strlist_t names)
 
   for (sl = names; sl; sl = sl->next)
     {
-      rc = get_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, NULL, 1, 0);
+      rc = get_best_pubkey_byname (ctrl,
+                                   no_local? GET_PUBKEY_NO_LOCAL
+                                   /*    */: GET_PUBKEY_NORMAL,
+                                   &ctx, NULL, sl->d, &keyblock, 1);
       if (rc)
        {
          if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY)
@@ -634,11 +933,11 @@ locate_one (ctrl_t ctrl, strlist_t names)
        {
          do
            {
-             list_keyblock (keyblock, 0, 0, opt.fingerprint, &listctx);
+             list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx);
              release_kbnode (keyblock);
            }
-         while (ctx && !get_pubkey_next (ctx, NULL, &keyblock));
-         get_pubkey_end (ctx);
+         while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock));
+         getkey_end (ctrl, ctx);
          ctx = NULL;
        }
     }
@@ -665,8 +964,39 @@ print_key_data (PKT_public_key * pk)
     }
 }
 
+
+/* Various public key screenings.  (Right now just ROCA).  With
+ * COLON_MODE set the output is formatted for use in the compliance
+ * field of a colon listing.
+ */
+static void
+print_pk_screening (PKT_public_key *pk, int colon_mode)
+{
+  gpg_error_t err;
+
+  if (is_RSA (pk->pubkey_algo) && pubkey_get_npkey (pk->pubkey_algo))
+    {
+      err = screen_key_for_roca (pk->pkey[0]);
+      if (!err)
+        ;
+      else if (gpg_err_code (err) == GPG_ERR_TRUE)
+        {
+          if (colon_mode)
+            es_fprintf (es_stdout, colon_mode > 1? " %d":"%d", 6001);
+          else
+            es_fprintf (es_stdout,
+                        "      Screening: ROCA vulnerability detected\n");
+        }
+      else if (!colon_mode)
+        es_fprintf (es_stdout, "      Screening: [ROCA check failed: %s]\n",
+                    gpg_strerror (err));
+    }
+
+}
+
+
 static void
-print_capabilities (PKT_public_key *pk, KBNODE keyblock)
+print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock)
 {
   unsigned int use = pk->pubkey_usage;
   int c_printed = 0;
@@ -694,6 +1024,13 @@ print_capabilities (PKT_public_key *pk, KBNODE keyblock)
   if ((use & PUBKEY_USAGE_AUTH))
     es_putc ('a', es_stdout);
 
+  if (use & PUBKEY_USAGE_RENC)
+    es_putc ('r', es_stdout);
+  if ((use & PUBKEY_USAGE_TIME))
+    es_putc ('t', es_stdout);
+  if ((use & PUBKEY_USAGE_GROUP))
+    es_putc ('g', es_stdout);
+
   if ((use & PUBKEY_USAGE_UNKNOWN))
     es_putc ('?', es_stdout);
 
@@ -774,7 +1111,7 @@ print_subpackets_colon (PKT_signature * sig)
 {
   byte *i;
 
-  assert (opt.show_subpackets);
+  log_assert (opt.show_subpackets);
 
   for (i = opt.show_subpackets; *i; i++)
     {
@@ -784,12 +1121,12 @@ print_subpackets_colon (PKT_signature * sig)
 
       seq = 0;
 
-      while ((p = enum_sig_subpkt (sig->hashed, *i, &len, &seq, &crit)))
+      while ((p = enum_sig_subpkt (sig, 1, *i, &len, &seq, &crit)))
        print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p);
 
       seq = 0;
 
-      while ((p = enum_sig_subpkt (sig->unhashed, *i, &len, &seq, &crit)))
+      while ((p = enum_sig_subpkt (sig, 0, *i, &len, &seq, &crit)))
        print_one_subpacket (*i, len, 0x00 | (crit ? 0x02 : 0), p);
     }
 }
@@ -823,9 +1160,8 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
                   (ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1,
                   uid->numattribs, (ulong) uid->created,
                   (ulong) uid->expiredate,
-                  ((uid->is_primary ? 0x01 : 0) | (uid->
-                                                   is_revoked ? 0x02 : 0) |
-                   (uid->is_expired ? 0x04 : 0)));
+                  ((uid->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) |
+                   (uid->flags.expired ? 0x04 : 0)));
          write_status_text (STATUS_ATTRIBUTE, buf);
        }
 
@@ -835,169 +1171,270 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
 }
 
 
-/* Print IPGP cert records instead of a standard key listing.  */
-static void
-list_keyblock_pka (kbnode_t keyblock)
+/* Order two signatures.  We first order by keyid and then by creation
+ * time.  */
+int
+cmp_signodes (const void *av, const void *bv)
 {
-  kbnode_t kbctx;
-  kbnode_t node;
-  PKT_public_key *pk;
-  char pkstrbuf[PUBKEY_STRING_SIZE];
-  char *hexfpr;
-
-  /* Get the keyid from the keyblock.  */
-  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
-  if (!node)
-    {
-      log_error ("Oops; key lost!\n");
-      dump_kbnode (keyblock);
-      return;
-    }
-
-  pk = node->pkt->pkt.public_key;
-
-  es_fprintf (es_stdout, ";; pub  %s/%s %s\n;; ",
-              pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-              keystr_from_pk (pk), datestr_from_pk (pk));
-  print_fingerprint (NULL, pk, 10);
-  hexfpr = hexfingerprint (pk);
-
-  for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
-    {
-      if (node->pkt->pkttype == PKT_USER_ID)
-       {
-         PKT_user_id *uid = node->pkt->pkt.user_id;
-          char *mbox;
-          char *p;
-
-         if (pk && (uid->is_expired || uid->is_revoked)
-             && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
-            continue;
-
-          es_fputs (";; uid  ", es_stdout);
-          print_utf8_buffer (es_stdout, uid->name, uid->len);
-         es_putc ('\n', es_stdout);
-          mbox = mailbox_from_userid (uid->name);
-          if (mbox && (p = strchr (mbox, '@')))
-            {
-              char hashbuf[20];
-              char *hash;
-              unsigned int len;
-
-              *p++ = 0;
-              es_fprintf (es_stdout, "$ORIGIN _pka.%s.\n", p);
-              gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox));
-              hash = zb32_encode (hashbuf, 8*20);
-              if (hash)
-                {
-                  len = strlen (hexfpr)/2;
-                  es_fprintf (es_stdout,
-                              "%s TYPE37 \\# %u 0006 0000 00 %02X %s\n",
-                              hash, 6 + len, len, hexfpr);
-                  xfree (hash);
-                }
-            }
-          xfree (mbox);
-       }
-
-    }
-  es_putc ('\n', es_stdout);
+  const kbnode_t an = *(const kbnode_t *)av;
+  const kbnode_t bn = *(const kbnode_t *)bv;
+  const PKT_signature *a;
+  const PKT_signature *b;
+  int i;
 
-  xfree (hexfpr);
+  /* log_assert (an->pkt->pkttype == PKT_SIGNATURE); */
+  /* log_assert (bn->pkt->pkttype == PKT_SIGNATURE); */
+
+  a = an->pkt->pkt.signature;
+  b = bn->pkt->pkt.signature;
+
+  /* Self-signatures are ordered first.  */
+  if ((an->flag & NODFLG_MARK_B) && !(bn->flag & NODFLG_MARK_B))
+    return -1;
+  if (!(an->flag & NODFLG_MARK_B) && (bn->flag & NODFLG_MARK_B))
+    return 1;
+
+  /* then the keyids.  (which are or course the same for self-sigs). */
+  i = keyid_cmp (a->keyid, b->keyid);
+  if (i)
+    return i;
+
+  /* Followed by creation time */
+  if (a->timestamp > b->timestamp)
+    return 1;
+  if (a->timestamp < b->timestamp)
+    return -1;
+
+  /* followed by the class in a way that a rev comes first.  */
+  if (a->sig_class > b->sig_class)
+    return 1;
+  if (a->sig_class < b->sig_class)
+    return -1;
+
+  /* To make the sort stable we compare the entire structure as last resort.  */
+  return memcmp (a, b, sizeof *a);
 }
 
 
+/* Helper for list_keyblock_print.  The caller must have set
+ * NODFLG_MARK_B to indicate self-signatures.  */
 static void
-list_keyblock_print (KBNODE keyblock, int secret, int fpr,
-                     struct keylist_context *listctx)
+list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node,
+                      struct keylist_context *listctx)
 {
-  int rc;
-  KBNODE kbctx;
-  KBNODE node;
-  PKT_public_key *pk;
-  int skip_sigs = 0;
-  int s2k_char;
-  char *hexgrip = NULL;
-  char *serialno = NULL;
-  char pkstrbuf[PUBKEY_STRING_SIZE];
+          /* (extra indentation to keep the diff history short)  */
+         PKT_signature *sig = node->pkt->pkt.signature;
+         int rc, sigrc;
+         char *sigstr;
+          char *reason_text = NULL;
+          char *reason_comment = NULL;
+          size_t reason_commentlen;
+          int reason_code = 0;
 
-  /* Get the keyid from the keyblock.  */
-  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
-  if (!node)
-    {
-      log_error ("Oops; key lost!\n");
-      dump_kbnode (keyblock);
-      return;
-    }
+         if (listctx->check_sigs)
+           {
+             rc = check_key_signature (ctrl, keyblock, node, NULL);
+             switch (gpg_err_code (rc))
+               {
+               case 0:
+                 listctx->good_sigs++;
+                 sigrc = '!';
+                 break;
+               case GPG_ERR_BAD_SIGNATURE:
+                 listctx->inv_sigs++;
+                 sigrc = '-';
+                 break;
+               case GPG_ERR_NO_PUBKEY:
+               case GPG_ERR_UNUSABLE_PUBKEY:
+                 listctx->no_key++;
+                 return;
+                case GPG_ERR_DIGEST_ALGO:
+                case GPG_ERR_PUBKEY_ALGO:
+                  if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS))
+                    return;
+                  /* fallthru. */
+               default:
+                 listctx->oth_err++;
+                 sigrc = '%';
+                 break;
+               }
 
-  pk = node->pkt->pkt.public_key;
+             /* TODO: Make sure a cached sig record here still has
+                the pk that issued it.  See also
+                keyedit.c:print_and_check_one_sig */
+           }
+         else
+           {
+              if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)
+                  && (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo)
+                                    == GPG_ERR_PUBKEY_ALGO)
+                      || gpg_err_code (openpgp_md_test_algo (sig->digest_algo)
+                                       == GPG_ERR_DIGEST_ALGO)
+                      || (sig->digest_algo == DIGEST_ALGO_SHA1
+                          && !(node->flag & NODFLG_MARK_B) /*no selfsig*/
+                          && !opt.flags.allow_weak_key_signatures)))
+                return;
+             rc = 0;
+             sigrc = ' ';
+           }
 
-  if (secret || opt.with_keygrip)
-    {
-      rc = hexkeygrip_from_pk (pk, &hexgrip);
-      if (rc)
-        log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
-    }
+         if (sig->sig_class == 0x20 || sig->sig_class == 0x28
+             || sig->sig_class == 0x30)
+            {
+              sigstr = "rev";
+              reason_code = get_revocation_reason (sig, &reason_text,
+                                                   &reason_comment,
+                                                   &reason_commentlen);
+            }
+         else if ((sig->sig_class & ~3) == 0x10)
+           sigstr = "sig";
+         else if (sig->sig_class == 0x18)
+           sigstr = "sig";
+         else if (sig->sig_class == 0x1F)
+           sigstr = "sig";
+         else
+           {
+             es_fprintf (es_stdout, "sig                             "
+                     "[unexpected signature class 0x%02x]\n",
+                     sig->sig_class);
+             return;
+           }
 
-  if (secret)
-    {
-      if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
-        s2k_char = serialno? '>':' ';
-      else
-        s2k_char = '#';  /* Key not found.  */
-    }
-  else
-    s2k_char = ' ';
+         es_fputs (sigstr, es_stdout);
+         es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s",
+                 sigrc, (sig->sig_class - 0x10 > 0 &&
+                         sig->sig_class - 0x10 <
+                         4) ? '0' + sig->sig_class - 0x10 : ' ',
+                 sig->flags.exportable ? ' ' : 'L',
+                 sig->flags.revocable ? ' ' : 'R',
+                 sig->flags.policy_url ? 'P' : ' ',
+                 sig->flags.notation ? 'N' : ' ',
+                 sig->flags.expired ? 'X' : ' ',
+                 (sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
+                                                 0) ? '0' +
+                 sig->trust_depth : ' ', keystr (sig->keyid),
+                 datestr_from_sig (sig));
+         if (opt.list_options & LIST_SHOW_SIG_EXPIRE)
+           es_fprintf (es_stdout, " %s", expirestr_from_sig (sig));
+         es_fprintf (es_stdout, "  ");
+         if (sigrc == '%')
+           es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
+         else if (sigrc == '?')
+           ;
+         else if ((node->flag & NODFLG_MARK_B))
+            es_fputs (_("[self-signature]"), es_stdout);
+          else if (!opt.fast_list_mode )
+           {
+             size_t n;
+             char *p = get_user_id (ctrl, sig->keyid, &n, NULL);
+             print_utf8_buffer (es_stdout, p, n);
+             xfree (p);
+           }
+         es_putc ('\n', es_stdout);
 
-  check_trustdb_stale ();
+         if (sig->flags.policy_url
+             && (opt.list_options & LIST_SHOW_POLICY_URLS))
+           show_policy_url (sig, 3, 0);
+
+         if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS))
+           show_notation (sig, 3, 0,
+                          ((opt.
+                            list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0)
+                          +
+                          ((opt.
+                            list_options & LIST_SHOW_USER_NOTATIONS) ? 2 :
+                           0));
+
+         if (sig->flags.pref_ks
+             && (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
+           show_keyserver_url (sig, 3, 0);
 
+          if (reason_text && (reason_code || reason_comment))
+            {
+              es_fprintf (es_stdout, "      %s%s\n",
+                          _("reason for revocation: "), reason_text);
+              if (reason_comment)
+                {
+                  const byte *s, *s_lf;
+                  size_t n, n_lf;
+
+                  s = reason_comment;
+                  n = reason_commentlen;
+                  s_lf = NULL;
+                  do
+                    {
+                      /* We don't want any empty lines, so we skip them.  */
+                      for (;n && *s == '\n'; s++, n--)
+                        ;
+                      if (n)
+                        {
+                          s_lf = memchr (s, '\n', n);
+                          n_lf = s_lf? s_lf - s : n;
+                          es_fprintf (es_stdout, "         %s",
+                                      _("revocation comment: "));
+                          es_write_sanitized (es_stdout, s, n_lf, NULL, NULL);
+                          es_putc ('\n', es_stdout);
+                          s += n_lf; n -= n_lf;
+                        }
+                    } while (s_lf);
+                }
+            }
 
-  es_fprintf (es_stdout, "%s%c  %s/%s %s",
-              secret? "sec":"pub",
-              s2k_char,
-              pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-              keystr_from_pk (pk), datestr_from_pk (pk));
+          xfree (reason_text);
+          xfree (reason_comment);
 
-  if ((opt.list_options & LIST_SHOW_USAGE))
-    {
-      es_fprintf (es_stdout, " [%s]", usagestr_from_pk (pk, 0));
-    }
-  if (pk->flags.revoked)
-    {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
-    }
-  else if (pk->has_expired)
+         /* fixme: check or list other sigs here */
+}
+
+
+static void
+list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
+                     struct keylist_context *listctx)
+{
+  int rc;
+  kbnode_t node;
+  PKT_public_key *pk;
+  u32 *mainkid;
+  int skip_sigs = 0;
+  char *hexgrip = NULL;
+  char *serialno = NULL;
+
+  /* Get the keyid from the keyblock.  */
+  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
+  if (!node)
     {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
+      log_error ("Oops; key lost!\n");
+      dump_kbnode (keyblock);
+      return;
     }
-  else if (pk->expiredate)
+
+  pk = node->pkt->pkt.public_key;
+  mainkid = pk_keyid (pk);
+
+  if (secret || opt.with_keygrip)
     {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
+      rc = hexkeygrip_from_pk (pk, &hexgrip);
+      if (rc)
+        log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
     }
 
-#if 0
-  /* I need to think about this some more.  It's easy enough to
-     include, but it looks sort of confusing in the listing... */
-  if (opt.list_options & LIST_SHOW_VALIDITY)
+  if (secret)
     {
-      int validity = get_validity (pk, NULL);
-      es_fprintf (es_stdout, " [%s]", trust_value_to_string (validity));
+      /* Encode some info about the secret key in SECRET.  */
+      if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
+        secret = serialno? 3 : 1;
+      else
+        secret = 2;  /* Key not found.  */
     }
-#endif
 
-  if (pk->pubkey_algo >= 100)
-    es_fprintf (es_stdout, " [experimental algorithm %d]", pk->pubkey_algo);
+  if (!listctx->no_validity)
+    check_trustdb_stale (ctrl);
 
-  es_fprintf (es_stdout, "\n");
+  /* Print the "pub" line and in KF_NONE mode the fingerprint.  */
+  print_key_line (ctrl, es_stdout, pk, secret);
 
   if (fpr)
-    print_fingerprint (NULL, pk, 0);
+    print_fingerprint (ctrl, NULL, pk, 0);
 
   if (opt.with_keygrip && hexgrip)
     es_fprintf (es_stdout, "      Keygrip = %s\n", hexgrip);
@@ -1008,13 +1445,35 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
   if (opt.with_key_data)
     print_key_data (pk);
 
-  for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
+  if (opt.with_key_screening)
+    print_pk_screening (pk, 0);
+
+  if (opt.with_key_origin
+      && (pk->keyorg || pk->keyupdate || pk->updateurl))
     {
+      char updatestr[MK_DATESTR_SIZE];
+
+      es_fprintf (es_stdout, "      origin=%s last=%s %s",
+                  key_origin_string (pk->keyorg),
+                  mk_datestr (updatestr, sizeof updatestr, pk->keyupdate),
+                  pk->updateurl? "url=":"");
+      if (pk->updateurl)
+        print_utf8_string (es_stdout, pk->updateurl);
+      es_putc ('\n', es_stdout);
+    }
+
+  for (node = keyblock; node; node = node->next)
+    {
+      if (is_deleted_kbnode (node))
+        continue;
+
       if (node->pkt->pkttype == PKT_USER_ID)
        {
          PKT_user_id *uid = node->pkt->pkt.user_id;
+          int indent;
+          int kl = opt.keyid_format == KF_NONE? 10 : keystrlen ();
 
-         if ((uid->is_expired || uid->is_revoked)
+         if ((uid->flags.expired || uid->flags.revoked)
              && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
            {
              skip_sigs = 1;
@@ -1026,31 +1485,74 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
          if (attrib_fp && uid->attrib_data != NULL)
            dump_attribs (uid, pk);
 
-         if ((uid->is_revoked || uid->is_expired)
-             || (opt.list_options & LIST_SHOW_UID_VALIDITY))
+         if ((uid->flags.revoked || uid->flags.expired)
+             || ((opt.list_options & LIST_SHOW_UID_VALIDITY)
+                  && !listctx->no_validity))
            {
              const char *validity;
-             int indent;
-
-             validity = uid_trust_string_fixed (pk, uid);
-             indent =
-               (keystrlen () + (opt.legacy_list_mode? 9:11)) -
-               atoi (uid_trust_string_fixed (NULL, NULL));
 
+             validity = uid_trust_string_fixed (ctrl, pk, uid);
+             indent = ((kl + (opt.legacy_list_mode? 9:11))
+                        - atoi (uid_trust_string_fixed (ctrl, NULL, NULL)));
              if (indent < 0 || indent > 40)
                indent = 0;
 
              es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity);
            }
          else
-           es_fprintf (es_stdout, "uid%*s",
-                        (int) keystrlen () + (opt.legacy_list_mode? 10:12), "");
+            {
+              indent = kl + (opt.legacy_list_mode? 10:12);
+              es_fprintf (es_stdout, "uid%*s", indent, "");
+            }
 
          print_utf8_buffer (es_stdout, uid->name, uid->len);
          es_putc ('\n', es_stdout);
 
+          if ((opt.list_options & LIST_SHOW_PREF_VERBOSE))
+            show_preferences (uid, indent+2, 0, 1);
+          else if ((opt.list_options & LIST_SHOW_PREF))
+            show_preferences (uid, indent+2, 0, 0);
+
+          if (opt.with_wkd_hash)
+            {
+              char *mbox, *hash, *p;
+              char hashbuf[32];
+
+              mbox = mailbox_from_userid (uid->name, 0);
+              if (mbox && (p = strchr (mbox, '@')))
+                {
+                  *p++ = 0;
+                  gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf,
+                                       mbox, strlen (mbox));
+                  hash = zb32_encode (hashbuf, 8*20);
+                  if (hash)
+                    {
+                      es_fprintf (es_stdout, "   %*s%s@%s\n",
+                                  indent, "", hash, p);
+                      xfree (hash);
+                    }
+                }
+              xfree (mbox);
+            }
+
+          if (opt.with_key_origin
+              && (uid->keyorg || uid->keyupdate || uid->updateurl))
+            {
+              char updatestr[MK_DATESTR_SIZE];
+
+              es_fprintf (es_stdout, "   %*sorigin=%s last=%s %s",
+                          indent, "",
+                          key_origin_string (uid->keyorg),
+                          mk_datestr (updatestr, sizeof updatestr,
+                                      uid->keyupdate),
+                          uid->updateurl? "url=":"");
+              if (uid->updateurl)
+                print_utf8_string (es_stdout, uid->updateurl);
+              es_putc ('\n', es_stdout);
+            }
+
          if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL)
-           show_photos (uid->attribs, uid->numattribs, pk, uid);
+           show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid);
        }
       else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
@@ -1076,46 +1578,17 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
             }
           if (secret)
             {
-              if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
-                s2k_char = serialno? '>':' ';
+              if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
+                secret = serialno? 3 : 1;
               else
-                s2k_char = '#';  /* Key not found.  */
+                secret = 2;  /* Key not found.  */
             }
-          else
-            s2k_char = ' ';
 
-         es_fprintf (es_stdout, "%s%c  %s/%s %s",
-                  secret? "ssb":"sub",
-                  s2k_char,
-                  pubkey_string (pk2, pkstrbuf, sizeof pkstrbuf),
-                 keystr_from_pk (pk2), datestr_from_pk (pk2));
-
-          if ((opt.list_options & LIST_SHOW_USAGE))
-            {
-              es_fprintf (es_stdout, " [%s]", usagestr_from_pk (pk2, 0));
-            }
-         if (pk2->flags.revoked)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         else if (pk2->has_expired)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         else if (pk2->expiredate)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         es_putc ('\n', es_stdout);
-         if (fpr > 1)
+          /* Print the "sub" line.  */
+          print_key_line (ctrl, es_stdout, pk2, secret);
+         if (fpr > 1 || opt.with_subkey_fingerprint)
             {
-              print_fingerprint (NULL, pk2, 0);
+              print_fingerprint (ctrl, NULL, pk2, 0);
               if (serialno)
                 print_card_serialno (serialno);
             }
@@ -1123,112 +1596,39 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
             es_fprintf (es_stdout, "      Keygrip = %s\n", hexgrip);
          if (opt.with_key_data)
            print_key_data (pk2);
+          if (opt.with_key_screening)
+            print_pk_screening (pk2, 0);
        }
       else if (opt.list_sigs
               && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs)
        {
-         PKT_signature *sig = node->pkt->pkt.signature;
-         int sigrc;
-         char *sigstr;
+          kbnode_t n;
+          unsigned int sigcount = 0;
+          kbnode_t *sigarray;
+          unsigned int idx;
 
-         if (listctx->check_sigs)
-           {
-             rc = check_key_signature (keyblock, node, NULL);
-             switch (gpg_err_code (rc))
-               {
-               case 0:
-                 listctx->good_sigs++;
-                 sigrc = '!';
-                 break;
-               case GPG_ERR_BAD_SIGNATURE:
-                 listctx->inv_sigs++;
-                 sigrc = '-';
-                 break;
-               case GPG_ERR_NO_PUBKEY:
-               case GPG_ERR_UNUSABLE_PUBKEY:
-                 listctx->no_key++;
-                 continue;
-               default:
-                 listctx->oth_err++;
-                 sigrc = '%';
-                 break;
-               }
+          for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
+            sigcount++;
+          sigarray = xcalloc (sigcount, sizeof *sigarray);
 
-             /* TODO: Make sure a cached sig record here still has
-                the pk that issued it.  See also
-                keyedit.c:print_and_check_one_sig */
-           }
-         else
-           {
-             rc = 0;
-             sigrc = ' ';
-           }
-
-         if (sig->sig_class == 0x20 || sig->sig_class == 0x28
-             || sig->sig_class == 0x30)
-           sigstr = "rev";
-         else if ((sig->sig_class & ~3) == 0x10)
-           sigstr = "sig";
-         else if (sig->sig_class == 0x18)
-           sigstr = "sig";
-         else if (sig->sig_class == 0x1F)
-           sigstr = "sig";
-         else
-           {
-             es_fprintf (es_stdout, "sig                             "
-                     "[unexpected signature class 0x%02x]\n",
-                     sig->sig_class);
-             continue;
-           }
-
-         es_fputs (sigstr, es_stdout);
-         es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s",
-                 sigrc, (sig->sig_class - 0x10 > 0 &&
-                         sig->sig_class - 0x10 <
-                         4) ? '0' + sig->sig_class - 0x10 : ' ',
-                 sig->flags.exportable ? ' ' : 'L',
-                 sig->flags.revocable ? ' ' : 'R',
-                 sig->flags.policy_url ? 'P' : ' ',
-                 sig->flags.notation ? 'N' : ' ',
-                 sig->flags.expired ? 'X' : ' ',
-                 (sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
-                                                 0) ? '0' +
-                 sig->trust_depth : ' ', keystr (sig->keyid),
-                 datestr_from_sig (sig));
-         if (opt.list_options & LIST_SHOW_SIG_EXPIRE)
-           es_fprintf (es_stdout, " %s", expirestr_from_sig (sig));
-         es_fprintf (es_stdout, "  ");
-         if (sigrc == '%')
-           es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
-         else if (sigrc == '?')
-           ;
-         else if (!opt.fast_list_mode)
-           {
-             size_t n;
-             char *p = get_user_id (sig->keyid, &n);
-             print_utf8_buffer (es_stdout, p, n);
-             xfree (p);
-           }
-         es_putc ('\n', es_stdout);
-
-         if (sig->flags.policy_url
-             && (opt.list_options & LIST_SHOW_POLICY_URLS))
-           show_policy_url (sig, 3, 0);
+          sigcount = 0;
+          for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
+            {
+              if (keyid_eq (mainkid, n->pkt->pkt.signature->keyid))
+                n->flag |= NODFLG_MARK_B;  /* Is a self-sig.  */
+              else
+                n->flag &= ~NODFLG_MARK_B;
 
-         if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS))
-           show_notation (sig, 3, 0,
-                          ((opt.
-                            list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0)
-                          +
-                          ((opt.
-                            list_options & LIST_SHOW_USER_NOTATIONS) ? 2 :
-                           0));
+              sigarray[sigcount++] = node = n;
+            }
+          /* Note that NODE is now at the last signature.  */
 
-         if (sig->flags.pref_ks
-             && (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
-           show_keyserver_url (sig, 3, 0);
+          if ((opt.list_options & LIST_SORT_SIGS))
+            qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes);
 
-         /* fixme: check or list other sigs here */
+          for (idx=0; idx < sigcount; idx++)
+            list_signature_print (ctrl, keyblock, sigarray[idx], listctx);
+          xfree (sigarray);
        }
     }
   es_putc ('\n', es_stdout);
@@ -1236,6 +1636,57 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
   xfree (hexgrip);
 }
 
+
+/* Do a simple key listing printing only the fingerprint and the mail
+ * address of valid keys.  */
+static void
+list_keyblock_simple (ctrl_t ctrl, kbnode_t keyblock)
+{
+  gpg_err_code_t ec;
+  kbnode_t kbctx;
+  kbnode_t node;
+  char hexfpr[2*MAX_FINGERPRINT_LEN+1];
+  char *mbox;
+
+  (void)ctrl;
+
+  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
+  if (!node)
+    {
+      log_error ("Oops; key lost!\n");
+      dump_kbnode (keyblock);
+      return;
+    }
+  hexfingerprint (node->pkt->pkt.public_key, hexfpr, sizeof hexfpr);
+
+  for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
+    {
+      if (node->pkt->pkttype == PKT_USER_ID)
+       {
+         PKT_user_id *uid = node->pkt->pkt.user_id;
+
+         if (uid->attrib_data)
+           continue;
+
+         if (uid->flags.expired || uid->flags.revoked)
+            continue;
+
+          mbox = mailbox_from_userid (uid->name, 0);
+          if (!mbox)
+            {
+              ec = gpg_err_code_from_syserror ();
+              if (ec != GPG_ERR_EINVAL)
+                log_error ("error getting mailbox from user-id: %s\n",
+                           gpg_strerror (ec));
+              continue;
+            }
+          es_fprintf (es_stdout, "%s %s\n", hexfpr, mbox);
+          xfree (mbox);
+       }
+    }
+}
+
+
 void
 print_revokers (estream_t fp, PKT_public_key * pk)
 {
@@ -1252,7 +1703,7 @@ print_revokers (estream_t fp, PKT_public_key * pk)
 
          es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid);
          p = pk->revkey[i].fpr;
-         for (j = 0; j < 20; j++, p++)
+         for (j = 0; j < pk->revkey[i].fprlen; j++, p++)
            es_fprintf (fp, "%02X", *p);
          es_fprintf (fp, ":%02x%s:\n",
                       pk->revkey[i].class,
@@ -1262,11 +1713,43 @@ print_revokers (estream_t fp, PKT_public_key * pk)
 }
 
 
+/* Print the compliance flags to field 18.  PK is the public key.
+ * KEYLENGTH is the length of the key in bits and CURVENAME is either
+ * NULL or the name of the curve.  The latter two args are here
+ * merely because the caller has already computed them.  */
+static void
+print_compliance_flags (PKT_public_key *pk,
+                        unsigned int keylength, const char *curvename)
+{
+  int any = 0;
+
+  if (!keylength)
+    keylength = nbits_from_pk (pk);
+
+  if (pk->version == 5)
+    {
+      es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout);
+      any++;
+    }
+  if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey,
+                            keylength, curvename))
+    {
+      es_fprintf (es_stdout, any ? " %s" : "%s",
+                 gnupg_status_compliance_flag (CO_DE_VS));
+      any++;
+    }
+
+  if (opt.with_key_screening)
+    print_pk_screening (pk, 1+any);
+}
+
+
 /* List a key in colon mode.  If SECRET is true this is a secret key
    record (i.e. requested via --list-secret-key).  If HAS_SECRET a
    secret key is available even if SECRET is not set.  */
 static void
-list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
+list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
+                     int secret, int has_secret)
 {
   int rc;
   KBNODE kbctx;
@@ -1274,12 +1757,17 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
   PKT_public_key *pk;
   u32 keyid[2];
   int trustletter = 0;
+  int trustletter_print;
+  int ownertrust_print;
   int ulti_hack = 0;
   int i;
-  char *p;
-  char *hexgrip = NULL;
+  char *hexgrip_buffer = NULL;
+  const char *hexgrip = NULL;
   char *serialno = NULL;
   int stubkey;
+  unsigned int keylength;
+  char *curve = NULL;
+  const char *curvename = NULL;
 
   /* Get the keyid from the keyblock.  */
   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
@@ -1293,45 +1781,59 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
   pk = node->pkt->pkt.public_key;
   if (secret || has_secret || opt.with_keygrip || opt.with_key_data)
     {
-      rc = hexkeygrip_from_pk (pk, &hexgrip);
+      rc = hexkeygrip_from_pk (pk, &hexgrip_buffer);
       if (rc)
         log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
+      /* In the error case we print an empty string so that we have a
+       * "grp" record for each primary and subkey - even if it is
+       * empty.  This may help to prevent sync problems.  */
+      hexgrip = hexgrip_buffer? hexgrip_buffer : "";
     }
   stubkey = 0;
-  if ((secret||has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno))
+  if ((secret || has_secret)
+      && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
     stubkey = 1;  /* Key not found.  */
 
   keyid_from_pk (pk, keyid);
-  es_fputs (secret? "sec:":"pub:", es_stdout);
   if (!pk->flags.valid)
-    es_putc ('i', es_stdout);
+    trustletter_print = 'i';
   else if (pk->flags.revoked)
-    es_putc ('r', es_stdout);
+    trustletter_print = 'r';
   else if (pk->has_expired)
-    es_putc ('e', es_stdout);
+    trustletter_print = 'e';
   else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
-    ;
+    trustletter_print = 0;
   else
     {
-      trustletter = get_validity_info (pk, NULL);
+      trustletter = get_validity_info (ctrl, keyblock, pk, NULL);
       if (trustletter == 'u')
         ulti_hack = 1;
-      es_putc (trustletter, es_stdout);
+      trustletter_print = trustletter;
     }
 
+  if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
+    ownertrust_print = get_ownertrust_info (ctrl, pk, 0);
+  else
+    ownertrust_print = 0;
+
+  keylength = nbits_from_pk (pk);
+
+  es_fputs (secret? "sec:":"pub:", es_stdout);
+  if (trustletter_print)
+    es_putc (trustletter_print, es_stdout);
   es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::",
-          nbits_from_pk (pk),
-          pk->pubkey_algo,
-          (ulong) keyid[0], (ulong) keyid[1],
-          colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
+              keylength,
+              pk->pubkey_algo,
+              (ulong) keyid[0], (ulong) keyid[1],
+              colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
 
-  if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
-    es_putc (get_ownertrust_info (pk), es_stdout);
+  if (ownertrust_print)
+    es_putc (ownertrust_print, es_stdout);
   es_putc (':', es_stdout);
 
   es_putc (':', es_stdout);
   es_putc (':', es_stdout);
-  print_capabilities (pk, keyblock);
+  print_capabilities (ctrl, pk, keyblock);
   es_putc (':', es_stdout);            /* End of field 13. */
   es_putc (':', es_stdout);            /* End of field 14. */
   if (secret || has_secret)
@@ -1349,56 +1851,57 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
       || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
       || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
     {
-      char *curve = openpgp_oid_to_str (pk->pkey[0]);
-      const char *name = openpgp_oid_to_curve (curve, 0);
-      if (!name)
-        name = curve;
-      es_fputs (name, es_stdout);
-      xfree (curve);
+      curve = openpgp_oid_to_str (pk->pkey[0]);
+      curvename = openpgp_oid_to_curve (curve, 0);
+      if (!curvename)
+        curvename = curve;
+      es_fputs (curvename, es_stdout);
     }
   es_putc (':', es_stdout);            /* End of field 17. */
+  print_compliance_flags (pk, keylength, curvename);
+  es_putc (':', es_stdout);            /* End of field 18 (compliance). */
+  if (pk->keyupdate)
+    es_fputs (colon_strtime (pk->keyupdate), es_stdout);
+  es_putc (':', es_stdout);            /* End of field 19 (last_update). */
+  es_fprintf (es_stdout, "%d%s", pk->keyorg, pk->updateurl? " ":"");
+  if (pk->updateurl)
+    es_write_sanitized (es_stdout, pk->updateurl, strlen (pk->updateurl),
+                        ":", NULL);
+  es_putc (':', es_stdout);            /* End of field 20 (origin). */
   es_putc ('\n', es_stdout);
 
   print_revokers (es_stdout, pk);
-  if (fpr)
-    print_fingerprint (NULL, pk, 0);
-  if (opt.with_key_data || opt.with_keygrip)
-    {
-      if (hexgrip)
-        es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
-      if (opt.with_key_data)
-        print_key_data (pk);
-    }
+  print_fingerprint (ctrl, NULL, pk, 0);
+  if (hexgrip)
+    es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
+  if (opt.with_key_data)
+    print_key_data (pk);
 
   for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
     {
       if (node->pkt->pkttype == PKT_USER_ID)
        {
-         char *str;
          PKT_user_id *uid = node->pkt->pkt.user_id;
+          int uid_validity;
+
+         if (attrib_fp && uid->attrib_data != NULL)
+           dump_attribs (uid, pk);
 
-         if (attrib_fp && node->pkt->pkt.user_id->attrib_data != NULL)
-           dump_attribs (node->pkt->pkt.user_id, pk);
-         /*
-          * Fixme: We need a valid flag here too
-          */
-         str = uid->attrib_data ? "uat" : "uid";
-         if (uid->is_revoked)
-           es_fprintf (es_stdout, "%s:r::::", str);
-         else if (uid->is_expired)
-           es_fprintf (es_stdout, "%s:e::::", str);
+         if (uid->flags.revoked)
+           uid_validity = 'r';
+         else if (uid->flags.expired)
+           uid_validity = 'e';
          else if (opt.no_expensive_trust_checks)
-           es_fprintf (es_stdout, "%s:::::", str);
-         else
-           {
-             int uid_validity;
+           uid_validity = 0;
+         else if (ulti_hack)
+            uid_validity = 'u';
+          else
+            uid_validity = get_validity_info (ctrl, keyblock, pk, uid);
 
-             if (!ulti_hack)
-               uid_validity = get_validity_info (pk, uid);
-             else
-               uid_validity = 'u';
-             es_fprintf (es_stdout, "%s:%c::::", str, uid_validity);
-           }
+          es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout);
+          if (uid_validity)
+            es_putc (uid_validity, es_stdout);
+          es_fputs ("::::", es_stdout);
 
          es_fprintf (es_stdout, "%s:", colon_strtime (uid->created));
          es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate));
@@ -1414,27 +1917,47 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
            es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len);
          else
            es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
-         es_putc (':', es_stdout);
+         es_fputs (":::::::::", es_stdout);
+          if (uid->keyupdate)
+            es_fputs (colon_strtime (uid->keyupdate), es_stdout);
+          es_putc (':', es_stdout);    /* End of field 19 (last_update). */
+          es_fprintf (es_stdout, "%d%s", uid->keyorg, uid->updateurl? " ":"");
+          if (uid->updateurl)
+            es_write_sanitized (es_stdout,
+                                uid->updateurl, strlen (uid->updateurl),
+                                ":", NULL);
+          es_putc (':', es_stdout);    /* End of field 20 (origin). */
          es_putc ('\n', es_stdout);
+#ifdef USE_TOFU
+         if (!uid->attrib_data && opt.with_tofu_info
+              && (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP))
+           {
+              /* Print a "tfs" record.  */
+              tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name);
+           }
+#endif /*USE_TOFU*/
        }
       else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
          u32 keyid2[2];
          PKT_public_key *pk2;
+          int need_hexgrip = !!hexgrip;
 
           pk2 = node->pkt->pkt.public_key;
-          xfree (hexgrip); hexgrip = NULL;
+          xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL;
           xfree (serialno); serialno = NULL;
-          if (secret || has_secret || opt.with_keygrip || opt.with_key_data)
+          if (need_hexgrip
+              || secret || has_secret || opt.with_keygrip || opt.with_key_data)
             {
-              rc = hexkeygrip_from_pk (pk2, &hexgrip);
+              rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer);
               if (rc)
                 log_error ("error computing a keygrip: %s\n",
                            gpg_strerror (rc));
+              hexgrip = hexgrip_buffer? hexgrip_buffer : "";
             }
           stubkey = 0;
           if ((secret||has_secret)
-              && agent_get_keyinfo (NULL, hexgrip, &serialno))
+              && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
             stubkey = 1;  /* Key not found.  */
 
          keyid_from_pk (pk2, keyid2);
@@ -1453,14 +1976,14 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
              if (trustletter)
                es_fprintf (es_stdout, "%c", trustletter);
            }
+          keylength = nbits_from_pk (pk2);
          es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::",
-                 nbits_from_pk (pk2),
-                 pk2->pubkey_algo,
-                 (ulong) keyid2[0], (ulong) keyid2[1],
-                 colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate)
-                 /* fixme: add LID and ownertrust here */
-           );
-         print_capabilities (pk2, NULL);
+                      keylength,
+                      pk2->pubkey_algo,
+                      (ulong) keyid2[0], (ulong) keyid2[1],
+                      colon_datestr_from_pk (pk2),
+                      colon_strtime (pk2->expiredate));
+         print_capabilities (ctrl, pk2, NULL);
           es_putc (':', es_stdout);    /* End of field 13. */
           es_putc (':', es_stdout);    /* End of field 14. */
           if (secret || has_secret)
@@ -1474,28 +1997,26 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
             }
           es_putc (':', es_stdout);    /* End of field 15. */
           es_putc (':', es_stdout);    /* End of field 16. */
-          if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
-              || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
-              || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+          if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
+              || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
+              || pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
             {
-              char *curve = openpgp_oid_to_str (pk->pkey[0]);
-              const char *name = openpgp_oid_to_curve (curve, 0);
-              if (!name)
-                name = curve;
-              es_fputs (name, es_stdout);
               xfree (curve);
+              curve = openpgp_oid_to_str (pk2->pkey[0]);
+              curvename = openpgp_oid_to_curve (curve, 0);
+              if (!curvename)
+                curvename = curve;
+              es_fputs (curvename, es_stdout);
             }
           es_putc (':', es_stdout);    /* End of field 17. */
+          print_compliance_flags (pk2, keylength, curvename);
+          es_putc (':', es_stdout);    /* End of field 18. */
          es_putc ('\n', es_stdout);
-         if (fpr > 1)
-           print_fingerprint (NULL, pk2, 0);
-         if (opt.with_key_data || opt.with_keygrip)
-            {
-              if (hexgrip)
-                es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
-              if (opt.with_key_data)
-                print_key_data (pk2);
-            }
+          print_fingerprint (ctrl, NULL, pk2, 0);
+          if (hexgrip)
+            es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
+          if (opt.with_key_data)
+            print_key_data (pk2);
        }
       else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE)
        {
@@ -1504,10 +2025,22 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
          char *sigstr;
          size_t fplen;
          byte fparray[MAX_FINGERPRINT_LEN];
+          char *siguid;
+          size_t siguidlen;
+          char *issuer_fpr = NULL;
+          char *reason_text = NULL;
+          char *reason_comment = NULL;
+          size_t reason_commentlen;
+          int reason_code = 0;  /* Init to silence compiler warning.  */
 
          if (sig->sig_class == 0x20 || sig->sig_class == 0x28
              || sig->sig_class == 0x30)
-           sigstr = "rev";
+            {
+              sigstr = "rev";
+              reason_code = get_revocation_reason (sig, &reason_text,
+                                                   &reason_comment,
+                                                   &reason_commentlen);
+            }
          else if ((sig->sig_class & ~3) == 0x10)
            sigstr = "sig";
          else if (sig->sig_class == 0x18)
@@ -1525,11 +2058,11 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
            {
              PKT_public_key *signer_pk = NULL;
 
-             fflush (stdout);
+             es_fflush (es_stdout);
              if (opt.no_sig_cache)
                signer_pk = xmalloc_clear (sizeof (PKT_public_key));
 
-             rc = check_key_signature2 (keyblock, node, NULL, signer_pk,
+             rc = check_key_signature2 (ctrl, keyblock, node, NULL, signer_pk,
                                         NULL, NULL, NULL);
              switch (gpg_err_code (rc))
                {
@@ -1561,8 +2094,23 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
          else
            {
              rc = 0;
-             sigrc = ' ';
+             sigrc = ' '; /* Note the fix-up below in --list-sigs mode.  */
            }
+
+         if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
+            {
+              int nouid;
+              siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid);
+              if (!opt.check_sigs && nouid)
+                sigrc = '?';  /* No key in local keyring.  */
+            }
+          else
+            {
+              siguid = NULL;
+              siguidlen = 0;
+            }
+
+
          es_fputs (sigstr, es_stdout);
          es_putc (':', es_stdout);
          if (sigrc != ' ')
@@ -1583,34 +2131,47 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
 
          if (sigrc == '%')
            es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
-         else if (sigrc == '?')
-           ;
-         else if (!opt.fast_list_mode)
-           {
-             size_t n;
-             p = get_user_id (sig->keyid, &n);
-             es_write_sanitized (es_stdout, p, n, ":", NULL);
-             xfree (p);
-           }
-         es_fprintf (es_stdout, ":%02x%c::", sig->sig_class,
-                 sig->flags.exportable ? 'x' : 'l');
+         else if (siguid)
+            es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL);
+
+         es_fprintf (es_stdout, ":%02x%c", sig->sig_class,
+                      sig->flags.exportable ? 'x' : 'l');
+          if (reason_text)
+            es_fprintf (es_stdout, ",%02x", reason_code);
+          es_fputs ("::", es_stdout);
 
          if (opt.no_sig_cache && opt.check_sigs && fprokay)
            {
              for (i = 0; i < fplen; i++)
                es_fprintf (es_stdout, "%02X", fparray[i]);
            }
+          else if ((issuer_fpr = issuer_fpr_string (sig)))
+            es_fputs (issuer_fpr, es_stdout);
 
-         es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo);
+         es_fprintf (es_stdout, ":::%d:", sig->digest_algo);
+
+          if (reason_comment)
+            {
+              es_fputs ("::::", es_stdout);
+              es_write_sanitized (es_stdout, reason_comment, reason_commentlen,
+                                  ":", NULL);
+              es_putc (':', es_stdout);
+            }
+          es_putc ('\n', es_stdout);
 
          if (opt.show_subpackets)
            print_subpackets_colon (sig);
 
          /* fixme: check or list other sigs here */
+          xfree (reason_text);
+          xfree (reason_comment);
+          xfree (siguid);
+          xfree (issuer_fpr);
        }
     }
 
-  xfree (hexgrip);
+  xfree (curve);
+  xfree (hexgrip_buffer);
   xfree (serialno);
 }
 
@@ -1629,7 +2190,7 @@ do_reorder_keyblock (KBNODE keyblock, int attr)
       if (node->pkt->pkttype == PKT_USER_ID &&
          ((attr && node->pkt->pkt.user_id->attrib_data) ||
           (!attr && !node->pkt->pkt.user_id->attrib_data)) &&
-         node->pkt->pkt.user_id->is_primary)
+         node->pkt->pkt.user_id->flags.primary)
        {
          primary = primary2 = node;
          for (node = node->next; node; primary2 = node, node = node->next)
@@ -1652,9 +2213,9 @@ do_reorder_keyblock (KBNODE keyblock, int attr)
       if (node->pkt->pkttype == PKT_USER_ID)
        break;
     }
-  assert (node);
-  assert (last);        /* The user ID is never the first packet.  */
-  assert (primary0);    /* Ditto (this is the node before primary).  */
+  log_assert (node);
+  log_assert (last);    /* The user ID is never the first packet.  */
+  log_assert (primary0); /* Ditto (this is the node before primary).  */
   if (node == primary)
     return; /* Already the first one.  */
 
@@ -1670,30 +2231,60 @@ reorder_keyblock (KBNODE keyblock)
   do_reorder_keyblock (keyblock, 0);
 }
 
+
 static void
-list_keyblock (KBNODE keyblock, int secret, int has_secret, int fpr,
+list_keyblock (ctrl_t ctrl,
+               KBNODE keyblock, int secret, int has_secret, int fpr,
                struct keylist_context *listctx)
 {
   reorder_keyblock (keyblock);
-  if (opt.print_pka_records)
-    list_keyblock_pka (keyblock);
-  else if (opt.with_colons)
-    list_keyblock_colon (keyblock, secret, has_secret, fpr);
+
+  if (list_filter.selkey)
+    {
+      int selected = 0;
+      struct impex_filter_parm_s parm;
+      parm.ctrl = ctrl;
+
+      for (parm.node = keyblock; parm.node; parm.node = parm.node->next)
+        {
+          if (recsel_select (list_filter.selkey, impex_filter_getval, &parm))
+            {
+              selected = 1;
+              break;
+            }
+        }
+      if (!selected)
+        return;  /* Skip this one.  */
+    }
+
+  if (opt.with_colons)
+    list_keyblock_colon (ctrl, keyblock, secret, has_secret);
+  else if ((opt.list_options & LIST_SHOW_ONLY_FPR_MBOX))
+    {
+      if (!listctx->no_validity)
+        check_trustdb_stale (ctrl);
+      list_keyblock_simple (ctrl, keyblock);
+    }
   else
-    list_keyblock_print (keyblock, secret, fpr, listctx);
+    list_keyblock_print (ctrl, keyblock, secret, fpr, listctx);
+
   if (secret)
     es_fflush (es_stdout);
 }
 
 
-/* Public function used by keygen to list a keyblock.  */
+/* Public function used by keygen to list a keyblock.  If NO_VALIDITY
+ * is set the validity of a key is never shown.  */
 void
-list_keyblock_direct (kbnode_t keyblock, int secret, int has_secret, int fpr)
+list_keyblock_direct (ctrl_t ctrl,
+                      kbnode_t keyblock, int secret, int has_secret, int fpr,
+                      int no_validity)
 {
   struct keylist_context listctx;
 
   memset (&listctx, 0, sizeof (listctx));
-  list_keyblock (keyblock, secret, has_secret, fpr, &listctx);
+  listctx.no_validity = !!no_validity;
+  list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx);
   keylist_context_release (&listctx);
 }
 
@@ -1719,6 +2310,7 @@ print_icao_hexdigit (estream_t fp, int c)
  *      3: direct use of tty but only primary key.
  *      4: direct use of tty but only subkey.
  *     10: Same as 0 but with_colons etc is ignored.
+ *     20: Same as 0 but using a compact format.
  *
  * Modes 1 and 2 will try and print both subkey and primary key
  * fingerprints.  A MODE with bit 7 set is used internally.  If
@@ -1726,15 +2318,18 @@ print_icao_hexdigit (estream_t fp, int c)
  * of es_stdout or instead of the TTY in modes 2 and 3.
  */
 void
-print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
+print_fingerprint (ctrl_t ctrl, estream_t override_fp,
+                   PKT_public_key *pk, int mode)
 {
-  byte array[MAX_FINGERPRINT_LEN], *p;
-  size_t i, n;
+  char hexfpr[2*MAX_FINGERPRINT_LEN+1];
+  char *p;
+  size_t i;
   estream_t fp;
   const char *text;
   int primary = 0;
   int with_colons = opt.with_colons;
   int with_icao   = opt.with_icao_spelling;
+  int compact = 0;
 
   if (mode == 10)
     {
@@ -1742,6 +2337,16 @@ print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
       with_colons = 0;
       with_icao = 0;
     }
+  else if (mode == 20)
+    {
+      mode = 0;
+      with_colons = 0;
+      compact = 1;
+    }
+
+  if (!opt.fingerprint && !opt.with_fingerprint
+      && opt.with_subkey_fingerprint)
+    compact = 1;
 
   if (pk->main_keyid[0] == pk->keyid[0]
       && pk->main_keyid[1] == pk->keyid[1])
@@ -1759,8 +2364,8 @@ print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
   if (!primary && (mode == 1 || mode == 2))
     {
       PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk));
-      get_pubkey (primary_pk, pk->main_keyid);
-      print_fingerprint (override_fp, primary_pk, (mode | 0x80));
+      get_pubkey (ctrl, primary_pk, pk->main_keyid);
+      print_fingerprint (ctrl, override_fp, primary_pk, (mode | 0x80));
       free_public_key (primary_pk);
     }
 
@@ -1795,50 +2400,49 @@ print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
   else
     {
       fp = override_fp? override_fp : es_stdout;
-      text = _("      Key fingerprint =");
+      if (opt.keyid_format == KF_NONE)
+        {
+          text = "     ";  /* To indent ICAO spelling.  */
+          compact = 1;
+        }
+      else
+        text = _("      Key fingerprint =");
     }
 
-  fingerprint_from_pk (pk, array, &n);
-  p = array;
+  hexfingerprint (pk, hexfpr, sizeof hexfpr);
   if (with_colons && !mode)
     {
-      es_fprintf (fp, "fpr:::::::::");
-      for (i = 0; i < n; i++, p++)
-       es_fprintf (fp, "%02X", *p);
-      es_putc (':', fp);
+      es_fprintf (fp, "fpr:::::::::%s:", hexfpr);
+    }
+  else if (compact && !opt.fingerprint && !opt.with_fingerprint)
+    {
+      tty_fprintf (fp, "%*s%s", 6, "", hexfpr);
     }
   else
     {
-      tty_fprintf (fp, "%s", text);
-      if (n == 20)
-       {
-         for (i = 0; i < n; i++, i++, p += 2)
-            tty_fprintf (fp, "%s %02X%02X", i==10? " ":"", *p, p[1]);
-       }
+      char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1];
+      format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr);
+      if (compact)
+        tty_fprintf (fp, "%*s%s", 6, "", fmtfpr);
       else
-       {
-         for (i = 0; i < n; i++, p++)
-            tty_fprintf (fp, "%s %02X", (i && !(i % 8))? " ":"", *p);
-       }
+        tty_fprintf (fp, "%s %s", text, fmtfpr);
     }
   tty_fprintf (fp, "\n");
   if (!with_colons && with_icao)
     {
-      p = array;
+      ;
       tty_fprintf (fp, "%*s\"", (int)strlen(text)+1, "");
-      for (i = 0; i < n; i++, p++)
+      for (i = 0, p = hexfpr; *p; i++, p++)
         {
           if (!i)
             ;
-          else if (!(i%4))
+          else if (!(i%10))
             tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, "");
-          else if (!(i%2))
+          else if (!(i%5))
             tty_fprintf (fp, "  ");
           else
             tty_fprintf (fp, " ");
-          print_icao_hexdigit (fp, *p >> 4);
-          tty_fprintf (fp, " ");
-          print_icao_hexdigit (fp, *p & 15);
+          print_icao_hexdigit (fp, xtoi_1 (p));
         }
       tty_fprintf (fp, "\"\n");
     }
@@ -1868,6 +2472,89 @@ print_card_serialno (const char *serialno)
 }
 
 
+/* Print a public or secret (sub)key line.  Example:
+ *
+ * pub   dsa2048 2007-12-31 [SC] [expires: 2018-12-31]
+ *       80615870F5BAD690333686D0F2AD85AC1E42B367
+ *
+ * pub   rsa2048 2017-12-31 [SC] [expires: 2028-12-31]
+ *       80615870F5BAD690333686D0F2AD85AC1E42B3671122334455
+ *
+ * Some global options may result in a different output format.  If
+ * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and
+ * depending on the value a flag character is shown:
+ *
+ *    1 := ' ' Regular secret key
+ *    2 := '#' Stub secret key
+ *    3 := '>' Secret key is on a token.
+ */
+void
+print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret)
+{
+  char pkstrbuf[PUBKEY_STRING_SIZE];
+
+  tty_fprintf (fp, "%s%c  %s",
+               pk->flags.primary? (secret? "sec":"pub")
+               /**/             : (secret? "ssb":"sub"),
+               secret == 2? '#' : secret == 3? '>' : ' ',
+               pubkey_string (pk, pkstrbuf, sizeof pkstrbuf));
+  if (opt.keyid_format != KF_NONE)
+    tty_fprintf (fp, "/%s", keystr_from_pk (pk));
+  tty_fprintf (fp, " %s", datestr_from_pk (pk));
+
+  if (pk->flags.primary
+      && !(openpgp_pk_algo_usage (pk->pubkey_algo)
+           & (PUBKEY_USAGE_CERT| PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH)))
+    {
+      /* A primary key which is really not capable to sign.  */
+      tty_fprintf (fp, " [INVALID_ALGO]");
+    }
+  else if ((opt.list_options & LIST_SHOW_USAGE))
+    {
+      tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0));
+    }
+
+  if (pk->flags.revoked)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+  else if (pk->has_expired)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+  else if (pk->expiredate)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+
+#if 0
+  /* I need to think about this some more.  It's easy enough to
+     include, but it looks sort of confusing in the listing... */
+  if (opt.list_options & LIST_SHOW_VALIDITY)
+    {
+      int validity = get_validity (ctrl, pk, NULL, NULL, 0);
+      tty_fprintf (fp, " [%s]", trust_value_to_string (validity));
+    }
+#endif
+
+  if (pk->pubkey_algo >= 100)
+    tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo);
+
+  tty_fprintf (fp, "\n");
+
+  /* if the user hasn't explicitly asked for human-readable
+     fingerprints, show compact fpr of primary key: */
+  if (pk->flags.primary &&
+      !opt.fingerprint && !opt.with_fingerprint)
+    print_fingerprint (ctrl, fp, pk, 20);
+}
+
 
 void
 set_attrib_fd (int fd)
@@ -1884,6 +2571,9 @@ set_attrib_fd (int fd)
   if (fd == -1)
     return;
 
+  if (! gnupg_fd_valid (fd))
+    log_fatal ("attribute-fd is invalid: %s\n", strerror (errno));
+
 #ifdef HAVE_DOSISH_SYSTEM
   setmode (fd, O_BINARY);
 #endif