xref: /illumos-gate/usr/src/cmd/iconv/iconv_list.c (revision e3c4d829)
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