PageRenderTime 58ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/nss/getent.c

https://gitlab.com/Namal/glibc
C | 960 lines | 759 code | 149 blank | 52 comment | 205 complexity | a509f05c809c26c3957a3b1717aefc16 MD5 | raw file
  1. /* Copyright (c) 1998-2016 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3. Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <http://www.gnu.org/licenses/>. */
  15. /* getent: get entries from administrative database. */
  16. #include <aliases.h>
  17. #include <argp.h>
  18. #include <ctype.h>
  19. #include <error.h>
  20. #include <grp.h>
  21. #include <gshadow.h>
  22. #include <libintl.h>
  23. #include <locale.h>
  24. #include <mcheck.h>
  25. #include <netdb.h>
  26. #include <pwd.h>
  27. #include <shadow.h>
  28. #include <stdbool.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <arpa/inet.h>
  33. #include <arpa/nameser.h>
  34. #include <netinet/ether.h>
  35. #include <netinet/in.h>
  36. #include <sys/socket.h>
  37. /* Get libc version number. */
  38. #include <version.h>
  39. #define PACKAGE _libc_intl_domainname
  40. /* Name and version of program. */
  41. static void print_version (FILE *stream, struct argp_state *state);
  42. void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
  43. /* Short description of parameters. */
  44. static const char args_doc[] = N_("database [key ...]");
  45. /* Supported options. */
  46. static const struct argp_option args_options[] =
  47. {
  48. { "service", 's', N_("CONFIG"), 0, N_("Service configuration to be used") },
  49. { "no-idn", 'i', NULL, 0, N_("disable IDN encoding") },
  50. { NULL, 0, NULL, 0, NULL },
  51. };
  52. /* Short description of program. */
  53. static const char doc[] = N_("Get entries from administrative database.");
  54. /* Prototype for option handler. */
  55. static error_t parse_option (int key, char *arg, struct argp_state *state);
  56. /* Function to print some extra text in the help message. */
  57. static char *more_help (int key, const char *text, void *input);
  58. /* Data structure to communicate with argp functions. */
  59. static struct argp argp =
  60. {
  61. args_options, parse_option, args_doc, doc, NULL, more_help
  62. };
  63. /* Additional getaddrinfo flags for IDN encoding. */
  64. static int idn_flags = AI_IDN | AI_CANONIDN;
  65. /* Print the version information. */
  66. static void
  67. print_version (FILE *stream, struct argp_state *state)
  68. {
  69. fprintf (stream, "getent %s%s\n", PKGVERSION, VERSION);
  70. fprintf (stream, gettext ("\
  71. Copyright (C) %s Free Software Foundation, Inc.\n\
  72. This is free software; see the source for copying conditions. There is NO\n\
  73. warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
  74. "), "2016");
  75. fprintf (stream, gettext ("Written by %s.\n"), "Thorsten Kukuk");
  76. }
  77. /* This is for aliases */
  78. static void
  79. print_aliases (struct aliasent *alias)
  80. {
  81. unsigned int i = 0;
  82. printf ("%s: ", alias->alias_name);
  83. for (i = strlen (alias->alias_name); i < 14; ++i)
  84. fputs_unlocked (" ", stdout);
  85. for (i = 0; i < alias->alias_members_len; ++i)
  86. printf ("%s%s",
  87. alias->alias_members [i],
  88. i + 1 == alias->alias_members_len ? "\n" : ", ");
  89. }
  90. static int
  91. aliases_keys (int number, char *key[])
  92. {
  93. int result = 0;
  94. int i;
  95. struct aliasent *alias;
  96. if (number == 0)
  97. {
  98. setaliasent ();
  99. while ((alias = getaliasent ()) != NULL)
  100. print_aliases (alias);
  101. endaliasent ();
  102. return result;
  103. }
  104. for (i = 0; i < number; ++i)
  105. {
  106. alias = getaliasbyname (key[i]);
  107. if (alias == NULL)
  108. result = 2;
  109. else
  110. print_aliases (alias);
  111. }
  112. return result;
  113. }
  114. /* This is for ethers */
  115. static int
  116. ethers_keys (int number, char *key[])
  117. {
  118. int result = 0;
  119. int i;
  120. if (number == 0)
  121. {
  122. fprintf (stderr, _("Enumeration not supported on %s\n"), "ethers");
  123. return 3;
  124. }
  125. for (i = 0; i < number; ++i)
  126. {
  127. struct ether_addr *ethp, eth;
  128. char buffer [1024], *p;
  129. ethp = ether_aton (key[i]);
  130. if (ethp != NULL)
  131. {
  132. if (ether_ntohost (buffer, ethp))
  133. {
  134. result = 2;
  135. continue;
  136. }
  137. p = buffer;
  138. }
  139. else
  140. {
  141. if (ether_hostton (key[i], &eth))
  142. {
  143. result = 2;
  144. continue;
  145. }
  146. p = key[i];
  147. ethp = &eth;
  148. }
  149. printf ("%s %s\n", ether_ntoa (ethp), p);
  150. }
  151. return result;
  152. }
  153. /* This is for group */
  154. static void
  155. print_group (struct group *grp)
  156. {
  157. if (putgrent (grp, stdout) != 0)
  158. fprintf (stderr, "error writing group entry: %m\n");
  159. }
  160. static int
  161. group_keys (int number, char *key[])
  162. {
  163. int result = 0;
  164. int i;
  165. struct group *grp;
  166. if (number == 0)
  167. {
  168. setgrent ();
  169. while ((grp = getgrent ()) != NULL)
  170. print_group (grp);
  171. endgrent ();
  172. return result;
  173. }
  174. for (i = 0; i < number; ++i)
  175. {
  176. errno = 0;
  177. char *ep;
  178. gid_t arg_gid = strtoul(key[i], &ep, 10);
  179. if (errno != EINVAL && *key[i] != '\0' && *ep == '\0')
  180. /* Valid numeric gid. */
  181. grp = getgrgid (arg_gid);
  182. else
  183. grp = getgrnam (key[i]);
  184. if (grp == NULL)
  185. result = 2;
  186. else
  187. print_group (grp);
  188. }
  189. return result;
  190. }
  191. /* This is for gshadow */
  192. static void
  193. print_gshadow (struct sgrp *sg)
  194. {
  195. if (putsgent (sg, stdout) != 0)
  196. fprintf (stderr, "error writing gshadow entry: %m\n");
  197. }
  198. static int
  199. gshadow_keys (int number, char *key[])
  200. {
  201. int result = 0;
  202. int i;
  203. if (number == 0)
  204. {
  205. struct sgrp *sg;
  206. setsgent ();
  207. while ((sg = getsgent ()) != NULL)
  208. print_gshadow (sg);
  209. endsgent ();
  210. return result;
  211. }
  212. for (i = 0; i < number; ++i)
  213. {
  214. struct sgrp *sg;
  215. sg = getsgnam (key[i]);
  216. if (sg == NULL)
  217. result = 2;
  218. else
  219. print_gshadow (sg);
  220. }
  221. return result;
  222. }
  223. /* This is for hosts */
  224. static void
  225. print_hosts (struct hostent *host)
  226. {
  227. unsigned int cnt;
  228. for (cnt = 0; host->h_addr_list[cnt] != NULL; ++cnt)
  229. {
  230. char buf[INET6_ADDRSTRLEN];
  231. const char *ip = inet_ntop (host->h_addrtype, host->h_addr_list[cnt],
  232. buf, sizeof (buf));
  233. printf ("%-15s %s", ip, host->h_name);
  234. unsigned int i;
  235. for (i = 0; host->h_aliases[i] != NULL; ++i)
  236. {
  237. putchar_unlocked (' ');
  238. fputs_unlocked (host->h_aliases[i], stdout);
  239. }
  240. putchar_unlocked ('\n');
  241. }
  242. }
  243. static int
  244. hosts_keys (int number, char *key[])
  245. {
  246. int result = 0;
  247. int i;
  248. struct hostent *host;
  249. if (number == 0)
  250. {
  251. sethostent (0);
  252. while ((host = gethostent ()) != NULL)
  253. print_hosts (host);
  254. endhostent ();
  255. return result;
  256. }
  257. for (i = 0; i < number; ++i)
  258. {
  259. struct hostent *host = NULL;
  260. char addr[IN6ADDRSZ];
  261. if (inet_pton (AF_INET6, key[i], &addr) > 0)
  262. host = gethostbyaddr (addr, IN6ADDRSZ, AF_INET6);
  263. else if (inet_pton (AF_INET, key[i], &addr) > 0)
  264. host = gethostbyaddr (addr, INADDRSZ, AF_INET);
  265. else if ((host = gethostbyname2 (key[i], AF_INET6)) == NULL)
  266. host = gethostbyname2 (key[i], AF_INET);
  267. if (host == NULL)
  268. result = 2;
  269. else
  270. print_hosts (host);
  271. }
  272. return result;
  273. }
  274. /* This is for hosts, but using getaddrinfo */
  275. static int
  276. ahosts_keys_int (int af, int xflags, int number, char *key[])
  277. {
  278. int result = 0;
  279. int i;
  280. struct hostent *host;
  281. if (number == 0)
  282. {
  283. sethostent (0);
  284. while ((host = gethostent ()) != NULL)
  285. print_hosts (host);
  286. endhostent ();
  287. return result;
  288. }
  289. struct addrinfo hint;
  290. memset (&hint, '\0', sizeof (hint));
  291. hint.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME
  292. | idn_flags | xflags);
  293. hint.ai_family = af;
  294. for (i = 0; i < number; ++i)
  295. {
  296. struct addrinfo *res;
  297. if (getaddrinfo (key[i], NULL, &hint, &res) != 0)
  298. result = 2;
  299. else
  300. {
  301. struct addrinfo *runp = res;
  302. while (runp != NULL)
  303. {
  304. char sockbuf[20];
  305. const char *sockstr;
  306. if (runp->ai_socktype == SOCK_STREAM)
  307. sockstr = "STREAM";
  308. else if (runp->ai_socktype == SOCK_DGRAM)
  309. sockstr = "DGRAM";
  310. else if (runp->ai_socktype == SOCK_RAW)
  311. sockstr = "RAW";
  312. #ifdef SOCK_SEQPACKET
  313. else if (runp->ai_socktype == SOCK_SEQPACKET)
  314. sockstr = "SEQPACKET";
  315. #endif
  316. #ifdef SOCK_RDM
  317. else if (runp->ai_socktype == SOCK_RDM)
  318. sockstr = "RDM";
  319. #endif
  320. #ifdef SOCK_DCCP
  321. else if (runp->ai_socktype == SOCK_DCCP)
  322. sockstr = "DCCP";
  323. #endif
  324. #ifdef SOCK_PACKET
  325. else if (runp->ai_socktype == SOCK_PACKET)
  326. sockstr = "PACKET";
  327. #endif
  328. else
  329. {
  330. snprintf (sockbuf, sizeof (sockbuf), "%d",
  331. runp->ai_socktype);
  332. sockstr = sockbuf;
  333. }
  334. char buf[INET6_ADDRSTRLEN];
  335. printf ("%-15s %-6s %s\n",
  336. inet_ntop (runp->ai_family,
  337. runp->ai_family == AF_INET
  338. ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr
  339. : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr,
  340. buf, sizeof (buf)),
  341. sockstr,
  342. runp->ai_canonname ?: "");
  343. runp = runp->ai_next;
  344. }
  345. freeaddrinfo (res);
  346. }
  347. }
  348. return result;
  349. }
  350. static int
  351. ahosts_keys (int number, char *key[])
  352. {
  353. return ahosts_keys_int (AF_UNSPEC, 0, number, key);
  354. }
  355. static int
  356. ahostsv4_keys (int number, char *key[])
  357. {
  358. return ahosts_keys_int (AF_INET, 0, number, key);
  359. }
  360. static int
  361. ahostsv6_keys (int number, char *key[])
  362. {
  363. return ahosts_keys_int (AF_INET6, AI_V4MAPPED, number, key);
  364. }
  365. /* This is for netgroup */
  366. static int
  367. netgroup_keys (int number, char *key[])
  368. {
  369. int result = 0;
  370. if (number == 0)
  371. {
  372. fprintf (stderr, _("Enumeration not supported on %s\n"), "netgroup");
  373. return 3;
  374. }
  375. if (number == 4)
  376. {
  377. char *host = strcmp (key[1], "*") == 0 ? NULL : key[1];
  378. char *user = strcmp (key[2], "*") == 0 ? NULL : key[2];
  379. char *domain = strcmp (key[3], "*") == 0 ? NULL : key[3];
  380. printf ("%-21s (%s,%s,%s) = %d\n",
  381. key[0], host ?: "", user ?: "", domain ?: "",
  382. innetgr (key[0], host, user, domain));
  383. }
  384. else if (number == 1)
  385. {
  386. if (!setnetgrent (key[0]))
  387. result = 2;
  388. else
  389. {
  390. char *p[3];
  391. printf ("%-21s", key[0]);
  392. while (getnetgrent (p, p + 1, p + 2))
  393. printf (" (%s,%s,%s)", p[0] ?: " ", p[1] ?: "", p[2] ?: "");
  394. putchar_unlocked ('\n');
  395. }
  396. }
  397. endnetgrent ();
  398. return result;
  399. }
  400. /* This is for initgroups */
  401. static int
  402. initgroups_keys (int number, char *key[])
  403. {
  404. int ngrps = 100;
  405. size_t grpslen = ngrps * sizeof (gid_t);
  406. gid_t *grps = alloca (grpslen);
  407. if (number == 0)
  408. {
  409. fprintf (stderr, _("Enumeration not supported on %s\n"), "initgroups");
  410. return 3;
  411. }
  412. for (int i = 0; i < number; ++i)
  413. {
  414. int no = ngrps;
  415. int n;
  416. while ((n = getgrouplist (key[i], -1, grps, &no)) == -1
  417. && no > ngrps)
  418. {
  419. grps = extend_alloca (grps, grpslen, no * sizeof (gid_t));
  420. ngrps = no;
  421. }
  422. if (n == -1)
  423. return 1;
  424. printf ("%-21s", key[i]);
  425. for (int j = 0; j < n; ++j)
  426. if (grps[j] != -1)
  427. printf (" %ld", (long int) grps[j]);
  428. putchar_unlocked ('\n');
  429. }
  430. return 0;
  431. }
  432. /* This is for networks */
  433. static void
  434. print_networks (struct netent *net)
  435. {
  436. unsigned int i;
  437. struct in_addr ip;
  438. ip.s_addr = htonl (net->n_net);
  439. printf ("%-21s %s", net->n_name, inet_ntoa (ip));
  440. i = 0;
  441. while (net->n_aliases[i] != NULL)
  442. {
  443. putchar_unlocked (' ');
  444. fputs_unlocked (net->n_aliases[i], stdout);
  445. ++i;
  446. }
  447. putchar_unlocked ('\n');
  448. }
  449. static int
  450. networks_keys (int number, char *key[])
  451. {
  452. int result = 0;
  453. int i;
  454. struct netent *net;
  455. if (number == 0)
  456. {
  457. setnetent (0);
  458. while ((net = getnetent ()) != NULL)
  459. print_networks (net);
  460. endnetent ();
  461. return result;
  462. }
  463. for (i = 0; i < number; ++i)
  464. {
  465. if (isdigit (key[i][0]))
  466. net = getnetbyaddr (ntohl (inet_addr (key[i])), AF_UNSPEC);
  467. else
  468. net = getnetbyname (key[i]);
  469. if (net == NULL)
  470. result = 2;
  471. else
  472. print_networks (net);
  473. }
  474. return result;
  475. }
  476. /* Now is all for passwd */
  477. static void
  478. print_passwd (struct passwd *pwd)
  479. {
  480. if (putpwent (pwd, stdout) != 0)
  481. fprintf (stderr, "error writing passwd entry: %m\n");
  482. }
  483. static int
  484. passwd_keys (int number, char *key[])
  485. {
  486. int result = 0;
  487. int i;
  488. struct passwd *pwd;
  489. if (number == 0)
  490. {
  491. setpwent ();
  492. while ((pwd = getpwent ()) != NULL)
  493. print_passwd (pwd);
  494. endpwent ();
  495. return result;
  496. }
  497. for (i = 0; i < number; ++i)
  498. {
  499. errno = 0;
  500. char *ep;
  501. uid_t arg_uid = strtoul(key[i], &ep, 10);
  502. if (errno != EINVAL && *key[i] != '\0' && *ep == '\0')
  503. /* Valid numeric uid. */
  504. pwd = getpwuid (arg_uid);
  505. else
  506. pwd = getpwnam (key[i]);
  507. if (pwd == NULL)
  508. result = 2;
  509. else
  510. print_passwd (pwd);
  511. }
  512. return result;
  513. }
  514. /* This is for protocols */
  515. static void
  516. print_protocols (struct protoent *proto)
  517. {
  518. unsigned int i;
  519. printf ("%-21s %d", proto->p_name, proto->p_proto);
  520. i = 0;
  521. while (proto->p_aliases[i] != NULL)
  522. {
  523. putchar_unlocked (' ');
  524. fputs_unlocked (proto->p_aliases[i], stdout);
  525. ++i;
  526. }
  527. putchar_unlocked ('\n');
  528. }
  529. static int
  530. protocols_keys (int number, char *key[])
  531. {
  532. int result = 0;
  533. int i;
  534. struct protoent *proto;
  535. if (number == 0)
  536. {
  537. setprotoent (0);
  538. while ((proto = getprotoent ()) != NULL)
  539. print_protocols (proto);
  540. endprotoent ();
  541. return result;
  542. }
  543. for (i = 0; i < number; ++i)
  544. {
  545. if (isdigit (key[i][0]))
  546. proto = getprotobynumber (atol (key[i]));
  547. else
  548. proto = getprotobyname (key[i]);
  549. if (proto == NULL)
  550. result = 2;
  551. else
  552. print_protocols (proto);
  553. }
  554. return result;
  555. }
  556. #if HAVE_SUNRPC
  557. /* Now is all for rpc */
  558. static void
  559. print_rpc (struct rpcent *rpc)
  560. {
  561. int i;
  562. printf ("%-15s %d%s",
  563. rpc->r_name, rpc->r_number, rpc->r_aliases[0] ? " " : "");
  564. for (i = 0; rpc->r_aliases[i]; ++i)
  565. printf (" %s", rpc->r_aliases[i]);
  566. putchar_unlocked ('\n');
  567. }
  568. static int
  569. rpc_keys (int number, char *key[])
  570. {
  571. int result = 0;
  572. int i;
  573. struct rpcent *rpc;
  574. if (number == 0)
  575. {
  576. setrpcent (0);
  577. while ((rpc = getrpcent ()) != NULL)
  578. print_rpc (rpc);
  579. endrpcent ();
  580. return result;
  581. }
  582. for (i = 0; i < number; ++i)
  583. {
  584. if (isdigit (key[i][0]))
  585. rpc = getrpcbynumber (atol (key[i]));
  586. else
  587. rpc = getrpcbyname (key[i]);
  588. if (rpc == NULL)
  589. result = 2;
  590. else
  591. print_rpc (rpc);
  592. }
  593. return result;
  594. }
  595. #endif
  596. /* for services */
  597. static void
  598. print_services (struct servent *serv)
  599. {
  600. unsigned int i;
  601. printf ("%-21s %d/%s", serv->s_name, ntohs (serv->s_port), serv->s_proto);
  602. i = 0;
  603. while (serv->s_aliases[i] != NULL)
  604. {
  605. putchar_unlocked (' ');
  606. fputs_unlocked (serv->s_aliases[i], stdout);
  607. ++i;
  608. }
  609. putchar_unlocked ('\n');
  610. }
  611. static int
  612. services_keys (int number, char *key[])
  613. {
  614. int result = 0;
  615. int i;
  616. struct servent *serv;
  617. if (!number)
  618. {
  619. setservent (0);
  620. while ((serv = getservent ()) != NULL)
  621. print_services (serv);
  622. endservent ();
  623. return result;
  624. }
  625. for (i = 0; i < number; ++i)
  626. {
  627. struct servent *serv;
  628. char *proto = strchr (key[i], '/');
  629. if (proto != NULL)
  630. *proto++ = '\0';
  631. char *endptr;
  632. long port = strtol (key[i], &endptr, 10);
  633. if (isdigit (key[i][0]) && *endptr == '\0'
  634. && 0 <= port && port <= 65535)
  635. serv = getservbyport (htons (port), proto);
  636. else
  637. serv = getservbyname (key[i], proto);
  638. if (serv == NULL)
  639. result = 2;
  640. else
  641. print_services (serv);
  642. }
  643. return result;
  644. }
  645. /* This is for shadow */
  646. static void
  647. print_shadow (struct spwd *sp)
  648. {
  649. if (putspent (sp, stdout) != 0)
  650. fprintf (stderr, "error writing shadow entry: %m\n");
  651. }
  652. static int
  653. shadow_keys (int number, char *key[])
  654. {
  655. int result = 0;
  656. int i;
  657. if (number == 0)
  658. {
  659. struct spwd *sp;
  660. setspent ();
  661. while ((sp = getspent ()) != NULL)
  662. print_shadow (sp);
  663. endspent ();
  664. return result;
  665. }
  666. for (i = 0; i < number; ++i)
  667. {
  668. struct spwd *sp;
  669. sp = getspnam (key[i]);
  670. if (sp == NULL)
  671. result = 2;
  672. else
  673. print_shadow (sp);
  674. }
  675. return result;
  676. }
  677. struct
  678. {
  679. const char *name;
  680. int (*func) (int number, char *key[]);
  681. } databases[] =
  682. {
  683. #define D(name) { #name, name ## _keys },
  684. D(ahosts)
  685. D(ahostsv4)
  686. D(ahostsv6)
  687. D(aliases)
  688. D(ethers)
  689. D(group)
  690. D(gshadow)
  691. D(hosts)
  692. D(initgroups)
  693. D(netgroup)
  694. D(networks)
  695. D(passwd)
  696. D(protocols)
  697. #if HAVE_SUNRPC
  698. D(rpc)
  699. #endif
  700. D(services)
  701. D(shadow)
  702. #undef D
  703. { NULL, NULL }
  704. };
  705. /* Handle arguments found by argp. */
  706. static error_t
  707. parse_option (int key, char *arg, struct argp_state *state)
  708. {
  709. char *endp;
  710. switch (key)
  711. {
  712. case 's':
  713. endp = strchr (arg, ':');
  714. if (endp == NULL)
  715. /* No specific database, change them all. */
  716. for (int i = 0; databases[i].name != NULL; ++i)
  717. __nss_configure_lookup (databases[i].name, arg);
  718. else
  719. {
  720. int i;
  721. for (i = 0; databases[i].name != NULL; ++i)
  722. if (strncmp (databases[i].name, arg, endp - arg) == 0)
  723. {
  724. __nss_configure_lookup (databases[i].name, endp + 1);
  725. break;
  726. }
  727. if (databases[i].name == NULL)
  728. error (EXIT_FAILURE, 0, gettext ("Unknown database name"));
  729. }
  730. break;
  731. case 'i':
  732. idn_flags = 0;
  733. break;
  734. default:
  735. return ARGP_ERR_UNKNOWN;
  736. }
  737. return 0;
  738. }
  739. static char *
  740. more_help (int key, const char *text, void *input)
  741. {
  742. switch (key)
  743. {
  744. size_t len;
  745. char *doc;
  746. FILE *fp;
  747. case ARGP_KEY_HELP_EXTRA:
  748. /* We print some extra information. */
  749. fp = open_memstream (&doc, &len);
  750. if (fp != NULL)
  751. {
  752. fputs_unlocked (_("Supported databases:\n"), fp);
  753. for (int i = 0, col = 0; databases[i].name != NULL; ++i)
  754. {
  755. len = strlen (databases[i].name);
  756. if (i != 0)
  757. {
  758. if (col + len > 72)
  759. {
  760. col = 0;
  761. fputc_unlocked ('\n', fp);
  762. }
  763. else
  764. fputc_unlocked (' ', fp);
  765. }
  766. fputs_unlocked (databases[i].name, fp);
  767. col += len + 1;
  768. }
  769. fputs ("\n\n", fp);
  770. fprintf (fp, gettext ("\
  771. For bug reporting instructions, please see:\n\
  772. %s.\n"), REPORT_BUGS_TO);
  773. if (fclose (fp) == 0)
  774. return doc;
  775. }
  776. break;
  777. default:
  778. break;
  779. }
  780. return (char *) text;
  781. }
  782. /* the main function */
  783. int
  784. main (int argc, char *argv[])
  785. {
  786. /* Debugging support. */
  787. mtrace ();
  788. /* Set locale via LC_ALL. */
  789. setlocale (LC_ALL, "");
  790. /* Set the text message domain. */
  791. textdomain (PACKAGE);
  792. /* Parse and process arguments. */
  793. int remaining;
  794. argp_parse (&argp, argc, argv, 0, &remaining, NULL);
  795. if ((argc - remaining) < 1)
  796. {
  797. error (0, 0, gettext ("wrong number of arguments"));
  798. argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
  799. return 1;
  800. }
  801. for (int i = 0; databases[i].name; ++i)
  802. if (argv[remaining][0] == databases[i].name[0]
  803. && !strcmp (argv[remaining], databases[i].name))
  804. return databases[i].func (argc - remaining - 1, &argv[remaining + 1]);
  805. fprintf (stderr, _("Unknown database: %s\n"), argv[remaining]);
  806. argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
  807. return 1;
  808. }