comm: add -Wall
[unleashed.git] / usr / src / cmd / iconv / iconv_list.c
blob4fac3506d8935e2c12164184707a5958512d56da
1 /*
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
5 * 1.0 of the CDDL.
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"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <unistd.h>
26 #include <alloca.h>
27 #include <sys/avl.h>
28 #include <sys/list.h>
29 #include <sys/param.h>
30 #include <stddef.h>
31 #include <dirent.h>
32 #include <unistd.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 {
39 avl_node_t cs_node;
40 char *cs_name;
41 list_t cs_aliases;
42 } codeset_t;
44 typedef struct csalias {
45 list_node_t a_node;
46 char *a_name;
47 } csalias_t;
49 static avl_tree_t cs_avl;
51 static void alias_destroy(csalias_t *);
54 * codesets
57 static int
58 cs_compare(const void *n1, const void *n2)
60 const codeset_t *c1 = n1;
61 const codeset_t *c2 = n2;
62 int rv;
64 rv = strcmp(c1->cs_name, c2->cs_name);
65 return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0);
68 static void
69 cs_insert(char *key)
71 codeset_t tmp, *cs;
72 avl_index_t where;
74 (void) memset(&tmp, 0, sizeof (tmp));
75 tmp.cs_name = key;
77 cs = avl_find(&cs_avl, &tmp, &where);
78 if (cs != NULL)
79 return; /* already there */
81 cs = calloc(1, sizeof (*cs));
82 if (cs == NULL) {
83 perror("cs_insert:calloc");
84 exit(1);
86 cs->cs_name = strdup(key);
87 if (cs->cs_name == NULL) {
88 perror("cs_insert:strdup");
89 exit(1);
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"
102 "canonical name:\n";
105 static void
106 cs_dump(void)
108 codeset_t *cs;
109 csalias_t *a;
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);
123 (void) printf(")");
125 (void) printf(",\n");
129 static void
130 cs_destroy(void)
132 void *cookie = NULL;
133 codeset_t *cs;
134 csalias_t *a;
136 while ((cs = avl_destroy_nodes(&cs_avl, &cookie)) != NULL) {
137 while ((a = list_remove_head(&cs->cs_aliases)) != NULL) {
138 alias_destroy(a);
140 free(cs->cs_name);
141 free(cs);
143 avl_destroy(&cs_avl);
147 * aliases
150 static void
151 alias_insert(char *codeset, char *alias)
153 codeset_t tcs, *cs;
154 csalias_t *a;
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);
163 if (cs == NULL)
164 return;
167 * Add this alias
169 a = calloc(1, sizeof (*a));
170 if (a == NULL) {
171 perror("alias_insert:calloc");
172 exit(1);
174 a->a_name = strdup(alias);
175 if (a->a_name == NULL) {
176 perror("alias_insert:strdup");
177 exit(1);
180 list_insert_tail(&cs->cs_aliases, a);
183 static void
184 alias_destroy(csalias_t *a)
186 free(a->a_name);
187 free(a);
191 static void
192 scan_dir(DIR *dh, char sep, char *suffix)
194 char namebuf[MAXNAMELEN];
195 struct dirent *de;
197 while ((de = readdir(dh)) != NULL) {
198 char *p2, *p1;
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)) >=
206 sizeof (namebuf))
207 continue;
209 /* Find suffix (.so | .t) */
210 p2 = strrchr(namebuf, *suffix);
211 if (p2 == NULL)
212 continue;
213 if (strcmp(p2, suffix) != 0)
214 continue;
215 *p2 = '\0';
217 p1 = strchr(namebuf, sep);
218 if (p1 == NULL)
219 continue;
220 *p1++ = '\0';
222 /* More than one sep? */
223 if (strchr(p1, sep) != NULL)
224 continue;
226 /* Empty strings? */
227 if (*namebuf == '\0' || *p1 == '\0')
228 continue;
230 /* OK, add both to the map. */
231 cs_insert(namebuf);
232 cs_insert(p1);
236 static void
237 scan_aliases(FILE *fh)
239 char linebuf[256];
240 char *p1, *p2;
242 while (fgets(linebuf, sizeof (linebuf), fh) != NULL) {
243 if (linebuf[0] == '#')
244 continue;
245 p1 = strchr(linebuf, ' ');
246 if (p1 == NULL)
247 continue;
248 *p1++ = '\0';
249 p2 = strchr(p1, '\n');
250 if (p2 == NULL)
251 continue;
252 *p2 = '\0';
253 alias_insert(p1, linebuf);
258 list_codesets(void)
260 DIR *dh;
261 FILE *fh;
263 avl_create(&cs_avl, cs_compare, sizeof (codeset_t),
264 offsetof(codeset_t, cs_node));
266 dh = opendir(PATH_LIBICONV);
267 if (dh == NULL) {
268 perror(PATH_LIBICONV);
269 return (1);
271 scan_dir(dh, '%', ".so");
272 rewinddir(dh);
273 scan_dir(dh, '.', ".t");
274 (void) closedir(dh);
276 dh = opendir(PATH_BTABLES);
277 if (dh == NULL) {
278 perror(PATH_BTABLES);
279 return (1);
281 scan_dir(dh, '%', ".bt");
282 (void) closedir(dh);
284 fh = fopen(PATH_ALIASES, "r");
285 if (fh == NULL) {
286 perror(PATH_ALIASES);
287 /* let's continue */
288 } else {
289 scan_aliases(fh);
290 (void) fclose(fh);
293 cs_dump();
295 cs_destroy();
297 return (0);