chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / nss / getent.c
1 /* Copyright (c) 1998-2008, 2009 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 <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 <gnu/option-groups.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     { 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 /* Print the version information.  */
79 static void
80 print_version (FILE *stream, struct argp_state *state)
81 {
82   fprintf (stream, "getent %s%s\n", PKGVERSION, VERSION);
83   fprintf (stream, gettext ("\
84 Copyright (C) %s Free Software Foundation, Inc.\n\
85 This is free software; see the source for copying conditions.  There is NO\n\
86 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
87 "), "2009");
88   fprintf (stream, gettext ("Written by %s.\n"), "Thorsten Kukuk");
89 }
90
91 #if __OPTION_EGLIBC_DB_ALIASES
92 /* This is for aliases */
93 static inline void
94 print_aliases (struct aliasent *alias)
95 {
96   unsigned int i = 0;
97
98   printf ("%s: ", alias->alias_name);
99   for  (i = strlen (alias->alias_name); i < 14; ++i)
100     fputs_unlocked (" ", stdout);
101
102   for (i = 0; i < alias->alias_members_len; ++i)
103     printf ("%s%s",
104             alias->alias_members [i],
105             i + 1 == alias->alias_members_len ? "\n" : ", ");
106 }
107
108 static int
109 aliases_keys (int number, char *key[])
110 {
111   int result = 0;
112   int i;
113   struct aliasent *alias;
114
115   if (number == 0)
116     {
117       setaliasent ();
118       while ((alias = getaliasent ()) != NULL)
119         print_aliases (alias);
120       endaliasent ();
121       return result;
122     }
123
124   for (i = 0; i < number; ++i)
125     {
126       alias = getaliasbyname (key[i]);
127
128       if (alias == NULL)
129         result = 2;
130       else
131         print_aliases (alias);
132     }
133
134   return result;
135 }
136 #endif /* __OPTION_EGLIBC_DB_ALIASES */
137
138 #if __OPTION_EGLIBC_INET
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 #endif /* __OPTION_EGLIBC_INET */
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 #if __OPTION_EGLIBC_INET
306 /* This is for hosts */
307 static void
308 print_hosts (struct hostent *host)
309 {
310   unsigned int cnt;
311
312   for (cnt = 0; host->h_addr_list[cnt] != NULL; ++cnt)
313     {
314       char buf[INET6_ADDRSTRLEN];
315       const char *ip = inet_ntop (host->h_addrtype, host->h_addr_list[cnt],
316                                   buf, sizeof (buf));
317
318       printf ("%-15s %s", ip, host->h_name);
319
320       unsigned int i;
321       for (i = 0; host->h_aliases[i] != NULL; ++i)
322         {
323           putchar_unlocked (' ');
324           fputs_unlocked (host->h_aliases[i], stdout);
325         }
326       putchar_unlocked ('\n');
327     }
328 }
329
330 static int
331 hosts_keys (int number, char *key[])
332 {
333   int result = 0;
334   int i;
335   struct hostent *host;
336
337   if (number == 0)
338     {
339       sethostent (0);
340       while ((host = gethostent ()) != NULL)
341         print_hosts (host);
342       endhostent ();
343       return result;
344     }
345
346   for (i = 0; i < number; ++i)
347     {
348       struct hostent *host = NULL;
349       char addr[IN6ADDRSZ];
350
351       if (inet_pton (AF_INET6, key[i], &addr) > 0)
352         host = gethostbyaddr (addr, IN6ADDRSZ, AF_INET6);
353       else if (inet_pton (AF_INET, key[i], &addr) > 0)
354         host = gethostbyaddr (addr, INADDRSZ, AF_INET);
355       else if ((host = gethostbyname2 (key[i], AF_INET6)) == NULL)
356         host = gethostbyname2 (key[i], AF_INET);
357
358       if (host == NULL)
359         result = 2;
360       else
361         print_hosts (host);
362     }
363
364   return result;
365 }
366
367 /* This is for hosts, but using getaddrinfo */
368 static int
369 ahosts_keys_int (int af, int xflags, int number, char *key[])
370 {
371   int result = 0;
372   int i;
373   struct hostent *host;
374
375   if (number == 0)
376     {
377       sethostent (0);
378       while ((host = gethostent ()) != NULL)
379         print_hosts (host);
380       endhostent ();
381       return result;
382     }
383
384   struct addrinfo hint;
385   memset (&hint, '\0', sizeof (hint));
386   hint.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME | xflags;
387   hint.ai_family = af;
388
389   for (i = 0; i < number; ++i)
390     {
391       struct addrinfo *res;
392
393       if (getaddrinfo (key[i], NULL, &hint, &res) != 0)
394         result = 2;
395       else
396         {
397           struct addrinfo *runp = res;
398
399           while (runp != NULL)
400             {
401               char sockbuf[20];
402               const char *sockstr;
403               if (runp->ai_socktype == SOCK_STREAM)
404                 sockstr = "STREAM";
405               else if (runp->ai_socktype == SOCK_DGRAM)
406                 sockstr = "DGRAM";
407               else if (runp->ai_socktype == SOCK_RAW)
408                 sockstr = "RAW";
409 #ifdef SOCK_SEQPACKET
410               else if (runp->ai_socktype == SOCK_SEQPACKET)
411                 sockstr = "SEQPACKET";
412 #endif
413 #ifdef SOCK_RDM
414               else if (runp->ai_socktype == SOCK_RDM)
415                 sockstr = "RDM";
416 #endif
417 #ifdef SOCK_DCCP
418               else if (runp->ai_socktype == SOCK_DCCP)
419                 sockstr = "DCCP";
420 #endif
421 #ifdef SOCK_PACKET
422               else if (runp->ai_socktype == SOCK_PACKET)
423                 sockstr = "PACKET";
424 #endif
425               else
426                 {
427                   snprintf (sockbuf, sizeof (sockbuf), "%d",
428                             runp->ai_socktype);
429                   sockstr = sockbuf;
430                 }
431
432               char buf[INET6_ADDRSTRLEN];
433               printf ("%-15s %-6s %s\n",
434                       inet_ntop (runp->ai_family,
435                                  runp->ai_family == AF_INET
436                                  ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr
437                                  : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr,
438                                  buf, sizeof (buf)),
439                       sockstr,
440                       runp->ai_canonname ?: "");
441
442               runp = runp->ai_next;
443             }
444
445           freeaddrinfo (res);
446         }
447     }
448
449   return result;
450 }
451
452 static int
453 ahosts_keys (int number, char *key[])
454 {
455   return ahosts_keys_int (AF_UNSPEC, 0, number, key);
456 }
457
458 static int
459 ahostsv4_keys (int number, char *key[])
460 {
461   return ahosts_keys_int (AF_INET, 0, number, key);
462 }
463
464 static int
465 ahostsv6_keys (int number, char *key[])
466 {
467   return ahosts_keys_int (AF_INET6, AI_V4MAPPED, number, key);
468 }
469
470 /* This is for netgroup */
471 static int
472 netgroup_keys (int number, char *key[])
473 {
474   int result = 0;
475   int i;
476
477   if (number == 0)
478     {
479       fprintf (stderr, _("Enumeration not supported on %s\n"), "netgroup");
480       return 3;
481     }
482
483   for (i = 0; i < number; ++i)
484     {
485       if (!setnetgrent (key[i]))
486         result = 2;
487       else
488         {
489           char *p[3];
490
491           printf ("%-21s", key[i]);
492
493           while (getnetgrent (p, p + 1, p + 2))
494             printf (" (%s, %s, %s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
495           putchar_unlocked ('\n');
496         }
497     }
498
499   endnetgrent ();
500
501   return result;
502 }
503
504 /* This is for networks */
505 static void
506 print_networks (struct netent *net)
507 {
508   unsigned int i;
509   struct in_addr ip;
510   ip.s_addr = htonl (net->n_net);
511
512   printf ("%-21s %s", net->n_name, inet_ntoa (ip));
513
514   i = 0;
515   while (net->n_aliases[i] != NULL)
516     {
517       putchar_unlocked (' ');
518       fputs_unlocked (net->n_aliases[i], stdout);
519       ++i;
520     }
521   putchar_unlocked ('\n');
522 }
523
524 static int
525 networks_keys (int number, char *key[])
526 {
527   int result = 0;
528   int i;
529   struct netent *net;
530
531   if (number == 0)
532     {
533       setnetent (0);
534       while ((net = getnetent ()) != NULL)
535         print_networks (net);
536       endnetent ();
537       return result;
538     }
539
540   for (i = 0; i < number; ++i)
541     {
542       if (isdigit (key[i][0]))
543         net = getnetbyaddr (ntohl (inet_addr (key[i])), AF_UNSPEC);
544       else
545         net = getnetbyname (key[i]);
546
547       if (net == NULL)
548         result = 2;
549       else
550         print_networks (net);
551     }
552
553   return result;
554 }
555 #endif /* __OPTION_EGLIBC_INET */
556
557 /* Now is all for passwd */
558 static inline void
559 print_passwd (struct passwd *pwd)
560 {
561   printf ("%s:%s:%lu:%lu:%s:%s:%s\n",
562           pwd->pw_name ? pwd->pw_name : "",
563           pwd->pw_passwd ? pwd->pw_passwd : "",
564           (unsigned long int) pwd->pw_uid,
565           (unsigned long int) pwd->pw_gid,
566           pwd->pw_gecos ? pwd->pw_gecos : "",
567           pwd->pw_dir ? pwd->pw_dir : "",
568           pwd->pw_shell ? pwd->pw_shell : "");
569 }
570
571 static int
572 passwd_keys (int number, char *key[])
573 {
574   int result = 0;
575   int i;
576   struct passwd *pwd;
577
578   if (number == 0)
579     {
580       setpwent ();
581       while ((pwd = getpwent ()) != NULL)
582         print_passwd (pwd);
583       endpwent ();
584       return result;
585     }
586
587   for (i = 0; i < number; ++i)
588     {
589       errno = 0;
590       char *ep;
591       uid_t arg_uid = strtoul(key[i], &ep, 10);
592
593       if (errno != EINVAL && *key[i] != '\0' && *ep == '\0')
594         /* Valid numeric uid.  */
595         pwd = getpwuid (arg_uid);
596       else
597         pwd = getpwnam (key[i]);
598
599       if (pwd == NULL)
600         result = 2;
601       else
602         print_passwd (pwd);
603     }
604
605   return result;
606 }
607
608 #if __OPTION_EGLIBC_INET
609 /* This is for protocols */
610 static inline void
611 print_protocols (struct protoent *proto)
612 {
613   unsigned int i;
614
615   printf ("%-21s %d", proto->p_name, proto->p_proto);
616
617   i = 0;
618   while (proto->p_aliases[i] != NULL)
619     {
620       putchar_unlocked (' ');
621       fputs_unlocked (proto->p_aliases[i], stdout);
622       ++i;
623     }
624   putchar_unlocked ('\n');
625 }
626
627 static int
628 protocols_keys (int number, char *key[])
629 {
630   int result = 0;
631   int i;
632   struct protoent *proto;
633
634   if (number == 0)
635     {
636       setprotoent (0);
637       while ((proto = getprotoent ()) != NULL)
638         print_protocols (proto);
639       endprotoent ();
640       return result;
641     }
642
643   for (i = 0; i < number; ++i)
644     {
645       if (isdigit (key[i][0]))
646         proto = getprotobynumber (atol (key[i]));
647       else
648         proto = getprotobyname (key[i]);
649
650       if (proto == NULL)
651         result = 2;
652       else
653         print_protocols (proto);
654     }
655
656   return result;
657 }
658
659 /* Now is all for rpc */
660 static inline void
661 print_rpc (struct rpcent *rpc)
662 {
663   int i;
664
665   printf ("%-15s %d%s",
666           rpc->r_name, rpc->r_number, rpc->r_aliases[0] ? " " : "");
667
668   for (i = 0; rpc->r_aliases[i]; ++i)
669     printf (" %s", rpc->r_aliases[i]);
670   putchar_unlocked ('\n');
671 }
672
673 static int
674 rpc_keys (int number, char *key[])
675 {
676   int result = 0;
677   int i;
678   struct rpcent *rpc;
679
680   if (number == 0)
681     {
682       setrpcent (0);
683       while ((rpc = getrpcent ()) != NULL)
684         print_rpc (rpc);
685       endrpcent ();
686       return result;
687     }
688
689   for (i = 0; i < number; ++i)
690     {
691       if (isdigit (key[i][0]))
692         rpc = getrpcbynumber (atol (key[i]));
693       else
694         rpc = getrpcbyname (key[i]);
695
696       if (rpc == NULL)
697         result = 2;
698       else
699         print_rpc (rpc);
700     }
701
702   return result;
703 }
704
705 /* for services */
706 static void
707 print_services (struct servent *serv)
708 {
709   unsigned int i;
710
711   printf ("%-21s %d/%s", serv->s_name, ntohs (serv->s_port), serv->s_proto);
712
713   i = 0;
714   while (serv->s_aliases[i] != NULL)
715     {
716       putchar_unlocked (' ');
717       fputs_unlocked (serv->s_aliases[i], stdout);
718       ++i;
719     }
720   putchar_unlocked ('\n');
721 }
722
723 static int
724 services_keys (int number, char *key[])
725 {
726   int result = 0;
727   int i;
728   struct servent *serv;
729
730   if (!number)
731     {
732       setservent (0);
733       while ((serv = getservent ()) != NULL)
734         print_services (serv);
735       endservent ();
736       return result;
737     }
738
739   for (i = 0; i < number; ++i)
740     {
741       struct servent *serv;
742       char *proto = strchr (key[i], '/');
743
744       if (proto != NULL)
745         *proto++ = '\0';
746
747       if (isdigit (key[i][0]))
748         serv = getservbyport (htons (atol (key[i])), proto);
749       else
750         serv = getservbyname (key[i], proto);
751
752       if (serv == NULL)
753         result = 2;
754       else
755         print_services (serv);
756     }
757
758   return result;
759 }
760 #endif /* __OPTION_EGLIBC_INET */
761
762 /* This is for shadow */
763 static void
764 print_shadow (struct spwd *sp)
765 {
766   printf ("%s:%s:",
767           sp->sp_namp ? sp->sp_namp : "",
768           sp->sp_pwdp ? sp->sp_pwdp : "");
769
770 #define SHADOW_FIELD(n) \
771   if (sp->n == -1)                                                            \
772     putchar_unlocked (':');                                                   \
773   else                                                                        \
774     printf ("%ld:", sp->n)
775
776   SHADOW_FIELD (sp_lstchg);
777   SHADOW_FIELD (sp_min);
778   SHADOW_FIELD (sp_max);
779   SHADOW_FIELD (sp_warn);
780   SHADOW_FIELD (sp_inact);
781   SHADOW_FIELD (sp_expire);
782   if (sp->sp_flag == ~0ul)
783     putchar_unlocked ('\n');
784   else
785     printf ("%lu\n", sp->sp_flag);
786 }
787
788 static int
789 shadow_keys (int number, char *key[])
790 {
791   int result = 0;
792   int i;
793
794   if (number == 0)
795     {
796       struct spwd *sp;
797
798       setspent ();
799       while ((sp = getspent ()) != NULL)
800         print_shadow (sp);
801       endpwent ();
802       return result;
803     }
804
805   for (i = 0; i < number; ++i)
806     {
807       struct spwd *sp;
808
809       sp = getspnam (key[i]);
810
811       if (sp == NULL)
812         result = 2;
813       else
814         print_shadow (sp);
815     }
816
817   return result;
818 }
819
820 struct
821   {
822     const char *name;
823     int (*func) (int number, char *key[]);
824   } databases[] =
825   {
826 #define D(name) { #name, name ## _keys },
827
828 #if __OPTION_EGLIBC_INET
829 #define DN(name) D(name)
830 #else
831 #define DN(name)
832 #endif
833
834 #if __OPTION_EGLIBC_DB_ALIASES
835 #define DA(name) D(name)
836 #else
837 #define DA(name)
838 #endif
839
840 DN(ahosts)
841 DN(ahostsv4)
842 DN(ahostsv6)
843 DA(aliases)
844 DN(ethers)
845 D(group)
846 D(gshadow)
847 DN(hosts)
848 DN(netgroup)
849 DN(networks)
850 D(passwd)
851 DN(protocols)
852 DN(rpc)
853 DN(services)
854 D(shadow)
855 #undef D
856     { NULL, NULL }
857   };
858
859 /* Handle arguments found by argp. */
860 static error_t
861 parse_option (int key, char *arg, struct argp_state *state)
862 {
863   char *endp;
864   switch (key)
865     {
866     case 's':
867       endp = strchr (arg, ':');
868       if (endp == NULL)
869         /* No specific database, change them all.  */
870         for (int i = 0; databases[i].name != NULL; ++i)
871           __nss_configure_lookup (databases[i].name, arg);
872       else
873         {
874           int i;
875           for (i = 0; databases[i].name != NULL; ++i)
876             if (strncmp (databases[i].name, arg, endp - arg) == 0)
877               {
878                 __nss_configure_lookup (databases[i].name, endp + 1);
879                 break;
880               }
881           if (databases[i].name == NULL)
882             error (EXIT_FAILURE, 0, gettext ("Unknown database name"));
883         }
884       break;
885
886     default:
887       return ARGP_ERR_UNKNOWN;
888     }
889
890   return 0;
891 }
892
893
894 static char *
895 more_help (int key, const char *text, void *input)
896 {
897   switch (key)
898     {
899       size_t len;
900       char *doc;
901       FILE *fp;
902
903     case ARGP_KEY_HELP_EXTRA:
904       /* We print some extra information.  */
905       fp = open_memstream (&doc, &len);
906       if (fp != NULL)
907         {
908           fputs_unlocked (_("Supported databases:\n"), fp);
909
910           for (int i = 0, col = 0; databases[i].name != NULL; ++i)
911             {
912               len = strlen (databases[i].name);
913               if (i != 0)
914                 {
915                   if (col + len > 72)
916                     {
917                       col = 0;
918                       fputc_unlocked ('\n', fp);
919                     }
920                   else
921                     fputc_unlocked (' ', fp);
922                 }
923
924               fputs_unlocked (databases[i].name, fp);
925               col += len + 1;
926             }
927
928           fputs ("\n\n", fp);
929
930           fprintf (fp, gettext ("\
931 For bug reporting instructions, please see:\n\
932 %s.\n"), REPORT_BUGS_TO);
933
934           if (fclose (fp) == 0)
935             return doc;
936         }
937       break;
938
939     default:
940       break;
941     }
942   return (char *) text;
943 }
944
945
946 /* the main function */
947 int
948 main (int argc, char *argv[])
949 {
950   /* Debugging support.  */
951   mtrace ();
952
953   /* Set locale via LC_ALL.  */
954   setlocale (LC_ALL, "");
955   /* Set the text message domain.  */
956   textdomain (PACKAGE);
957
958   /* Parse and process arguments.  */
959   int remaining;
960   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
961
962   if ((argc - remaining) < 1)
963     {
964       error (0, 0, gettext ("wrong number of arguments"));
965       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
966       return 1;
967     }
968
969   for (int i = 0; databases[i].name; ++i)
970     if (argv[remaining][0] == databases[i].name[0]
971         && !strcmp (argv[remaining], databases[i].name))
972       return databases[i].func (argc - remaining - 1, &argv[remaining + 1]);
973
974   fprintf (stderr, _("Unknown database: %s\n"), argv[remaining]);
975   argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
976   return 1;
977 }