nss_db: Quash read implicit declaration warning
[platform/upstream/glibc.git] / nss / getent.c
1 /* Copyright (c) 1998-2008, 2009, 2010, 2011 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 /* getent: get entries from administrative database.  */
21
22 #include <aliases.h>
23 #include <argp.h>
24 #include <ctype.h>
25 #include <error.h>
26 #include <grp.h>
27 #include <gshadow.h>
28 #include <libintl.h>
29 #include <locale.h>
30 #include <mcheck.h>
31 #include <netdb.h>
32 #include <pwd.h>
33 #include <shadow.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <arpa/inet.h>
39 #include <arpa/nameser.h>
40 #include <netinet/ether.h>
41 #include <netinet/in.h>
42 #include <sys/socket.h>
43
44 /* Get libc version number.  */
45 #include <version.h>
46
47 #define PACKAGE _libc_intl_domainname
48
49 /* Name and version of program.  */
50 static void print_version (FILE *stream, struct argp_state *state);
51 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
52
53 /* Short description of parameters.  */
54 static const char args_doc[] = N_("database [key ...]");
55
56 /* Supported options. */
57 static const struct argp_option args_options[] =
58   {
59     { "service", 's', "CONFIG", 0, N_("Service configuration to be used") },
60     { "no-idn", 'i', NULL, 0, N_("disable IDN encoding") },
61     { NULL, 0, NULL, 0, NULL },
62   };
63
64 /* Short description of program.  */
65 static const char doc[] = N_("Get entries from administrative database.");
66
67 /* Prototype for option handler.  */
68 static error_t parse_option (int key, char *arg, struct argp_state *state);
69
70 /* Function to print some extra text in the help message.  */
71 static char *more_help (int key, const char *text, void *input);
72
73 /* Data structure to communicate with argp functions.  */
74 static struct argp argp =
75   {
76     args_options, parse_option, args_doc, doc, NULL, more_help
77   };
78
79 /* Additional getaddrinfo flags for IDN encoding.  */
80 static int idn_flags = AI_IDN | AI_CANONIDN;
81
82 /* Print the version information.  */
83 static void
84 print_version (FILE *stream, struct argp_state *state)
85 {
86   fprintf (stream, "getent (GNU %s) %s\n", PACKAGE, VERSION);
87   fprintf (stream, gettext ("\
88 Copyright (C) %s Free Software Foundation, Inc.\n\
89 This is free software; see the source for copying conditions.  There is NO\n\
90 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
91 "), "2011");
92   fprintf (stream, gettext ("Written by %s.\n"), "Thorsten Kukuk");
93 }
94
95 /* This is for aliases */
96 static inline void
97 print_aliases (struct aliasent *alias)
98 {
99   unsigned int i = 0;
100
101   printf ("%s: ", alias->alias_name);
102   for  (i = strlen (alias->alias_name); i < 14; ++i)
103     fputs_unlocked (" ", stdout);
104
105   for (i = 0; i < alias->alias_members_len; ++i)
106     printf ("%s%s",
107             alias->alias_members [i],
108             i + 1 == alias->alias_members_len ? "\n" : ", ");
109 }
110
111 static int
112 aliases_keys (int number, char *key[])
113 {
114   int result = 0;
115   int i;
116   struct aliasent *alias;
117
118   if (number == 0)
119     {
120       setaliasent ();
121       while ((alias = getaliasent ()) != NULL)
122         print_aliases (alias);
123       endaliasent ();
124       return result;
125     }
126
127   for (i = 0; i < number; ++i)
128     {
129       alias = getaliasbyname (key[i]);
130
131       if (alias == NULL)
132         result = 2;
133       else
134         print_aliases (alias);
135     }
136
137   return result;
138 }
139
140 /* This is for ethers */
141 static int
142 ethers_keys (int number, char *key[])
143 {
144   int result = 0;
145   int i;
146
147   if (number == 0)
148     {
149       fprintf (stderr, _("Enumeration not supported on %s\n"), "ethers");
150       return 3;
151     }
152
153   for (i = 0; i < number; ++i)
154     {
155       struct ether_addr *ethp, eth;
156       char buffer [1024], *p;
157
158       ethp = ether_aton (key[i]);
159       if (ethp != NULL)
160         {
161           if (ether_ntohost (buffer, ethp))
162             {
163               result = 2;
164               continue;
165             }
166           p = buffer;
167         }
168       else
169         {
170           if (ether_hostton (key[i], &eth))
171             {
172               result = 2;
173               continue;
174             }
175           p = key[i];
176           ethp = &eth;
177         }
178       printf ("%s %s\n", ether_ntoa (ethp), p);
179     }
180
181   return result;
182 }
183
184 /* This is for group */
185 static inline void
186 print_group (struct group *grp)
187 {
188   unsigned int i = 0;
189
190   printf ("%s:%s:%lu:", grp->gr_name ? grp->gr_name : "",
191           grp->gr_passwd ? grp->gr_passwd : "",
192           (unsigned long int) grp->gr_gid);
193
194   while (grp->gr_mem[i] != NULL)
195     {
196       fputs_unlocked (grp->gr_mem[i], stdout);
197       ++i;
198       if (grp->gr_mem[i] != NULL)
199         putchar_unlocked (',');
200     }
201   putchar_unlocked ('\n');
202 }
203
204 static int
205 group_keys (int number, char *key[])
206 {
207   int result = 0;
208   int i;
209   struct group *grp;
210
211   if (number == 0)
212     {
213       setgrent ();
214       while ((grp = getgrent ()) != NULL)
215         print_group (grp);
216       endgrent ();
217       return result;
218     }
219
220   for (i = 0; i < number; ++i)
221     {
222       errno = 0;
223       char *ep;
224       gid_t arg_gid = strtoul(key[i], &ep, 10);
225
226       if (errno != EINVAL && *key[i] != '\0' && *ep == '\0')
227         /* Valid numeric gid.  */
228         grp = getgrgid (arg_gid);
229       else
230         grp = getgrnam (key[i]);
231
232       if (grp == NULL)
233         result = 2;
234       else
235         print_group (grp);
236     }
237
238   return result;
239 }
240
241 /* This is for gshadow */
242 static void
243 print_gshadow (struct sgrp *sg)
244 {
245   unsigned int i = 0;
246
247   printf ("%s:%s:",
248           sg->sg_namp ? sg->sg_namp : "",
249           sg->sg_passwd ? sg->sg_passwd : "");
250
251   while (sg->sg_adm[i] != NULL)
252     {
253       fputs_unlocked (sg->sg_adm[i], stdout);
254       ++i;
255       if (sg->sg_adm[i] != NULL)
256         putchar_unlocked (',');
257     }
258
259   putchar_unlocked (':');
260
261   i = 0;
262   while (sg->sg_mem[i] != NULL)
263     {
264       fputs_unlocked (sg->sg_mem[i], stdout);
265       ++i;
266       if (sg->sg_mem[i] != NULL)
267         putchar_unlocked (',');
268     }
269
270   putchar_unlocked ('\n');
271 }
272
273 static int
274 gshadow_keys (int number, char *key[])
275 {
276   int result = 0;
277   int i;
278
279   if (number == 0)
280     {
281       struct sgrp *sg;
282
283       setsgent ();
284       while ((sg = getsgent ()) != NULL)
285         print_gshadow (sg);
286       endsgent ();
287       return result;
288     }
289
290   for (i = 0; i < number; ++i)
291     {
292       struct sgrp *sg;
293
294       sg = getsgnam (key[i]);
295
296       if (sg == NULL)
297         result = 2;
298       else
299         print_gshadow (sg);
300     }
301
302   return result;
303 }
304
305 /* This is for hosts */
306 static void
307 print_hosts (struct hostent *host)
308 {
309   unsigned int cnt;
310
311   for (cnt = 0; host->h_addr_list[cnt] != NULL; ++cnt)
312     {
313       char buf[INET6_ADDRSTRLEN];
314       const char *ip = inet_ntop (host->h_addrtype, host->h_addr_list[cnt],
315                                   buf, sizeof (buf));
316
317       printf ("%-15s %s", ip, host->h_name);
318
319       unsigned int i;
320       for (i = 0; host->h_aliases[i] != NULL; ++i)
321         {
322           putchar_unlocked (' ');
323           fputs_unlocked (host->h_aliases[i], stdout);
324         }
325       putchar_unlocked ('\n');
326     }
327 }
328
329 static int
330 hosts_keys (int number, char *key[])
331 {
332   int result = 0;
333   int i;
334   struct hostent *host;
335
336   if (number == 0)
337     {
338       sethostent (0);
339       while ((host = gethostent ()) != NULL)
340         print_hosts (host);
341       endhostent ();
342       return result;
343     }
344
345   for (i = 0; i < number; ++i)
346     {
347       struct hostent *host = NULL;
348       char addr[IN6ADDRSZ];
349
350       if (inet_pton (AF_INET6, key[i], &addr) > 0)
351         host = gethostbyaddr (addr, IN6ADDRSZ, AF_INET6);
352       else if (inet_pton (AF_INET, key[i], &addr) > 0)
353         host = gethostbyaddr (addr, INADDRSZ, AF_INET);
354       else if ((host = gethostbyname2 (key[i], AF_INET6)) == NULL)
355         host = gethostbyname2 (key[i], AF_INET);
356
357       if (host == NULL)
358         result = 2;
359       else
360         print_hosts (host);
361     }
362
363   return result;
364 }
365
366 /* This is for hosts, but using getaddrinfo */
367 static int
368 ahosts_keys_int (int af, int xflags, int number, char *key[])
369 {
370   int result = 0;
371   int i;
372   struct hostent *host;
373
374   if (number == 0)
375     {
376       sethostent (0);
377       while ((host = gethostent ()) != NULL)
378         print_hosts (host);
379       endhostent ();
380       return result;
381     }
382
383   struct addrinfo hint;
384   memset (&hint, '\0', sizeof (hint));
385   hint.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME
386                    | idn_flags | xflags);
387   hint.ai_family = af;
388
389   for (i = 0; i < number; ++i)
390     {
391       struct addrinfo *res;
392
393       if (getaddrinfo (key[i], NULL, &hint, &res) != 0)
394         result = 2;
395       else
396         {
397           struct addrinfo *runp = res;
398
399           while (runp != NULL)
400             {
401               char sockbuf[20];
402               const char *sockstr;
403               if (runp->ai_socktype == SOCK_STREAM)
404                 sockstr = "STREAM";
405               else if (runp->ai_socktype == SOCK_DGRAM)
406                 sockstr = "DGRAM";
407               else if (runp->ai_socktype == SOCK_RAW)
408                 sockstr = "RAW";
409 #ifdef SOCK_SEQPACKET
410               else if (runp->ai_socktype == SOCK_SEQPACKET)
411                 sockstr = "SEQPACKET";
412 #endif
413 #ifdef SOCK_RDM
414               else if (runp->ai_socktype == SOCK_RDM)
415                 sockstr = "RDM";
416 #endif
417 #ifdef SOCK_DCCP
418               else if (runp->ai_socktype == SOCK_DCCP)
419                 sockstr = "DCCP";
420 #endif
421 #ifdef SOCK_PACKET
422               else if (runp->ai_socktype == SOCK_PACKET)
423                 sockstr = "PACKET";
424 #endif
425               else
426                 {
427                   snprintf (sockbuf, sizeof (sockbuf), "%d",
428                             runp->ai_socktype);
429                   sockstr = sockbuf;
430                 }
431
432               char buf[INET6_ADDRSTRLEN];
433               printf ("%-15s %-6s %s\n",
434                       inet_ntop (runp->ai_family,
435                                  runp->ai_family == AF_INET
436                                  ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr
437                                  : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr,
438                                  buf, sizeof (buf)),
439                       sockstr,
440                       runp->ai_canonname ?: "");
441
442               runp = runp->ai_next;
443             }
444
445           freeaddrinfo (res);
446         }
447     }
448
449   return result;
450 }
451
452 static int
453 ahosts_keys (int number, char *key[])
454 {
455   return ahosts_keys_int (AF_UNSPEC, 0, number, key);
456 }
457
458 static int
459 ahostsv4_keys (int number, char *key[])
460 {
461   return ahosts_keys_int (AF_INET, 0, number, key);
462 }
463
464 static int
465 ahostsv6_keys (int number, char *key[])
466 {
467   return ahosts_keys_int (AF_INET6, AI_V4MAPPED, number, key);
468 }
469
470 /* This is for netgroup */
471 static int
472 netgroup_keys (int number, char *key[])
473 {
474   int result = 0;
475   int i;
476
477   if (number == 0)
478     {
479       fprintf (stderr, _("Enumeration not supported on %s\n"), "netgroup");
480       return 3;
481     }
482
483   for (i = 0; i < number; ++i)
484     {
485       if (!setnetgrent (key[i]))
486         result = 2;
487       else
488         {
489           char *p[3];
490
491           printf ("%-21s", key[i]);
492
493           while (getnetgrent (p, p + 1, p + 2))
494             printf (" (%s, %s, %s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
495           putchar_unlocked ('\n');
496         }
497     }
498
499   endnetgrent ();
500
501   return result;
502 }
503
504 /* This is for initgroups */
505 static int
506 initgroups_keys (int number, char *key[])
507 {
508   int ngrps = 100;
509   size_t grpslen = ngrps * sizeof (gid_t);
510   gid_t *grps = alloca (grpslen);
511
512   for (int i = 0; i < number; ++i)
513     {
514       int no = ngrps;
515       int n;
516       while ((n = getgrouplist (key[i], -1, grps, &no)) == -1
517              && no > ngrps)
518         {
519           grps = extend_alloca (grps, grpslen, no * sizeof (gid_t));
520           ngrps = no;
521         }
522
523       if (n == -1)
524         return 1;
525
526       printf ("%-21s", key[i]);
527       for (int j = 0; j < n; ++j)
528         if (grps[j] != -1)
529           printf (" %ld", (long int) grps[j]);
530       putchar_unlocked ('\n');
531     }
532
533   return 0;
534 }
535
536 /* This is for networks */
537 static void
538 print_networks (struct netent *net)
539 {
540   unsigned int i;
541   struct in_addr ip;
542   ip.s_addr = htonl (net->n_net);
543
544   printf ("%-21s %s", net->n_name, inet_ntoa (ip));
545
546   i = 0;
547   while (net->n_aliases[i] != NULL)
548     {
549       putchar_unlocked (' ');
550       fputs_unlocked (net->n_aliases[i], stdout);
551       ++i;
552     }
553   putchar_unlocked ('\n');
554 }
555
556 static int
557 networks_keys (int number, char *key[])
558 {
559   int result = 0;
560   int i;
561   struct netent *net;
562
563   if (number == 0)
564     {
565       setnetent (0);
566       while ((net = getnetent ()) != NULL)
567         print_networks (net);
568       endnetent ();
569       return result;
570     }
571
572   for (i = 0; i < number; ++i)
573     {
574       if (isdigit (key[i][0]))
575         net = getnetbyaddr (ntohl (inet_addr (key[i])), AF_UNSPEC);
576       else
577         net = getnetbyname (key[i]);
578
579       if (net == NULL)
580         result = 2;
581       else
582         print_networks (net);
583     }
584
585   return result;
586 }
587
588 /* Now is all for passwd */
589 static inline void
590 print_passwd (struct passwd *pwd)
591 {
592   printf ("%s:%s:%lu:%lu:%s:%s:%s\n",
593           pwd->pw_name ? pwd->pw_name : "",
594           pwd->pw_passwd ? pwd->pw_passwd : "",
595           (unsigned long int) pwd->pw_uid,
596           (unsigned long int) pwd->pw_gid,
597           pwd->pw_gecos ? pwd->pw_gecos : "",
598           pwd->pw_dir ? pwd->pw_dir : "",
599           pwd->pw_shell ? pwd->pw_shell : "");
600 }
601
602 static int
603 passwd_keys (int number, char *key[])
604 {
605   int result = 0;
606   int i;
607   struct passwd *pwd;
608
609   if (number == 0)
610     {
611       setpwent ();
612       while ((pwd = getpwent ()) != NULL)
613         print_passwd (pwd);
614       endpwent ();
615       return result;
616     }
617
618   for (i = 0; i < number; ++i)
619     {
620       errno = 0;
621       char *ep;
622       uid_t arg_uid = strtoul(key[i], &ep, 10);
623
624       if (errno != EINVAL && *key[i] != '\0' && *ep == '\0')
625         /* Valid numeric uid.  */
626         pwd = getpwuid (arg_uid);
627       else
628         pwd = getpwnam (key[i]);
629
630       if (pwd == NULL)
631         result = 2;
632       else
633         print_passwd (pwd);
634     }
635
636   return result;
637 }
638
639 /* This is for protocols */
640 static inline void
641 print_protocols (struct protoent *proto)
642 {
643   unsigned int i;
644
645   printf ("%-21s %d", proto->p_name, proto->p_proto);
646
647   i = 0;
648   while (proto->p_aliases[i] != NULL)
649     {
650       putchar_unlocked (' ');
651       fputs_unlocked (proto->p_aliases[i], stdout);
652       ++i;
653     }
654   putchar_unlocked ('\n');
655 }
656
657 static int
658 protocols_keys (int number, char *key[])
659 {
660   int result = 0;
661   int i;
662   struct protoent *proto;
663
664   if (number == 0)
665     {
666       setprotoent (0);
667       while ((proto = getprotoent ()) != NULL)
668         print_protocols (proto);
669       endprotoent ();
670       return result;
671     }
672
673   for (i = 0; i < number; ++i)
674     {
675       if (isdigit (key[i][0]))
676         proto = getprotobynumber (atol (key[i]));
677       else
678         proto = getprotobyname (key[i]);
679
680       if (proto == NULL)
681         result = 2;
682       else
683         print_protocols (proto);
684     }
685
686   return result;
687 }
688
689 /* Now is all for rpc */
690 static inline void
691 print_rpc (struct rpcent *rpc)
692 {
693   int i;
694
695   printf ("%-15s %d%s",
696           rpc->r_name, rpc->r_number, rpc->r_aliases[0] ? " " : "");
697
698   for (i = 0; rpc->r_aliases[i]; ++i)
699     printf (" %s", rpc->r_aliases[i]);
700   putchar_unlocked ('\n');
701 }
702
703 static int
704 rpc_keys (int number, char *key[])
705 {
706   int result = 0;
707   int i;
708   struct rpcent *rpc;
709
710   if (number == 0)
711     {
712       setrpcent (0);
713       while ((rpc = getrpcent ()) != NULL)
714         print_rpc (rpc);
715       endrpcent ();
716       return result;
717     }
718
719   for (i = 0; i < number; ++i)
720     {
721       if (isdigit (key[i][0]))
722         rpc = getrpcbynumber (atol (key[i]));
723       else
724         rpc = getrpcbyname (key[i]);
725
726       if (rpc == NULL)
727         result = 2;
728       else
729         print_rpc (rpc);
730     }
731
732   return result;
733 }
734
735 /* for services */
736 static void
737 print_services (struct servent *serv)
738 {
739   unsigned int i;
740
741   printf ("%-21s %d/%s", serv->s_name, ntohs (serv->s_port), serv->s_proto);
742
743   i = 0;
744   while (serv->s_aliases[i] != NULL)
745     {
746       putchar_unlocked (' ');
747       fputs_unlocked (serv->s_aliases[i], stdout);
748       ++i;
749     }
750   putchar_unlocked ('\n');
751 }
752
753 static int
754 services_keys (int number, char *key[])
755 {
756   int result = 0;
757   int i;
758   struct servent *serv;
759
760   if (!number)
761     {
762       setservent (0);
763       while ((serv = getservent ()) != NULL)
764         print_services (serv);
765       endservent ();
766       return result;
767     }
768
769   for (i = 0; i < number; ++i)
770     {
771       struct servent *serv;
772       char *proto = strchr (key[i], '/');
773
774       if (proto != NULL)
775         *proto++ = '\0';
776
777       if (isdigit (key[i][0]))
778         serv = getservbyport (htons (atol (key[i])), proto);
779       else
780         serv = getservbyname (key[i], proto);
781
782       if (serv == NULL)
783         result = 2;
784       else
785         print_services (serv);
786     }
787
788   return result;
789 }
790
791 /* This is for shadow */
792 static void
793 print_shadow (struct spwd *sp)
794 {
795   printf ("%s:%s:",
796           sp->sp_namp ? sp->sp_namp : "",
797           sp->sp_pwdp ? sp->sp_pwdp : "");
798
799 #define SHADOW_FIELD(n) \
800   if (sp->n == -1)                                                            \
801     putchar_unlocked (':');                                                   \
802   else                                                                        \
803     printf ("%ld:", sp->n)
804
805   SHADOW_FIELD (sp_lstchg);
806   SHADOW_FIELD (sp_min);
807   SHADOW_FIELD (sp_max);
808   SHADOW_FIELD (sp_warn);
809   SHADOW_FIELD (sp_inact);
810   SHADOW_FIELD (sp_expire);
811   if (sp->sp_flag == ~0ul)
812     putchar_unlocked ('\n');
813   else
814     printf ("%lu\n", sp->sp_flag);
815 }
816
817 static int
818 shadow_keys (int number, char *key[])
819 {
820   int result = 0;
821   int i;
822
823   if (number == 0)
824     {
825       struct spwd *sp;
826
827       setspent ();
828       while ((sp = getspent ()) != NULL)
829         print_shadow (sp);
830       endpwent ();
831       return result;
832     }
833
834   for (i = 0; i < number; ++i)
835     {
836       struct spwd *sp;
837
838       sp = getspnam (key[i]);
839
840       if (sp == NULL)
841         result = 2;
842       else
843         print_shadow (sp);
844     }
845
846   return result;
847 }
848
849 struct
850   {
851     const char *name;
852     int (*func) (int number, char *key[]);
853   } databases[] =
854   {
855 #define D(name) { #name, name ## _keys },
856 D(ahosts)
857 D(ahostsv4)
858 D(ahostsv6)
859 D(aliases)
860 D(ethers)
861 D(group)
862 D(gshadow)
863 D(hosts)
864 D(initgroups)
865 D(netgroup)
866 D(networks)
867 D(passwd)
868 D(protocols)
869 D(rpc)
870 D(services)
871 D(shadow)
872 #undef D
873     { NULL, NULL }
874   };
875
876 /* Handle arguments found by argp. */
877 static error_t
878 parse_option (int key, char *arg, struct argp_state *state)
879 {
880   char *endp;
881   switch (key)
882     {
883     case 's':
884       endp = strchr (arg, ':');
885       if (endp == NULL)
886         /* No specific database, change them all.  */
887         for (int i = 0; databases[i].name != NULL; ++i)
888           __nss_configure_lookup (databases[i].name, arg);
889       else
890         {
891           int i;
892           for (i = 0; databases[i].name != NULL; ++i)
893             if (strncmp (databases[i].name, arg, endp - arg) == 0)
894               {
895                 __nss_configure_lookup (databases[i].name, endp + 1);
896                 break;
897               }
898           if (databases[i].name == NULL)
899             error (EXIT_FAILURE, 0, gettext ("Unknown database name"));
900         }
901       break;
902
903     case 'i':
904       idn_flags = 0;
905       break;
906
907     default:
908       return ARGP_ERR_UNKNOWN;
909     }
910
911   return 0;
912 }
913
914
915 static char *
916 more_help (int key, const char *text, void *input)
917 {
918   switch (key)
919     {
920       size_t len;
921       char *doc;
922       FILE *fp;
923
924     case ARGP_KEY_HELP_EXTRA:
925       /* We print some extra information.  */
926       fp = open_memstream (&doc, &len);
927       if (fp != NULL)
928         {
929           fputs_unlocked (_("Supported databases:\n"), fp);
930
931           for (int i = 0, col = 0; databases[i].name != NULL; ++i)
932             {
933               len = strlen (databases[i].name);
934               if (i != 0)
935                 {
936                   if (col + len > 72)
937                     {
938                       col = 0;
939                       fputc_unlocked ('\n', fp);
940                     }
941                   else
942                     fputc_unlocked (' ', fp);
943                 }
944
945               fputs_unlocked (databases[i].name, fp);
946               col += len + 1;
947             }
948
949           fputs ("\n\n", fp);
950
951           fputs (gettext ("\
952 For bug reporting instructions, please see:\n\
953 <http://www.gnu.org/software/libc/bugs.html>.\n"), fp);
954
955           if (fclose (fp) == 0)
956             return doc;
957         }
958       break;
959
960     default:
961       break;
962     }
963   return (char *) text;
964 }
965
966
967 /* the main function */
968 int
969 main (int argc, char *argv[])
970 {
971   /* Debugging support.  */
972   mtrace ();
973
974   /* Set locale via LC_ALL.  */
975   setlocale (LC_ALL, "");
976   /* Set the text message domain.  */
977   textdomain (PACKAGE);
978
979   /* Parse and process arguments.  */
980   int remaining;
981   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
982
983   if ((argc - remaining) < 1)
984     {
985       error (0, 0, gettext ("wrong number of arguments"));
986       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
987       return 1;
988     }
989
990   for (int i = 0; databases[i].name; ++i)
991     if (argv[remaining][0] == databases[i].name[0]
992         && !strcmp (argv[remaining], databases[i].name))
993       return databases[i].func (argc - remaining - 1, &argv[remaining + 1]);
994
995   fprintf (stderr, _("Unknown database: %s\n"), argv[remaining]);
996   argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
997   return 1;
998 }