2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
17 * implement "iconv -l"
29 #include <sys/param.h>
34 #define PATH_LIBICONV "/usr/lib/iconv"
35 #define PATH_BTABLES "/usr/lib/iconv/geniconvtbl/binarytables"
36 #define PATH_ALIASES "/usr/lib/iconv/alias"
38 typedef struct codeset
{
44 typedef struct csalias
{
49 static avl_tree_t cs_avl
;
51 static void alias_destroy(csalias_t
*);
58 cs_compare(const void *n1
, const void *n2
)
60 const codeset_t
*c1
= n1
;
61 const codeset_t
*c2
= n2
;
64 rv
= strcmp(c1
->cs_name
, c2
->cs_name
);
65 return ((rv
< 0) ? -1 : (rv
> 0) ? 1 : 0);
74 (void) memset(&tmp
, 0, sizeof (tmp
));
77 cs
= avl_find(&cs_avl
, &tmp
, &where
);
79 return; /* already there */
81 cs
= calloc(1, sizeof (*cs
));
83 perror("cs_insert:calloc");
86 cs
->cs_name
= strdup(key
);
87 if (cs
->cs_name
== NULL
) {
88 perror("cs_insert:strdup");
91 list_create(&cs
->cs_aliases
, sizeof (csalias_t
),
92 offsetof(csalias_t
, a_node
));
94 avl_insert(&cs_avl
, cs
, where
);
97 const char topmatter
[] =
98 "The following are all supported code set names. All combinations\n"
99 "of those names are not necessarily available for the pair of the\n"
100 "fromcode-tocode. Some of those code set names have aliases, which\n"
101 "are case-insensitive and described in parentheses following the\n"
111 (void) puts(topmatter
);
113 for (cs
= avl_first(&cs_avl
); cs
!= NULL
;
114 cs
= AVL_NEXT(&cs_avl
, cs
)) {
116 (void) printf(" %s", cs
->cs_name
);
117 if (!list_is_empty(&cs
->cs_aliases
)) {
118 a
= list_head(&cs
->cs_aliases
);
119 (void) printf(" (%s", a
->a_name
);
120 while ((a
= list_next(&cs
->cs_aliases
, a
)) != NULL
) {
121 (void) printf(", %s", a
->a_name
);
125 (void) printf(",\n");
136 while ((cs
= avl_destroy_nodes(&cs_avl
, &cookie
)) != NULL
) {
137 while ((a
= list_remove_head(&cs
->cs_aliases
)) != NULL
) {
143 avl_destroy(&cs_avl
);
151 alias_insert(char *codeset
, char *alias
)
157 * Find the codeset. If non-existent,
158 * ignore aliases of this codeset.
160 (void) memset(&tcs
, 0, sizeof (tcs
));
161 tcs
.cs_name
= codeset
;
162 cs
= avl_find(&cs_avl
, &tcs
, NULL
);
169 a
= calloc(1, sizeof (*a
));
171 perror("alias_insert:calloc");
174 a
->a_name
= strdup(alias
);
175 if (a
->a_name
== NULL
) {
176 perror("alias_insert:strdup");
180 list_insert_tail(&cs
->cs_aliases
, a
);
184 alias_destroy(csalias_t
*a
)
192 scan_dir(DIR *dh
, char sep
, char *suffix
)
194 char namebuf
[MAXNAMELEN
];
197 while ((de
= readdir(dh
)) != NULL
) {
201 * We'll modify, so let's copy. If the dirent name is
202 * longer than MAXNAMELEN, then it can't possibly be a
203 * valid pair of codeset names, so just skip it.
205 if (strlcpy(namebuf
, de
->d_name
, sizeof (namebuf
)) >=
209 /* Find suffix (.so | .t) */
210 p2
= strrchr(namebuf
, *suffix
);
213 if (strcmp(p2
, suffix
) != 0)
217 p1
= strchr(namebuf
, sep
);
222 /* More than one sep? */
223 if (strchr(p1
, sep
) != NULL
)
227 if (*namebuf
== '\0' || *p1
== '\0')
230 /* OK, add both to the map. */
237 scan_aliases(FILE *fh
)
242 while (fgets(linebuf
, sizeof (linebuf
), fh
) != NULL
) {
243 if (linebuf
[0] == '#')
245 p1
= strchr(linebuf
, ' ');
249 p2
= strchr(p1
, '\n');
253 alias_insert(p1
, linebuf
);
263 avl_create(&cs_avl
, cs_compare
, sizeof (codeset_t
),
264 offsetof(codeset_t
, cs_node
));
266 dh
= opendir(PATH_LIBICONV
);
268 perror(PATH_LIBICONV
);
271 scan_dir(dh
, '%', ".so");
273 scan_dir(dh
, '.', ".t");
276 dh
= opendir(PATH_BTABLES
);
278 perror(PATH_BTABLES
);
281 scan_dir(dh
, '%', ".bt");
284 fh
= fopen(PATH_ALIASES
, "r");
286 perror(PATH_ALIASES
);