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