1*91e1e26aSAlexander Pyhalov /*
2*91e1e26aSAlexander Pyhalov * CDDL HEADER START
3*91e1e26aSAlexander Pyhalov *
4*91e1e26aSAlexander Pyhalov * The contents of this file are subject to the terms of the
5*91e1e26aSAlexander Pyhalov * Common Development and Distribution License (the "License").
6*91e1e26aSAlexander Pyhalov * You may not use this file except in compliance with the License.
7*91e1e26aSAlexander Pyhalov *
8*91e1e26aSAlexander Pyhalov * You can obtain a copy of the license at src/OPENSOLARIS.LICENSE
9*91e1e26aSAlexander Pyhalov * or http://www.opensolaris.org/os/licensing.
10*91e1e26aSAlexander Pyhalov * See the License for the specific language governing permissions
11*91e1e26aSAlexander Pyhalov * and limitations under the License.
12*91e1e26aSAlexander Pyhalov *
13*91e1e26aSAlexander Pyhalov * When distributing Covered Code, include this CDDL HEADER in each
14*91e1e26aSAlexander Pyhalov * file and include the License file at src/OPENSOLARIS.LICENSE.
15*91e1e26aSAlexander Pyhalov * If applicable, add the following below this CDDL HEADER, with the
16*91e1e26aSAlexander Pyhalov * fields enclosed by brackets "[]" replaced with your own identifying
17*91e1e26aSAlexander Pyhalov * information: Portions Copyright [yyyy] [name of copyright owner]
18*91e1e26aSAlexander Pyhalov *
19*91e1e26aSAlexander Pyhalov * CDDL HEADER END
20*91e1e26aSAlexander Pyhalov */
21*91e1e26aSAlexander Pyhalov /*
22*91e1e26aSAlexander Pyhalov * Copyright (c) 2008, by Sun Microsystems, Inc.
23*91e1e26aSAlexander Pyhalov * All rights reserved.
24*91e1e26aSAlexander Pyhalov */
25*91e1e26aSAlexander Pyhalov
26*91e1e26aSAlexander Pyhalov #include <stdio.h>
27*91e1e26aSAlexander Pyhalov #include <errno.h>
28*91e1e26aSAlexander Pyhalov #include <stdlib.h>
29*91e1e26aSAlexander Pyhalov #include <sys/types.h>
30*91e1e26aSAlexander Pyhalov #define __NEED_UNI_2_VISCII__
31*91e1e26aSAlexander Pyhalov #include <unicode_viscii.h> /* Unicode to viscii mapping table */
32*91e1e26aSAlexander Pyhalov #include "common_defs.h"
33*91e1e26aSAlexander Pyhalov
34*91e1e26aSAlexander Pyhalov #define MSB 0x80 /* most significant bit */
35*91e1e26aSAlexander Pyhalov #define ONEBYTE 0xff /* right most byte */
36*91e1e26aSAlexander Pyhalov
37*91e1e26aSAlexander Pyhalov #define NON_ID_CHAR '?' /* non-identified character */
38*91e1e26aSAlexander Pyhalov
39*91e1e26aSAlexander Pyhalov
40*91e1e26aSAlexander Pyhalov
41*91e1e26aSAlexander Pyhalov typedef struct _icv_state {
42*91e1e26aSAlexander Pyhalov char keepc[6]; /* maximum # byte of UTF8 code */
43*91e1e26aSAlexander Pyhalov short ustate;
44*91e1e26aSAlexander Pyhalov int _errno; /* internal errno */
45*91e1e26aSAlexander Pyhalov } _iconv_st;
46*91e1e26aSAlexander Pyhalov
47*91e1e26aSAlexander Pyhalov enum _USTATE { U0, U1, U2, U3, U4, U5, U6, U7 };
48*91e1e26aSAlexander Pyhalov
49*91e1e26aSAlexander Pyhalov
50*91e1e26aSAlexander Pyhalov /*
51*91e1e26aSAlexander Pyhalov * Open; called from iconv_open()
52*91e1e26aSAlexander Pyhalov */
53*91e1e26aSAlexander Pyhalov void *
_icv_open()54*91e1e26aSAlexander Pyhalov _icv_open()
55*91e1e26aSAlexander Pyhalov {
56*91e1e26aSAlexander Pyhalov _iconv_st *st;
57*91e1e26aSAlexander Pyhalov
58*91e1e26aSAlexander Pyhalov if ((st = (_iconv_st *)malloc(sizeof(_iconv_st))) == NULL) {
59*91e1e26aSAlexander Pyhalov errno = ENOMEM;
60*91e1e26aSAlexander Pyhalov return ((void *) -1);
61*91e1e26aSAlexander Pyhalov }
62*91e1e26aSAlexander Pyhalov
63*91e1e26aSAlexander Pyhalov st->ustate = U0;
64*91e1e26aSAlexander Pyhalov st->_errno = 0;
65*91e1e26aSAlexander Pyhalov return ((void *) st);
66*91e1e26aSAlexander Pyhalov }
67*91e1e26aSAlexander Pyhalov
68*91e1e26aSAlexander Pyhalov
69*91e1e26aSAlexander Pyhalov /*
70*91e1e26aSAlexander Pyhalov * Close; called from iconv_close()
71*91e1e26aSAlexander Pyhalov */
72*91e1e26aSAlexander Pyhalov void
_icv_close(_iconv_st * st)73*91e1e26aSAlexander Pyhalov _icv_close(_iconv_st *st)
74*91e1e26aSAlexander Pyhalov {
75*91e1e26aSAlexander Pyhalov if (!st)
76*91e1e26aSAlexander Pyhalov errno = EBADF;
77*91e1e26aSAlexander Pyhalov else
78*91e1e26aSAlexander Pyhalov free(st);
79*91e1e26aSAlexander Pyhalov }
80*91e1e26aSAlexander Pyhalov
81*91e1e26aSAlexander Pyhalov
82*91e1e26aSAlexander Pyhalov /*
83*91e1e26aSAlexander Pyhalov * Actual conversion; called from iconv()
84*91e1e26aSAlexander Pyhalov */
85*91e1e26aSAlexander Pyhalov /*=========================================================
86*91e1e26aSAlexander Pyhalov *
87*91e1e26aSAlexander Pyhalov * State Machine for interpreting UTF8 code
88*91e1e26aSAlexander Pyhalov *
89*91e1e26aSAlexander Pyhalov *=========================================================
90*91e1e26aSAlexander Pyhalov * 4 byte unicode
91*91e1e26aSAlexander Pyhalov * +----->------->------------> U5 -----> U6-------> U7---+
92*91e1e26aSAlexander Pyhalov * | |
93*91e1e26aSAlexander Pyhalov * | 3 byte unicode |
94*91e1e26aSAlexander Pyhalov * +----->------->-------+ |
95*91e1e26aSAlexander Pyhalov * | | |
96*91e1e26aSAlexander Pyhalov * ^ v |
97*91e1e26aSAlexander Pyhalov * | 2 byte U2 ---> U3 |
98*91e1e26aSAlexander Pyhalov * | unicode v |
99*91e1e26aSAlexander Pyhalov * +------> U0 -------> U1 +-------->U4---+ |
100*91e1e26aSAlexander Pyhalov * ^ ascii | | ^ | |
101*91e1e26aSAlexander Pyhalov * | | +-------->--------->--------+ | |
102*91e1e26aSAlexander Pyhalov * | v v V
103*91e1e26aSAlexander Pyhalov * +----<---+-----<------------<------------<------------+---------+
104*91e1e26aSAlexander Pyhalov *
105*91e1e26aSAlexander Pyhalov *=========================================================*/
106*91e1e26aSAlexander Pyhalov size_t
_icv_iconv(_iconv_st * st,char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft)107*91e1e26aSAlexander Pyhalov _icv_iconv(_iconv_st *st, char **inbuf, size_t *inbytesleft,
108*91e1e26aSAlexander Pyhalov char **outbuf, size_t *outbytesleft)
109*91e1e26aSAlexander Pyhalov {
110*91e1e26aSAlexander Pyhalov char c1 = '\0', c2 = '\0';
111*91e1e26aSAlexander Pyhalov int uconv_num = 0;
112*91e1e26aSAlexander Pyhalov unsigned long uni = 0;
113*91e1e26aSAlexander Pyhalov int utf8_len = 0;
114*91e1e26aSAlexander Pyhalov
115*91e1e26aSAlexander Pyhalov #ifdef DEBUG
116*91e1e26aSAlexander Pyhalov fprintf(stderr, "========== iconv(): UTF2 --> GBK2K ==========\n");
117*91e1e26aSAlexander Pyhalov #endif
118*91e1e26aSAlexander Pyhalov if (st == NULL) {
119*91e1e26aSAlexander Pyhalov errno = EBADF;
120*91e1e26aSAlexander Pyhalov return ((size_t) -1);
121*91e1e26aSAlexander Pyhalov }
122*91e1e26aSAlexander Pyhalov
123*91e1e26aSAlexander Pyhalov if (inbuf == NULL || *inbuf == NULL) { /* Reset request. */
124*91e1e26aSAlexander Pyhalov st->ustate = U0;
125*91e1e26aSAlexander Pyhalov st->_errno = 0;
126*91e1e26aSAlexander Pyhalov return ((size_t) 0);
127*91e1e26aSAlexander Pyhalov }
128*91e1e26aSAlexander Pyhalov
129*91e1e26aSAlexander Pyhalov st->_errno = 0; /* reset internal errno */
130*91e1e26aSAlexander Pyhalov errno = 0; /* reset external errno */
131*91e1e26aSAlexander Pyhalov
132*91e1e26aSAlexander Pyhalov /* a state machine for interpreting UTF8 code */
133*91e1e26aSAlexander Pyhalov while (*inbytesleft > 0 && *outbytesleft > 0) {
134*91e1e26aSAlexander Pyhalov
135*91e1e26aSAlexander Pyhalov uchar_t first_byte;
136*91e1e26aSAlexander Pyhalov unsigned short ch = 0;
137*91e1e26aSAlexander Pyhalov switch (st->ustate) {
138*91e1e26aSAlexander Pyhalov case U0:
139*91e1e26aSAlexander Pyhalov /*
140*91e1e26aSAlexander Pyhalov * assuming ASCII in the beginning
141*91e1e26aSAlexander Pyhalov */
142*91e1e26aSAlexander Pyhalov if ((**inbuf & MSB) == 0) { /* ASCII */
143*91e1e26aSAlexander Pyhalov **outbuf = **inbuf;
144*91e1e26aSAlexander Pyhalov (*outbuf)++;
145*91e1e26aSAlexander Pyhalov (*outbytesleft)--;
146*91e1e26aSAlexander Pyhalov } else {
147*91e1e26aSAlexander Pyhalov if ((**inbuf & 0xe0) == 0xc0) {
148*91e1e26aSAlexander Pyhalov /* 2 byte unicode 0xc0..0xdf */
149*91e1e26aSAlexander Pyhalov /* invalid sequence if the first char is either 0xc0 or 0xc1 */
150*91e1e26aSAlexander Pyhalov if ( number_of_bytes_in_utf8_char[((uchar_t)**inbuf)] == ICV_TYPE_ILLEGAL_CHAR )
151*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
152*91e1e26aSAlexander Pyhalov else {
153*91e1e26aSAlexander Pyhalov st->ustate = U1;
154*91e1e26aSAlexander Pyhalov st->keepc[0] = **inbuf;
155*91e1e26aSAlexander Pyhalov }
156*91e1e26aSAlexander Pyhalov } else if ((**inbuf & 0xf0) == 0xe0) { /* 3 byte 0xe0..0xf0 */
157*91e1e26aSAlexander Pyhalov st->ustate = U2;
158*91e1e26aSAlexander Pyhalov st->keepc[0] = **inbuf;
159*91e1e26aSAlexander Pyhalov } else {
160*91e1e26aSAlexander Pyhalov /* four bytes of UTF-8 sequences */
161*91e1e26aSAlexander Pyhalov if ( number_of_bytes_in_utf8_char[((uchar_t)**inbuf)] == ICV_TYPE_ILLEGAL_CHAR )
162*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
163*91e1e26aSAlexander Pyhalov else {
164*91e1e26aSAlexander Pyhalov st->ustate = U5;
165*91e1e26aSAlexander Pyhalov st->keepc[0] = **inbuf;
166*91e1e26aSAlexander Pyhalov }
167*91e1e26aSAlexander Pyhalov }
168*91e1e26aSAlexander Pyhalov }
169*91e1e26aSAlexander Pyhalov break;
170*91e1e26aSAlexander Pyhalov case U1:
171*91e1e26aSAlexander Pyhalov /* 2 byte utf-8 encoding */
172*91e1e26aSAlexander Pyhalov if ((**inbuf & 0xc0) == MSB) {
173*91e1e26aSAlexander Pyhalov utf8_len = 2;
174*91e1e26aSAlexander Pyhalov st->keepc[1] = **inbuf;
175*91e1e26aSAlexander Pyhalov
176*91e1e26aSAlexander Pyhalov c1 = (st->keepc[0]&0x1c)>>2;
177*91e1e26aSAlexander Pyhalov c2 = ((st->keepc[0]&0x03)<<6) | ((st->keepc[1])&0x3f);
178*91e1e26aSAlexander Pyhalov st->ustate = U4;
179*91e1e26aSAlexander Pyhalov #ifdef DEBUG
180*91e1e26aSAlexander Pyhalov fprintf(stderr, "UTF8: %02x%02x --> ",
181*91e1e26aSAlexander Pyhalov st->keepc[0]&ONEBYTE, st->keepc[1]&ONEBYTE);
182*91e1e26aSAlexander Pyhalov #endif
183*91e1e26aSAlexander Pyhalov continue; /* should not advance *inbuf */
184*91e1e26aSAlexander Pyhalov } else {
185*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
186*91e1e26aSAlexander Pyhalov }
187*91e1e26aSAlexander Pyhalov break;
188*91e1e26aSAlexander Pyhalov case U2:
189*91e1e26aSAlexander Pyhalov /* 3 byte unicode - 2nd byte */
190*91e1e26aSAlexander Pyhalov first_byte = (uchar_t)st->keepc[0];
191*91e1e26aSAlexander Pyhalov /* if the first byte is 0xed, it is illegal sequence if the second
192*91e1e26aSAlexander Pyhalov * one is between 0xa0 and 0xbf because surrogate section is ill-formed
193*91e1e26aSAlexander Pyhalov */
194*91e1e26aSAlexander Pyhalov if (((uchar_t)**inbuf) < valid_min_2nd_byte[first_byte] ||
195*91e1e26aSAlexander Pyhalov ((uchar_t)**inbuf) > valid_max_2nd_byte[first_byte] )
196*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
197*91e1e26aSAlexander Pyhalov else {
198*91e1e26aSAlexander Pyhalov st->ustate = U3;
199*91e1e26aSAlexander Pyhalov st->keepc[1] = **inbuf;
200*91e1e26aSAlexander Pyhalov }
201*91e1e26aSAlexander Pyhalov break;
202*91e1e26aSAlexander Pyhalov case U3:
203*91e1e26aSAlexander Pyhalov /* 3 byte unicode - 3rd byte */
204*91e1e26aSAlexander Pyhalov if ((**inbuf & 0xc0) == MSB) {
205*91e1e26aSAlexander Pyhalov st->ustate = U4;
206*91e1e26aSAlexander Pyhalov utf8_len = 3;
207*91e1e26aSAlexander Pyhalov st->keepc[2] = **inbuf;
208*91e1e26aSAlexander Pyhalov c1 = ((st->keepc[0]&0x0f)<<4) |
209*91e1e26aSAlexander Pyhalov ((st->keepc[1]&0x3c)>>2);
210*91e1e26aSAlexander Pyhalov c2 = ((st->keepc[1]&0x03)<<6) | ((**inbuf)&0x3f);
211*91e1e26aSAlexander Pyhalov #ifdef DEBUG
212*91e1e26aSAlexander Pyhalov fprintf(stderr, "UTF8: %02x%02x%02x --> ", st->keepc[0]&ONEBYTE,
213*91e1e26aSAlexander Pyhalov st->keepc[1]&ONEBYTE, **inbuf&ONEBYTE);
214*91e1e26aSAlexander Pyhalov #endif
215*91e1e26aSAlexander Pyhalov continue; /* should not advance *inbuf */
216*91e1e26aSAlexander Pyhalov } else {
217*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
218*91e1e26aSAlexander Pyhalov }
219*91e1e26aSAlexander Pyhalov break;
220*91e1e26aSAlexander Pyhalov case U4:
221*91e1e26aSAlexander Pyhalov uni = (unsigned long) ((c1 & ONEBYTE) << 8) + (c2 & ONEBYTE);
222*91e1e26aSAlexander Pyhalov if (!uni_2_viscii(uni, (unsigned char*)&ch)) {
223*91e1e26aSAlexander Pyhalov **outbuf = NON_ID_CHAR;
224*91e1e26aSAlexander Pyhalov uconv_num += utf8_len;
225*91e1e26aSAlexander Pyhalov } else {
226*91e1e26aSAlexander Pyhalov **outbuf = ch;
227*91e1e26aSAlexander Pyhalov }
228*91e1e26aSAlexander Pyhalov (*outbuf)++;
229*91e1e26aSAlexander Pyhalov (*outbytesleft)--;
230*91e1e26aSAlexander Pyhalov st->ustate = U0;
231*91e1e26aSAlexander Pyhalov break;
232*91e1e26aSAlexander Pyhalov case U5:
233*91e1e26aSAlexander Pyhalov first_byte = st->keepc[0];
234*91e1e26aSAlexander Pyhalov
235*91e1e26aSAlexander Pyhalov /* if the first byte is 0xf0, it is illegal sequence if
236*91e1e26aSAlexander Pyhalov * the second one is between 0x80 and 0x8f
237*91e1e26aSAlexander Pyhalov * for Four-Byte UTF: U+10000..U+10FFFF
238*91e1e26aSAlexander Pyhalov * */
239*91e1e26aSAlexander Pyhalov if (((uchar_t)**inbuf) < valid_min_2nd_byte[first_byte] ||
240*91e1e26aSAlexander Pyhalov ((uchar_t)**inbuf) > valid_max_2nd_byte[first_byte] )
241*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
242*91e1e26aSAlexander Pyhalov else {
243*91e1e26aSAlexander Pyhalov st->ustate = U6;
244*91e1e26aSAlexander Pyhalov st->keepc[1] = **inbuf;
245*91e1e26aSAlexander Pyhalov }
246*91e1e26aSAlexander Pyhalov break;
247*91e1e26aSAlexander Pyhalov case U6:
248*91e1e26aSAlexander Pyhalov if ((**inbuf & 0xc0) == MSB) {
249*91e1e26aSAlexander Pyhalov /* 0x80..0xbf */
250*91e1e26aSAlexander Pyhalov st->ustate = U7;
251*91e1e26aSAlexander Pyhalov st->keepc[2] = **inbuf;
252*91e1e26aSAlexander Pyhalov } else
253*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
254*91e1e26aSAlexander Pyhalov break;
255*91e1e26aSAlexander Pyhalov case U7:
256*91e1e26aSAlexander Pyhalov if ((**inbuf & 0xc0) == MSB) {
257*91e1e26aSAlexander Pyhalov /* 0x80..0xbf */
258*91e1e26aSAlexander Pyhalov /* replace with double NON_ID_CHARs */
259*91e1e26aSAlexander Pyhalov if ( *outbytesleft < 1 )
260*91e1e26aSAlexander Pyhalov st->_errno = errno = E2BIG;
261*91e1e26aSAlexander Pyhalov else {
262*91e1e26aSAlexander Pyhalov **outbuf = NON_ID_CHAR;
263*91e1e26aSAlexander Pyhalov (*outbytesleft) -= 1;
264*91e1e26aSAlexander Pyhalov uconv_num++;
265*91e1e26aSAlexander Pyhalov st->ustate = U0;
266*91e1e26aSAlexander Pyhalov }
267*91e1e26aSAlexander Pyhalov } else
268*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
269*91e1e26aSAlexander Pyhalov break;
270*91e1e26aSAlexander Pyhalov default:
271*91e1e26aSAlexander Pyhalov /* should never come here */
272*91e1e26aSAlexander Pyhalov st->_errno = errno = EILSEQ;
273*91e1e26aSAlexander Pyhalov st->ustate = U0; /* reset state */
274*91e1e26aSAlexander Pyhalov break;
275*91e1e26aSAlexander Pyhalov }
276*91e1e26aSAlexander Pyhalov
277*91e1e26aSAlexander Pyhalov if (st->_errno) {
278*91e1e26aSAlexander Pyhalov #ifdef DEBUG
279*91e1e26aSAlexander Pyhalov fprintf(stderr, "!!!!!\tst->_errno = %d\tst->ustate = %d\n",
280*91e1e26aSAlexander Pyhalov st->_errno, st->ustate);
281*91e1e26aSAlexander Pyhalov #endif
282*91e1e26aSAlexander Pyhalov break;
283*91e1e26aSAlexander Pyhalov }
284*91e1e26aSAlexander Pyhalov
285*91e1e26aSAlexander Pyhalov (*inbuf)++;
286*91e1e26aSAlexander Pyhalov (*inbytesleft)--;
287*91e1e26aSAlexander Pyhalov }
288*91e1e26aSAlexander Pyhalov
289*91e1e26aSAlexander Pyhalov if (*inbytesleft == 0 && st->ustate != U0)
290*91e1e26aSAlexander Pyhalov errno = EINVAL;
291*91e1e26aSAlexander Pyhalov
292*91e1e26aSAlexander Pyhalov if (*inbytesleft > 0 && *outbytesleft == 0)
293*91e1e26aSAlexander Pyhalov errno = E2BIG;
294*91e1e26aSAlexander Pyhalov
295*91e1e26aSAlexander Pyhalov if (errno) {
296*91e1e26aSAlexander Pyhalov int num_reversed_bytes = 0;
297*91e1e26aSAlexander Pyhalov
298*91e1e26aSAlexander Pyhalov switch (st->ustate)
299*91e1e26aSAlexander Pyhalov {
300*91e1e26aSAlexander Pyhalov case U1:
301*91e1e26aSAlexander Pyhalov num_reversed_bytes = 1;
302*91e1e26aSAlexander Pyhalov break;
303*91e1e26aSAlexander Pyhalov case U2:
304*91e1e26aSAlexander Pyhalov num_reversed_bytes = 1;
305*91e1e26aSAlexander Pyhalov break;
306*91e1e26aSAlexander Pyhalov case U3:
307*91e1e26aSAlexander Pyhalov num_reversed_bytes = 2;
308*91e1e26aSAlexander Pyhalov break;
309*91e1e26aSAlexander Pyhalov case U4:
310*91e1e26aSAlexander Pyhalov num_reversed_bytes = utf8_len - 1;
311*91e1e26aSAlexander Pyhalov break;
312*91e1e26aSAlexander Pyhalov case U5:
313*91e1e26aSAlexander Pyhalov num_reversed_bytes = 1;
314*91e1e26aSAlexander Pyhalov break;
315*91e1e26aSAlexander Pyhalov case U6:
316*91e1e26aSAlexander Pyhalov num_reversed_bytes = 2;
317*91e1e26aSAlexander Pyhalov break;
318*91e1e26aSAlexander Pyhalov case U7:
319*91e1e26aSAlexander Pyhalov num_reversed_bytes = 3;
320*91e1e26aSAlexander Pyhalov break;
321*91e1e26aSAlexander Pyhalov }
322*91e1e26aSAlexander Pyhalov
323*91e1e26aSAlexander Pyhalov /*
324*91e1e26aSAlexander Pyhalov * if error, *inbuf points to the byte following the last byte
325*91e1e26aSAlexander Pyhalov * successfully used in conversion.
326*91e1e26aSAlexander Pyhalov */
327*91e1e26aSAlexander Pyhalov *inbuf -= num_reversed_bytes;
328*91e1e26aSAlexander Pyhalov *inbytesleft += num_reversed_bytes;
329*91e1e26aSAlexander Pyhalov st->ustate = U0;
330*91e1e26aSAlexander Pyhalov
331*91e1e26aSAlexander Pyhalov return ((size_t) -1);
332*91e1e26aSAlexander Pyhalov }
333*91e1e26aSAlexander Pyhalov
334*91e1e26aSAlexander Pyhalov return uconv_num;
335*91e1e26aSAlexander Pyhalov }
336