1#ifndef lint
2static char *rcsid = "$Id: filemapper.c,v 1.1 2003/06/04 00:25:53 marka Exp $";
3#endif
4
5/*
6 * Copyright (c) 2001,2002 Japan Network Information Center.
7 * All rights reserved.
8 *
9 * By using this file, you agree to the terms and conditions set forth bellow.
10 *
11 * 			LICENSE TERMS AND CONDITIONS
12 *
13 * The following License Terms and Conditions apply, unless a different
14 * license is obtained from Japan Network Information Center ("JPNIC"),
15 * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
16 * Chiyoda-ku, Tokyo 101-0047, Japan.
17 *
18 * 1. Use, Modification and Redistribution (including distribution of any
19 *    modified or derived work) in source and/or binary forms is permitted
20 *    under this License Terms and Conditions.
21 *
22 * 2. Redistribution of source code must retain the copyright notices as they
23 *    appear in each source code file, this License Terms and Conditions.
24 *
25 * 3. Redistribution in binary form must reproduce the Copyright Notice,
26 *    this License Terms and Conditions, in the documentation and/or other
27 *    materials provided with the distribution.  For the purposes of binary
28 *    distribution the "Copyright Notice" refers to the following language:
29 *    "Copyright (c) 2000-2002 Japan Network Information Center.  All rights reserved."
30 *
31 * 4. The name of JPNIC may not be used to endorse or promote products
32 *    derived from this Software without specific prior written approval of
33 *    JPNIC.
34 *
35 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
36 *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
38 *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
39 *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42 *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
43 *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
44 *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
45 *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
46 */
47
48#include <config.h>
49
50#include <stdlib.h>
51#include <stdio.h>
52#include <string.h>
53#include <ctype.h>
54
55#include <idn/result.h>
56#include <idn/assert.h>
57#include <idn/log.h>
58#include <idn/logmacro.h>
59#include <idn/debug.h>
60#include <idn/ucs4.h>
61#include <idn/ucsmap.h>
62#include <idn/filemapper.h>
63
64#define SUPPORT_VERSIONING
65
66#define UCSBUF_LOCAL_SIZE	20
67
68typedef struct ucsbuf {
69	unsigned long *ucs;
70	size_t size;
71	size_t len;
72	unsigned long local[UCSBUF_LOCAL_SIZE];
73} ucsbuf_t;
74
75struct idn__filemapper {
76	idn_ucsmap_t map;
77};
78
79static void		ucsbuf_init(ucsbuf_t *b);
80static idn_result_t	ucsbuf_grow(ucsbuf_t *b);
81static idn_result_t	ucsbuf_append(ucsbuf_t *b, unsigned long v);
82static void		ucsbuf_free(ucsbuf_t *b);
83static idn_result_t	read_file(const char *file, FILE *fp,
84				  idn_ucsmap_t map);
85static idn_result_t	get_map(char *p, ucsbuf_t *b);
86static char 		*get_ucs(char *p, unsigned long *vp);
87
88
89idn_result_t
90idn__filemapper_create(const char *file, idn__filemapper_t *ctxp) {
91	FILE *fp;
92	idn__filemapper_t ctx;
93	idn_result_t r;
94
95	assert(file != NULL && ctxp != NULL);
96
97	TRACE(("idn__filemapper_create(file=\"%-.100s\")\n", file));
98
99	if ((fp = fopen(file, "r")) == NULL) {
100		WARNING(("idn__filemapper_create: cannot open %-.100s\n",
101			 file));
102		return (idn_nofile);
103	}
104	if ((ctx = malloc(sizeof(struct idn__filemapper))) == NULL)
105		return (idn_nomemory);
106
107	if ((r = idn_ucsmap_create(&ctx->map)) != idn_success) {
108		free(ctx);
109		return (r);
110	}
111
112	r = read_file(file, fp, ctx->map);
113	fclose(fp);
114
115	if (r == idn_success) {
116		idn_ucsmap_fix(ctx->map);
117		*ctxp = ctx;
118	} else {
119		idn_ucsmap_destroy(ctx->map);
120		free(ctx);
121	}
122	return (r);
123}
124
125void
126idn__filemapper_destroy(idn__filemapper_t ctx) {
127
128	assert(ctx != NULL);
129
130	TRACE(("idn__filemapper_destroy()\n"));
131
132	idn_ucsmap_destroy(ctx->map);
133	free(ctx);
134}
135
136idn_result_t
137idn__filemapper_map(idn__filemapper_t ctx, const unsigned long *from,
138		    unsigned long *to, size_t tolen)
139{
140	idn_result_t r = idn_success;
141	ucsbuf_t ub;
142
143	assert(ctx != NULL && from != NULL && to != NULL);
144
145	TRACE(("idn__filemapper_map(from=\"%s\")\n",
146	       idn__debug_ucs4xstring(from, 50)));
147
148	/* Initialize temporary buffer. */
149	ucsbuf_init(&ub);
150
151	while (*from != '\0') {
152		/* Try mapping. */
153		r = idn_ucsmap_map(ctx->map, *from, ub.ucs, ub.size, &ub.len);
154		switch (r) {
155		case idn_buffer_overflow:
156			/* Temporary buffer too small.  Enlarge and retry. */
157			if ((r = ucsbuf_grow(&ub)) != idn_success)
158				break;
159			continue;
160		case idn_nomapping:
161			/* There is no mapping. */
162			r = idn_success;
163			/* fallthrough */
164		case idn_success:
165			if (tolen < ub.len) {
166				r = idn_buffer_overflow;
167				goto ret;
168			}
169			memcpy(to, ub.ucs, sizeof(*to) * ub.len);
170			to += ub.len;
171			tolen -= ub.len;
172			break;
173		default:
174			goto ret;
175		}
176		from++;
177	}
178
179 ret:
180	ucsbuf_free(&ub);
181
182	if (r == idn_success) {
183		/* Terminate with NUL. */
184		if (tolen == 0)
185			return (idn_buffer_overflow);
186		*to = '\0';
187	}
188
189	return (r);
190}
191
192static void
193ucsbuf_init(ucsbuf_t *b) {
194	b->ucs = b->local;
195	b->size = UCSBUF_LOCAL_SIZE;
196	b->len = 0;
197}
198
199static idn_result_t
200ucsbuf_grow(ucsbuf_t *b) {
201	unsigned long *newbuf;
202
203	b->size *= 2;
204	if (b->ucs == b->local) {
205		b->ucs = malloc(sizeof(unsigned long) * b->size);
206		if (b->ucs == NULL)
207			return (idn_nomemory);
208		memcpy(b->ucs, b->local, sizeof(b->local));
209	} else {
210		newbuf = realloc(b->ucs, sizeof(unsigned long) * b->size);
211		if (newbuf == NULL)
212			return (idn_nomemory);
213		b->ucs = newbuf;
214	}
215	return (idn_success);
216}
217
218static idn_result_t
219ucsbuf_append(ucsbuf_t *b, unsigned long v) {
220	idn_result_t r;
221
222	if (b->len + 1 > b->size) {
223		r = ucsbuf_grow(b);
224		if (r != idn_success)
225			return (r);
226	}
227	b->ucs[b->len++] = v;
228	return (idn_success);
229}
230
231static void
232ucsbuf_free(ucsbuf_t *b) {
233	if (b->ucs != b->local && b->ucs != NULL)
234		free(b->ucs);
235}
236
237static idn_result_t
238read_file(const char *file, FILE *fp, idn_ucsmap_t map) {
239	char line[1024];
240	ucsbuf_t ub;
241	idn_result_t r = idn_success;
242	int lineno = 0;
243
244	ucsbuf_init(&ub);
245
246	while (fgets(line, sizeof(line), fp) != NULL) {
247		char *p = line;
248
249		lineno++;
250		while (isspace((unsigned char)*p))
251			p++;
252		if (*p == '\0' || *p == '#')
253			continue;
254#ifdef SUPPORT_VERSIONING
255		/* Skip version tag. */
256		if (lineno == 1 && strncmp("version=", line, 8) == 0)
257			continue;
258#endif
259	again:
260		ub.len = 0;
261		r = get_map(p, &ub);
262		switch (r) {
263		case idn_success:
264			r = idn_ucsmap_add(map, ub.ucs[0],
265					   &ub.ucs[1], ub.len - 1);
266			break;
267		case idn_buffer_overflow:
268			if ((r = ucsbuf_grow(&ub)) != idn_success)
269				break;
270			goto again;
271		case idn_invalid_syntax:
272			WARNING(("syntax error in file \"%-.100s\" line %d: "
273				 "%-.100s", file, lineno, line));
274			/* fall through */
275		default:
276			ucsbuf_free(&ub);
277			return (r);
278		}
279	}
280	ucsbuf_free(&ub);
281	return (r);
282}
283
284static idn_result_t
285get_map(char *p, ucsbuf_t *b) {
286	unsigned long v;
287	idn_result_t r = idn_success;
288
289	for (;;) {
290		if ((p = get_ucs(p, &v)) == NULL)
291			return (idn_invalid_syntax);
292		if ((r = ucsbuf_append(b, v)) != idn_success)
293			return (r);
294		if (b->len == 1) {
295			if (*p != ';')
296				return (idn_invalid_syntax);
297			p++;
298			while (isspace((unsigned char)*p))
299				p++;
300		}
301
302		if (*p == ';' || *p == '#' || *p == '\0')
303			return (r);
304	}
305	return (r);
306}
307
308static char *
309get_ucs(char *p, unsigned long *vp) {
310	char *end;
311
312	/* Skip leading space */
313	while (isspace((unsigned char)*p))
314		p++;
315
316	/* Skip optional 'U+' */
317	if (strncmp(p, "U+", 2) == 0)
318		p += 2;
319
320	*vp = strtoul(p, &end, 16);
321	if (end == p) {
322		INFO(("idn__filemapper_create: UCS code point expected\n"));
323		return (NULL);
324	}
325	p = end;
326
327	/* Skip trailing space */
328	while (isspace((unsigned char)*p))
329		p++;
330	return p;
331}
332
333idn_result_t
334idn__filemapper_createproc(const char *parameter, void **ctxp) {
335	return idn__filemapper_create(parameter, (idn__filemapper_t *)ctxp);
336}
337
338void
339idn__filemapper_destroyproc(void *ctxp) {
340	idn__filemapper_destroy((idn__filemapper_t)ctxp);
341}
342
343idn_result_t
344idn__filemapper_mapproc(void *ctx, const unsigned long *from,
345			unsigned long *to, size_t tolen) {
346	return idn__filemapper_map((idn__filemapper_t)ctx, from, to, tolen);
347}
348