Add the ability to pass a NULL output buffer and/or an unlimited
[sgt/charset] / fromucs.c
1 /*
2 * fromucs.c - convert Unicode to other character sets.
3 */
4
5 #include "charset.h"
6 #include "internal.h"
7
8 struct charset_emit_param {
9 char *output;
10 int outlen;
11 int writtenlen;
12 int stopped;
13 };
14
15 static void charset_emit(void *ctx, long int output)
16 {
17 struct charset_emit_param *param = (struct charset_emit_param *)ctx;
18
19 if (param->outlen != 0) {
20 if (param->output)
21 *param->output++ = output;
22 if (param->outlen > 0)
23 param->outlen--;
24 param->writtenlen++;
25 } else {
26 param->stopped = 1;
27 }
28 }
29
30 int charset_from_unicode(const wchar_t **input, int *inlen,
31 char *output, int outlen,
32 int charset, charset_state *state, int *error)
33 {
34 charset_spec const *spec = charset_find_spec(charset);
35 charset_state localstate = CHARSET_INIT_STATE;
36 struct charset_emit_param param;
37 int locallen;
38
39 if (!input) {
40 locallen = 1;
41 inlen = &locallen;
42 }
43
44 param.output = output;
45 param.outlen = outlen;
46 param.writtenlen = 0;
47 param.stopped = 0;
48
49 if (state)
50 localstate = *state; /* structure copy */
51 if (error)
52 *error = FALSE;
53
54 while (*inlen > 0) {
55 int lenbefore = param.writtenlen;
56 int ret;
57
58 if (input)
59 ret = spec->write(spec, **input, &localstate,
60 charset_emit, &param);
61 else
62 ret = spec->write(spec, -1, &localstate, charset_emit, &param);
63 if (error && !ret) {
64 /*
65 * We have hit a difficult character, which the user
66 * wants to know about. Leave now.
67 */
68 *error = TRUE;
69 return lenbefore;
70 }
71 if (param.stopped) {
72 /*
73 * The emit function has _tried_ to output some
74 * characters, but ran up against the end of the
75 * buffer. Leave immediately, and return what happened
76 * _before_ attempting to process this character.
77 */
78 return lenbefore;
79 }
80 if (state)
81 *state = localstate; /* structure copy */
82 if (input)
83 (*input)++;
84 (*inlen)--;
85 }
86 return param.writtenlen;
87 }