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