* keylist.c (show_notation): Use bits to select which sort of notation 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 keyid[2];
116   char *p;
117
118   keyid_from_sk (sk, keyid);
119   p=get_user_id_printable(keyid);
120
121   tty_printf ("\nsec  %4u%c/%s %s   %s\n",
122               nbits_from_sk (sk),
123               pubkey_letter (sk->pubkey_algo),
124               keystr(keyid), datestr_from_sk (sk), p);
125     
126   m_free (p);
127 }
128
129 /* Print information about the public key.  With FP passed as NULL,
130    the tty output interface is used, otherwise output is directted to
131    the given stream. */
132 void
133 print_pubkey_info (FILE *fp, PKT_public_key *pk)
134 {
135   u32 keyid[2];
136   char *p;
137
138   keyid_from_pk (pk, keyid);
139   p=get_user_id_printable(keyid);
140
141   if (fp)
142     fprintf (fp, "pub  %4u%c/%s %s   %s\n",
143              nbits_from_pk (pk),
144              pubkey_letter (pk->pubkey_algo),
145              keystr(keyid), datestr_from_pk (pk), p);
146   else
147     tty_printf ("\npub  %4u%c/%s %s   %s\n",
148                 nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo),
149                 keystr(keyid), datestr_from_pk (pk), p);
150
151   m_free (p);
152 }
153
154 /*
155   mode=0 for stdout.
156   mode=1 for log_info + status messages
157   mode=2 for status messages only
158 */
159
160 void
161 show_policy_url(PKT_signature *sig,int indent,int mode)
162 {
163   const byte *p;
164   size_t len;
165   int seq=0,crit;
166   FILE *fp=mode?log_stream():stdout;
167
168   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,&len,&seq,&crit)))
169     {
170       if(mode!=2)
171         {
172           int i;
173           char *str;
174
175           for(i=0;i<indent;i++)
176             putchar(' ');
177
178           if(crit)
179             str=_("Critical signature policy: ");
180           else
181             str=_("Signature policy: ");
182           if(mode)
183             log_info("%s",str);
184           else
185             printf("%s",str);
186           print_utf8_string(fp,p,len);
187           fprintf(fp,"\n");
188         }
189
190       if(mode)
191         write_status_buffer ( STATUS_POLICY_URL, p, len, 0 );
192     }
193 }
194
195 /*
196   mode=0 for stdout.
197   mode=1 for log_info + status messages
198   mode=2 for status messages only
199 */
200 /* TODO: use this */
201 void
202 show_keyserver_url(PKT_signature *sig,int indent,int mode)
203 {
204   const byte *p;
205   size_t len;
206   int seq=0,crit;
207   FILE *fp=mode?log_stream():stdout;
208
209   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&len,&seq,&crit)))
210     {
211       if(mode!=2)
212         {
213           int i;
214           char *str;
215
216           for(i=0;i<indent;i++)
217             putchar(' ');
218
219           if(crit)
220             str=_("Critical preferred keyserver: ");
221           else
222             str=_("Preferred keyserver: ");
223           if(mode)
224             log_info("%s",str);
225           else
226             printf("%s",str);
227           print_utf8_string(fp,p,len);
228           fprintf(fp,"\n");
229         }
230
231       /* TODO: put in a status-fd tag for preferred keyservers */
232     }
233 }
234
235 /*
236   mode=0 for stdout.
237   mode=1 for log_info + status messages
238   mode=2 for status messages only
239
240   which bits:
241   1 == standard notations
242   2 == user notations
243 */
244
245 void
246 show_notation(PKT_signature *sig,int indent,int mode,int which)
247 {
248   const byte *p;
249   size_t len;
250   int seq=0,crit;
251   FILE *fp=mode?log_stream():stdout;
252
253   if(which==0)
254     which=3;
255
256   /* There may be multiple notations in the same sig. */
257
258   while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit)))
259     if(len>=8)
260       {
261         int n1,n2;
262
263         n1=(p[4]<<8)|p[5];
264         n2=(p[6]<<8)|p[7];
265
266         if(8+n1+n2!=len)
267           {
268             log_info(_("WARNING: invalid notation data found\n"));
269             continue;
270           }
271
272         if(mode!=2)
273           {
274             int has_at=!!memchr(p+8,'@',n1);
275
276             if((which&1 && !has_at) || (which&2 && has_at))
277               {
278                 int i;
279                 char *str;
280
281                 for(i=0;i<indent;i++)
282                   putchar(' ');
283
284                 /* This is UTF8 */
285                 if(crit)
286                   str=_("Critical signature notation: ");
287                 else
288                   str=_("Signature notation: ");
289                 if(mode)
290                   log_info("%s",str);
291                 else
292                   printf("%s",str);
293                 print_utf8_string(fp,p+8,n1);
294                 fprintf(fp,"=");
295
296                 if(*p&0x80)
297                   print_utf8_string(fp,p+8+n1,n2);
298                 else
299                   fprintf(fp,"[ %s ]",_("not human readable"));
300
301                 fprintf(fp,"\n");
302               }
303           }
304
305         if(mode)
306           {
307             write_status_buffer ( STATUS_NOTATION_NAME, p+8   , n1, 0 );
308             write_status_buffer ( STATUS_NOTATION_DATA, p+8+n1, n2, 50 );
309           }
310       }
311   else
312     log_info(_("WARNING: invalid notation data found\n"));
313 }
314
315 static void
316 print_signature_stats(struct sig_stats *s)
317 {
318   if( s->inv_sigs == 1 )
319     tty_printf(_("1 bad signature\n") );
320   else if( s->inv_sigs )
321     tty_printf(_("%d bad signatures\n"), s->inv_sigs );
322   if( s->no_key == 1 )
323     tty_printf(_("1 signature not checked due to a missing key\n") );
324   else if( s->no_key )
325     tty_printf(_("%d signatures not checked due to missing keys\n"),s->no_key);
326   if( s->oth_err == 1 )
327     tty_printf(_("1 signature not checked due to an error\n") );
328   else if( s->oth_err )
329     tty_printf(_("%d signatures not checked due to errors\n"), s->oth_err );
330 }
331
332 static void
333 list_all( int secret )
334 {
335     KEYDB_HANDLE hd;
336     KBNODE keyblock = NULL;
337     int rc=0;
338     const char *lastresname, *resname;
339     struct sig_stats stats;
340
341     memset(&stats,0,sizeof(stats));
342
343     hd = keydb_new (secret);
344     if (!hd)
345         rc = G10ERR_GENERAL;
346     else
347         rc = keydb_search_first (hd);
348     if( rc ) {
349         if( rc != -1 )
350             log_error("keydb_search_first failed: %s\n", g10_errstr(rc) );
351         goto leave;
352     }
353
354     lastresname = NULL;
355     do {
356         rc = keydb_get_keyblock (hd, &keyblock);
357         if (rc) {
358             log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
359             goto leave;
360         }
361         if(!opt.with_colons)
362           {
363             resname = keydb_get_resource_name (hd);
364             if (lastresname != resname )
365               {
366                 int i;
367
368                 printf("%s\n", resname );
369                 for(i=strlen(resname); i; i-- )
370                   putchar('-');
371                 putchar('\n');
372                 lastresname = resname;
373               }
374           }
375         merge_keys_and_selfsig( keyblock );
376         list_keyblock( keyblock, secret, opt.fingerprint,
377                        opt.check_sigs?&stats:NULL);
378         release_kbnode( keyblock ); 
379         keyblock = NULL;
380     } while (!(rc = keydb_search_next (hd)));
381     if( rc && rc != -1 )
382         log_error ("keydb_search_next failed: %s\n", g10_errstr(rc));
383
384     if(opt.check_sigs && !opt.with_colons)
385       print_signature_stats(&stats);
386
387   leave:
388     release_kbnode (keyblock);
389     keydb_release (hd);
390 }
391
392
393 static void
394 list_one( STRLIST names, int secret )
395 {
396     int rc = 0;
397     KBNODE keyblock = NULL;
398     GETKEY_CTX ctx;
399     const char *resname;
400     char *keyring_str = _("Keyring");
401     int i;
402     struct sig_stats stats;
403
404     memset(&stats,0,sizeof(stats));
405
406     /* fixme: using the bynames function has the disadvantage that we
407      * don't know wether one of the names given was not found.  OTOH,
408      * this function has the advantage to list the names in the
409      * sequence as defined by the keyDB and does not duplicate
410      * outputs.  A solution could be do test whether all given have
411      * been listed (this needs a way to use the keyDB search
412      * functions) or to have the search function return indicators for
413      * found names.  Yet another way is to use the keydb search
414      * facilities directly. */
415     if( secret ) {
416         rc = get_seckey_bynames( &ctx, NULL, names, &keyblock );
417         if( rc ) {
418             log_error("error reading key: %s\n",  g10_errstr(rc) );
419             get_seckey_end( ctx );
420             return;
421         }
422         do {
423             if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
424                 resname = keydb_get_resource_name (get_ctx_handle(ctx));
425                 printf("%s: %s\n", keyring_str, resname);
426                 for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
427                     putchar('-');
428                 putchar('\n');
429             }
430             list_keyblock( keyblock, 1, opt.fingerprint, NULL );
431             release_kbnode( keyblock );
432         } while( !get_seckey_next( ctx, NULL, &keyblock ) );
433         get_seckey_end( ctx );
434     }
435     else {
436         rc = get_pubkey_bynames( &ctx, NULL, names, &keyblock );
437         if( rc ) {
438             log_error("error reading key: %s\n", g10_errstr(rc) );
439             get_pubkey_end( ctx );
440             return;
441         }
442         do {
443           if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
444                 resname = keydb_get_resource_name (get_ctx_handle(ctx));
445                 printf("%s: %s\n", keyring_str, resname);
446                 for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
447                     putchar('-');
448                 putchar('\n');
449             }
450             list_keyblock( keyblock, 0, opt.fingerprint,
451                            opt.check_sigs?&stats:NULL );
452             release_kbnode( keyblock );
453         } while( !get_pubkey_next( ctx, NULL, &keyblock ) );
454         get_pubkey_end( ctx );
455     }
456
457     if(opt.check_sigs && !opt.with_colons)
458       print_signature_stats(&stats);
459 }
460
461 static void
462 print_key_data( PKT_public_key *pk )
463 {
464     int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
465     int i;
466
467     for(i=0; i < n; i++ ) {
468         printf("pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
469         mpi_print(stdout, pk->pkey[i], 1 );
470         putchar(':');
471         putchar('\n');
472     }
473 }
474
475 static void
476 print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock)
477 {
478   if(pk || (sk && sk->protect.s2k.mode!=1001))
479     {
480       unsigned int use = pk? pk->pubkey_usage : sk->pubkey_usage;
481     
482       if ( use & PUBKEY_USAGE_ENC )
483         putchar ('e');
484
485       if ( use & PUBKEY_USAGE_SIG )
486         {
487           putchar ('s');
488           if( pk? pk->is_primary : sk->is_primary )
489             putchar ('c');
490         }
491
492       if ( (use & PUBKEY_USAGE_AUTH) )
493         putchar ('a');
494     }
495
496     if ( keyblock ) { /* figure out the usable capabilities */
497         KBNODE k;
498         int enc=0, sign=0, cert=0, auth=0, disabled=0;
499
500         for (k=keyblock; k; k = k->next ) {
501             if ( k->pkt->pkttype == PKT_PUBLIC_KEY 
502                  || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
503                 pk = k->pkt->pkt.public_key;
504
505                 if(pk->is_primary)
506                   disabled=pk_is_disabled(pk);
507
508                 if ( pk->is_valid && !pk->is_revoked && !pk->has_expired ) {
509                     if ( pk->pubkey_usage & PUBKEY_USAGE_ENC )
510                         enc = 1;
511                     if ( pk->pubkey_usage & PUBKEY_USAGE_SIG )
512                       {
513                         sign = 1;
514                         if(pk->is_primary)
515                           cert = 1;
516                       }
517                     if ( (pk->pubkey_usage & PUBKEY_USAGE_AUTH) )
518                       auth = 1;
519                 }
520             }
521             else if ( k->pkt->pkttype == PKT_SECRET_KEY 
522                       || k->pkt->pkttype == PKT_SECRET_SUBKEY ) {
523                 sk = k->pkt->pkt.secret_key;
524                 if ( sk->is_valid && !sk->is_revoked && !sk->has_expired
525                      && sk->protect.s2k.mode!=1001 ) {
526                     if ( sk->pubkey_usage & PUBKEY_USAGE_ENC )
527                         enc = 1;
528                     if ( sk->pubkey_usage & PUBKEY_USAGE_SIG )
529                       {
530                         sign = 1;
531                         if(sk->is_primary)
532                           cert = 1;
533                       }
534                     if ( (sk->pubkey_usage & PUBKEY_USAGE_AUTH) )
535                         auth = 1;
536                 }
537             }
538         }
539         if (enc)
540             putchar ('E');
541         if (sign)
542             putchar ('S');
543         if (cert)
544             putchar ('C');
545         if (auth)
546             putchar ('A');
547         if (disabled)
548             putchar ('D');
549     }
550
551     putchar(':');
552 }
553
554 void
555 dump_attribs(const PKT_user_id *uid,PKT_public_key *pk,PKT_secret_key *sk)
556 {
557   int i;
558
559   if(!attrib_fp)
560     return;
561
562   for(i=0;i<uid->numattribs;i++)
563     {
564       if(is_status_enabled())
565         {
566           byte array[MAX_FINGERPRINT_LEN], *p;
567           char buf[(MAX_FINGERPRINT_LEN*2)+90];
568           size_t j,n;
569
570           if(pk)
571             fingerprint_from_pk( pk, array, &n );
572           else if(sk)
573             fingerprint_from_sk( sk, array, &n );
574           else
575             BUG();
576
577           p = array;
578           for(j=0; j < n ; j++, p++ )
579             sprintf(buf+2*j, "%02X", *p );
580
581           sprintf(buf+strlen(buf)," %lu %u %u %u %lu %lu %u",
582                   (ulong)uid->attribs[i].len,uid->attribs[i].type,i+1,
583                   uid->numattribs,(ulong)uid->created,(ulong)uid->expiredate,
584                   ((uid->is_primary?0x01:0)|
585                    (uid->is_revoked?0x02:0)|
586                    (uid->is_expired?0x04:0)));
587           write_status_text(STATUS_ATTRIBUTE,buf);
588         }
589
590       fwrite(uid->attribs[i].data,uid->attribs[i].len,1,attrib_fp);
591     }
592 }
593
594 static void
595 list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque )
596 {
597     int rc = 0;
598     KBNODE kbctx;
599     KBNODE node;
600     PKT_public_key *pk;
601     PKT_secret_key *sk;
602     int any=0;
603     struct sig_stats *stats=opaque;
604     int skip_sigs=0;
605     int newformat=((opt.list_options&LIST_SHOW_VALIDITY) && !secret)
606       || (opt.list_options & (LIST_SHOW_UNUSABLE_UIDS
607                               | LIST_SHOW_UNUSABLE_SUBKEYS))
608       || (keystrlen()>10);
609
610     /* get the keyid from the keyblock */
611     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
612     if( !node ) {
613         log_error("Oops; key lost!\n");
614         dump_kbnode( keyblock );
615         return;
616     }
617
618     if( secret )
619       {
620         pk = NULL;
621         sk = node->pkt->pkt.secret_key;
622
623         printf("sec%c  %4u%c/%s %s%s",(sk->protect.s2k.mode==1001)?'#':
624                (sk->protect.s2k.mode==1002)?'>':' ',
625                nbits_from_sk( sk ),pubkey_letter( sk->pubkey_algo ),
626                keystr_from_sk(sk),datestr_from_sk( sk ),newformat?"":" " );
627
628         if(newformat && sk->expiredate )
629           printf(_(" [expires: %s]"), expirestr_from_sk( sk ) );
630       }
631     else
632       {
633 #if 0
634         int validity;
635 #endif
636         pk = node->pkt->pkt.public_key;
637         sk = NULL;
638
639 #if 0
640         validity=get_validity(pk,NULL);
641 #endif
642
643         check_trustdb_stale();
644
645         printf("pub   %4u%c/%s %s%s",
646                nbits_from_pk(pk),pubkey_letter(pk->pubkey_algo),
647                keystr_from_pk(pk),datestr_from_pk( pk ),newformat?"":" " );
648
649         /* We didn't include this before in the key listing, but there
650            is room in the new format, so why not? */
651         if(newformat)
652           {
653             if(pk->is_revoked)
654               printf(_(" [revoked: %s]"), revokestr_from_pk( pk ) );
655             else if(pk->has_expired)
656               printf(_(" [expired: %s]"), expirestr_from_pk( pk ) );
657             else if(pk->expiredate)
658               printf(_(" [expires: %s]"), expirestr_from_pk( pk ) );
659           }
660
661 #if 0
662         /* I need to think about this some more.  It's easy enough to
663            include, but it looks sort of confusing in the
664            listing... */
665         if(opt.list_options&LIST_SHOW_VALIDITY)
666           printf(" [%s]",trust_value_to_string(validity));
667 #endif
668       }
669
670     for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
671         if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
672             PKT_user_id *uid=node->pkt->pkt.user_id;
673
674             if((uid->is_expired || uid->is_revoked)
675                && !(opt.list_options&LIST_SHOW_UNUSABLE_UIDS))
676               {
677                 skip_sigs=1;
678                 continue;
679               }
680             else
681               skip_sigs=0;
682
683             if(attrib_fp && uid->attrib_data!=NULL)
684               dump_attribs(uid,pk,sk);
685
686             if(!any && newformat)
687               printf("\n");
688
689             if((uid->is_revoked || uid->is_expired)
690                || ((opt.list_options&LIST_SHOW_VALIDITY) && pk))
691               {
692                 const char *validity;
693                 int indent;
694
695                 if(uid->is_revoked)
696                   validity=_("revoked");
697                 else if(uid->is_expired)
698                   validity=_("expired");
699                 else
700                   validity=trust_value_to_string(get_validity(pk,uid));
701
702                 indent=(keystrlen()+7)-strlen(validity);
703
704                 if(indent<0)
705                   indent=0;
706
707                 printf("uid%*s[%s] ",indent,"",validity);
708               }
709             else if(newformat)
710               printf("uid%*s",keystrlen()+10,"");
711             else if(any)
712               printf("uid%*s",keystrlen()+21,"");
713
714             print_utf8_string( stdout, uid->name, uid->len );
715             putchar('\n');
716             if( !any ) {
717                 if( fpr )
718                     print_fingerprint( pk, sk, 0 );
719                 if( opt.with_key_data )
720                     print_key_data( pk );
721                 any = 1;
722             }
723
724             if((opt.list_options&LIST_SHOW_PHOTOS) && uid->attribs!=NULL)
725               show_photos(uid->attribs,uid->numattribs,pk,sk);
726         }
727         else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
728           {
729             PKT_public_key *pk2 = node->pkt->pkt.public_key;
730
731             if((pk2->is_revoked || pk2->has_expired)
732                && !(opt.list_options&LIST_SHOW_UNUSABLE_SUBKEYS))
733               {
734                 skip_sigs=1;
735                 continue;
736               }
737             else
738               skip_sigs=0;
739
740             if( !any )
741               {
742                 putchar('\n');
743                 if( fpr )
744                   print_fingerprint( pk, sk, 0 ); /* of the main key */
745                 any = 1;
746               }
747
748             printf("sub   %4u%c/%s %s",
749                    nbits_from_pk( pk2 ),pubkey_letter( pk2->pubkey_algo ),
750                    keystr_from_pk(pk2),datestr_from_pk(pk2));
751             if( pk2->is_revoked )
752               printf(_(" [revoked: %s]"), revokestr_from_pk(pk2));
753             else if( pk2->has_expired )
754               printf(_(" [expired: %s]"), expirestr_from_pk( pk2 ) );
755             else if( pk2->expiredate )
756               printf(_(" [expires: %s]"), expirestr_from_pk( pk2 ) );
757             putchar('\n');
758             if( fpr > 1 )
759               print_fingerprint( pk2, NULL, 0 );
760             if( opt.with_key_data )
761               print_key_data( pk2 );
762           }
763         else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
764           {
765             PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
766
767             if( !any )
768               {
769                 putchar('\n');
770                 if( fpr )
771                   print_fingerprint( pk, sk, 0 ); /* of the main key */
772                 any = 1;
773               }
774
775             printf("ssb%c  %4u%c/%s %s",
776                    (sk2->protect.s2k.mode==1001)?'#':
777                    (sk2->protect.s2k.mode==1002)?'>':' ',
778                    nbits_from_sk( sk2 ),pubkey_letter( sk2->pubkey_algo ),
779                    keystr_from_sk(sk2),datestr_from_sk( sk2 ) );
780             if( sk2->expiredate )
781               printf(_(" [expires: %s]"), expirestr_from_sk( sk2 ) );
782             putchar('\n');
783             if( fpr > 1 )
784               print_fingerprint( NULL, sk2, 0 );
785           }
786         else if( opt.list_sigs
787                  && node->pkt->pkttype == PKT_SIGNATURE
788                  && !skip_sigs ) {
789             PKT_signature *sig = node->pkt->pkt.signature;
790             int sigrc;
791             char *sigstr;
792
793             if( stats ) {
794                 /*fflush(stdout);*/
795                 rc = check_key_signature( keyblock, node, NULL );
796                 switch( rc ) {
797                  case 0:                 sigrc = '!'; break;
798                  case G10ERR_BAD_SIGN:   stats->inv_sigs++; sigrc = '-'; break;
799                  case G10ERR_NO_PUBKEY: 
800                  case G10ERR_UNU_PUBKEY: stats->no_key++; continue;
801                  default:                stats->oth_err++; sigrc = '%'; break;
802                 }
803
804                 /* TODO: Make sure a cached sig record here still has
805                    the pk that issued it.  See also
806                    keyedit.c:print_and_check_one_sig */
807             }
808             else {
809                 rc = 0;
810                 sigrc = ' ';
811             }
812
813             if( !any ) { /* no user id, (maybe a revocation follows)*/
814               /* Check if the pk is really revoked - there could be a
815                  0x20 sig packet there even if we are not revoked
816                  (say, if a revocation key issued the packet, but the
817                  revocation key isn't present to verify it.) */
818                 if( sig->sig_class == 0x20 && pk->is_revoked )
819                     puts("[revoked]");
820                 else if( sig->sig_class == 0x18 )
821                     puts("[key binding]");
822                 else if( sig->sig_class == 0x28 )
823                     puts("[subkey revoked]");
824                 else
825                     putchar('\n');
826                 if( fpr )
827                     print_fingerprint( pk, sk, 0 );
828                 any=1;
829             }
830
831             if( sig->sig_class == 0x20 || sig->sig_class == 0x28
832                                        || sig->sig_class == 0x30 )
833                sigstr = "rev";
834             else if( (sig->sig_class&~3) == 0x10 )
835                sigstr = "sig";
836             else if( sig->sig_class == 0x18 )
837                sigstr = "sig";
838             else if( sig->sig_class == 0x1F )
839                sigstr = "sig";
840             else {
841                 printf("sig                             "
842                        "[unexpected signature class 0x%02x]\n",sig->sig_class );
843                 continue;
844             }
845
846             fputs( sigstr, stdout );
847             printf("%c%c %c%c%c%c%c%c %s %s",
848                    sigrc,(sig->sig_class-0x10>0 &&
849                           sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ',
850                    sig->flags.exportable?' ':'L',
851                    sig->flags.revocable?' ':'R',
852                    sig->flags.policy_url?'P':' ',
853                    sig->flags.notation?'N':' ',
854                    sig->flags.expired?'X':' ',
855                    (sig->trust_depth>9)?'T':
856                    (sig->trust_depth>0)?'0'+sig->trust_depth:' ',
857                    keystr(sig->keyid),datestr_from_sig(sig));
858             if(opt.list_options&LIST_SHOW_SIG_EXPIRE)
859               printf(" %s", expirestr_from_sig(sig));
860             printf("  ");
861             if( sigrc == '%' )
862                 printf("[%s] ", g10_errstr(rc) );
863             else if( sigrc == '?' )
864                 ;
865             else if ( !opt.fast_list_mode ) {
866                 size_t n;
867                 char *p = get_user_id( sig->keyid, &n );
868                 print_utf8_string( stdout, p, n );
869                 m_free(p);
870             }
871             putchar('\n');
872
873             if(sig->flags.policy_url
874                && (opt.list_options&LIST_SHOW_POLICY_URLS))
875               show_policy_url(sig,3,0);
876
877             if(sig->flags.notation && (opt.list_options&LIST_SHOW_NOTATIONS))
878               show_notation(sig,3,0,
879                             ((opt.list_options&LIST_SHOW_STD_NOTATIONS)?1:0)+
880                             ((opt.list_options&LIST_SHOW_USER_NOTATIONS)?2: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 }