r24780: More work allowing libutil to be used by external users.
[kai/samba.git] / source4 / lib / charset / charcnv.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Character set conversion Extensions
4    Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001
5    Copyright (C) Andrew Tridgell 2001
6    Copyright (C) Simo Sorce 2001
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22 #include "includes.h"
23 #include "system/iconv.h"
24
25 /**
26  * @file
27  *
28  * @brief Character-set conversion routines built on our iconv.
29  * 
30  * @note Samba's internal character set (at least in the 3.0 series)
31  * is always the same as the one for the Unix filesystem.  It is
32  * <b>not</b> necessarily UTF-8 and may be different on machines that
33  * need i18n filenames to be compatible with Unix software.  It does
34  * have to be a superset of ASCII.  All multibyte sequences must start
35  * with a byte with the high bit set.
36  *
37  * @sa lib/iconv.c
38  */
39
40 char *unix_charset = NULL;
41 char *dos_charset = NULL;
42 char *display_charset = NULL;
43
44 /**
45  * Return the name of a charset to give to iconv().
46  **/
47 static const char *charset_name(charset_t ch)
48 {
49         switch (ch) {
50         case CH_UTF16: return "UTF-16LE";
51         case CH_UNIX: return unix_charset;
52         case CH_DOS: return dos_charset;
53         case CH_DISPLAY: return display_charset;
54         case CH_UTF8: return "UTF8";
55         case CH_UTF16BE: return "UTF-16BE";
56         default:
57         return "ASCII";
58         }
59 }
60
61 static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS];
62
63 /**
64  re-initialize iconv conversion descriptors
65 **/
66 _PUBLIC_ void init_iconv(void)
67 {
68         unsigned c1, c2;
69         for (c1=0;c1<NUM_CHARSETS;c1++) {
70                 for (c2=0;c2<NUM_CHARSETS;c2++) {
71                         if (conv_handles[c1][c2] != NULL) {
72                                 if (conv_handles[c1][c2] != (smb_iconv_t)-1) {
73                                         smb_iconv_close(conv_handles[c1][c2]);
74                                 }
75                                 conv_handles[c1][c2] = NULL;
76                         }
77                 }
78         }
79
80 }
81
82 /*
83   on-demand initialisation of conversion handles
84 */
85 static smb_iconv_t get_conv_handle(charset_t from, charset_t to)
86 {
87         const char *n1, *n2;
88         static int initialised;
89         /* auto-free iconv memory on exit so valgrind reports are easier
90            to look at */
91         if (initialised == 0) {
92                 initialised = 1;
93                 
94 #ifdef LC_ALL
95                 /* we set back the locale to C to get ASCII-compatible
96                    toupper/lower functions.  For now we do not need
97                    any other POSIX localisations anyway. When we
98                    should really need localized string functions one
99                    day we need to write our own ascii_tolower etc.
100                 */
101                 setlocale(LC_ALL, "C");
102 #endif
103
104                 atexit(init_iconv);
105         }
106
107         if (conv_handles[from][to]) {
108                 return conv_handles[from][to];
109         }
110
111         n1 = charset_name(from);
112         n2 = charset_name(to);
113
114         conv_handles[from][to] = smb_iconv_open(n2,n1);
115         
116         if (conv_handles[from][to] == (smb_iconv_t)-1) {
117                 if ((from == CH_DOS || to == CH_DOS) &&
118                     strcasecmp(charset_name(CH_DOS), "ASCII") != 0) {
119                         DEBUG(0,("dos charset '%s' unavailable - using ASCII\n",
120                                  charset_name(CH_DOS)));
121                         lp_set_cmdline("dos charset", "ASCII");
122
123                         n1 = charset_name(from);
124                         n2 = charset_name(to);
125                         
126                         conv_handles[from][to] = smb_iconv_open(n2,n1);
127                 }
128         }
129
130         return conv_handles[from][to];
131 }
132
133
134 /**
135  * Convert string from one encoding to another, making error checking etc
136  *
137  * @param src pointer to source string (multibyte or singlebyte)
138  * @param srclen length of the source string in bytes
139  * @param dest pointer to destination string (multibyte or singlebyte)
140  * @param destlen maximal length allowed for string
141  * @returns the number of bytes occupied in the destination
142  **/
143 _PUBLIC_ ssize_t convert_string(charset_t from, charset_t to,
144                       void const *src, size_t srclen, 
145                       void *dest, size_t destlen)
146 {
147         size_t i_len, o_len;
148         size_t retval;
149         const char* inbuf = (const char*)src;
150         char* outbuf = (char*)dest;
151         smb_iconv_t descriptor;
152
153         if (srclen == (size_t)-1)
154                 srclen = strlen(src)+1;
155
156         descriptor = get_conv_handle(from, to);
157
158         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
159                 /* conversion not supported, use as is */
160                 size_t len = MIN(srclen,destlen);
161                 memcpy(dest,src,len);
162                 return len;
163         }
164
165         i_len=srclen;
166         o_len=destlen;
167         retval = smb_iconv(descriptor,  &inbuf, &i_len, &outbuf, &o_len);
168         if(retval==(size_t)-1) {
169                 const char *reason;
170                 switch(errno) {
171                         case EINVAL:
172                                 reason="Incomplete multibyte sequence";
173                                 return -1;
174                         case E2BIG:
175                                 reason="No more room"; 
176                                 if (from == CH_UNIX) {
177                                         DEBUG(0,("E2BIG: convert_string(%s,%s): srclen=%d destlen=%d - '%s'\n",
178                                                  charset_name(from), charset_name(to),
179                                                  (int)srclen, (int)destlen, 
180                                                  (const char *)src));
181                                 } else {
182                                         DEBUG(0,("E2BIG: convert_string(%s,%s): srclen=%d destlen=%d\n",
183                                                  charset_name(from), charset_name(to),
184                                                  (int)srclen, (int)destlen));
185                                 }
186                                return -1;
187                         case EILSEQ:
188                                reason="Illegal multibyte sequence";
189                                return -1;
190                 }
191                 /* smb_panic(reason); */
192         }
193         return destlen-o_len;
194 }
195
196 /**
197  * Convert between character sets, allocating a new buffer using talloc for the result.
198  *
199  * @param srclen length of source buffer.
200  * @param dest always set at least to NULL
201  * @note -1 is not accepted for srclen.
202  *
203  * @returns Size in bytes of the converted string; or -1 in case of error.
204  **/
205
206 _PUBLIC_ ssize_t convert_string_talloc(TALLOC_CTX *ctx, charset_t from, charset_t to,
207                               void const *src, size_t srclen, void **dest)
208 {
209         size_t i_len, o_len, destlen;
210         size_t retval;
211         const char *inbuf = (const char *)src;
212         char *outbuf, *ob;
213         smb_iconv_t descriptor;
214
215         *dest = NULL;
216
217         if (src == NULL || srclen == (size_t)-1 || srclen == 0)
218                 return (size_t)-1;
219
220         descriptor = get_conv_handle(from, to);
221
222         if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) {
223                 /* conversion not supported, return -1*/
224                 DEBUG(3, ("convert_string_talloc: conversion from %s to %s not supported!\n",
225                           charset_name(from), charset_name(to)));
226                 return -1;
227         }
228
229         /* it is _very_ rare that a conversion increases the size by
230            more than 3x */
231         destlen = srclen;
232         outbuf = NULL;
233 convert:
234         destlen = 2 + (destlen*3);
235         ob = talloc_realloc(ctx, outbuf, char, destlen);
236         if (!ob) {
237                 DEBUG(0, ("convert_string_talloc: realloc failed!\n"));
238                 talloc_free(outbuf);
239                 return (size_t)-1;
240         } else {
241                 outbuf = ob;
242         }
243
244         /* we give iconv 2 less bytes to allow us to terminate at the
245            end */
246         i_len = srclen;
247         o_len = destlen-2;
248         retval = smb_iconv(descriptor,
249                            &inbuf, &i_len,
250                            &outbuf, &o_len);
251         if(retval == (size_t)-1)                {
252                 const char *reason="unknown error";
253                 switch(errno) {
254                         case EINVAL:
255                                 reason="Incomplete multibyte sequence";
256                                 break;
257                         case E2BIG:
258                                 goto convert;           
259                         case EILSEQ:
260                                 reason="Illegal multibyte sequence";
261                                 break;
262                 }
263                 DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf));
264                 talloc_free(ob);
265                 return (size_t)-1;
266         }
267         
268         destlen = (destlen-2) - o_len;
269
270         /* guarantee null termination in all charsets */
271         SSVAL(ob, destlen, 0);
272
273         *dest = ob;
274
275         return destlen;
276 }
277
278 /**
279  * Copy a string from a char* unix src to a dos codepage string destination.
280  *
281  * @return the number of bytes occupied by the string in the destination.
282  *
283  * @param flags can include
284  * <dl>
285  * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd>
286  * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd>
287  * </dl>
288  *
289  * @param dest_len the maximum length in bytes allowed in the
290  * destination.  If @p dest_len is -1 then no maximum is used.
291  **/
292 static ssize_t push_ascii(void *dest, const char *src, size_t dest_len, int flags)
293 {
294         size_t src_len;
295         ssize_t ret;
296
297         if (flags & STR_UPPER) {
298                 char *tmpbuf = strupper_talloc(NULL, src);
299                 if (tmpbuf == NULL) {
300                         return -1;
301                 }
302                 ret = push_ascii(dest, tmpbuf, dest_len, flags & ~STR_UPPER);
303                 talloc_free(tmpbuf);
304                 return ret;
305         }
306
307         src_len = strlen(src);
308
309         if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII))
310                 src_len++;
311
312         return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len);
313 }
314
315 /**
316  * Copy a string from a unix char* src to an ASCII destination,
317  * allocating a buffer using talloc().
318  *
319  * @param dest always set at least to NULL 
320  *
321  * @returns The number of bytes occupied by the string in the destination
322  *         or -1 in case of error.
323  **/
324 _PUBLIC_ ssize_t push_ascii_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
325 {
326         size_t src_len = strlen(src)+1;
327         *dest = NULL;
328         return convert_string_talloc(ctx, CH_UNIX, CH_DOS, src, src_len, (void **)dest);
329 }
330
331
332 /**
333  * Copy a string from a dos codepage source to a unix char* destination.
334  *
335  * The resulting string in "dest" is always null terminated.
336  *
337  * @param flags can have:
338  * <dl>
339  * <dt>STR_TERMINATE</dt>
340  * <dd>STR_TERMINATE means the string in @p src
341  * is null terminated, and src_len is ignored.</dd>
342  * </dl>
343  *
344  * @param src_len is the length of the source area in bytes.
345  * @returns the number of bytes occupied by the string in @p src.
346  **/
347 static ssize_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
348 {
349         size_t ret;
350
351         if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) {
352                 if (src_len == (size_t)-1) {
353                         src_len = strlen(src) + 1;
354                 } else {
355                         size_t len = strnlen(src, src_len);
356                         if (len < src_len)
357                                 len++;
358                         src_len = len;
359                 }
360         }
361
362         ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len);
363
364         if (dest_len)
365                 dest[MIN(ret, dest_len-1)] = 0;
366
367         return src_len;
368 }
369
370 /**
371  * Copy a string from a char* src to a unicode destination.
372  *
373  * @returns the number of bytes occupied by the string in the destination.
374  *
375  * @param flags can have:
376  *
377  * <dl>
378  * <dt>STR_TERMINATE <dd>means include the null termination.
379  * <dt>STR_UPPER     <dd>means uppercase in the destination.
380  * <dt>STR_NOALIGN   <dd>means don't do alignment.
381  * </dl>
382  *
383  * @param dest_len is the maximum length allowed in the
384  * destination. If dest_len is -1 then no maxiumum is used.
385  **/
386 static ssize_t push_ucs2(void *dest, const char *src, size_t dest_len, int flags)
387 {
388         size_t len=0;
389         size_t src_len = strlen(src);
390         size_t ret;
391
392         if (flags & STR_UPPER) {
393                 char *tmpbuf = strupper_talloc(NULL, src);
394                 if (tmpbuf == NULL) {
395                         return -1;
396                 }
397                 ret = push_ucs2(dest, tmpbuf, dest_len, flags & ~STR_UPPER);
398                 talloc_free(tmpbuf);
399                 return ret;
400         }
401
402         if (flags & STR_TERMINATE)
403                 src_len++;
404
405         if (ucs2_align(NULL, dest, flags)) {
406                 *(char *)dest = 0;
407                 dest = (void *)((char *)dest + 1);
408                 if (dest_len) dest_len--;
409                 len++;
410         }
411
412         /* ucs2 is always a multiple of 2 bytes */
413         dest_len &= ~1;
414
415         ret = convert_string(CH_UNIX, CH_UTF16, src, src_len, dest, dest_len);
416         if (ret == (size_t)-1) {
417                 return 0;
418         }
419
420         len += ret;
421
422         return len;
423 }
424
425
426 /**
427  * Copy a string from a unix char* src to a UCS2 destination,
428  * allocating a buffer using talloc().
429  *
430  * @param dest always set at least to NULL 
431  *
432  * @returns The number of bytes occupied by the string in the destination
433  *         or -1 in case of error.
434  **/
435 _PUBLIC_ ssize_t push_ucs2_talloc(TALLOC_CTX *ctx, void **dest, const char *src)
436 {
437         size_t src_len = strlen(src)+1;
438         *dest = NULL;
439         return convert_string_talloc(ctx, CH_UNIX, CH_UTF16, src, src_len, dest);
440 }
441
442
443 /**
444  * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer using talloc
445  *
446  * @param dest always set at least to NULL 
447  *
448  * @returns The number of bytes occupied by the string in the destination
449  **/
450
451 _PUBLIC_ ssize_t push_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
452 {
453         size_t src_len = strlen(src)+1;
454         *dest = NULL;
455         return convert_string_talloc(ctx, CH_UNIX, CH_UTF8, src, src_len, (void **)dest);
456 }
457
458 /**
459  Copy a string from a ucs2 source to a unix char* destination.
460  Flags can have:
461   STR_TERMINATE means the string in src is null terminated.
462   STR_NOALIGN   means don't try to align.
463  if STR_TERMINATE is set then src_len is ignored if it is -1.
464  src_len is the length of the source area in bytes
465  Return the number of bytes occupied by the string in src.
466  The resulting string in "dest" is always null terminated.
467 **/
468
469 static size_t pull_ucs2(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
470 {
471         size_t ret;
472
473         if (ucs2_align(NULL, src, flags)) {
474                 src = (const void *)((const char *)src + 1);
475                 if (src_len > 0)
476                         src_len--;
477         }
478
479         if (flags & STR_TERMINATE) {
480                 if (src_len == (size_t)-1) {
481                         src_len = utf16_len(src);
482                 } else {
483                         src_len = utf16_len_n(src, src_len);
484                 }
485         }
486
487         /* ucs2 is always a multiple of 2 bytes */
488         if (src_len != (size_t)-1)
489                 src_len &= ~1;
490         
491         ret = convert_string(CH_UTF16, CH_UNIX, src, src_len, dest, dest_len);
492         if (dest_len)
493                 dest[MIN(ret, dest_len-1)] = 0;
494
495         return src_len;
496 }
497
498 /**
499  * Copy a string from a ASCII src to a unix char * destination, allocating a buffer using talloc
500  *
501  * @param dest always set at least to NULL 
502  *
503  * @returns The number of bytes occupied by the string in the destination
504  **/
505
506 _PUBLIC_ ssize_t pull_ascii_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
507 {
508         size_t src_len = strlen(src)+1;
509         *dest = NULL;
510         return convert_string_talloc(ctx, CH_DOS, CH_UNIX, src, src_len, (void **)dest);
511 }
512
513 /**
514  * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer using talloc
515  *
516  * @param dest always set at least to NULL 
517  *
518  * @returns The number of bytes occupied by the string in the destination
519  **/
520
521 _PUBLIC_ ssize_t pull_ucs2_talloc(TALLOC_CTX *ctx, char **dest, const void *src)
522 {
523         size_t src_len = utf16_len(src);
524         *dest = NULL;
525         return convert_string_talloc(ctx, CH_UTF16, CH_UNIX, src, src_len, (void **)dest);
526 }
527
528 /**
529  * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer using talloc
530  *
531  * @param dest always set at least to NULL 
532  *
533  * @returns The number of bytes occupied by the string in the destination
534  **/
535
536 _PUBLIC_ ssize_t pull_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src)
537 {
538         size_t src_len = strlen(src)+1;
539         *dest = NULL;
540         return convert_string_talloc(ctx, CH_UTF8, CH_UNIX, src, src_len, (void **)dest);
541 }
542
543 /**
544  Copy a string from a char* src to a unicode or ascii
545  dos codepage destination choosing unicode or ascii based on the 
546  flags in the SMB buffer starting at base_ptr.
547  Return the number of bytes occupied by the string in the destination.
548  flags can have:
549   STR_TERMINATE means include the null termination.
550   STR_UPPER     means uppercase in the destination.
551   STR_ASCII     use ascii even with unicode packet.
552   STR_NOALIGN   means don't do alignment.
553  dest_len is the maximum length allowed in the destination. If dest_len
554  is -1 then no maxiumum is used.
555 **/
556
557 _PUBLIC_ ssize_t push_string(void *dest, const char *src, size_t dest_len, int flags)
558 {
559         if (flags & STR_ASCII) {
560                 return push_ascii(dest, src, dest_len, flags);
561         } else if (flags & STR_UNICODE) {
562                 return push_ucs2(dest, src, dest_len, flags);
563         } else {
564                 smb_panic("push_string requires either STR_ASCII or STR_UNICODE flag to be set");
565                 return -1;
566         }
567 }
568
569
570 /**
571  Copy a string from a unicode or ascii source (depending on
572  the packet flags) to a char* destination.
573  Flags can have:
574   STR_TERMINATE means the string in src is null terminated.
575   STR_UNICODE   means to force as unicode.
576   STR_ASCII     use ascii even with unicode packet.
577   STR_NOALIGN   means don't do alignment.
578  if STR_TERMINATE is set then src_len is ignored is it is -1
579  src_len is the length of the source area in bytes.
580  Return the number of bytes occupied by the string in src.
581  The resulting string in "dest" is always null terminated.
582 **/
583
584 _PUBLIC_ ssize_t pull_string(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
585 {
586         if (flags & STR_ASCII) {
587                 return pull_ascii(dest, src, dest_len, src_len, flags);
588         } else if (flags & STR_UNICODE) {
589                 return pull_ucs2(dest, src, dest_len, src_len, flags);
590         } else {
591                 smb_panic("pull_string requires either STR_ASCII or STR_UNICODE flag to be set");
592                 return -1;
593         }
594 }
595
596
597 /*
598   return the unicode codepoint for the next multi-byte CH_UNIX character
599   in the string
600
601   also return the number of bytes consumed (which tells the caller
602   how many bytes to skip to get to the next CH_UNIX character)
603
604   return INVALID_CODEPOINT if the next character cannot be converted
605 */
606 _PUBLIC_ codepoint_t next_codepoint(const char *str, size_t *size)
607 {
608         /* it cannot occupy more than 4 bytes in UTF16 format */
609         uint8_t buf[4];
610         smb_iconv_t descriptor;
611         size_t ilen_orig;
612         size_t ilen;
613         size_t olen;
614         char *outbuf;
615
616         if ((str[0] & 0x80) == 0) {
617                 *size = 1;
618                 return (codepoint_t)str[0];
619         }
620
621         /* we assume that no multi-byte character can take
622            more than 5 bytes. This is OK as we only
623            support codepoints up to 1M */
624         ilen_orig = strnlen(str, 5);
625         ilen = ilen_orig;
626
627         descriptor = get_conv_handle(CH_UNIX, CH_UTF16);
628         if (descriptor == (smb_iconv_t)-1) {
629                 *size = 1;
630                 return INVALID_CODEPOINT;
631         }
632
633         /* this looks a little strange, but it is needed to cope
634            with codepoints above 64k */
635         olen = 2;
636         outbuf = (char *)buf;
637         smb_iconv(descriptor,  &str, &ilen, &outbuf, &olen);
638         if (olen == 2) {
639                 olen = 4;
640                 outbuf = (char *)buf;
641                 smb_iconv(descriptor,  &str, &ilen, &outbuf, &olen);
642                 if (olen == 4) {
643                         /* we didn't convert any bytes */
644                         *size = 1;
645                         return INVALID_CODEPOINT;
646                 }
647                 olen = 4 - olen;
648         } else {
649                 olen = 2 - olen;
650         }
651
652         *size = ilen_orig - ilen;
653
654         if (olen == 2) {
655                 return (codepoint_t)SVAL(buf, 0);
656         }
657         if (olen == 4) {
658                 /* decode a 4 byte UTF16 character manually */
659                 return (codepoint_t)0x10000 + 
660                         (buf[2] | ((buf[3] & 0x3)<<8) | 
661                          (buf[0]<<10) | ((buf[1] & 0x3)<<18));
662         }
663
664         /* no other length is valid */
665         return INVALID_CODEPOINT;
666 }
667
668 /*
669   push a single codepoint into a CH_UNIX string the target string must
670   be able to hold the full character, which is guaranteed if it is at
671   least 5 bytes in size. The caller may pass less than 5 bytes if they
672   are sure the character will fit (for example, you can assume that
673   uppercase/lowercase of a character will not add more than 1 byte)
674
675   return the number of bytes occupied by the CH_UNIX character, or
676   -1 on failure
677 */
678 _PUBLIC_ ssize_t push_codepoint(char *str, codepoint_t c)
679 {
680         smb_iconv_t descriptor;
681         uint8_t buf[4];
682         size_t ilen, olen;
683         const char *inbuf;
684         
685         if (c < 128) {
686                 *str = c;
687                 return 1;
688         }
689
690         descriptor = get_conv_handle(CH_UTF16, CH_UNIX);
691         if (descriptor == (smb_iconv_t)-1) {
692                 return -1;
693         }
694
695         if (c < 0x10000) {
696                 ilen = 2;
697                 olen = 5;
698                 inbuf = (char *)buf;
699                 SSVAL(buf, 0, c);
700                 smb_iconv(descriptor, &inbuf, &ilen, &str, &olen);
701                 if (ilen != 0) {
702                         return -1;
703                 }
704                 return 5 - olen;
705         }
706
707         c -= 0x10000;
708
709         buf[0] = (c>>10) & 0xFF;
710         buf[1] = (c>>18) | 0xd8;
711         buf[2] = c & 0xFF;
712         buf[3] = ((c>>8) & 0x3) | 0xdc;
713
714         ilen = 4;
715         olen = 5;
716         inbuf = (char *)buf;
717
718         smb_iconv(descriptor, &inbuf, &ilen, &str, &olen);
719         if (ilen != 0) {
720                 return -1;
721         }
722         return 5 - olen;
723 }