148edc7cfSGordon Ross /*
248edc7cfSGordon Ross * This file and its contents are supplied under the terms of the
348edc7cfSGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0.
448edc7cfSGordon Ross * You may only use this file in accordance with the terms of version
548edc7cfSGordon Ross * 1.0 of the CDDL.
648edc7cfSGordon Ross *
748edc7cfSGordon Ross * A full copy of the text of the CDDL should have accompanied this
848edc7cfSGordon Ross * source. A copy of the CDDL is also available via the Internet at
948edc7cfSGordon Ross * http://www.illumos.org/license/CDDL.
1048edc7cfSGordon Ross */
1148edc7cfSGordon Ross
1248edc7cfSGordon Ross /*
1348edc7cfSGordon Ross * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
1448edc7cfSGordon Ross */
1548edc7cfSGordon Ross
1648edc7cfSGordon Ross /*
1748edc7cfSGordon Ross * implement "iconv -l"
1848edc7cfSGordon Ross */
1948edc7cfSGordon Ross
2048edc7cfSGordon Ross #include <stdio.h>
2148edc7cfSGordon Ross #include <stdlib.h>
2248edc7cfSGordon Ross #include <string.h>
2348edc7cfSGordon Ross #include <errno.h>
2448edc7cfSGordon Ross #include <limits.h>
2548edc7cfSGordon Ross #include <unistd.h>
2648edc7cfSGordon Ross #include <alloca.h>
2748edc7cfSGordon Ross #include <sys/avl.h>
2848edc7cfSGordon Ross #include <sys/list.h>
2948edc7cfSGordon Ross #include <sys/param.h>
3048edc7cfSGordon Ross #include <stddef.h>
3148edc7cfSGordon Ross #include <dirent.h>
3248edc7cfSGordon Ross #include <unistd.h>
3348edc7cfSGordon Ross
3448edc7cfSGordon Ross #define PATH_LIBICONV "/usr/lib/iconv"
3548edc7cfSGordon Ross #define PATH_BTABLES "/usr/lib/iconv/geniconvtbl/binarytables"
3648edc7cfSGordon Ross #define PATH_ALIASES "/usr/lib/iconv/alias"
3748edc7cfSGordon Ross
3848edc7cfSGordon Ross typedef struct codeset {
3948edc7cfSGordon Ross avl_node_t cs_node;
4048edc7cfSGordon Ross char *cs_name;
4148edc7cfSGordon Ross list_t cs_aliases;
4248edc7cfSGordon Ross } codeset_t;
4348edc7cfSGordon Ross
4448edc7cfSGordon Ross typedef struct csalias {
4548edc7cfSGordon Ross list_node_t a_node;
4648edc7cfSGordon Ross char *a_name;
4748edc7cfSGordon Ross } csalias_t;
4848edc7cfSGordon Ross
4948edc7cfSGordon Ross static avl_tree_t cs_avl;
5048edc7cfSGordon Ross
5148edc7cfSGordon Ross static void alias_destroy(csalias_t *);
5248edc7cfSGordon Ross
5348edc7cfSGordon Ross /*
5448edc7cfSGordon Ross * codesets
5548edc7cfSGordon Ross */
5648edc7cfSGordon Ross
5748edc7cfSGordon Ross static int
cs_compare(const void * n1,const void * n2)5848edc7cfSGordon Ross cs_compare(const void *n1, const void *n2)
5948edc7cfSGordon Ross {
6048edc7cfSGordon Ross const codeset_t *c1 = n1;
6148edc7cfSGordon Ross const codeset_t *c2 = n2;
6248edc7cfSGordon Ross int rv;
6348edc7cfSGordon Ross
6448edc7cfSGordon Ross rv = strcmp(c1->cs_name, c2->cs_name);
6548edc7cfSGordon Ross return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0);
6648edc7cfSGordon Ross }
6748edc7cfSGordon Ross
6848edc7cfSGordon Ross static void
cs_insert(char * key)6948edc7cfSGordon Ross cs_insert(char *key)
7048edc7cfSGordon Ross {
7148edc7cfSGordon Ross codeset_t tmp, *cs;
7248edc7cfSGordon Ross avl_index_t where;
7348edc7cfSGordon Ross
7448edc7cfSGordon Ross (void) memset(&tmp, 0, sizeof (tmp));
7548edc7cfSGordon Ross tmp.cs_name = key;
7648edc7cfSGordon Ross
7748edc7cfSGordon Ross cs = avl_find(&cs_avl, &tmp, &where);
7848edc7cfSGordon Ross if (cs != NULL)
7948edc7cfSGordon Ross return; /* already there */
8048edc7cfSGordon Ross
8148edc7cfSGordon Ross cs = calloc(1, sizeof (*cs));
8248edc7cfSGordon Ross if (cs == NULL) {
8348edc7cfSGordon Ross perror("cs_insert:calloc");
8448edc7cfSGordon Ross exit(1);
8548edc7cfSGordon Ross }
8648edc7cfSGordon Ross cs->cs_name = strdup(key);
8748edc7cfSGordon Ross if (cs->cs_name == NULL) {
8848edc7cfSGordon Ross perror("cs_insert:strdup");
8948edc7cfSGordon Ross exit(1);
9048edc7cfSGordon Ross }
9148edc7cfSGordon Ross list_create(&cs->cs_aliases, sizeof (csalias_t),
9248edc7cfSGordon Ross offsetof(csalias_t, a_node));
9348edc7cfSGordon Ross
9448edc7cfSGordon Ross avl_insert(&cs_avl, cs, where);
9548edc7cfSGordon Ross }
9648edc7cfSGordon Ross
9748edc7cfSGordon Ross const char topmatter[] =
9848edc7cfSGordon Ross "The following are all supported code set names. All combinations\n"
9948edc7cfSGordon Ross "of those names are not necessarily available for the pair of the\n"
10048edc7cfSGordon Ross "fromcode-tocode. Some of those code set names have aliases, which\n"
10148edc7cfSGordon Ross "are case-insensitive and described in parentheses following the\n"
10248edc7cfSGordon Ross "canonical name:\n";
10348edc7cfSGordon Ross
10448edc7cfSGordon Ross
10548edc7cfSGordon Ross static void
cs_dump(void)10648edc7cfSGordon Ross cs_dump(void)
10748edc7cfSGordon Ross {
10848edc7cfSGordon Ross codeset_t *cs;
10948edc7cfSGordon Ross csalias_t *a;
11048edc7cfSGordon Ross
11148edc7cfSGordon Ross (void) puts(topmatter);
11248edc7cfSGordon Ross
11348edc7cfSGordon Ross for (cs = avl_first(&cs_avl); cs != NULL;
11448edc7cfSGordon Ross cs = AVL_NEXT(&cs_avl, cs)) {
11548edc7cfSGordon Ross
11648edc7cfSGordon Ross (void) printf(" %s", cs->cs_name);
11748edc7cfSGordon Ross if (!list_is_empty(&cs->cs_aliases)) {
11848edc7cfSGordon Ross a = list_head(&cs->cs_aliases);
11948edc7cfSGordon Ross (void) printf(" (%s", a->a_name);
12048edc7cfSGordon Ross while ((a = list_next(&cs->cs_aliases, a)) != NULL) {
12148edc7cfSGordon Ross (void) printf(", %s", a->a_name);
12248edc7cfSGordon Ross }
12348edc7cfSGordon Ross (void) printf(")");
12448edc7cfSGordon Ross }
12548edc7cfSGordon Ross (void) printf(",\n");
12648edc7cfSGordon Ross }
12748edc7cfSGordon Ross }
12848edc7cfSGordon Ross
12948edc7cfSGordon Ross static void
cs_destroy(void)13048edc7cfSGordon Ross cs_destroy(void)
13148edc7cfSGordon Ross {
13248edc7cfSGordon Ross void *cookie = NULL;
13348edc7cfSGordon Ross codeset_t *cs;
13448edc7cfSGordon Ross csalias_t *a;
13548edc7cfSGordon Ross
13648edc7cfSGordon Ross while ((cs = avl_destroy_nodes(&cs_avl, &cookie)) != NULL) {
13748edc7cfSGordon Ross while ((a = list_remove_head(&cs->cs_aliases)) != NULL) {
13848edc7cfSGordon Ross alias_destroy(a);
13948edc7cfSGordon Ross }
14048edc7cfSGordon Ross free(cs->cs_name);
14148edc7cfSGordon Ross free(cs);
14248edc7cfSGordon Ross }
14348edc7cfSGordon Ross avl_destroy(&cs_avl);
14448edc7cfSGordon Ross }
14548edc7cfSGordon Ross
14648edc7cfSGordon Ross /*
14748edc7cfSGordon Ross * aliases
14848edc7cfSGordon Ross */
14948edc7cfSGordon Ross
15048edc7cfSGordon Ross static void
alias_insert(char * codeset,char * alias)15148edc7cfSGordon Ross alias_insert(char *codeset, char *alias)
15248edc7cfSGordon Ross {
15348edc7cfSGordon Ross codeset_t tcs, *cs;
15448edc7cfSGordon Ross csalias_t *a;
15548edc7cfSGordon Ross
15648edc7cfSGordon Ross /*
15748edc7cfSGordon Ross * Find the codeset. If non-existent,
15848edc7cfSGordon Ross * ignore aliases of this codeset.
15948edc7cfSGordon Ross */
16048edc7cfSGordon Ross (void) memset(&tcs, 0, sizeof (tcs));
16148edc7cfSGordon Ross tcs.cs_name = codeset;
16248edc7cfSGordon Ross cs = avl_find(&cs_avl, &tcs, NULL);
16348edc7cfSGordon Ross if (cs == NULL)
16448edc7cfSGordon Ross return;
16548edc7cfSGordon Ross
16648edc7cfSGordon Ross /*
16748edc7cfSGordon Ross * Add this alias
16848edc7cfSGordon Ross */
16948edc7cfSGordon Ross a = calloc(1, sizeof (*a));
17048edc7cfSGordon Ross if (a == NULL) {
17148edc7cfSGordon Ross perror("alias_insert:calloc");
17248edc7cfSGordon Ross exit(1);
17348edc7cfSGordon Ross }
17448edc7cfSGordon Ross a->a_name = strdup(alias);
17548edc7cfSGordon Ross if (a->a_name == NULL) {
17648edc7cfSGordon Ross perror("alias_insert:strdup");
17748edc7cfSGordon Ross exit(1);
17848edc7cfSGordon Ross }
17948edc7cfSGordon Ross
18048edc7cfSGordon Ross list_insert_tail(&cs->cs_aliases, a);
18148edc7cfSGordon Ross }
18248edc7cfSGordon Ross
18348edc7cfSGordon Ross static void
alias_destroy(csalias_t * a)18448edc7cfSGordon Ross alias_destroy(csalias_t *a)
18548edc7cfSGordon Ross {
18648edc7cfSGordon Ross free(a->a_name);
18748edc7cfSGordon Ross free(a);
18848edc7cfSGordon Ross }
18948edc7cfSGordon Ross
19048edc7cfSGordon Ross
19148edc7cfSGordon Ross static void
scan_dir(DIR * dh,char sep,char * suffix)19248edc7cfSGordon Ross scan_dir(DIR *dh, char sep, char *suffix)
19348edc7cfSGordon Ross {
19448edc7cfSGordon Ross char namebuf[MAXNAMELEN];
19548edc7cfSGordon Ross struct dirent *de;
19648edc7cfSGordon Ross
19748edc7cfSGordon Ross while ((de = readdir(dh)) != NULL) {
19848edc7cfSGordon Ross char *p2, *p1;
19948edc7cfSGordon Ross
20048edc7cfSGordon Ross /*
20148edc7cfSGordon Ross * We'll modify, so let's copy. If the dirent name is
20248edc7cfSGordon Ross * longer than MAXNAMELEN, then it can't possibly be a
20348edc7cfSGordon Ross * valid pair of codeset names, so just skip it.
20448edc7cfSGordon Ross */
20548edc7cfSGordon Ross if (strlcpy(namebuf, de->d_name, sizeof (namebuf)) >=
20648edc7cfSGordon Ross sizeof (namebuf))
20748edc7cfSGordon Ross continue;
20848edc7cfSGordon Ross
209*e3c4d829SRichard Lowe /* Find suffix */
21048edc7cfSGordon Ross p2 = strrchr(namebuf, *suffix);
21148edc7cfSGordon Ross if (p2 == NULL)
21248edc7cfSGordon Ross continue;
21348edc7cfSGordon Ross if (strcmp(p2, suffix) != 0)
21448edc7cfSGordon Ross continue;
21548edc7cfSGordon Ross *p2 = '\0';
21648edc7cfSGordon Ross
21748edc7cfSGordon Ross p1 = strchr(namebuf, sep);
21848edc7cfSGordon Ross if (p1 == NULL)
21948edc7cfSGordon Ross continue;
22048edc7cfSGordon Ross *p1++ = '\0';
22148edc7cfSGordon Ross
22248edc7cfSGordon Ross /* More than one sep? */
22348edc7cfSGordon Ross if (strchr(p1, sep) != NULL)
22448edc7cfSGordon Ross continue;
22548edc7cfSGordon Ross
22648edc7cfSGordon Ross /* Empty strings? */
22748edc7cfSGordon Ross if (*namebuf == '\0' || *p1 == '\0')
22848edc7cfSGordon Ross continue;
22948edc7cfSGordon Ross
23048edc7cfSGordon Ross /* OK, add both to the map. */
23148edc7cfSGordon Ross cs_insert(namebuf);
23248edc7cfSGordon Ross cs_insert(p1);
23348edc7cfSGordon Ross }
23448edc7cfSGordon Ross }
23548edc7cfSGordon Ross
23648edc7cfSGordon Ross static void
scan_aliases(FILE * fh)23748edc7cfSGordon Ross scan_aliases(FILE *fh)
23848edc7cfSGordon Ross {
23948edc7cfSGordon Ross char linebuf[256];
24048edc7cfSGordon Ross char *p1, *p2;
24148edc7cfSGordon Ross
24248edc7cfSGordon Ross while (fgets(linebuf, sizeof (linebuf), fh) != NULL) {
24348edc7cfSGordon Ross if (linebuf[0] == '#')
24448edc7cfSGordon Ross continue;
24548edc7cfSGordon Ross p1 = strchr(linebuf, ' ');
24648edc7cfSGordon Ross if (p1 == NULL)
24748edc7cfSGordon Ross continue;
24848edc7cfSGordon Ross *p1++ = '\0';
24948edc7cfSGordon Ross p2 = strchr(p1, '\n');
25048edc7cfSGordon Ross if (p2 == NULL)
25148edc7cfSGordon Ross continue;
25248edc7cfSGordon Ross *p2 = '\0';
25348edc7cfSGordon Ross alias_insert(p1, linebuf);
25448edc7cfSGordon Ross }
25548edc7cfSGordon Ross }
25648edc7cfSGordon Ross
25748edc7cfSGordon Ross int
list_codesets(void)25848edc7cfSGordon Ross list_codesets(void)
25948edc7cfSGordon Ross {
26048edc7cfSGordon Ross DIR *dh;
26148edc7cfSGordon Ross FILE *fh;
26248edc7cfSGordon Ross
26348edc7cfSGordon Ross avl_create(&cs_avl, cs_compare, sizeof (codeset_t),
26448edc7cfSGordon Ross offsetof(codeset_t, cs_node));
26548edc7cfSGordon Ross
26648edc7cfSGordon Ross dh = opendir(PATH_LIBICONV);
26748edc7cfSGordon Ross if (dh == NULL) {
26848edc7cfSGordon Ross perror(PATH_LIBICONV);
26948edc7cfSGordon Ross return (1);
27048edc7cfSGordon Ross }
27148edc7cfSGordon Ross scan_dir(dh, '%', ".so");
27248edc7cfSGordon Ross (void) closedir(dh);
27348edc7cfSGordon Ross
27448edc7cfSGordon Ross dh = opendir(PATH_BTABLES);
27548edc7cfSGordon Ross if (dh == NULL) {
27648edc7cfSGordon Ross perror(PATH_BTABLES);
27748edc7cfSGordon Ross return (1);
27848edc7cfSGordon Ross }
27948edc7cfSGordon Ross scan_dir(dh, '%', ".bt");
28048edc7cfSGordon Ross (void) closedir(dh);
28148edc7cfSGordon Ross
28248edc7cfSGordon Ross fh = fopen(PATH_ALIASES, "r");
28348edc7cfSGordon Ross if (fh == NULL) {
28448edc7cfSGordon Ross perror(PATH_ALIASES);
28548edc7cfSGordon Ross /* let's continue */
28648edc7cfSGordon Ross } else {
28748edc7cfSGordon Ross scan_aliases(fh);
28848edc7cfSGordon Ross (void) fclose(fh);
28948edc7cfSGordon Ross }
29048edc7cfSGordon Ross
29148edc7cfSGordon Ross cs_dump();
29248edc7cfSGordon Ross
29348edc7cfSGordon Ross cs_destroy();
29448edc7cfSGordon Ross
29548edc7cfSGordon Ross return (0);
29648edc7cfSGordon Ross }
297