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