]> git.ipfire.org Git - thirdparty/glibc.git/blob - nss/getent.c
Fix reads for sizes larger than INT_MAX in AF_INET lookup
[thirdparty/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', N_("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 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 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 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 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 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 endspent ();
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 }