* keydb.h, keyid.c (keystr_from_pk, keystr_from_sk): New functions to pull
[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 )
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     int any=0;
604     struct sig_stats *stats=opaque;
605     int skip_sigs=0;
606     int newformat=((opt.list_options&LIST_SHOW_VALIDITY) && !secret)
607       || (opt.list_options & (LIST_SHOW_UNUSABLE_UIDS
608                               | LIST_SHOW_UNUSABLE_SUBKEYS))
609       || (keystrlen()>8);
610
611     /* get the keyid from the keyblock */
612     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
613     if( !node ) {
614         log_error("Oops; key lost!\n");
615         dump_kbnode( keyblock );
616         return;
617     }
618
619     if( secret )
620       {
621         pk = NULL;
622         sk = node->pkt->pkt.secret_key;
623
624         printf("sec%c  %4u%c/%s %s%s",(sk->protect.s2k.mode==1001)?'#':
625                (sk->protect.s2k.mode==1002)?'>':' ',
626                nbits_from_sk( sk ),pubkey_letter( sk->pubkey_algo ),
627                keystr_from_sk(sk),datestr_from_sk( sk ),newformat?"":" " );
628
629         if(newformat && sk->expiredate )
630           printf(_(" [expires: %s]"), expirestr_from_sk( sk ) );
631       }
632     else
633       {
634 #if 0
635         int validity;
636 #endif
637         pk = node->pkt->pkt.public_key;
638         sk = NULL;
639
640 #if 0
641         validity=get_validity(pk,NULL);
642 #endif
643
644         check_trustdb_stale();
645
646         printf("pub   %4u%c/%s %s%s",
647                nbits_from_pk(pk),pubkey_letter(pk->pubkey_algo),
648                keystr_from_pk(pk),datestr_from_pk( pk ),newformat?"":" " );
649
650         /* We didn't include this before in the key listing, but there
651            is room in the new format, so why not? */
652         if(newformat)
653           {
654             if(pk->is_revoked)
655               printf(_(" [revoked: %s]"), revokestr_from_pk( pk ) );
656             else if(pk->has_expired)
657               printf(_(" [expired: %s]"), expirestr_from_pk( pk ) );
658             else if(pk->expiredate)
659               printf(_(" [expires: %s]"), expirestr_from_pk( pk ) );
660           }
661
662 #if 0
663         /* I need to think about this some more.  It's easy enough to
664            include, but it looks sort of confusing in the
665            listing... */
666         if(opt.list_options&LIST_SHOW_VALIDITY)
667           printf(" [%s]",trust_value_to_string(validity));
668 #endif
669       }
670
671     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
672         if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
673             PKT_user_id *uid=node->pkt->pkt.user_id;
674
675             if((uid->is_expired || uid->is_revoked)
676                && !(opt.list_options&LIST_SHOW_UNUSABLE_UIDS))
677               {
678                 skip_sigs=1;
679                 continue;
680               }
681             else
682               skip_sigs=0;
683
684             if(attrib_fp && uid->attrib_data!=NULL)
685               dump_attribs(uid,pk,sk);
686
687             if(!any && newformat)
688               printf("\n");
689
690             if((uid->is_revoked || uid->is_expired)
691                || ((opt.list_options&LIST_SHOW_VALIDITY) && pk))
692               {
693                 const char *validity;
694                 int indent;
695
696                 if(uid->is_revoked)
697                   validity=_("revoked");
698                 else if(uid->is_expired)
699                   validity=_("expired");
700                 else
701                   validity=trust_value_to_string(get_validity(pk,uid));
702
703                 indent=(keystrlen()+7)-strlen(validity);
704
705                 if(indent<0)
706                   indent=0;
707
708                 printf("uid%*s[%s] ",indent,"",validity);
709               }
710             else if(newformat)
711               printf("uid%*s",keystrlen()+10,"");
712             else if(any)
713               printf("uid%*s",29,"");
714
715             print_utf8_string( stdout, uid->name, uid->len );
716             putchar('\n');
717             if( !any ) {
718                 if( fpr )
719                     print_fingerprint( pk, sk, 0 );
720                 if( opt.with_key_data )
721                     print_key_data( pk );
722                 any = 1;
723             }
724
725             if((opt.list_options&LIST_SHOW_PHOTOS) && uid->attribs!=NULL)
726               show_photos(uid->attribs,uid->numattribs,pk,sk);
727         }
728         else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
729           {
730             PKT_public_key *pk2 = node->pkt->pkt.public_key;
731
732             if((pk2->is_revoked || pk2->has_expired)
733                && !(opt.list_options&LIST_SHOW_UNUSABLE_SUBKEYS))
734               {
735                 skip_sigs=1;
736                 continue;
737               }
738             else
739               skip_sigs=0;
740
741             if( !any )
742               {
743                 putchar('\n');
744                 if( fpr )
745                   print_fingerprint( pk, sk, 0 ); /* of the main key */
746                 any = 1;
747               }
748
749             printf("sub   %4u%c/%s %s",
750                    nbits_from_pk( pk2 ),pubkey_letter( pk2->pubkey_algo ),
751                    keystr_from_pk(pk2),datestr_from_pk(pk2));
752             if( pk2->is_revoked )
753               printf(_(" [revoked: %s]"), revokestr_from_pk(pk2));
754             else if( pk2->has_expired )
755               printf(_(" [expired: %s]"), expirestr_from_pk( pk2 ) );
756             else if( pk2->expiredate )
757               printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) );
758             putchar('\n');
759             if( fpr > 1 )
760               print_fingerprint( pk2, NULL, 0 );
761             if( opt.with_key_data )
762               print_key_data( pk2 );
763           }
764         else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
765           {
766             PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
767
768             if( !any )
769               {
770                 putchar('\n');
771                 if( fpr )
772                   print_fingerprint( pk, sk, 0 ); /* of the main key */
773                 any = 1;
774               }
775
776             printf("ssb%c  %4u%c/%s %s",
777                    (sk->protect.s2k.mode==1001)?'#':
778                    (sk->protect.s2k.mode==1002)?'>':' ',
779                    nbits_from_sk( sk2 ),pubkey_letter( sk2->pubkey_algo ),
780                    keystr_from_sk(sk2),datestr_from_sk( sk2 ) );
781             if( sk2->expiredate )
782               printf(_(" [expires: %s]"), expirestr_from_sk( sk2 ) );
783             putchar('\n');
784             if( fpr > 1 )
785               print_fingerprint( NULL, sk2, 0 );
786           }
787         else if( opt.list_sigs
788                  && node->pkt->pkttype == PKT_SIGNATURE
789                  && !skip_sigs ) {
790             PKT_signature *sig = node->pkt->pkt.signature;
791             int sigrc;
792             char *sigstr;
793
794             if( stats ) {
795                 /*fflush(stdout);*/
796                 rc = check_key_signature( keyblock, node, NULL );
797                 switch( rc ) {
798                  case 0:                 sigrc = '!'; break;
799                  case G10ERR_BAD_SIGN:   stats->inv_sigs++; sigrc = '-'; break;
800                  case G10ERR_NO_PUBKEY: 
801                  case G10ERR_UNU_PUBKEY: stats->no_key++; continue;
802                  default:                stats->oth_err++; sigrc = '%'; break;
803                 }
804
805                 /* TODO: Make sure a cached sig record here still has
806                    the pk that issued it.  See also
807                    keyedit.c:print_and_check_one_sig */
808             }
809             else {
810                 rc = 0;
811                 sigrc = ' ';
812             }
813
814             if( !any ) { /* no user id, (maybe a revocation follows)*/
815               /* Check if the pk is really revoked - there could be a
816                  0x20 sig packet there even if we are not revoked
817                  (say, if a revocation key issued the packet, but the
818                  revocation key isn't present to verify it.) */
819                 if( sig->sig_class == 0x20 && pk->is_revoked )
820                     puts("[revoked]");
821                 else if( sig->sig_class == 0x18 )
822                     puts("[key binding]");
823                 else if( sig->sig_class == 0x28 )
824                     puts("[subkey revoked]");
825                 else
826                     putchar('\n');
827                 if( fpr )
828                     print_fingerprint( pk, sk, 0 );
829                 any=1;
830             }
831
832             if( sig->sig_class == 0x20 || sig->sig_class == 0x28
833                                        || sig->sig_class == 0x30 )
834                sigstr = "rev";
835             else if( (sig->sig_class&~3) == 0x10 )
836                sigstr = "sig";
837             else if( sig->sig_class == 0x18 )
838                sigstr = "sig";
839             else if( sig->sig_class == 0x1F )
840                sigstr = "sig";
841             else {
842                 printf("sig                             "
843                        "[unexpected signature class 0x%02x]\n",sig->sig_class );
844                 continue;
845             }
846
847             fputs( sigstr, stdout );
848             printf("%c%c %c%c%c%c%c%c %s %s",
849                    sigrc,(sig->sig_class-0x10>0 &&
850                           sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ',
851                    sig->flags.exportable?' ':'L',
852                    sig->flags.revocable?' ':'R',
853                    sig->flags.policy_url?'P':' ',
854                    sig->flags.notation?'N':' ',
855                    sig->flags.expired?'X':' ',
856                    (sig->trust_depth>9)?'T':
857                    (sig->trust_depth>0)?'0'+sig->trust_depth:' ',
858                    keystr(sig->keyid),datestr_from_sig(sig));
859             if(opt.list_options&LIST_SHOW_SIG_EXPIRE)
860               printf(" %s", expirestr_from_sig(sig));
861             printf("  ");
862             if( sigrc == '%' )
863                 printf("[%s] ", g10_errstr(rc) );
864             else if( sigrc == '?' )
865                 ;
866             else if ( !opt.fast_list_mode ) {
867                 size_t n;
868                 char *p = get_user_id( sig->keyid, &n );
869                 print_utf8_string( stdout, p, n );
870                 m_free(p);
871             }
872             putchar('\n');
873
874             if(sig->flags.policy_url
875                && (opt.list_options&LIST_SHOW_POLICY_URLS))
876               show_policy_url(sig,3,0);
877
878             if(sig->flags.notation
879                && (opt.list_options&LIST_SHOW_NOTATIONS))
880               show_notation(sig,3,0);
881
882             if(sig->flags.pref_ks
883                && (opt.list_options&LIST_SHOW_KEYSERVER_URLS))
884               show_keyserver_url(sig,3,0);
885
886             /* fixme: check or list other sigs here */
887         }
888     }
889     putchar('\n');
890 }
891
892
893 static void
894 list_keyblock_colon( KBNODE keyblock, int secret, int fpr )
895 {
896     int rc = 0;
897     KBNODE kbctx;
898     KBNODE node;
899     PKT_public_key *pk;
900     PKT_secret_key *sk;
901     u32 keyid[2];
902     int any=0;
903     int trustletter = 0;
904     int ulti_hack = 0;
905     int i;
906
907     /* get the keyid from the keyblock */
908     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
909     if( !node ) {
910         log_error("Oops; key lost!\n");
911         dump_kbnode( keyblock );
912         return;
913     }
914
915     if( secret ) {
916         pk = NULL;
917         sk = node->pkt->pkt.secret_key;
918         keyid_from_sk( sk, keyid );
919         printf("sec::%u:%d:%08lX%08lX:%s:%s:::",
920                     nbits_from_sk( sk ),
921                     sk->pubkey_algo,
922                     (ulong)keyid[0],(ulong)keyid[1],
923                     colon_datestr_from_sk( sk ),
924                     colon_strtime (sk->expiredate)
925                     /* fixme: add LID here */ );
926     }
927     else {
928         pk = node->pkt->pkt.public_key;
929         sk = NULL;
930         keyid_from_pk( pk, keyid );
931         fputs( "pub:", stdout );
932         if ( !pk->is_valid )
933             putchar ('i');
934         else if ( pk->is_revoked )
935             putchar ('r');
936         else if ( pk->has_expired )
937             putchar ('e');
938         else if ( opt.fast_list_mode || opt.no_expensive_trust_checks ) 
939             ;
940         else {
941             trustletter = get_validity_info ( pk, NULL );
942             if( trustletter == 'u' )
943                 ulti_hack = 1;
944             putchar(trustletter);
945         }
946         printf(":%u:%d:%08lX%08lX:%s:%s::",
947                     nbits_from_pk( pk ),
948                     pk->pubkey_algo,
949                     (ulong)keyid[0],(ulong)keyid[1],
950                     colon_datestr_from_pk( pk ),
951                     colon_strtime (pk->expiredate) );
952         if( !opt.fast_list_mode && !opt.no_expensive_trust_checks  )
953             putchar( get_ownertrust_info(pk) );
954             putchar(':');
955     }
956     
957     if (opt.fixed_list_mode) {
958         /* do not merge the first uid with the primary key */
959         putchar(':');
960         putchar(':');
961         print_capabilities (pk, sk, keyblock);
962         if (secret) {
963           putchar(':'); /* End of field 13. */
964           putchar(':'); /* End of field 14. */
965           if (sk->protect.s2k.mode == 1001)
966             putchar('#'); /* Key is just a stub. */
967           else if (sk->protect.s2k.mode == 1002) {
968             /* Key is stored on an external token (card) or handled by
969                the gpg-agent.  Print the serial number of that token
970                here. */
971             for (i=0; i < sk->protect.ivlen; i++)
972               printf ("%02X", sk->protect.iv[i]);
973           }
974           putchar(':'); /* End of field 15. */
975         }
976         putchar('\n');
977         if( fpr )
978             print_fingerprint( pk, sk, 0 );
979         if( opt.with_key_data )
980             print_key_data( pk );
981         any = 1;
982     }
983
984
985     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
986         if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
987             PKT_user_id *uid=node->pkt->pkt.user_id;
988             if(attrib_fp && node->pkt->pkt.user_id->attrib_data!=NULL)
989               dump_attribs(node->pkt->pkt.user_id,pk,sk);
990             /*
991              * Fixme: We need a is_valid flag here too 
992              */
993             if( any ) {
994                 char *str=uid->attrib_data?"uat":"uid";
995                 /* If we're listing a secret key, leave out the
996                    validity values for now.  This is handled better in
997                    1.9. */
998                 if ( sk )
999                     printf("%s:::::",str);
1000                 else if ( uid->is_revoked )
1001                     printf("%s:r::::",str);
1002                 else if ( uid->is_expired )
1003                     printf("%s:e::::",str);
1004                 else if ( opt.no_expensive_trust_checks )
1005                     printf("%s:::::",str);
1006                 else {
1007                     int uid_validity;
1008
1009                     if( pk && !ulti_hack )
1010                       uid_validity=get_validity_info (pk, uid);
1011                     else
1012                         uid_validity = 'u';
1013                     printf("%s:%c::::",str,uid_validity);
1014                 }
1015
1016                 printf("%s:",colon_strtime(uid->created));
1017                 printf("%s:",colon_strtime(uid->expiredate));
1018
1019                 namehash_from_uid(uid);
1020
1021                 for(i=0; i < 20; i++ )
1022                   printf("%02X",uid->namehash[i]);
1023
1024                 printf("::");
1025             }
1026             if(uid->attrib_data)
1027               printf("%u %lu",uid->numattribs,uid->attrib_len);
1028             else
1029               print_string(stdout,uid->name,uid->len, ':' );
1030             putchar(':');
1031             if (any)
1032                 putchar('\n');
1033             else {
1034                 putchar(':');
1035                 print_capabilities (pk, sk, keyblock);
1036                 putchar('\n');
1037                 if( fpr )
1038                     print_fingerprint( pk, sk, 0 );
1039                 if( opt.with_key_data )
1040                     print_key_data( pk );
1041                 any = 1;
1042             }
1043         }
1044         else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
1045             u32 keyid2[2];
1046             PKT_public_key *pk2 = node->pkt->pkt.public_key;
1047
1048             if( !any ) {
1049                 putchar(':');
1050                 putchar(':');
1051                 print_capabilities (pk, sk, keyblock);
1052                 putchar('\n');
1053                 if( fpr )
1054                     print_fingerprint( pk, sk, 0 ); /* of the main key */
1055                 any = 1;
1056             }
1057
1058             keyid_from_pk( pk2, keyid2 );
1059             fputs ("sub:", stdout );
1060             if ( !pk2->is_valid )
1061                 putchar ('i');
1062             else if ( pk2->is_revoked )
1063                 putchar ('r');
1064             else if ( pk2->has_expired )
1065                 putchar ('e');
1066             else if ( opt.fast_list_mode || opt.no_expensive_trust_checks )
1067                 ;
1068             else {
1069                 /* trustletter should always be defined here */
1070                 if(trustletter)
1071                   printf("%c", trustletter );
1072             }
1073             printf(":%u:%d:%08lX%08lX:%s:%s:::::",
1074                         nbits_from_pk( pk2 ),
1075                         pk2->pubkey_algo,
1076                         (ulong)keyid2[0],(ulong)keyid2[1],
1077                         colon_datestr_from_pk( pk2 ),
1078                         colon_strtime (pk2->expiredate)
1079                         /* fixme: add LID and ownertrust here */
1080                                                 );
1081             print_capabilities (pk2, NULL, NULL);
1082             putchar('\n');
1083             if( fpr > 1 )
1084                 print_fingerprint( pk2, NULL, 0 );
1085             if( opt.with_key_data )
1086                 print_key_data( pk2 );
1087         }
1088         else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
1089             u32 keyid2[2];
1090             PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
1091
1092             if( !any ) {
1093                 putchar(':');
1094                 putchar(':');
1095                 print_capabilities (pk, sk, keyblock);
1096                 putchar('\n');
1097                 if( fpr )
1098                     print_fingerprint( pk, sk, 0 ); /* of the main key */
1099                 any = 1;
1100             }
1101
1102             keyid_from_sk( sk2, keyid2 );
1103             printf("ssb::%u:%d:%08lX%08lX:%s:%s:::::",
1104                         nbits_from_sk( sk2 ),
1105                         sk2->pubkey_algo,
1106                         (ulong)keyid2[0],(ulong)keyid2[1],
1107                         colon_datestr_from_sk( sk2 ),
1108                         colon_strtime (sk2->expiredate)
1109                    /* fixme: add LID */ );
1110             print_capabilities (NULL, sk2, NULL);
1111             if (opt.fixed_list_mode) {
1112               /* We print the serial number only in fixed list mode
1113                  for the primary key so, so avoid questions we print
1114                  it for subkeys also only in this mode.  There is no
1115                  technical reason, though. */
1116               putchar(':'); /* End of field 13. */
1117               putchar(':'); /* End of field 14. */
1118               if (sk2->protect.s2k.mode == 1001)
1119                 putchar('#'); /* Key is just a stub. */
1120               else if (sk2->protect.s2k.mode == 1002) {
1121                 /* Key is stored on an external token (card) or handled by
1122                    the gpg-agent.  Print the serial number of that token
1123                    here. */
1124                 for (i=0; i < sk2->protect.ivlen; i++)
1125                   printf ("%02X", sk2->protect.iv[i]);
1126               }
1127               putchar(':'); /* End of field 15. */
1128             }
1129             putchar ('\n');
1130             if( fpr > 1 )
1131               print_fingerprint( NULL, sk2, 0 );
1132         }
1133         else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) {
1134             PKT_signature *sig = node->pkt->pkt.signature;
1135             int sigrc,fprokay=0;
1136             char *sigstr;
1137             size_t fplen;
1138             byte fparray[MAX_FINGERPRINT_LEN];
1139
1140             if( !any ) { /* no user id, (maybe a revocation follows)*/
1141                 if( sig->sig_class == 0x20 )
1142                     fputs("[revoked]:", stdout);
1143                 else if( sig->sig_class == 0x18 )
1144                     fputs("[key binding]:", stdout);
1145                 else if( sig->sig_class == 0x28 )
1146                     fputs("[subkey revoked]:", stdout);
1147                 else
1148                     putchar (':');
1149                 putchar(':');
1150                 print_capabilities (pk, sk, keyblock);
1151                 putchar('\n');
1152                 if( fpr )
1153                     print_fingerprint( pk, sk, 0 );
1154                 any=1;
1155             }
1156
1157             if( sig->sig_class == 0x20 || sig->sig_class == 0x28
1158                                        || sig->sig_class == 0x30 )
1159                sigstr = "rev";
1160             else if( (sig->sig_class&~3) == 0x10 )
1161                sigstr = "sig";
1162             else if( sig->sig_class == 0x18 )
1163                sigstr = "sig";
1164             else if( sig->sig_class == 0x1F )
1165                sigstr = "sig";
1166             else {
1167                 printf ("sig::::::::::%02x%c:\n",
1168                         sig->sig_class, sig->flags.exportable?'x':'l');
1169                 continue;
1170             }
1171             if( opt.check_sigs ) {
1172                 PKT_public_key *signer_pk=NULL;
1173
1174                 fflush(stdout);
1175                 if(opt.no_sig_cache)
1176                   signer_pk=m_alloc_clear(sizeof(PKT_public_key));
1177
1178                 rc = check_key_signature2( keyblock, node, NULL, signer_pk,
1179                                            NULL, NULL, NULL );
1180                 switch( rc ) {
1181                   case 0:                  sigrc = '!'; break;
1182                   case G10ERR_BAD_SIGN:    sigrc = '-'; break;
1183                   case G10ERR_NO_PUBKEY: 
1184                   case G10ERR_UNU_PUBKEY:  sigrc = '?'; break;
1185                   default:                 sigrc = '%'; break;
1186                 }
1187
1188                 if(opt.no_sig_cache)
1189                   {
1190                     if(rc==0)
1191                       {
1192                         fingerprint_from_pk (signer_pk, fparray, &fplen);
1193                         fprokay=1;
1194                       }
1195                     free_public_key(signer_pk);
1196                   }
1197             }
1198             else {
1199                 rc = 0;
1200                 sigrc = ' ';
1201             }
1202             fputs( sigstr, stdout );
1203             putchar(':');
1204             if( sigrc != ' ' )
1205                 putchar(sigrc);
1206             printf("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
1207                    (ulong)sig->keyid[0], (ulong)sig->keyid[1],
1208                    colon_datestr_from_sig(sig),
1209                    colon_expirestr_from_sig(sig));
1210
1211             if(sig->trust_depth || sig->trust_value)
1212               printf("%d %d",sig->trust_depth,sig->trust_value);
1213             printf(":");
1214
1215             if(sig->trust_regexp)
1216               print_string(stdout,sig->trust_regexp,
1217                            strlen(sig->trust_regexp),':');
1218             printf(":");
1219
1220             if( sigrc == '%' )
1221                 printf("[%s] ", g10_errstr(rc) );
1222             else if( sigrc == '?' )
1223                 ;
1224             else if ( !opt.fast_list_mode ) {
1225                 size_t n;
1226                 char *p = get_user_id( sig->keyid, &n );
1227                 print_string( stdout, p, n, ':' );
1228                 m_free(p);
1229             }
1230             printf(":%02x%c:", sig->sig_class,sig->flags.exportable?'x':'l');
1231
1232             if(opt.no_sig_cache && opt.check_sigs && fprokay)
1233               {
1234                 printf(":");
1235
1236                 for (i=0; i < fplen ; i++ )
1237                   printf ("%02X", fparray[i] );
1238
1239                 printf(":");
1240               }
1241
1242             printf("\n");
1243
1244             /* fixme: check or list other sigs here */
1245         }
1246     }
1247     if( !any ) {/* oops, no user id */
1248         putchar(':');
1249         putchar(':');
1250         print_capabilities (pk, sk, keyblock);
1251         putchar('\n');
1252     }
1253 }
1254
1255 /*
1256  * Reorder the keyblock so that the primary user ID (and not attribute
1257  * packet) comes first.  Fixme: Replace this by a generic sort
1258  * function.  */
1259 void
1260 reorder_keyblock (KBNODE keyblock)
1261 {
1262     KBNODE primary = NULL, primary0 = NULL, primary2 = NULL;
1263     KBNODE last, node;
1264
1265     for (node=keyblock; node; primary0=node, node = node->next) {
1266         if( node->pkt->pkttype == PKT_USER_ID &&
1267             !node->pkt->pkt.user_id->attrib_data &&
1268             node->pkt->pkt.user_id->is_primary ) {
1269             primary = primary2 = node;
1270             for (node=node->next; node; primary2=node, node = node->next ) {
1271                 if( node->pkt->pkttype == PKT_USER_ID 
1272                     || node->pkt->pkttype == PKT_PUBLIC_SUBKEY 
1273                     || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
1274                     break;
1275                 }
1276             }
1277             break;
1278         }
1279     }
1280     if ( !primary )
1281         return;  /* no primary key flag found (should not happen) */
1282
1283     for (last=NULL, node=keyblock; node; last = node, node = node->next) {
1284         if( node->pkt->pkttype == PKT_USER_ID )
1285             break;
1286     }
1287     assert (node);
1288     assert (last); /* the user ID is never the first packet */
1289     assert (primary0);  /* ditto (this is the node before primary) */
1290     if ( node == primary )
1291         return; /* already the first one */
1292
1293     last->next = primary;
1294     primary0->next = primary2->next;
1295     primary2->next = node;
1296 }
1297
1298 void
1299 list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque )
1300 {
1301     reorder_keyblock (keyblock);
1302     if (opt.with_colons)
1303         list_keyblock_colon (keyblock, secret, fpr );
1304     else
1305         list_keyblock_print (keyblock, secret, fpr, opaque );
1306 }
1307
1308 /*
1309  * standard function to print the finperprint.
1310  * mode 0: as used in key listings, opt.with_colons is honored
1311  *      1: print using log_info ()
1312  *      2: direct use of tty
1313  *      3: direct use of tty but only primary key.
1314  * modes 1 and 2 will try and print both subkey and primary key fingerprints
1315  */
1316 void
1317 print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode )
1318 {
1319     byte array[MAX_FINGERPRINT_LEN], *p;
1320     size_t i, n;
1321     FILE *fp;
1322     const char *text;
1323     int primary=0;
1324
1325     if(sk)
1326       {
1327         if(sk->main_keyid[0]==sk->keyid[0] && sk->main_keyid[1]==sk->keyid[1])
1328           primary=1;
1329       }
1330     else
1331       {
1332         if(pk->main_keyid[0]==pk->keyid[0] && pk->main_keyid[1]==pk->keyid[1])
1333           primary=1;
1334       }
1335
1336     /* Just to be safe */
1337     if(mode&0x80 && !primary)
1338       {
1339         log_error("primary key is not really primary!\n");
1340         return;
1341       }
1342
1343     mode&=~0x80;
1344
1345     if(!primary && (mode==1 || mode==2))
1346       {
1347         if(sk)
1348           {
1349             PKT_secret_key *primary_sk=m_alloc_clear(sizeof(*primary_sk));
1350             get_seckey(primary_sk,sk->main_keyid);
1351             print_fingerprint(NULL,primary_sk,mode|0x80);
1352             free_secret_key(primary_sk);
1353           }
1354         else
1355           {
1356             PKT_public_key *primary_pk=m_alloc_clear(sizeof(*primary_pk));
1357             get_pubkey(primary_pk,pk->main_keyid);
1358             print_fingerprint(primary_pk,NULL,mode|0x80);
1359             free_public_key(primary_pk);
1360           }
1361       }
1362
1363     if (mode == 1) {
1364         fp = log_stream ();
1365         if(primary)
1366           text = _("Primary key fingerprint:");
1367         else
1368           text = _("     Subkey fingerprint:");
1369     }
1370     else if (mode == 2) {
1371         fp = NULL; /* use tty */
1372         /* Translators: this should fit into 24 bytes to that the fingerprint
1373          * data is properly aligned with the user ID */
1374         if(primary)
1375           text = _(" Primary key fingerprint:");
1376         else
1377           text = _("      Subkey fingerprint:");
1378     }
1379     else if (mode == 3) {
1380         fp = NULL; /* use tty */
1381         text = _("      Key fingerprint =");
1382     }
1383     else {
1384         fp = stdout;
1385         text = _("      Key fingerprint =");
1386     }
1387   
1388     if (sk)
1389         fingerprint_from_sk (sk, array, &n);
1390     else
1391         fingerprint_from_pk (pk, array, &n);
1392     p = array;
1393     if (opt.with_colons && !mode) {
1394         fprintf (fp, "fpr:::::::::");
1395         for (i=0; i < n ; i++, p++ )
1396             fprintf (fp, "%02X", *p );
1397         putc(':', fp);
1398     }
1399     else {
1400         if (fp)
1401             fputs (text, fp);
1402         else
1403             tty_printf ("%s", text);
1404         if (n == 20) {
1405             for (i=0; i < n ; i++, i++, p += 2 ) {
1406                 if (fp) {
1407                     if (i == 10 )
1408                         putc(' ', fp);
1409                     fprintf (fp, " %02X%02X", *p, p[1] );
1410                 }
1411                 else {
1412                     if (i == 10 )
1413                         tty_printf (" ");
1414                     tty_printf (" %02X%02X", *p, p[1]);
1415                 }
1416             }
1417         }
1418         else {
1419             for (i=0; i < n ; i++, p++ ) {
1420                 if (fp) {
1421                     if (i && !(i%8) )
1422                         putc (' ', fp);
1423                     fprintf (fp, " %02X", *p );
1424                 }
1425                 else {
1426                     if (i && !(i%8) )
1427                         tty_printf (" ");
1428                     tty_printf (" %02X", *p );
1429                 }
1430             }
1431         }
1432     }
1433     if (fp)
1434         putc ('\n', fp);
1435     else
1436         tty_printf ("\n");
1437 }
1438
1439 void set_attrib_fd(int fd)
1440 {
1441   static int last_fd=-1;
1442
1443   if ( fd != -1 && last_fd == fd )
1444     return;
1445
1446   if ( attrib_fp && attrib_fp != stdout && attrib_fp != stderr )
1447     fclose (attrib_fp);
1448   attrib_fp = NULL;
1449   if ( fd == -1 ) 
1450     return;
1451
1452   if( fd == 1 )
1453     attrib_fp = stdout;
1454   else if( fd == 2 )
1455     attrib_fp = stderr;
1456   else
1457     attrib_fp = fdopen( fd, "wb" );
1458   if( !attrib_fp ) {
1459     log_fatal("can't open fd %d for attribute output: %s\n",
1460               fd, strerror(errno));
1461   }
1462
1463   last_fd = fd;
1464 }