Implement caching of nscd
[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   if (number == 4)
484     {
485       char *host = strcmp (key[1], "*") == 0 ? NULL : key[1];
486       char *user = strcmp (key[2], "*") == 0 ? NULL : key[2];
487       char *domain = strcmp (key[3], "*") == 0 ? NULL : key[3];
488
489       printf ("%-21s (%s,%s,%s) = %d\n",
490               key[0], host ?: "", user ?: "", domain ?: "",
491               innetgr (key[0], host, user, domain));
492     }
493   else if (number == 1)
494     {
495       if (!setnetgrent (key[0]))
496         result = 2;
497       else
498         {
499           char *p[3];
500
501           printf ("%-21s", key[0]);
502
503           while (getnetgrent (p, p + 1, p + 2))
504             printf (" (%s,%s,%s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
505           putchar_unlocked ('\n');
506         }
507     }
508
509   endnetgrent ();
510
511   return result;
512 }
513
514 /* This is for initgroups */
515 static int
516 initgroups_keys (int number, char *key[])
517 {
518   int ngrps = 100;
519   size_t grpslen = ngrps * sizeof (gid_t);
520   gid_t *grps = alloca (grpslen);
521
522   for (int i = 0; i < number; ++i)
523     {
524       int no = ngrps;
525       int n;
526       while ((n = getgrouplist (key[i], -1, grps, &no)) == -1
527              && no > ngrps)
528         {
529           grps = extend_alloca (grps, grpslen, no * sizeof (gid_t));
530           ngrps = no;
531         }
532
533       if (n == -1)
534         return 1;
535
536       printf ("%-21s", key[i]);
537       for (int j = 0; j < n; ++j)
538         if (grps[j] != -1)
539           printf (" %ld", (long int) grps[j]);
540       putchar_unlocked ('\n');
541     }
542
543   return 0;
544 }
545
546 /* This is for networks */
547 static void
548 print_networks (struct netent *net)
549 {
550   unsigned int i;
551   struct in_addr ip;
552   ip.s_addr = htonl (net->n_net);
553
554   printf ("%-21s %s", net->n_name, inet_ntoa (ip));
555
556   i = 0;
557   while (net->n_aliases[i] != NULL)
558     {
559       putchar_unlocked (' ');
560       fputs_unlocked (net->n_aliases[i], stdout);
561       ++i;
562     }
563   putchar_unlocked ('\n');
564 }
565
566 static int
567 networks_keys (int number, char *key[])
568 {
569   int result = 0;
570   int i;
571   struct netent *net;
572
573   if (number == 0)
574     {
575       setnetent (0);
576       while ((net = getnetent ()) != NULL)
577         print_networks (net);
578       endnetent ();
579       return result;
580     }
581
582   for (i = 0; i < number; ++i)
583     {
584       if (isdigit (key[i][0]))
585         net = getnetbyaddr (ntohl (inet_addr (key[i])), AF_UNSPEC);
586       else
587         net = getnetbyname (key[i]);
588
589       if (net == NULL)
590         result = 2;
591       else
592         print_networks (net);
593     }
594
595   return result;
596 }
597
598 /* Now is all for passwd */
599 static inline void
600 print_passwd (struct passwd *pwd)
601 {
602   printf ("%s:%s:%lu:%lu:%s:%s:%s\n",
603           pwd->pw_name ? pwd->pw_name : "",
604           pwd->pw_passwd ? pwd->pw_passwd : "",
605           (unsigned long int) pwd->pw_uid,
606           (unsigned long int) pwd->pw_gid,
607           pwd->pw_gecos ? pwd->pw_gecos : "",
608           pwd->pw_dir ? pwd->pw_dir : "",
609           pwd->pw_shell ? pwd->pw_shell : "");
610 }
611
612 static int
613 passwd_keys (int number, char *key[])
614 {
615   int result = 0;
616   int i;
617   struct passwd *pwd;
618
619   if (number == 0)
620     {
621       setpwent ();
622       while ((pwd = getpwent ()) != NULL)
623         print_passwd (pwd);
624       endpwent ();
625       return result;
626     }
627
628   for (i = 0; i < number; ++i)
629     {
630       errno = 0;
631       char *ep;
632       uid_t arg_uid = strtoul(key[i], &ep, 10);
633
634       if (errno != EINVAL && *key[i] != '\0' && *ep == '\0')
635         /* Valid numeric uid.  */
636         pwd = getpwuid (arg_uid);
637       else
638         pwd = getpwnam (key[i]);
639
640       if (pwd == NULL)
641         result = 2;
642       else
643         print_passwd (pwd);
644     }
645
646   return result;
647 }
648
649 /* This is for protocols */
650 static inline void
651 print_protocols (struct protoent *proto)
652 {
653   unsigned int i;
654
655   printf ("%-21s %d", proto->p_name, proto->p_proto);
656
657   i = 0;
658   while (proto->p_aliases[i] != NULL)
659     {
660       putchar_unlocked (' ');
661       fputs_unlocked (proto->p_aliases[i], stdout);
662       ++i;
663     }
664   putchar_unlocked ('\n');
665 }
666
667 static int
668 protocols_keys (int number, char *key[])
669 {
670   int result = 0;
671   int i;
672   struct protoent *proto;
673
674   if (number == 0)
675     {
676       setprotoent (0);
677       while ((proto = getprotoent ()) != NULL)
678         print_protocols (proto);
679       endprotoent ();
680       return result;
681     }
682
683   for (i = 0; i < number; ++i)
684     {
685       if (isdigit (key[i][0]))
686         proto = getprotobynumber (atol (key[i]));
687       else
688         proto = getprotobyname (key[i]);
689
690       if (proto == NULL)
691         result = 2;
692       else
693         print_protocols (proto);
694     }
695
696   return result;
697 }
698
699 /* Now is all for rpc */
700 static inline void
701 print_rpc (struct rpcent *rpc)
702 {
703   int i;
704
705   printf ("%-15s %d%s",
706           rpc->r_name, rpc->r_number, rpc->r_aliases[0] ? " " : "");
707
708   for (i = 0; rpc->r_aliases[i]; ++i)
709     printf (" %s", rpc->r_aliases[i]);
710   putchar_unlocked ('\n');
711 }
712
713 static int
714 rpc_keys (int number, char *key[])
715 {
716   int result = 0;
717   int i;
718   struct rpcent *rpc;
719
720   if (number == 0)
721     {
722       setrpcent (0);
723       while ((rpc = getrpcent ()) != NULL)
724         print_rpc (rpc);
725       endrpcent ();
726       return result;
727     }
728
729   for (i = 0; i < number; ++i)
730     {
731       if (isdigit (key[i][0]))
732         rpc = getrpcbynumber (atol (key[i]));
733       else
734         rpc = getrpcbyname (key[i]);
735
736       if (rpc == NULL)
737         result = 2;
738       else
739         print_rpc (rpc);
740     }
741
742   return result;
743 }
744
745 /* for services */
746 static void
747 print_services (struct servent *serv)
748 {
749   unsigned int i;
750
751   printf ("%-21s %d/%s", serv->s_name, ntohs (serv->s_port), serv->s_proto);
752
753   i = 0;
754   while (serv->s_aliases[i] != NULL)
755     {
756       putchar_unlocked (' ');
757       fputs_unlocked (serv->s_aliases[i], stdout);
758       ++i;
759     }
760   putchar_unlocked ('\n');
761 }
762
763 static int
764 services_keys (int number, char *key[])
765 {
766   int result = 0;
767   int i;
768   struct servent *serv;
769
770   if (!number)
771     {
772       setservent (0);
773       while ((serv = getservent ()) != NULL)
774         print_services (serv);
775       endservent ();
776       return result;
777     }
778
779   for (i = 0; i < number; ++i)
780     {
781       struct servent *serv;
782       char *proto = strchr (key[i], '/');
783
784       if (proto != NULL)
785         *proto++ = '\0';
786
787       if (isdigit (key[i][0]))
788         serv = getservbyport (htons (atol (key[i])), proto);
789       else
790         serv = getservbyname (key[i], proto);
791
792       if (serv == NULL)
793         result = 2;
794       else
795         print_services (serv);
796     }
797
798   return result;
799 }
800
801 /* This is for shadow */
802 static void
803 print_shadow (struct spwd *sp)
804 {
805   printf ("%s:%s:",
806           sp->sp_namp ? sp->sp_namp : "",
807           sp->sp_pwdp ? sp->sp_pwdp : "");
808
809 #define SHADOW_FIELD(n) \
810   if (sp->n == -1)                                                            \
811     putchar_unlocked (':');                                                   \
812   else                                                                        \
813     printf ("%ld:", sp->n)
814
815   SHADOW_FIELD (sp_lstchg);
816   SHADOW_FIELD (sp_min);
817   SHADOW_FIELD (sp_max);
818   SHADOW_FIELD (sp_warn);
819   SHADOW_FIELD (sp_inact);
820   SHADOW_FIELD (sp_expire);
821   if (sp->sp_flag == ~0ul)
822     putchar_unlocked ('\n');
823   else
824     printf ("%lu\n", sp->sp_flag);
825 }
826
827 static int
828 shadow_keys (int number, char *key[])
829 {
830   int result = 0;
831   int i;
832
833   if (number == 0)
834     {
835       struct spwd *sp;
836
837       setspent ();
838       while ((sp = getspent ()) != NULL)
839         print_shadow (sp);
840       endpwent ();
841       return result;
842     }
843
844   for (i = 0; i < number; ++i)
845     {
846       struct spwd *sp;
847
848       sp = getspnam (key[i]);
849
850       if (sp == NULL)
851         result = 2;
852       else
853         print_shadow (sp);
854     }
855
856   return result;
857 }
858
859 struct
860   {
861     const char *name;
862     int (*func) (int number, char *key[]);
863   } databases[] =
864   {
865 #define D(name) { #name, name ## _keys },
866 D(ahosts)
867 D(ahostsv4)
868 D(ahostsv6)
869 D(aliases)
870 D(ethers)
871 D(group)
872 D(gshadow)
873 D(hosts)
874 D(initgroups)
875 D(netgroup)
876 D(networks)
877 D(passwd)
878 D(protocols)
879 D(rpc)
880 D(services)
881 D(shadow)
882 #undef D
883     { NULL, NULL }
884   };
885
886 /* Handle arguments found by argp. */
887 static error_t
888 parse_option (int key, char *arg, struct argp_state *state)
889 {
890   char *endp;
891   switch (key)
892     {
893     case 's':
894       endp = strchr (arg, ':');
895       if (endp == NULL)
896         /* No specific database, change them all.  */
897         for (int i = 0; databases[i].name != NULL; ++i)
898           __nss_configure_lookup (databases[i].name, arg);
899       else
900         {
901           int i;
902           for (i = 0; databases[i].name != NULL; ++i)
903             if (strncmp (databases[i].name, arg, endp - arg) == 0)
904               {
905                 __nss_configure_lookup (databases[i].name, endp + 1);
906                 break;
907               }
908           if (databases[i].name == NULL)
909             error (EXIT_FAILURE, 0, gettext ("Unknown database name"));
910         }
911       break;
912
913     case 'i':
914       idn_flags = 0;
915       break;
916
917     default:
918       return ARGP_ERR_UNKNOWN;
919     }
920
921   return 0;
922 }
923
924
925 static char *
926 more_help (int key, const char *text, void *input)
927 {
928   switch (key)
929     {
930       size_t len;
931       char *doc;
932       FILE *fp;
933
934     case ARGP_KEY_HELP_EXTRA:
935       /* We print some extra information.  */
936       fp = open_memstream (&doc, &len);
937       if (fp != NULL)
938         {
939           fputs_unlocked (_("Supported databases:\n"), fp);
940
941           for (int i = 0, col = 0; databases[i].name != NULL; ++i)
942             {
943               len = strlen (databases[i].name);
944               if (i != 0)
945                 {
946                   if (col + len > 72)
947                     {
948                       col = 0;
949                       fputc_unlocked ('\n', fp);
950                     }
951                   else
952                     fputc_unlocked (' ', fp);
953                 }
954
955               fputs_unlocked (databases[i].name, fp);
956               col += len + 1;
957             }
958
959           fputs ("\n\n", fp);
960
961           fputs (gettext ("\
962 For bug reporting instructions, please see:\n\
963 <http://www.gnu.org/software/libc/bugs.html>.\n"), fp);
964
965           if (fclose (fp) == 0)
966             return doc;
967         }
968       break;
969
970     default:
971       break;
972     }
973   return (char *) text;
974 }
975
976
977 /* the main function */
978 int
979 main (int argc, char *argv[])
980 {
981   /* Debugging support.  */
982   mtrace ();
983
984   /* Set locale via LC_ALL.  */
985   setlocale (LC_ALL, "");
986   /* Set the text message domain.  */
987   textdomain (PACKAGE);
988
989   /* Parse and process arguments.  */
990   int remaining;
991   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
992
993   if ((argc - remaining) < 1)
994     {
995       error (0, 0, gettext ("wrong number of arguments"));
996       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
997       return 1;
998     }
999
1000   for (int i = 0; databases[i].name; ++i)
1001     if (argv[remaining][0] == databases[i].name[0]
1002         && !strcmp (argv[remaining], databases[i].name))
1003       return databases[i].func (argc - remaining - 1, &argv[remaining + 1]);
1004
1005   fprintf (stderr, _("Unknown database: %s\n"), argv[remaining]);
1006   argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
1007   return 1;
1008 }