1 // ----------------------------------------------------------------------------
2 // outputencoder.cxx -- output charset conversion
3 //
4 // Copyright (C) 2012
5 // Andrej Lajovic, S57LN
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
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 fldigi. If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22
23 #include <cstring>
24 #include <iostream>
25 #include <string>
26 #include <tiniconv.h>
27 #include <outputencoder.h>
28
29 #include "config.h"
30 #include "debug.h"
31
32 using namespace std;
33
34 /*
35 OutputEncoder accepts UTF-8 strings at input, converts them to the
36 selected encoding and outputs them one character at a time.
37 */
38
39
40 /*
41 Constructor. Look up tiniconv.h for the list of possible values of
42 charset_in.
43 */
OutputEncoder(const int charset_out,unsigned int buffer_size)44 OutputEncoder::OutputEncoder(const int charset_out, unsigned int buffer_size)
45 {
46 this->buffer_size = buffer_size;
47 buffer = new unsigned char[buffer_size];
48 encoding_ptr = buffer;
49 pop_ptr = buffer;
50 set_output_encoding(charset_out);
51 }
52
53
54 /*
55 Destructor.
56 */
~OutputEncoder(void)57 OutputEncoder::~OutputEncoder(void)
58 {
59 delete[] buffer;
60 }
61
62
63 /*
64 Set output encoding. Look up tiniconv.h for the list of possible values of
65 charset_in.
66 */
set_output_encoding(const int charset_out)67 void OutputEncoder::set_output_encoding(const int charset_out)
68 {
69 tiniconv_init(TINICONV_CHARSET_UTF_8, charset_out, TINICONV_OPTION_IGNORE_OUT_ILSEQ, &ctx);
70 }
71
72
73 /*
74 Push input data into the encoder.
75 */
push(string s)76 void OutputEncoder::push(string s)
77 {
78 int available = buffer_size - (encoding_ptr - buffer);
79 int consumed_in;
80 int consumed_out;
81
82 int status = tiniconv_convert(&ctx,
83 (unsigned char*)s.data(), s.length(), &consumed_in,
84 encoding_ptr, available, &consumed_out);
85 if (status != TINICONV_CONVERT_OK) {
86 LOG_ERROR("Error %s",
87 status == TINICONV_CONVERT_IN_TOO_SMALL ? "input too small" :
88 status == TINICONV_CONVERT_OUT_TOO_SMALL ? "output too small" :
89 status == TINICONV_CONVERT_IN_ILSEQ ? "input illegal sequence" :
90 status == TINICONV_CONVERT_OUT_ILSEQ ? "output illegal sequence" :
91 "unknown error");
92 return;
93 }
94
95 encoding_ptr += consumed_out;
96
97 if (consumed_in < (int)s.length())
98 {
99 // All input data was not consumed, possibly because the
100 // output buffer was too small. Try to vacuum the buffer,
101 // i.e., remove the data that was already pop()ed.
102 memmove(buffer, pop_ptr, buffer + buffer_size - pop_ptr);
103 encoding_ptr -= (pop_ptr - buffer);
104 pop_ptr = buffer;
105
106 // Now try again; fingers crossed. We don't check for
107 // success anymore, because there is nothing that we can do
108 // if the buffer is still too small.
109 int available = buffer_size - (encoding_ptr - buffer);
110 tiniconv_convert(&ctx,
111 (unsigned char*)s.data()+consumed_in, s.length()-consumed_in, &consumed_in,
112 encoding_ptr, available, &consumed_out);
113
114 encoding_ptr += consumed_out;
115 }
116 }
117
118
119 /*
120 Pop a single character of the encoded data.
121 Returns -1 in case there is no data available.
122 */
pop(void)123 const unsigned int OutputEncoder::pop(void)
124 {
125 if (pop_ptr == encoding_ptr)
126 return(-1);
127
128 unsigned int c = *pop_ptr++;
129
130 // Note that by only advancing pop_ptr, we leave stale data at the
131 // beginning of the buffer, so sooner or later it will clutter up.
132 // If there is no data left to send, both encoding_ptr and pop_ptr
133 // can be safely reset to the beginning of the buffer; we handle
134 // this trivial case here. More thorough vacuuming will be performed
135 // in push() if the need arises.
136 if (pop_ptr == encoding_ptr)
137 pop_ptr = encoding_ptr = buffer;
138
139 return(c);
140 }
141
142 // return next character to be pop'd, do not advance pointers;
143
peek(void)144 const unsigned int OutputEncoder::peek(void)
145 {
146 if (pop_ptr == encoding_ptr) return -1;
147 return *pop_ptr;
148 }
149
150