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