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