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 * 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 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 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