1 /*
2  * This file is part of the TINICONV Library.
3  *
4  * The TINICONV Library is free software; you can redistribute it
5  * and/or modify it under the terms of the GNU Library General Public
6  * License version 2 as published by the Free Software Foundation.
7  */
8 // ----------------------------------------------------------------------------
9 // Copyright (C) 2014
10 //              David Freese, W1HKJ
11 //
12 // This file is part of fldigi
13 //
14 // fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22 
23 #include "tiniconv.h"
24 #include "tiniconv_int.h"
25 
26 #include <string.h>
27 #include <assert.h>
28 
tiniconv_init(int in_charset_id,int out_charset_id,int options,struct tiniconv_ctx_s * ctx)29 int tiniconv_init(int in_charset_id, int out_charset_id, int options, struct tiniconv_ctx_s *ctx)
30 {
31   assert(ctx != NULL);
32 
33   if (in_charset_id < 0 || in_charset_id >= TINICONV_CHARSETSIZE)
34     return TINICONV_INIT_IN_CHARSET_NA;
35   if (out_charset_id < 0 || out_charset_id >= TINICONV_CHARSETSIZE)
36     return TINICONV_INIT_OUT_CHARSET_NA;
37 
38   memset(ctx, 0, sizeof(*ctx));
39   ctx->mb2wc = tiniconv_charset_map[in_charset_id].mb2wc;
40   ctx->flushwc = tiniconv_charset_map[in_charset_id].flushwc;
41   ctx->wc2mb = tiniconv_charset_map[out_charset_id].wc2mb;
42   ctx->reset = tiniconv_charset_map[out_charset_id].reset;
43   ctx->options = options;
44   if (!TINICONV_OPTION_GET_OUT_ILSEQ_CHAR(options))
45     ctx->options = ctx->options | TINICONV_OPTION_OUT_ILSEQ_CHAR('?');
46 
47   return TINICONV_INIT_OK;
48 }
49 
tiniconv_convert(struct tiniconv_ctx_s * ctx,unsigned char const * in_buf,int in_size,int * p_in_size_consumed,unsigned char * out_buf,int out_size,int * p_out_size_consumed)50 int tiniconv_convert(struct tiniconv_ctx_s *ctx,
51   unsigned char const *in_buf, int in_size, int *p_in_size_consumed,
52   unsigned char *out_buf, int out_size, int *p_out_size_consumed)
53 {
54   ucs4_t wc;
55   int in_idx, out_idx;
56   int result, last_result;
57   conv_state_t last_istate;
58 
59   assert(ctx != NULL);
60   assert(in_buf != NULL);
61   assert(out_buf != NULL);
62 
63   for (in_idx = 0, out_idx = 0; in_idx < in_size && out_idx < out_size;)
64   {
65   	last_istate = ctx->istate;
66     /* typedef int (*xxx_mb2wc_t) (conv_t conv, ucs4_t *pwc, unsigned char const *s, int n); */
67     result = ctx->mb2wc(ctx, &wc, in_buf + in_idx, in_size - in_idx);
68     assert(result <= in_size - in_idx);
69     if (result < 0)
70     {
71       if (result == RET_ILSEQ)
72       {
73         if (ctx->options & TINICONV_OPTION_IGNORE_IN_ILSEQ)
74         {
75           ctx->istate = 0;
76           in_idx ++;
77           continue;
78         }
79         else
80         {
81           result = TINICONV_CONVERT_IN_ILSEQ;
82           goto exit;
83         }
84       }
85       else if (result == RET_TOOSMALL)
86       {
87         result = TINICONV_CONVERT_IN_TOO_SMALL;
88         goto exit;
89       }
90       else
91       {
92       	in_idx += RET_TOOFEW(result);
93       	continue;
94       }
95     }
96     in_idx += last_result = result;
97 
98     /* typedef int (*xxx_wc2mb_t) (conv_t conv, unsigned char *r, ucs4_t wc, int n); */
99     result = ctx->wc2mb(ctx, out_buf + out_idx, wc, out_size - out_idx);
100     assert(result <= out_size - out_idx);
101     if (result < 0)
102     {
103       if (result == RET_ILUNI)
104       {
105         if (ctx->options & TINICONV_OPTION_IGNORE_OUT_ILSEQ)
106         {
107           out_buf[out_idx ++] = TINICONV_OPTION_GET_OUT_ILSEQ_CHAR(ctx->options);
108           ctx->ostate = 0;
109           continue;
110         }
111         else
112         {
113           result = TINICONV_CONVERT_OUT_ILSEQ;
114           in_idx -= last_result; /* discarding the last read sequence */
115           ctx->istate = last_istate;
116           goto exit;
117         }
118       }
119       else if (result == RET_TOOSMALL)
120       {
121         result = TINICONV_CONVERT_OUT_TOO_SMALL;
122         in_idx -= last_result; /* discarding the last read sequence */
123         ctx->istate = last_istate;
124         goto exit;
125       }
126     }
127     out_idx += result;
128   }
129   result = TINICONV_CONVERT_OK;
130 
131 exit:
132   if (p_in_size_consumed)
133     *p_in_size_consumed = in_idx;
134   if (p_out_size_consumed)
135     *p_out_size_consumed = out_idx;
136   return result;
137 }
138