1/*
2 * From andy@pylesos.asp-linux.com.ua  Tue Dec  3 14:17:38 2002
3 * (polished, aeb)
4 *
5 * Manpages for a given language have a traditional character set.
6 * E.g., for Russian this is koi8r.
7 * If the user uses a different locale, throw in an invocation of iconv.
8 *
9 * Exports:
10 *   const char *get_converter (const char *path);
11 * Conversion is to the users locale. Conversion is from the
12 * manpage charset, found in environment variables, or in
13 * PATH/.charset, where PATH is the directory (below that) containing
14 * the man page.
15 *
16 * TODO: adapt this to man.conf way
17 */
18
19/*
20 * By default iconv is not used - this is the wrong interface.
21 * But if you want it, define USE_ICONV.
22 */
23#undef USE_ICONV
24
25#include <stdio.h>	/* NULL */
26
27#if defined __GLIBC__ && __GLIBC__ >= 2 && defined USE_ICONV
28#include <stdlib.h>	/* getenv */
29#include <unistd.h>	/* access */
30#include <string.h>	/* strcmp */
31#include <locale.h>	/* setlocale */
32#include <langinfo.h>	/* nl_langinfo */
33#include <iconv.h>	/* iconv_open */
34#include "man-iconv.h"	/* get_converter */
35#include "util.h"	/* my_strdup */
36#include "man.h"	/* debug */
37
38static char *
39find_iconv(void) {
40	static char *iconv_path = NULL;
41	static int inited = 0;
42
43	if (!inited) {
44		char *file = getenv("MAN_ICONV_PATH");
45		if (!file)
46			file = "/usr/bin/iconv";
47		if (access(file, X_OK) == 0)
48			iconv_path = my_strdup(file);
49		inited = 1;
50	}
51	return iconv_path;
52}
53
54static char *
55iconv_extra_flags(void) {
56	static char *iconv_flags = "-cs";
57	static int inited = 0;
58
59	if (!inited) {
60		char *opt = getenv("MAN_ICONV_OPT");
61		if (opt)
62			iconv_flags = my_strdup(opt);
63		inited = 1;
64	}
65	return iconv_flags;
66}
67
68static char *
69get_locale_charset (void) {
70	char *old_lc_ctype, *charset;
71
72	if ((charset = getenv("MAN_ICONV_OUTPUT_CHARSET")) == NULL) {
73		old_lc_ctype = setlocale(LC_CTYPE, "");
74		charset = nl_langinfo(CODESET);
75		setlocale(LC_CTYPE, old_lc_ctype);
76	}
77	return charset;
78}
79
80static char *
81get_man_charset (const char *path) {
82	char *charset_env, *file, *path2, *p;
83	FILE *f = NULL;
84
85	charset_env = getenv("MAN_ICONV_INPUT_CHARSET");
86	if (charset_env)
87		return charset_env;
88
89	if (!path || !*path)
90		return NULL;
91
92	if (debug)
93		fprintf(stderr, "get_man_charset: path=%s\n", path);
94
95	/* strip trailing "/.." and try that directory first */
96	path2 = my_strdup(path);
97	p = strrchr(path2, '/');
98	if (p && !strcmp(p, "/..")) {
99		*p = 0;
100		file = my_xsprintf("%s/.charset", path2);
101		f = fopen(file, "r");
102		free(file);
103	}
104	free(path2);
105
106	/* if that fails, try path itself */
107	if (f == NULL) {
108		file = my_xsprintf("%s/.charset", path);
109		f = fopen(file, "r");
110		free(file);
111	}
112
113	if (f) {
114		char charset[100], *p;
115
116		fgets(charset, sizeof(charset), f);
117		fclose(f);
118		fprintf(stderr, "read %s\n", charset);
119		p = strchr(charset, '\n');
120		if (p) {
121			*p = 0;
122			return my_strdup(charset);
123		}
124	}
125	return NULL;
126}
127
128static int
129is_conversion_supported (char *from, char *to) {
130	iconv_t cd;
131
132	if (!from || !*from || !to || !*to || !strcmp(from,to))
133		return 0;
134	if ((cd = iconv_open(to, from)) != (iconv_t) -1) {
135		iconv_close(cd);
136		return 1;
137	}
138	return 0;
139}
140
141const char *
142get_converter (const char *path) {
143	char *from, *to, *iconv_path;
144
145	iconv_path = find_iconv();
146	from = get_man_charset(path);
147	to = get_locale_charset();
148	if (debug)
149		fprintf(stderr, "get_converter: iconv_path=%s from=%s to=%s\n",
150			iconv_path, from, to);
151	if (iconv_path && is_conversion_supported(from, to))
152		return my_xsprintf("%s %s -f %s -t %s",
153				   iconv_path, iconv_extra_flags(), from, to);
154	return NULL;
155}
156#else
157#include "man-iconv.h"
158
159const char *
160get_converter (const char *path) {
161	return NULL;
162}
163#endif /* __GLIBC__ && __GLIBC__ >= 2 */
164