* options.h, g10.c (main): Add a more flexible --keyid-format option to
[gnupg.git] / g10 / keylist.c
1 /* keylist.c
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
3  *               2004 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #include "options.h"
30 #include "packet.h"
31 #include "errors.h"
32 #include "keydb.h"
33 #include "memory.h"
34 #include "photoid.h"
35 #include "util.h"
36 #include "ttyio.h"
37 #include "trustdb.h"
38 #include "main.h"
39 #include "i18n.h"
40 #include "status.h"
41
42 static void list_all(int);
43 static void list_one( STRLIST names, int secret);
44
45 struct sig_stats
46 {
47   int inv_sigs;
48   int no_key;
49   int oth_err;
50 };
51
52 static FILE *attrib_fp=NULL;
53
54 /****************
55  * List the keys
56  * If list is NULL, all available keys are listed
57  */
58 void
59 public_key_list( STRLIST list )
60 {
61   if(opt.with_colons)
62     {
63       byte trust_model,marginals,completes,cert_depth;
64       ulong created,nextcheck;
65
66       read_trust_options(&trust_model,&created,&nextcheck,
67                          &marginals,&completes,&cert_depth);
68
69       printf("tru:");
70
71       if(nextcheck && nextcheck <= make_timestamp())
72         printf("o");
73       if(trust_model!=opt.trust_model)
74         printf("t");
75       if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
76         {
77           if(marginals!=opt.marginals_needed)
78             printf("m");
79           if(completes!=opt.completes_needed)
80             printf("c");
81           if(cert_depth!=opt.max_cert_depth)
82             printf("d");
83         }
84
85       printf(":%d:%lu:%lu",trust_model,created,nextcheck);
86
87       /* Only show marginals, completes, and cert_depth in the classic
88          or PGP trust models since they are not meaningful
89          otherwise. */
90
91       if(trust_model==TM_PGP || trust_model==TM_CLASSIC)
92         printf(":%d:%d:%d",marginals,completes,cert_depth);
93
94       printf("\n");
95     }
96
97   if( !list )
98     list_all(0);
99   else
100     list_one( list, 0 );
101 }
102
103 void
104 secret_key_list( STRLIST list )
105 {
106     if( !list )
107         list_all(1);
108     else  /* List by user id */
109         list_one( list, 1 );
110 }
111
112 void
113 print_seckey_info (PKT_secret_key *sk)
114 {
115     u32 sk_keyid[2];
116     size_t n;
117     char *p;
118
119     keyid_from_sk (sk, sk_keyid);
120     tty_printf ("\nsec  %4u%c/%08lX %s   ",
121                 nbits_from_sk (sk),
122                 pubkey_letter (sk->pubkey_algo),
123                 (ulong)sk_keyid[1], datestr_from_sk (sk));
124     
125     p = get_user_id (sk_keyid, &n);
126     tty_print_utf8_string (p, n);
127     m_free (p);
128
129     tty_printf ("\n");   
130 }
131
132 /* Print information about the public key.  With FP passed as NULL,
133    the tty output interface is used, otherwise output is directted to
134    the given stream. */
135 void
136 print_pubkey_info (FILE *fp, PKT_public_key *pk)
137 {
138   u32 pk_keyid[2];
139   size_t n;
140   char *p;
141
142   keyid_from_pk (pk, pk_keyid);
143   if (fp)
144     fprintf (fp, "pub  %4u%c/%08lX %s   ",
145              nbits_from_pk (pk),
146              pubkey_letter (pk->pubkey_algo),
147              (ulong)pk_keyid[1], datestr_from_pk (pk));
148   else
149     tty_printf ("\npub  %4u%c/%08lX %s   ",
150                 nbits_from_pk (pk),
151                 pubkey_letter (pk->pubkey_algo),
152                 (ulong)pk_keyid[1], datestr_from_pk (pk));
153
154   p = get_user_id (pk_keyid, &n);
155   if (fp)
156     print_utf8_string2 (fp, p, n, '\n');
157   else
158     tty_print_utf8_string (p, n);
159   m_free (p);
160   
161   if (fp)
162     putc ('\n', fp);
163   else
164     tty_printf ("\n\n"); 
165 }
166
167 /*
168   mode=0 for stdout.
169   mode=1 for log_info + status messages
170   mode=2 for status messages only
171 */
172
173 void
174 show_policy_url(PKT_signature *sig,int indent,int mode)
175 {
176   const byte *p;
177   size_t len;
178   int seq=0,crit;
179   FILE *fp=mode?log_stream():stdout;
180
181   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,&len,&seq,&crit)))
182     {
183       if(mode!=2)
184         {
185           int i;
186           char *str;
187
188           for(i=0;i<indent;i++)
189             putchar(' ');
190
191           if(crit)
192             str=_("Critical signature policy: ");
193           else
194             str=_("Signature policy: ");
195           if(mode)
196             log_info("%s",str);
197           else
198             printf("%s",str);
199           print_utf8_string(fp,p,len);
200           fprintf(fp,"\n");
201         }
202
203       if(mode)
204         write_status_buffer ( STATUS_POLICY_URL, p, len, 0 );
205     }
206 }
207
208 /*
209   mode=0 for stdout.
210   mode=1 for log_info + status messages
211   mode=2 for status messages only
212 */
213 /* TODO: use this */
214 void
215 show_keyserver_url(PKT_signature *sig,int indent,int mode)
216 {
217   const byte *p;
218   size_t len;
219   int seq=0,crit;
220   FILE *fp=mode?log_stream():stdout;
221
222   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&len,&seq,&crit)))
223     {
224       if(mode!=2)
225         {
226           int i;
227           char *str;
228
229           for(i=0;i<indent;i++)
230             putchar(' ');
231
232           if(crit)
233             str=_("Critical preferred keyserver: ");
234           else
235             str=_("Preferred keyserver: ");
236           if(mode)
237             log_info("%s",str);
238           else
239             printf("%s",str);
240           print_utf8_string(fp,p,len);
241           fprintf(fp,"\n");
242         }
243
244       /* TODO: put in a status-fd tag for preferred keyservers */
245     }
246 }
247
248 /*
249   mode=0 for stdout.
250   mode=1 for log_info + status messages
251   mode=2 for status messages only
252 */
253
254 void
255 show_notation(PKT_signature *sig,int indent,int mode)
256 {
257   const byte *p;
258   size_t len;
259   int seq=0,crit;
260   FILE *fp=mode?log_stream():stdout;
261
262   /* There may be multiple notations in the same sig. */
263
264   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit)))
265     if(len>=8)
266       {
267         int n1,n2;
268
269         n1=(p[4]<<8)|p[5];
270         n2=(p[6]<<8)|p[7];
271
272         if(8+n1+n2!=len)
273           {
274             log_info(_("WARNING: invalid notation data found\n"));
275             return;
276           }
277
278         if(mode!=2)
279           {
280             int i;
281             char *str;
282
283             for(i=0;i<indent;i++)
284               putchar(' ');
285
286             /* This is UTF8 */
287             if(crit)
288               str=_("Critical signature notation: ");
289             else
290               str=_("Signature notation: ");
291             if(mode)
292               log_info("%s",str);
293             else
294               printf("%s",str);
295             print_utf8_string(fp,p+8,n1);
296             fprintf(fp,"=");
297
298             if(*p&0x80)
299               print_utf8_string(fp,p+8+n1,n2);
300             else
301               fprintf(fp,"[ %s ]",_("not human readable"));
302
303             fprintf(fp,"\n");
304           }
305
306         if(mode)
307           {
308             write_status_buffer ( STATUS_NOTATION_NAME, p+8   , n1, 0 );
309             write_status_buffer ( STATUS_NOTATION_DATA, p+8+n1, n2, 50 );
310           }
311       }
312   else
313     log_info(_("WARNING: invalid notation data found\n"));
314 }
315
316 static void
317 print_signature_stats(struct sig_stats *s)
318 {
319   if( s->inv_sigs == 1 )
320     tty_printf(_("1 bad signature\n") );
321   else if( s->inv_sigs )
322     tty_printf(_("%d bad signatures\n"), s->inv_sigs );
323   if( s->no_key == 1 )
324     tty_printf(_("1 signature not checked due to a missing key\n") );
325   else if( s->no_key )
326     tty_printf(_("%d signatures not checked due to missing keys\n"),s->no_key);
327   if( s->oth_err == 1 )
328     tty_printf(_("1 signature not checked due to an error\n") );
329   else if( s->oth_err )
330     tty_printf(_("%d signatures not checked due to errors\n"), s->oth_err );
331 }
332
333 static void
334 list_all( int secret )
335 {
336     KEYDB_HANDLE hd;
337     KBNODE keyblock = NULL;
338     int rc=0;
339     const char *lastresname, *resname;
340     struct sig_stats stats;
341
342     memset(&stats,0,sizeof(stats));
343
344     hd = keydb_new (secret);
345     if (!hd)
346         rc = G10ERR_GENERAL;
347     else
348         rc = keydb_search_first (hd);
349     if( rc ) {
350         if( rc != -1 )
351             log_error("keydb_search_first failed: %s\n", g10_errstr(rc) );
352         goto leave;
353     }
354
355     lastresname = NULL;
356     do {
357         rc = keydb_get_keyblock (hd, &keyblock);
358         if (rc) {
359             log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
360             goto leave;
361         }
362         if(!opt.with_colons)
363           {
364             resname = keydb_get_resource_name (hd);
365             if (lastresname != resname )
366               {
367                 int i;
368
369                 printf("%s\n", resname );
370                 for(i=strlen(resname); i; i-- )
371                   putchar('-');
372                 putchar('\n');
373                 lastresname = resname;
374               }
375           }
376         merge_keys_and_selfsig( keyblock );
377         list_keyblock( keyblock, secret, opt.fingerprint,
378                        opt.check_sigs?&stats:NULL);
379         release_kbnode( keyblock ); 
380         keyblock = NULL;
381     } while (!(rc = keydb_search_next (hd)));
382     if( rc && rc != -1 )
383         log_error ("keydb_search_next failed: %s\n", g10_errstr(rc));
384
385     if(opt.check_sigs && !opt.with_colons)
386       print_signature_stats(&stats);
387
388   leave:
389     release_kbnode (keyblock);
390     keydb_release (hd);
391 }
392
393
394 static void
395 list_one( STRLIST names, int secret )
396 {
397     int rc = 0;
398     KBNODE keyblock = NULL;
399     GETKEY_CTX ctx;
400     const char *resname;
401     char *keyring_str = _("Keyring");
402     int i;
403     struct sig_stats stats;
404
405     memset(&stats,0,sizeof(stats));
406
407     /* fixme: using the bynames function has the disadvantage that we
408      * don't know wether one of the names given was not found.  OTOH,
409      * this function has the advantage to list the names in the
410      * sequence as defined by the keyDB and does not duplicate
411      * outputs.  A solution could be do test whether all given have
412      * been listed (this needs a way to use the keyDB search
413      * functions) or to have the search function return indicators for
414      * found names.  Yet another way is to use the keydb search
415      * facilities directly. */
416     if( secret ) {
417         rc = get_seckey_bynames( &ctx, NULL, names, &keyblock );
418         if( rc ) {
419             log_error("error reading key: %s\n",  g10_errstr(rc) );
420             get_seckey_end( ctx );
421             return;
422         }
423         do {
424             if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
425                 resname = keydb_get_resource_name (get_ctx_handle(ctx));
426                 printf("%s: %s\n", keyring_str, resname);
427                 for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
428                     putchar('-');
429                 putchar('\n');
430             }
431             list_keyblock( keyblock, 1, opt.fingerprint, NULL );
432             release_kbnode( keyblock );
433         } while( !get_seckey_next( ctx, NULL, &keyblock ) );
434         get_seckey_end( ctx );
435     }
436     else {
437         rc = get_pubkey_bynames( &ctx, NULL, names, &keyblock );
438         if( rc ) {
439             log_error("error reading key: %s\n", g10_errstr(rc) );
440             get_pubkey_end( ctx );
441             return;
442         }
443         do {
444           if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
445                 resname = keydb_get_resource_name (get_ctx_handle(ctx));
446                 printf("%s: %s\n", keyring_str, resname);
447                 for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
448                     putchar('-');
449                 putchar('\n');
450             }
451             list_keyblock( keyblock, 0, opt.fingerprint,
452                            opt.check_sigs?&stats:NULL );
453             release_kbnode( keyblock );
454         } while( !get_pubkey_next( ctx, NULL, &keyblock ) );
455         get_pubkey_end( ctx );
456     }
457
458     if(opt.check_sigs && !opt.with_colons)
459       print_signature_stats(&stats);
460 }
461
462 static void
463 print_key_data( PKT_public_key *pk, u32 *keyid )
464 {
465     int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
466     int i;
467
468     for(i=0; i < n; i++ ) {
469         printf("pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
470         mpi_print(stdout, pk->pkey[i], 1 );
471         putchar(':');
472         putchar('\n');
473     }
474 }
475
476 static void
477 print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock)
478 {
479   if(pk || (sk && sk->protect.s2k.mode!=1001))
480     {
481       unsigned int use = pk? pk->pubkey_usage : sk->pubkey_usage;
482     
483       if ( use & PUBKEY_USAGE_ENC )
484         putchar ('e');
485
486       if ( use & PUBKEY_USAGE_SIG )
487         {
488           putchar ('s');
489           if( pk? pk->is_primary : sk->is_primary )
490             putchar ('c');
491         }
492
493       if ( (use & PUBKEY_USAGE_AUTH) )
494         putchar ('a');
495     }
496
497     if ( keyblock ) { /* figure out the usable capabilities */
498         KBNODE k;
499         int enc=0, sign=0, cert=0, auth=0, disabled=0;
500
501         for (k=keyblock; k; k = k->next ) {
502             if ( k->pkt->pkttype == PKT_PUBLIC_KEY 
503                  || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
504                 pk = k->pkt->pkt.public_key;
505
506                 if(pk->is_primary)
507                   disabled=pk_is_disabled(pk);
508
509                 if ( pk->is_valid && !pk->is_revoked && !pk->has_expired ) {
510                     if ( pk->pubkey_usage & PUBKEY_USAGE_ENC )
511                         enc = 1;
512                     if ( pk->pubkey_usage & PUBKEY_USAGE_SIG )
513                       {
514                         sign = 1;
515                         if(pk->is_primary)
516                           cert = 1;
517                       }
518                     if ( (pk->pubkey_usage & PUBKEY_USAGE_AUTH) )
519                       auth = 1;
520                 }
521             }
522             else if ( k->pkt->pkttype == PKT_SECRET_KEY 
523                       || k->pkt->pkttype == PKT_SECRET_SUBKEY ) {
524                 sk = k->pkt->pkt.secret_key;
525                 if ( sk->is_valid && !sk->is_revoked && !sk->has_expired
526                      && sk->protect.s2k.mode!=1001 ) {
527                     if ( sk->pubkey_usage & PUBKEY_USAGE_ENC )
528                         enc = 1;
529                     if ( sk->pubkey_usage & PUBKEY_USAGE_SIG )
530                       {
531                         sign = 1;
532                         if(sk->is_primary)
533                           cert = 1;
534                       }
535                     if ( (sk->pubkey_usage & PUBKEY_USAGE_AUTH) )
536                         auth = 1;
537                 }
538             }
539         }
540         if (enc)
541             putchar ('E');
542         if (sign)
543             putchar ('S');
544         if (cert)
545             putchar ('C');
546         if (auth)
547             putchar ('A');
548         if (disabled)
549             putchar ('D');
550     }
551
552     putchar(':');
553 }
554
555 void
556 dump_attribs(const PKT_user_id *uid,PKT_public_key *pk,PKT_secret_key *sk)
557 {
558   int i;
559
560   if(!attrib_fp)
561     return;
562
563   for(i=0;i<uid->numattribs;i++)
564     {
565       if(is_status_enabled())
566         {
567           byte array[MAX_FINGERPRINT_LEN], *p;
568           char buf[(MAX_FINGERPRINT_LEN*2)+90];
569           size_t j,n;
570
571           if(pk)
572             fingerprint_from_pk( pk, array, &n );
573           else if(sk)
574             fingerprint_from_sk( sk, array, &n );
575           else
576             BUG();
577
578           p = array;
579           for(j=0; j < n ; j++, p++ )
580             sprintf(buf+2*j, "%02X", *p );
581
582           sprintf(buf+strlen(buf)," %lu %u %u %u %lu %lu %u",
583                   (ulong)uid->attribs[i].len,uid->attribs[i].type,i+1,
584                   uid->numattribs,(ulong)uid->created,(ulong)uid->expiredate,
585                   ((uid->is_primary?0x01:0)|
586                    (uid->is_revoked?0x02:0)|
587                    (uid->is_expired?0x04:0)));
588           write_status_text(STATUS_ATTRIBUTE,buf);
589         }
590
591       fwrite(uid->attribs[i].data,uid->attribs[i].len,1,attrib_fp);
592     }
593 }
594
595 static void
596 list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque )
597 {
598     int rc = 0;
599     KBNODE kbctx;
600     KBNODE node;
601     PKT_public_key *pk;
602     PKT_secret_key *sk;
603     u32 keyid[2];
604     int any=0;
605     struct sig_stats *stats=opaque;
606     int skip_sigs=0;
607     int newformat=((opt.list_options&LIST_SHOW_VALIDITY) && !secret)
608       || (opt.list_options & (LIST_SHOW_UNUSABLE_UIDS
609                               | LIST_SHOW_UNUSABLE_SUBKEYS))
610       || (keystrlen()>8);
611
612     /* get the keyid from the keyblock */
613     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
614     if( !node ) {
615         log_error("Oops; key lost!\n");
616         dump_kbnode( keyblock );
617         return;
618     }
619
620     if( secret )
621       {
622         pk = NULL;
623         sk = node->pkt->pkt.secret_key;
624         keyid_from_sk( sk, keyid );
625
626         printf("sec%c  %4u%c/%s %s%s",(sk->protect.s2k.mode==1001)?'#':
627                (sk->protect.s2k.mode==1002)?'>':' ',
628                nbits_from_sk( sk ),pubkey_letter( sk->pubkey_algo ),
629                keystr(keyid),datestr_from_sk( sk ),newformat?"":" " );
630
631         if(newformat && sk->expiredate )
632           printf(_(" [expires: %s]"), expirestr_from_sk( sk ) );
633       }
634     else
635       {
636 #if 0
637         int validity;
638 #endif
639         pk = node->pkt->pkt.public_key;
640         sk = NULL;
641         keyid_from_pk( pk, keyid );
642
643 #if 0
644         validity=get_validity(pk,NULL);
645 #endif
646
647         check_trustdb_stale();
648
649         printf("pub   %4u%c/%s %s%s",
650                nbits_from_pk(pk),pubkey_letter(pk->pubkey_algo),
651                keystr(keyid),datestr_from_pk( pk ),newformat?"":" " );
652
653         /* We didn't include this before in the key listing, but there
654            is room in the new format, so why not? */
655         if(newformat)
656           {
657             if(pk->is_revoked)
658               printf(_(" [revoked: %s]"), revokestr_from_pk( pk ) );
659             else if(pk->has_expired)
660               printf(_(" [expired: %s]"), expirestr_from_pk( pk ) );
661             else if(pk->expiredate)
662               printf(_(" [expires: %s]"), expirestr_from_pk( pk ) );
663           }
664
665 #if 0
666         /* I need to think about this some more.  It's easy enough to
667            include, but it looks sort of confusing in the
668            listing... */
669         if(opt.list_options&LIST_SHOW_VALIDITY)
670           printf(" [%s]",trust_value_to_string(validity));
671 #endif
672       }
673
674     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
675         if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
676             PKT_user_id *uid=node->pkt->pkt.user_id;
677
678             if((uid->is_expired || uid->is_revoked)
679                && !(opt.list_options&LIST_SHOW_UNUSABLE_UIDS))
680               {
681                 skip_sigs=1;
682                 continue;
683               }
684             else
685               skip_sigs=0;
686
687             if(attrib_fp && uid->attrib_data!=NULL)
688               dump_attribs(uid,pk,sk);
689
690             if(!any && newformat)
691               printf("\n");
692
693             if((uid->is_revoked || uid->is_expired)
694                || ((opt.list_options&LIST_SHOW_VALIDITY) && pk))
695               {
696                 const char *validity;
697                 int indent;
698
699                 if(uid->is_revoked)
700                   validity=_("revoked");
701                 else if(uid->is_expired)
702                   validity=_("expired");
703                 else
704                   validity=trust_value_to_string(get_validity(pk,uid));
705
706                 indent=(keystrlen()+7)-strlen(validity);
707
708                 if(indent<0)
709                   indent=0;
710
711                 printf("uid%*s[%s] ",indent,"",validity);
712               }
713             else if(newformat)
714               printf("uid%*s",keystrlen()+10,"");
715             else if(any)
716               printf("uid%*s",29,"");
717
718             print_utf8_string( stdout, uid->name, uid->len );
719             putchar('\n');
720             if( !any ) {
721                 if( fpr )
722                     print_fingerprint( pk, sk, 0 );
723                 if( opt.with_key_data )
724                     print_key_data( pk, keyid );
725                 any = 1;
726             }
727
728             if((opt.list_options&LIST_SHOW_PHOTOS) && uid->attribs!=NULL)
729               show_photos(uid->attribs,uid->numattribs,pk,sk);
730         }
731         else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
732             u32 keyid2[2];
733             PKT_public_key *pk2 = node->pkt->pkt.public_key;
734
735             if((pk2->is_revoked || pk2->has_expired)
736                && !(opt.list_options&LIST_SHOW_UNUSABLE_SUBKEYS))
737               {
738                 skip_sigs=1;
739                 continue;
740               }
741             else
742               skip_sigs=0;
743
744             if( !any ) {
745                 putchar('\n');
746                 if( fpr )
747                     print_fingerprint( pk, sk, 0 ); /* of the main key */
748                 any = 1;
749             }
750
751             keyid_from_pk( pk2, keyid2 );
752             printf("sub   %4u%c/%s %s",
753                    nbits_from_pk( pk2 ),pubkey_letter( pk2->pubkey_algo ),
754                    keystr(keyid2),datestr_from_pk(pk2));
755             if( pk2->is_revoked )
756               printf(_(" [revoked: %s]"), revokestr_from_pk(pk2));
757             else if( pk2->has_expired )
758                 printf(_(" [expired: %s]"), expirestr_from_pk( pk2 ) );
759             else if( pk2->expiredate )
760                 printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) );
761             putchar('\n');
762             if( fpr > 1 )
763                 print_fingerprint( pk2, NULL, 0 );
764             if( opt.with_key_data )
765                 print_key_data( pk2, keyid2 );
766         }
767         else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
768             u32 keyid2[2];
769             PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
770
771             if( !any ) {
772                 putchar('\n');
773                 if( fpr )
774                     print_fingerprint( pk, sk, 0 ); /* of the main key */
775                 any = 1;
776             }
777
778             keyid_from_sk( sk2, keyid2 );
779             printf("ssb%c  %4u%c/%s %s",
780                    (sk->protect.s2k.mode==1001)?'#':
781                    (sk->protect.s2k.mode==1002)?'>':' ',
782                    nbits_from_sk( sk2 ),pubkey_letter( sk2->pubkey_algo ),
783                    keystr(keyid2),datestr_from_sk( sk2 ) );
784             if( sk2->expiredate )
785               printf(_(" [expires: %s]"), expirestr_from_sk( sk2 ) );
786             putchar('\n');
787             if( fpr > 1 )
788                 print_fingerprint( NULL, sk2, 0 );
789         }
790         else if( opt.list_sigs
791                  && node->pkt->pkttype == PKT_SIGNATURE
792                  && !skip_sigs ) {
793             PKT_signature *sig = node->pkt->pkt.signature;
794             int sigrc;
795             char *sigstr;
796
797             if( stats ) {
798                 /*fflush(stdout);*/
799                 rc = check_key_signature( keyblock, node, NULL );
800                 switch( rc ) {
801                  case 0:                 sigrc = '!'; break;
802                  case G10ERR_BAD_SIGN:   stats->inv_sigs++; sigrc = '-'; break;
803                  case G10ERR_NO_PUBKEY: 
804                  case G10ERR_UNU_PUBKEY: stats->no_key++; continue;
805                  default:                stats->oth_err++; sigrc = '%'; break;
806                 }
807
808                 /* TODO: Make sure a cached sig record here still has
809                    the pk that issued it.  See also
810                    keyedit.c:print_and_check_one_sig */
811             }
812             else {
813                 rc = 0;
814                 sigrc = ' ';
815             }
816
817             if( !any ) { /* no user id, (maybe a revocation follows)*/
818               /* Check if the pk is really revoked - there could be a
819                  0x20 sig packet there even if we are not revoked
820                  (say, if a revocation key issued the packet, but the
821                  revocation key isn't present to verify it.) */
822                 if( sig->sig_class == 0x20 && pk->is_revoked )
823                     puts("[revoked]");
824                 else if( sig->sig_class == 0x18 )
825                     puts("[key binding]");
826                 else if( sig->sig_class == 0x28 )
827                     puts("[subkey revoked]");
828                 else
829                     putchar('\n');
830                 if( fpr )
831                     print_fingerprint( pk, sk, 0 );
832                 any=1;
833             }
834
835             if( sig->sig_class == 0x20 || sig->sig_class == 0x28
836                                        || sig->sig_class == 0x30 )
837                sigstr = "rev";
838             else if( (sig->sig_class&~3) == 0x10 )
839                sigstr = "sig";
840             else if( sig->sig_class == 0x18 )
841                sigstr = "sig";
842             else if( sig->sig_class == 0x1F )
843                sigstr = "sig";
844             else {
845                 printf("sig                             "
846                        "[unexpected signature class 0x%02x]\n",sig->sig_class );
847                 continue;
848             }
849
850             fputs( sigstr, stdout );
851             printf("%c%c %c%c%c%c%c%c %s %s",
852                    sigrc,(sig->sig_class-0x10>0 &&
853                           sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ',
854                    sig->flags.exportable?' ':'L',
855                    sig->flags.revocable?' ':'R',
856                    sig->flags.policy_url?'P':' ',
857                    sig->flags.notation?'N':' ',
858                    sig->flags.expired?'X':' ',
859                    (sig->trust_depth>9)?'T':
860                    (sig->trust_depth>0)?'0'+sig->trust_depth:' ',
861                    keystr(sig->keyid),datestr_from_sig(sig));
862             if(opt.list_options&LIST_SHOW_SIG_EXPIRE)
863               printf(" %s", expirestr_from_sig(sig));
864             printf("  ");
865             if( sigrc == '%' )
866                 printf("[%s] ", g10_errstr(rc) );
867             else if( sigrc == '?' )
868                 ;
869             else if ( !opt.fast_list_mode ) {
870                 size_t n;
871                 char *p = get_user_id( sig->keyid, &n );
872                 print_utf8_string( stdout, p, n );
873                 m_free(p);
874             }
875             putchar('\n');
876
877             if(sig->flags.policy_url
878                && (opt.list_options&LIST_SHOW_POLICY_URLS))
879               show_policy_url(sig,3,0);
880
881             if(sig->flags.notation
882                && (opt.list_options&LIST_SHOW_NOTATIONS))
883               show_notation(sig,3,0);
884
885             if(sig->flags.pref_ks
886                && (opt.list_options&LIST_SHOW_KEYSERVER_URLS))
887               show_keyserver_url(sig,3,0);
888
889             /* fixme: check or list other sigs here */
890         }
891     }
892     putchar('\n');
893 }
894
895
896 static void
897 list_keyblock_colon( KBNODE keyblock, int secret, int fpr )
898 {
899     int rc = 0;
900     KBNODE kbctx;
901     KBNODE node;
902     PKT_public_key *pk;
903     PKT_secret_key *sk;
904     u32 keyid[2];
905     int any=0;
906     int trustletter = 0;
907     int ulti_hack = 0;
908     int i;
909
910     /* get the keyid from the keyblock */
911     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
912     if( !node ) {
913         log_error("Oops; key lost!\n");
914         dump_kbnode( keyblock );
915         return;
916     }
917
918     if( secret ) {
919         pk = NULL;
920         sk = node->pkt->pkt.secret_key;
921         keyid_from_sk( sk, keyid );
922         printf("sec::%u:%d:%08lX%08lX:%s:%s:::",
923                     nbits_from_sk( sk ),
924                     sk->pubkey_algo,
925                     (ulong)keyid[0],(ulong)keyid[1],
926                     colon_datestr_from_sk( sk ),
927                     colon_strtime (sk->expiredate)
928                     /* fixme: add LID here */ );
929     }
930     else {
931         pk = node->pkt->pkt.public_key;
932         sk = NULL;
933         keyid_from_pk( pk, keyid );
934         fputs( "pub:", stdout );
935         if ( !pk->is_valid )
936             putchar ('i');
937         else if ( pk->is_revoked )
938             putchar ('r');
939         else if ( pk->has_expired )
940             putchar ('e');
941         else if ( opt.fast_list_mode || opt.no_expensive_trust_checks ) 
942             ;
943         else {
944             trustletter = get_validity_info ( pk, NULL );
945             if( trustletter == 'u' )
946                 ulti_hack = 1;
947             putchar(trustletter);
948         }
949         printf(":%u:%d:%08lX%08lX:%s:%s::",
950                     nbits_from_pk( pk ),
951                     pk->pubkey_algo,
952                     (ulong)keyid[0],(ulong)keyid[1],
953                     colon_datestr_from_pk( pk ),
954                     colon_strtime (pk->expiredate) );
955         if( !opt.fast_list_mode && !opt.no_expensive_trust_checks  )
956             putchar( get_ownertrust_info(pk) );
957             putchar(':');
958     }
959     
960     if (opt.fixed_list_mode) {
961         /* do not merge the first uid with the primary key */
962         putchar(':');
963         putchar(':');
964         print_capabilities (pk, sk, keyblock);
965         if (secret) {
966           putchar(':'); /* End of field 13. */
967           putchar(':'); /* End of field 14. */
968           if (sk->protect.s2k.mode == 1001)
969             putchar('#'); /* Key is just a stub. */
970           else if (sk->protect.s2k.mode == 1002) {
971             /* Key is stored on an external token (card) or handled by
972                the gpg-agent.  Print the serial number of that token
973                here. */
974             for (i=0; i < sk->protect.ivlen; i++)
975               printf ("%02X", sk->protect.iv[i]);
976           }
977           putchar(':'); /* End of field 15. */
978         }
979         putchar('\n');
980         if( fpr )
981             print_fingerprint( pk, sk, 0 );
982         if( opt.with_key_data )
983             print_key_data( pk, keyid );
984         any = 1;
985     }
986
987
988     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
989         if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
990             PKT_user_id *uid=node->pkt->pkt.user_id;
991             if(attrib_fp && node->pkt->pkt.user_id->attrib_data!=NULL)
992               dump_attribs(node->pkt->pkt.user_id,pk,sk);
993             /*
994              * Fixme: We need a is_valid flag here too 
995              */
996             if( any ) {
997                 char *str=uid->attrib_data?"uat":"uid";
998                 /* If we're listing a secret key, leave out the
999                    validity values for now.  This is handled better in
1000                    1.9. */
1001                 if ( sk )
1002                     printf("%s:::::",str);
1003                 else if ( uid->is_revoked )
1004                     printf("%s:r::::",str);
1005                 else if ( uid->is_expired )
1006                     printf("%s:e::::",str);
1007                 else if ( opt.no_expensive_trust_checks )
1008                     printf("%s:::::",str);
1009                 else {
1010                     int uid_validity;
1011
1012                     if( pk && !ulti_hack )
1013                       uid_validity=get_validity_info (pk, uid);
1014                     else
1015                         uid_validity = 'u';
1016                     printf("%s:%c::::",str,uid_validity);
1017                 }
1018
1019                 printf("%s:",colon_strtime(uid->created));
1020                 printf("%s:",colon_strtime(uid->expiredate));
1021
1022                 namehash_from_uid(uid);
1023
1024                 for(i=0; i < 20; i++ )
1025                   printf("%02X",uid->namehash[i]);
1026
1027                 printf("::");
1028             }
1029             if(uid->attrib_data)
1030               printf("%u %lu",uid->numattribs,uid->attrib_len);
1031             else
1032               print_string(stdout,uid->name,uid->len, ':' );
1033             putchar(':');
1034             if (any)
1035                 putchar('\n');
1036             else {
1037                 putchar(':');
1038                 print_capabilities (pk, sk, keyblock);
1039                 putchar('\n');
1040                 if( fpr )
1041                     print_fingerprint( pk, sk, 0 );
1042                 if( opt.with_key_data )
1043                     print_key_data( pk, keyid );
1044                 any = 1;
1045             }
1046         }
1047         else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
1048             u32 keyid2[2];
1049             PKT_public_key *pk2 = node->pkt->pkt.public_key;
1050
1051             if( !any ) {
1052                 putchar(':');
1053                 putchar(':');
1054                 print_capabilities (pk, sk, keyblock);
1055                 putchar('\n');
1056                 if( fpr )
1057                     print_fingerprint( pk, sk, 0 ); /* of the main key */
1058                 any = 1;
1059             }
1060
1061             keyid_from_pk( pk2, keyid2 );
1062             fputs ("sub:", stdout );
1063             if ( !pk2->is_valid )
1064                 putchar ('i');
1065             else if ( pk2->is_revoked )
1066                 putchar ('r');
1067             else if ( pk2->has_expired )
1068                 putchar ('e');
1069             else if ( opt.fast_list_mode || opt.no_expensive_trust_checks )
1070                 ;
1071             else {
1072                 /* trustletter should always be defined here */
1073                 if(trustletter)
1074                   printf("%c", trustletter );
1075             }
1076             printf(":%u:%d:%08lX%08lX:%s:%s:::::",
1077                         nbits_from_pk( pk2 ),
1078                         pk2->pubkey_algo,
1079                         (ulong)keyid2[0],(ulong)keyid2[1],
1080                         colon_datestr_from_pk( pk2 ),
1081                         colon_strtime (pk2->expiredate)
1082                         /* fixme: add LID and ownertrust here */
1083                                                 );
1084             print_capabilities (pk2, NULL, NULL);
1085             putchar('\n');
1086             if( fpr > 1 )
1087                 print_fingerprint( pk2, NULL, 0 );
1088             if( opt.with_key_data )
1089                 print_key_data( pk2, keyid2 );
1090         }
1091         else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
1092             u32 keyid2[2];
1093             PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
1094
1095             if( !any ) {
1096                 putchar(':');
1097                 putchar(':');
1098                 print_capabilities (pk, sk, keyblock);
1099                 putchar('\n');
1100                 if( fpr )
1101                     print_fingerprint( pk, sk, 0 ); /* of the main key */
1102                 any = 1;
1103             }
1104
1105             keyid_from_sk( sk2, keyid2 );
1106             printf("ssb::%u:%d:%08lX%08lX:%s:%s:::::",
1107                         nbits_from_sk( sk2 ),
1108                         sk2->pubkey_algo,
1109                         (ulong)keyid2[0],(ulong)keyid2[1],
1110                         colon_datestr_from_sk( sk2 ),
1111                         colon_strtime (sk2->expiredate)
1112                    /* fixme: add LID */ );
1113             print_capabilities (NULL, sk2, NULL);
1114             if (opt.fixed_list_mode) {
1115               /* We print the serial number only in fixed list mode
1116                  for the primary key so, so avoid questions we print
1117                  it for subkeys also only in this mode.  There is no
1118                  technical reason, though. */
1119               putchar(':'); /* End of field 13. */
1120               putchar(':'); /* End of field 14. */
1121               if (sk2->protect.s2k.mode == 1001)
1122                 putchar('#'); /* Key is just a stub. */
1123               else if (sk2->protect.s2k.mode == 1002) {
1124                 /* Key is stored on an external token (card) or handled by
1125                    the gpg-agent.  Print the serial number of that token
1126                    here. */
1127                 for (i=0; i < sk2->protect.ivlen; i++)
1128                   printf ("%02X", sk2->protect.iv[i]);
1129               }
1130               putchar(':'); /* End of field 15. */
1131             }
1132             putchar ('\n');
1133             if( fpr > 1 )
1134               print_fingerprint( NULL, sk2, 0 );
1135         }
1136         else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) {
1137             PKT_signature *sig = node->pkt->pkt.signature;
1138             int sigrc,fprokay=0;
1139             char *sigstr;
1140             size_t fplen;
1141             byte fparray[MAX_FINGERPRINT_LEN];
1142
1143             if( !any ) { /* no user id, (maybe a revocation follows)*/
1144                 if( sig->sig_class == 0x20 )
1145                     fputs("[revoked]:", stdout);
1146                 else if( sig->sig_class == 0x18 )
1147                     fputs("[key binding]:", stdout);
1148                 else if( sig->sig_class == 0x28 )
1149                     fputs("[subkey revoked]:", stdout);
1150                 else
1151                     putchar (':');
1152                 putchar(':');
1153                 print_capabilities (pk, sk, keyblock);
1154                 putchar('\n');
1155                 if( fpr )
1156                     print_fingerprint( pk, sk, 0 );
1157                 any=1;
1158             }
1159
1160             if( sig->sig_class == 0x20 || sig->sig_class == 0x28
1161                                        || sig->sig_class == 0x30 )
1162                sigstr = "rev";
1163             else if( (sig->sig_class&~3) == 0x10 )
1164                sigstr = "sig";
1165             else if( sig->sig_class == 0x18 )
1166                sigstr = "sig";
1167             else if( sig->sig_class == 0x1F )
1168                sigstr = "sig";
1169             else {
1170                 printf ("sig::::::::::%02x%c:\n",
1171                         sig->sig_class, sig->flags.exportable?'x':'l');
1172                 continue;
1173             }
1174             if( opt.check_sigs ) {
1175                 PKT_public_key *signer_pk=NULL;
1176
1177                 fflush(stdout);
1178                 if(opt.no_sig_cache)
1179                   signer_pk=m_alloc_clear(sizeof(PKT_public_key));
1180
1181                 rc = check_key_signature2( keyblock, node, NULL, signer_pk,
1182                                            NULL, NULL, NULL );
1183                 switch( rc ) {
1184                   case 0:                  sigrc = '!'; break;
1185                   case G10ERR_BAD_SIGN:    sigrc = '-'; break;
1186                   case G10ERR_NO_PUBKEY: 
1187                   case G10ERR_UNU_PUBKEY:  sigrc = '?'; break;
1188                   default:                 sigrc = '%'; break;
1189                 }
1190
1191                 if(opt.no_sig_cache)
1192                   {
1193                     if(rc==0)
1194                       {
1195                         fingerprint_from_pk (signer_pk, fparray, &fplen);
1196                         fprokay=1;
1197                       }
1198                     free_public_key(signer_pk);
1199                   }
1200             }
1201             else {
1202                 rc = 0;
1203                 sigrc = ' ';
1204             }
1205             fputs( sigstr, stdout );
1206             putchar(':');
1207             if( sigrc != ' ' )
1208                 putchar(sigrc);
1209             printf("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
1210                    (ulong)sig->keyid[0], (ulong)sig->keyid[1],
1211                    colon_datestr_from_sig(sig),
1212                    colon_expirestr_from_sig(sig));
1213
1214             if(sig->trust_depth || sig->trust_value)
1215               printf("%d %d",sig->trust_depth,sig->trust_value);
1216             printf(":");
1217
1218             if(sig->trust_regexp)
1219               print_string(stdout,sig->trust_regexp,
1220                            strlen(sig->trust_regexp),':');
1221             printf(":");
1222
1223             if( sigrc == '%' )
1224                 printf("[%s] ", g10_errstr(rc) );
1225             else if( sigrc == '?' )
1226                 ;
1227             else if ( !opt.fast_list_mode ) {
1228                 size_t n;
1229                 char *p = get_user_id( sig->keyid, &n );
1230                 print_string( stdout, p, n, ':' );
1231                 m_free(p);
1232             }
1233             printf(":%02x%c:", sig->sig_class,sig->flags.exportable?'x':'l');
1234
1235             if(opt.no_sig_cache && opt.check_sigs && fprokay)
1236               {
1237                 printf(":");
1238
1239                 for (i=0; i < fplen ; i++ )
1240                   printf ("%02X", fparray[i] );
1241
1242                 printf(":");
1243               }
1244
1245             printf("\n");
1246
1247             /* fixme: check or list other sigs here */
1248         }
1249     }
1250     if( !any ) {/* oops, no user id */
1251         putchar(':');
1252         putchar(':');
1253         print_capabilities (pk, sk, keyblock);
1254         putchar('\n');
1255     }
1256 }
1257
1258 /*
1259  * Reorder the keyblock so that the primary user ID (and not attribute
1260  * packet) comes first.  Fixme: Replace this by a generic sort
1261  * function.  */
1262 void
1263 reorder_keyblock (KBNODE keyblock)
1264 {
1265     KBNODE primary = NULL, primary0 = NULL, primary2 = NULL;
1266     KBNODE last, node;
1267
1268     for (node=keyblock; node; primary0=node, node = node->next) {
1269         if( node->pkt->pkttype == PKT_USER_ID &&
1270             !node->pkt->pkt.user_id->attrib_data &&
1271             node->pkt->pkt.user_id->is_primary ) {
1272             primary = primary2 = node;
1273             for (node=node->next; node; primary2=node, node = node->next ) {
1274                 if( node->pkt->pkttype == PKT_USER_ID 
1275                     || node->pkt->pkttype == PKT_PUBLIC_SUBKEY 
1276                     || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
1277                     break;
1278                 }
1279             }
1280             break;
1281         }
1282     }
1283     if ( !primary )
1284         return;  /* no primary key flag found (should not happen) */
1285
1286     for (last=NULL, node=keyblock; node; last = node, node = node->next) {
1287         if( node->pkt->pkttype == PKT_USER_ID )
1288             break;
1289     }
1290     assert (node);
1291     assert (last); /* the user ID is never the first packet */
1292     assert (primary0);  /* ditto (this is the node before primary) */
1293     if ( node == primary )
1294         return; /* already the first one */
1295
1296     last->next = primary;
1297     primary0->next = primary2->next;
1298     primary2->next = node;
1299 }
1300
1301 void
1302 list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque )
1303 {
1304     reorder_keyblock (keyblock);
1305     if (opt.with_colons)
1306         list_keyblock_colon (keyblock, secret, fpr );
1307     else
1308         list_keyblock_print (keyblock, secret, fpr, opaque );
1309 }
1310
1311 /*
1312  * standard function to print the finperprint.
1313  * mode 0: as used in key listings, opt.with_colons is honored
1314  *      1: print using log_info ()
1315  *      2: direct use of tty
1316  *      3: direct use of tty but only primary key.
1317  * modes 1 and 2 will try and print both subkey and primary key fingerprints
1318  */
1319 void
1320 print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode )
1321 {
1322     byte array[MAX_FINGERPRINT_LEN], *p;
1323     size_t i, n;
1324     FILE *fp;
1325     const char *text;
1326     int primary=0;
1327
1328     if(sk)
1329       {
1330         if(sk->main_keyid[0]==sk->keyid[0] && sk->main_keyid[1]==sk->keyid[1])
1331           primary=1;
1332       }
1333     else
1334       {
1335         if(pk->main_keyid[0]==pk->keyid[0] && pk->main_keyid[1]==pk->keyid[1])
1336           primary=1;
1337       }
1338
1339     /* Just to be safe */
1340     if(mode&0x80 && !primary)
1341       {
1342         log_error("primary key is not really primary!\n");
1343         return;
1344       }
1345
1346     mode&=~0x80;
1347
1348     if(!primary && (mode==1 || mode==2))
1349       {
1350         if(sk)
1351           {
1352             PKT_secret_key *primary_sk=m_alloc_clear(sizeof(*primary_sk));
1353             get_seckey(primary_sk,sk->main_keyid);
1354             print_fingerprint(NULL,primary_sk,mode|0x80);
1355             free_secret_key(primary_sk);
1356           }
1357         else
1358           {
1359             PKT_public_key *primary_pk=m_alloc_clear(sizeof(*primary_pk));
1360             get_pubkey(primary_pk,pk->main_keyid);
1361             print_fingerprint(primary_pk,NULL,mode|0x80);
1362             free_public_key(primary_pk);
1363           }
1364       }
1365
1366     if (mode == 1) {
1367         fp = log_stream ();
1368         if(primary)
1369           text = _("Primary key fingerprint:");
1370         else
1371           text = _("     Subkey fingerprint:");
1372     }
1373     else if (mode == 2) {
1374         fp = NULL; /* use tty */
1375         /* Translators: this should fit into 24 bytes to that the fingerprint
1376          * data is properly aligned with the user ID */
1377         if(primary)
1378           text = _(" Primary key fingerprint:");
1379         else
1380           text = _("      Subkey fingerprint:");
1381     }
1382     else if (mode == 3) {
1383         fp = NULL; /* use tty */
1384         text = _("      Key fingerprint =");
1385     }
1386     else {
1387         fp = stdout;
1388         text = _("      Key fingerprint =");
1389     }
1390   
1391     if (sk)
1392         fingerprint_from_sk (sk, array, &n);
1393     else
1394         fingerprint_from_pk (pk, array, &n);
1395     p = array;
1396     if (opt.with_colons && !mode) {
1397         fprintf (fp, "fpr:::::::::");
1398         for (i=0; i < n ; i++, p++ )
1399             fprintf (fp, "%02X", *p );
1400         putc(':', fp);
1401     }
1402     else {
1403         if (fp)
1404             fputs (text, fp);
1405         else
1406             tty_printf ("%s", text);
1407         if (n == 20) {
1408             for (i=0; i < n ; i++, i++, p += 2 ) {
1409                 if (fp) {
1410                     if (i == 10 )
1411                         putc(' ', fp);
1412                     fprintf (fp, " %02X%02X", *p, p[1] );
1413                 }
1414                 else {
1415                     if (i == 10 )
1416                         tty_printf (" ");
1417                     tty_printf (" %02X%02X", *p, p[1]);
1418                 }
1419             }
1420         }
1421         else {
1422             for (i=0; i < n ; i++, p++ ) {
1423                 if (fp) {
1424                     if (i && !(i%8) )
1425                         putc (' ', fp);
1426                     fprintf (fp, " %02X", *p );
1427                 }
1428                 else {
1429                     if (i && !(i%8) )
1430                         tty_printf (" ");
1431                     tty_printf (" %02X", *p );
1432                 }
1433             }
1434         }
1435     }
1436     if (fp)
1437         putc ('\n', fp);
1438     else
1439         tty_printf ("\n");
1440 }
1441
1442 void set_attrib_fd(int fd)
1443 {
1444   static int last_fd=-1;
1445
1446   if ( fd != -1 && last_fd == fd )
1447     return;
1448
1449   if ( attrib_fp && attrib_fp != stdout && attrib_fp != stderr )
1450     fclose (attrib_fp);
1451   attrib_fp = NULL;
1452   if ( fd == -1 ) 
1453     return;
1454
1455   if( fd == 1 )
1456     attrib_fp = stdout;
1457   else if( fd == 2 )
1458     attrib_fp = stderr;
1459   else
1460     attrib_fp = fdopen( fd, "wb" );
1461   if( !attrib_fp ) {
1462     log_fatal("can't open fd %d for attribute output: %s\n",
1463               fd, strerror(errno));
1464   }
1465
1466   last_fd = fd;
1467 }