2 * FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
\r
3 * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
4 * Authors include James Walmsley, Hein Tibosch and Richard Barry
\r
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
7 * this software and associated documentation files (the "Software"), to deal in
\r
8 * the Software without restriction, including without limitation the rights to
\r
9 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
10 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
11 * subject to the following conditions:
\r
13 * The above copyright notice and this permission notice shall be included in all
\r
14 * copies or substantial portions of the Software.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * https://www.FreeRTOS.org
\r
31 * @defgroup STRING FreeRTOS+FAT String Library
\r
32 * @brief Portable String Library for FreeRTOS+FAT
\r
41 #include "ff_headers.h"
\r
43 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
49 * These will eventually be moved into a platform independent string
\r
50 * library. Which will be optional. (To allow the use of system specific versions).
\r
53 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
54 void FF_cstrntowcs( FF_T_WCHAR *wcsDest, const int8_t *szpSource, uint32_t ulLength )
\r
56 while( ( *szpSource != '\0' ) && ( ulLength-- != 0 ) )
\r
58 *( wcsDest++ ) = *( szpSource++ );
\r
62 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
63 /*-----------------------------------------------------------*/
\r
65 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
66 void FF_cstrtowcs( FF_T_WCHAR *wcsDest, const int8_t *szpSource )
\r
68 while( *szpSource != '\0' )
\r
70 *wcsDest++ = ( FF_T_WCHAR ) *( szpSource++ );
\r
74 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
75 /*-----------------------------------------------------------*/
\r
77 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
78 void FF_wcstocstr( int8_t *szpDest, const FF_T_WCHAR *wcsSource )
\r
80 while( *wcsSource != '\0' )
\r
82 *szpDest++ = ( int8_t )*( wcsSource++ );
\r
86 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
87 /*-----------------------------------------------------------*/
\r
89 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
90 void FF_wcsntocstr( int8_t *szpDest, const FF_T_WCHAR *wcsSource, uint32_t ulLength )
\r
92 while( ( *wcsSource != '\0' ) && ( ulLength-- != 0 ) )
\r
94 *( szpDest++ ) = ( int8_t ) *( wcsSource++ );
\r
98 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
99 /*-----------------------------------------------------------*/
\r
101 /*-----------------------------------------------------------*/
\r
103 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 )
\r
104 void FF_toupper( FF_T_WCHAR *string, uint32_t ulLength )
\r
108 for( i = 0; i < ulLength; i++ )
\r
110 string[ i ] = towupper( string[ i ] );
\r
113 /*-----------------------------------------------------------*/
\r
115 void FF_tolower( FF_T_WCHAR *string, uint32_t ulLength )
\r
118 for( i = 0; i < ulLength; i++ )
\r
120 string[ i ] = towlower( string[ i ] );
\r
123 /*-----------------------------------------------------------*/
\r
125 #else /* ffconfigUNICODE_UTF16_SUPPORT */
\r
126 void FF_toupper( char *string, uint32_t ulLength )
\r
130 for( i = 0; i < ulLength; i++ )
\r
132 if( ( string[ i ] >= 'a' ) && ( string[ i ] <= 'z' ) )
\r
136 if( string[ i ] == '\0' )
\r
142 /*-----------------------------------------------------------*/
\r
144 void FF_tolower( char *string, uint32_t ulLength )
\r
148 for( i = 0; i < ulLength; i++ )
\r
150 if( ( string[ i ] >= 'A' ) && ( string[ i ] <= 'Z' ) )
\r
154 if( string[ i ] == '\0' )
\r
160 /*-----------------------------------------------------------*/
\r
162 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
167 * @brief Compares 2 strings for the specified length, and returns pdTRUE is they are identical
\r
168 * otherwise pdFALSE is returned.
\r
172 #if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
\r
173 BaseType_t FF_strmatch( const char *str1, const char *str2, BaseType_t xLength )
\r
175 register BaseType_t i;
\r
176 register char char1, char2;
\r
180 xLength = strlen( str1 );
\r
181 if( xLength != ( BaseType_t )strlen( str2 ) )
\r
187 for( i = 0; i < xLength; i++ )
\r
191 if( ( char1 >= 'A' ) && ( char1 <= 'Z' ) )
\r
195 if( ( char2 >= 'A' ) && ( char2 <= 'Z' ) )
\r
200 if( char1 != char2 )
\r
208 #else /* ffconfigUNICODE_UTF16_SUPPORT */
\r
209 BaseType_t FF_strmatch( const FF_T_WCHAR *str1, const FF_T_WCHAR *str2, BaseType_t xLength )
\r
211 register BaseType_t i;
\r
212 register FF_T_WCHAR char1, char2;
\r
216 xLength = wcslen( str1 );
\r
217 if( xLength != wcslen( str2 ) )
\r
223 for( i = 0; i < xLength; i++ )
\r
225 char1 = towlower( str1[ i ] );
\r
226 char2 = towlower( str2[ i ] );
\r
227 if( char1 != char2 )
\r
235 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
239 * @brief A re-entrant Strtok function. No documentation is provided :P
\r
240 * Use at your own risk. (This is for FreeRTOS+FAT's use only).
\r
243 #if( ffconfigUNICODE_UTF16_SUPPORT == 0 )
\r
244 char *FF_strtok( const char *string, char *token, uint16_t *tokenNumber, BaseType_t *last, BaseType_t xLength )
\r
246 uint16_t i,y, tokenStart, tokenEnd = 0;
\r
251 if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
\r
258 while( i < xLength )
\r
260 if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
\r
263 if( y == *tokenNumber )
\r
265 tokenStart = ( uint16_t )( i + 1 );
\r
267 if( y == ( *tokenNumber + 1 ) )
\r
276 if( tokenEnd == 0 )
\r
278 if( *last == pdTRUE )
\r
288 if( ( tokenEnd - tokenStart ) < ffconfigMAX_FILENAME )
\r
290 memcpy( token, ( string + tokenStart ), ( uint32_t )( tokenEnd - tokenStart ) );
\r
291 token[ tokenEnd - tokenStart ] = '\0';
\r
295 memcpy( token, ( string + tokenStart ), ( uint32_t )( ffconfigMAX_FILENAME ) );
\r
296 token[ ffconfigMAX_FILENAME - 1 ] = '\0';
\r
298 /*token[tokenEnd - tokenStart] = '\0'; */
\r
303 #else /* ffconfigUNICODE_UTF16_SUPPORT */
\r
304 FF_T_WCHAR *FF_strtok( const FF_T_WCHAR *string, FF_T_WCHAR *token, uint16_t *tokenNumber, BaseType_t *last, BaseType_t xLength )
\r
306 uint16_t i,y, tokenStart, tokenEnd = 0;
\r
311 if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
\r
318 while( i < xLength )
\r
320 if( ( string[ i ] == '\\' ) || ( string[ i ] == '/' ) )
\r
323 if( y == *tokenNumber )
\r
325 tokenStart = ( uint16_t ) ( i + 1 );
\r
327 if( y == ( *tokenNumber + 1 ) )
\r
336 if( tokenEnd == 0 )
\r
338 if( *last == pdTRUE )
\r
348 if( ( tokenEnd - tokenStart ) < ffconfigMAX_FILENAME )
\r
350 memcpy( token, ( string + tokenStart ), ( uint32_t )( tokenEnd - tokenStart ) * sizeof( FF_T_WCHAR ) );
\r
351 token[ tokenEnd - tokenStart ] = '\0';
\r
355 memcpy( token, ( string + tokenStart ), ( uint32_t )( ffconfigMAX_FILENAME ) * sizeof( FF_T_WCHAR ) );
\r
356 token[ ffconfigMAX_FILENAME - 1 ] = '\0';
\r
358 /*token[tokenEnd - tokenStart] = '\0'; */
\r
363 #endif /* ffconfigUNICODE_UTF16_SUPPORT */
\r
365 /* UTF-8 Routines */
\r
368 UCS-4 range (hex.) UTF-8 octet sequence (binary)
\r
369 0000 0000-0000 007F 0xxxxxxx
\r
370 0000 0080-0000 07FF 110xxxxx 10xxxxxx
\r
371 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
\r
373 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
\r
374 0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
\r
375 0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
\r
378 #if ( ( ffconfigUNICODE_UTF16_SUPPORT != 0 ) && ( WCHAR_MAX > 0xFFFF ) ) || ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
379 UBaseType_t FF_GetUtf16SequenceLen( uint16_t usLeadChar )
\r
381 UBaseType_t uxReturn;
\r
382 if( ( usLeadChar & 0xFC00 ) == 0xD800 )
\r
392 } /* FF_GetUtf16SequenceLen() */
\r
394 /*-----------------------------------------------------------*/
\r
397 Returns the number of UTF-8 units read.
\r
398 Will not exceed ulSize UTF-16 units. (ulSize * 2 bytes).
\r
401 UCS-4 range (hex.) UTF-8 octet sequence (binary)
\r
402 0000 0000-0000 007F 0xxxxxxx
\r
403 0000 0080-0000 07FF 110xxxxx 10xxxxxx
\r
404 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
\r
406 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
\r
407 0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
\r
408 0400 0000-7FFF FFFF 1111110x 10xxxxxx ... 10xxxxxx -- We don't encode these because we won't receive them. (Invalid UNICODE).
\r
410 #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
411 int32_t FF_Utf8ctoUtf16c( uint16_t *utf16Dest, const uint8_t *utf8Source, uint32_t ulSize )
\r
413 uint32_t ulUtf32char;
\r
414 uint16_t utf16Source = 0;
\r
415 register int32_t lSequenceNumber = 0;
\r
417 /* Count number of set bits before a zero. */
\r
418 while( ( ( *utf8Source != '\0' ) & ( 0x80 >> ( lSequenceNumber ) ) ) )
\r
423 if( lSequenceNumber == 0UL )
\r
428 if( ulSize == 0UL )
\r
430 /* Returned value becomes an error, with the highest bit set. */
\r
431 lSequenceNumber = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF8CTOUTF16C;
\r
435 switch( lSequenceNumber )
\r
438 utf16Source = (uint16_t) *utf8Source;
\r
439 memcpy(utf16Dest,&utf16Source,sizeof(uint16_t));
\r
443 utf16Source =(uint16_t) ((*utf8Source & 0x1F) << 6) | ((*(utf8Source + 1) & 0x3F));
\r
444 memcpy(utf16Dest,&utf16Source,sizeof(uint16_t));
\r
448 utf16Source =(uint16_t) ((*utf8Source & 0x0F) << 12) | ((*(utf8Source + 1) & 0x3F) << 6) | ((*(utf8Source + 2) & 0x3F));
\r
449 memcpy(utf16Dest,&utf16Source,sizeof(uint16_t));
\r
455 /* Returned value becomes an error. */
\r
456 lSequenceNumber = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF8CTOUTF16C;
\r
460 /* Convert to UTF-32 and then into UTF-16 */
\r
461 ulUtf32char = ( uint16_t )
\r
462 ( ( *utf8Source & 0x0F ) << 18 ) |
\r
463 ( ( *( utf8Source + 1 ) & 0x3F ) << 12 ) |
\r
464 ( ( *( utf8Source + 2 ) & 0x3F ) << 6 ) |
\r
465 ( ( *( utf8Source + 3 ) & 0x3F ) );
\r
467 utf16Source = ( uint16_t ) ( ( ( ulUtf32char - 0x10000 ) & 0xFFC00 ) >> 10 ) | 0xD800;
\r
468 memcpy( utf16Dest, &utf16Source, sizeof( uint16_t ) );
\r
469 utf16Source = ( uint16_t ) ( ( ( ulUtf32char - 0x10000 ) & 0x003FF ) >> 00 ) | 0xDC00;
\r
470 memcpy( utf16Dest + 1, &utf16Source, sizeof( uint16_t ) );
\r
479 return lSequenceNumber;
\r
480 } /* FF_Utf8ctoUtf16c() */
\r
481 #endif /* ffconfigUNICODE_UTF8_SUPPORT */
\r
483 /*-----------------------------------------------------------*/
\r
486 Returns the number of UTF-8 units required to encode the UTF-16 sequence.
\r
487 Will not exceed ulSize UTF-8 units. (ulSize * 1 bytes).
\r
489 #if ( ffconfigUNICODE_UTF8_SUPPORT != 0 )
\r
490 int32_t FF_Utf16ctoUtf8c( uint8_t *utf8Dest, const uint16_t *utf16Source, uint32_t ulSize )
\r
492 uint32_t ulUtf32char;
\r
493 uint16_t ulUtf16char;
\r
494 int32_t lReturn = 0L;
\r
498 if( ulSize == 0UL )
\r
500 lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
\r
503 memcpy( &ulUtf16char, utf16Source, sizeof( uint16_t ) );
\r
505 /* A surrogate sequence was encountered. Must transform to UTF32 first. */
\r
506 if( ( ulUtf16char & 0xF800) == 0xD800 )
\r
508 ulUtf32char = ( ( uint32_t ) ( ulUtf16char & 0x003FF ) << 10 ) + 0x10000;
\r
510 memcpy( &ulUtf16char, utf16Source + 1, sizeof( uint16_t ) );
\r
511 if( ( ulUtf16char & 0xFC00 ) != 0xDC00 )
\r
513 /* Invalid UTF-16 sequence. */
\r
514 lReturn = FF_ERR_UNICODE_INVALID_SEQUENCE | FF_UTF16CTOUTF8C;
\r
517 ulUtf32char |= ( ( uint32_t ) ( ulUtf16char & 0x003FF ) );
\r
521 ulUtf32char = ( uint32_t ) ulUtf16char;
\r
524 /* Now convert to the UTF-8 sequence. */
\r
525 /* Single byte UTF-8 sequence. */
\r
526 if( ulUtf32char < 0x00000080 )
\r
528 *( utf8Dest + 0 ) = ( uint8_t )ulUtf32char;
\r
533 /* Double byte UTF-8 sequence. */
\r
534 if( ulUtf32char < 0x00000800 )
\r
538 lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
\r
542 *( utf8Dest + 0 ) = ( uint8_t ) ( 0xC0 | ( ( ulUtf32char >> 6 ) & 0x1F ) );
\r
543 *( utf8Dest + 1 ) = ( uint8_t ) ( 0x80 | ( ( ulUtf32char >> 0 ) & 0x3F ) );
\r
549 /* Triple byte UTF-8 sequence. */
\r
550 if( ulUtf32char < 0x00010000 )
\r
554 lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
\r
558 *( utf8Dest + 0 ) = ( uint8_t ) ( 0xE0 | ( ( ulUtf32char >> 12 ) & 0x0F ) );
\r
559 *( utf8Dest + 1 ) = ( uint8_t ) ( 0x80 | ( ( ulUtf32char >> 6 ) & 0x3F ) );
\r
560 *( utf8Dest + 2 ) = ( uint8_t ) ( 0x80 | ( ( ulUtf32char >> 0 ) & 0x3F ) );
\r
566 /* Quadruple byte UTF-8 sequence. */
\r
567 if( ulUtf32char < 0x00200000 )
\r
571 lReturn = FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF16CTOUTF8C;
\r
575 *( utf8Dest + 0 ) = ( uint8_t ) (0xF0 | ( ( ulUtf32char >> 18 ) & 0x07 ) );
\r
576 *( utf8Dest + 1 ) = ( uint8_t ) (0x80 | ( ( ulUtf32char >> 12 ) & 0x3F ) );
\r
577 *( utf8Dest + 2 ) = ( uint8_t ) (0x80 | ( ( ulUtf32char >> 6 ) & 0x3F ) );
\r
578 *( utf8Dest + 3 ) = ( uint8_t ) (0x80 | ( ( ulUtf32char >> 0 ) & 0x3F ) );
\r
583 lReturn = FF_ERR_UNICODE_INVALID_CODE | FF_UTF16CTOUTF8C; /* Invalid Character */
\r
588 } /* FF_Utf16ctoUtf8c() */
\r
589 #endif /* ffconfigUNICODE_UTF8_SUPPORT */
\r
590 /*-----------------------------------------------------------*/
\r
592 /* UTF-16 Support Functions */
\r
594 /* Converts a UTF-32 Character into its equivalent UTF-16 sequence. */
\r
595 #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) && ( WCHAR_MAX > 0xFFFF )
\r
596 int32_t FF_Utf32ctoUtf16c( uint16_t *utf16Dest, uint32_t utf32char, uint32_t ulSize )
\r
599 /* Check that its a valid UTF-32 wide-char! */
\r
601 /* This range is not a valid Unicode code point. */
\r
602 if( ( utf32char >= 0xD800 ) && ( utf32char <= 0xDFFF ) )
\r
604 lReturn = FF_ERR_UNICODE_INVALID_CODE | FF_UTF32CTOUTF16C; /* Invalid character. */
\r
606 else if( utf32char < 0x10000 )
\r
608 *utf16Dest = (uint16_t) utf32char; /* Simple conversion! Char comes within UTF-16 space (without surrogates). */
\r
611 else if( ulSize < 2 )
\r
613 lReturn FF_ERR_UNICODE_DEST_TOO_SMALL | FF_UTF32CTOUTF16C; /* Not enough UTF-16 units to record this character. */
\r
615 else if( utf32char < 0x00200000 )
\r
617 /* Conversion to a UTF-16 Surrogate pair! */
\r
618 /*valueImage = utf32char - 0x10000; */
\r
619 *( utf16Dest + 0 ) = ( uint16_t ) ( ( ( utf32char - 0x10000 ) & 0xFFC00 ) >> 10 ) | 0xD800;
\r
620 *( utf16Dest + 1 ) = ( uint16_t ) ( ( ( utf32char - 0x10000 ) & 0x003FF ) >> 00 ) | 0xDC00;
\r
622 lReturn = 2; /* Surrogate pair encoded value. */
\r
626 /* Invalid Character */
\r
627 lReturn = FF_ERR_UNICODE_INVALID_CODE | FF_UTF32CTOUTF16C;
\r
631 } /* FF_Utf32ctoUtf16c() */
\r
632 #endif /* #if( ffconfigUNICODE_UTF16_SUPPORT != 0 ) && ( WCHAR_MAX > 0xFFFF ) */
\r
633 /*-----------------------------------------------------------*/
\r
635 /* Converts a UTF-16 sequence into its equivalent UTF-32 code point. */
\r
636 #if( ffconfigNOT_USED_FOR_NOW != 0 )
\r
637 int32_t FF_Utf16ctoUtf32c( uint32_t *utf32Dest, const uint16_t *utf16Source )
\r
641 /*Not a surrogate sequence. */
\r
642 if( ( *utf16Source & 0xFC00 ) != 0xD800 )
\r
644 *utf32Dest = ( uint32_t )*utf16Source;
\r
645 lReturn = 1; /* A single UTF-16 item was used to represent the character. */
\r
649 *utf32Dest = ( ( uint32_t) ( * ( utf16Source + 0 ) & 0x003FF ) << 10 ) + 0x10000;
\r
650 if( ( *(utf16Source + 1) & 0xFC00 ) != 0xDC00 )
\r
652 lReturn = FF_ERR_UNICODE_INVALID_SEQUENCE | FF_UTF16CTOUTF32C; /* Invalid UTF-16 sequence. */
\r
656 *utf32Dest |= ( ( uint32_t ) ( *( utf16Source + 1 ) & 0x003FF ) );
\r
657 lReturn = 2; /* 2 utf-16 units make up the Unicode code-point. */
\r
662 } /* FF_Utf16ctoUtf32c() */
\r
663 #endif /* ffconfigNOT_USED_FOR_NOW */
\r
664 /*-----------------------------------------------------------*/
\r
667 Returns the total number of UTF-16 items required to represent
\r
668 the provided UTF-32 string in UTF-16 form.
\r
671 UBaseType_t FF_Utf32GetUtf16Len( const uint32_t *utf32String )
\r
673 UBaseType_t utf16len = 0;
\r
675 while( *utf32String )
\r
677 if( *utf32String++ <= 0xFFFF )
\r
690 /*-----------------------------------------------------------*/
\r
693 /* String conversions */
\r
695 #if( ffconfigNOT_USED_FOR_NOW != 0 )
\r
696 int32_t FF_Utf32stoUtf8s( uint8_t *Utf8String, uint32_t *Utf32String )
\r
700 uint16_t utf16buffer[ 2 ];
\r
702 while( Utf32String[ i ] != '\0' )
\r
704 /* Convert to a UTF16 char. */
\r
705 FF_Utf32ctoUtf16c( utf16buffer, Utf32String[ i ], 2 );
\r
706 /* Now convert the UTF16 to UTF8 sequence. */
\r
707 y += FF_Utf16ctoUtf8c( &Utf8String[ y ], utf16buffer, 4 );
\r
711 Utf8String[ y ] = '\0';
\r
714 } /* FF_Utf32stoUtf8s() */
\r
715 #endif /* ffconfigNOT_USED_FOR_NOW */
\r
716 /*-----------------------------------------------------------*/
\r