LCOV - code coverage report
Current view: top level - basic - utf8.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 270 301 89.7 %
Date: 2019-08-23 13:36:53 Functions: 20 20 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 159 208 76.4 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : /* Parts of this file are based on the GLIB utf8 validation functions. The
       4                 :            :  * original license text follows. */
       5                 :            : 
       6                 :            : /* gutf8.c - Operations on UTF-8 strings.
       7                 :            :  *
       8                 :            :  * Copyright (C) 1999 Tom Tromey
       9                 :            :  * Copyright (C) 2000 Red Hat, Inc.
      10                 :            :  *
      11                 :            :  * This library is free software; you can redistribute it and/or
      12                 :            :  * modify it under the terms of the GNU Library General Public
      13                 :            :  * License as published by the Free Software Foundation; either
      14                 :            :  * version 2 of the License, or (at your option) any later version.
      15                 :            :  *
      16                 :            :  * This library is distributed in the hope that it will be useful,
      17                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      19                 :            :  * Library General Public License for more details.
      20                 :            :  *
      21                 :            :  * You should have received a copy of the GNU Library General Public
      22                 :            :  * License along with this library; if not, write to the Free Software
      23                 :            :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
      24                 :            :  */
      25                 :            : 
      26                 :            : #include <errno.h>
      27                 :            : #include <stdbool.h>
      28                 :            : #include <stdlib.h>
      29                 :            : #include <string.h>
      30                 :            : 
      31                 :            : #include "alloc-util.h"
      32                 :            : #include "gunicode.h"
      33                 :            : #include "hexdecoct.h"
      34                 :            : #include "macro.h"
      35                 :            : #include "string-util.h"
      36                 :            : #include "utf8.h"
      37                 :            : 
      38                 :      31836 : bool unichar_is_valid(char32_t ch) {
      39                 :            : 
      40         [ -  + ]:      31836 :         if (ch >= 0x110000) /* End of unicode space */
      41                 :          0 :                 return false;
      42         [ -  + ]:      31836 :         if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
      43                 :          0 :                 return false;
      44   [ +  +  -  + ]:      31836 :         if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
      45                 :          0 :                 return false;
      46         [ -  + ]:      31836 :         if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
      47                 :          0 :                 return false;
      48                 :            : 
      49                 :      31836 :         return true;
      50                 :            : }
      51                 :            : 
      52                 :       1488 : static bool unichar_is_control(char32_t ch) {
      53                 :            : 
      54                 :            :         /*
      55                 :            :           0 to ' '-1 is the C0 range.
      56                 :            :           DEL=0x7F, and DEL+1 to 0x9F is C1 range.
      57                 :            :           '\t' is in C0 range, but more or less harmless and commonly used.
      58                 :            :         */
      59                 :            : 
      60   [ +  +  +  +  :       1524 :         return (ch < ' ' && !IN_SET(ch, '\t', '\n')) ||
             +  +  +  + ]
      61         [ -  + ]:         36 :                 (0x7F <= ch && ch <= 0x9F);
      62                 :            : }
      63                 :            : 
      64                 :            : /* count of characters used to encode one unicode char */
      65                 :    1306404 : static size_t utf8_encoded_expected_len(uint8_t c) {
      66         [ +  + ]:    1306404 :         if (c < 0x80)
      67                 :    1087304 :                 return 1;
      68         [ +  + ]:     219100 :         if ((c & 0xe0) == 0xc0)
      69                 :      95896 :                 return 2;
      70         [ +  + ]:     123204 :         if ((c & 0xf0) == 0xe0)
      71                 :     122444 :                 return 3;
      72         [ +  + ]:        760 :         if ((c & 0xf8) == 0xf0)
      73                 :        644 :                 return 4;
      74         [ -  + ]:        116 :         if ((c & 0xfc) == 0xf8)
      75                 :          0 :                 return 5;
      76         [ -  + ]:        116 :         if ((c & 0xfe) == 0xfc)
      77                 :          0 :                 return 6;
      78                 :            : 
      79                 :        116 :         return 0;
      80                 :            : }
      81                 :            : 
      82                 :            : /* decode one unicode char */
      83                 :     249764 : int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
      84                 :            :         char32_t unichar;
      85                 :            :         size_t len, i;
      86                 :            : 
      87         [ -  + ]:     249764 :         assert(str);
      88                 :            : 
      89                 :     249764 :         len = utf8_encoded_expected_len(str[0]);
      90                 :            : 
      91   [ +  +  +  +  :     249764 :         switch (len) {
                -  -  - ]
      92                 :      67288 :         case 1:
      93                 :      67288 :                 *ret_unichar = (char32_t)str[0];
      94                 :      67288 :                 return 0;
      95                 :      81464 :         case 2:
      96                 :      81464 :                 unichar = str[0] & 0x1f;
      97                 :      81464 :                 break;
      98                 :     100400 :         case 3:
      99                 :     100400 :                 unichar = (char32_t)str[0] & 0x0f;
     100                 :     100400 :                 break;
     101                 :        612 :         case 4:
     102                 :        612 :                 unichar = (char32_t)str[0] & 0x07;
     103                 :        612 :                 break;
     104                 :          0 :         case 5:
     105                 :          0 :                 unichar = (char32_t)str[0] & 0x03;
     106                 :          0 :                 break;
     107                 :          0 :         case 6:
     108                 :          0 :                 unichar = (char32_t)str[0] & 0x01;
     109                 :          0 :                 break;
     110                 :          0 :         default:
     111                 :          0 :                 return -EINVAL;
     112                 :            :         }
     113                 :            : 
     114         [ +  + ]:     466544 :         for (i = 1; i < len; i++) {
     115         [ +  + ]:     284092 :                 if (((char32_t)str[i] & 0xc0) != 0x80)
     116                 :         24 :                         return -EINVAL;
     117                 :            : 
     118                 :     284068 :                 unichar <<= 6;
     119                 :     284068 :                 unichar |= (char32_t)str[i] & 0x3f;
     120                 :            :         }
     121                 :            : 
     122                 :     182452 :         *ret_unichar = unichar;
     123                 :            : 
     124                 :     182452 :         return 0;
     125                 :            : }
     126                 :            : 
     127                 :       1404 : bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
     128                 :            :         const char *p;
     129                 :            : 
     130         [ -  + ]:       1404 :         assert(str);
     131                 :            : 
     132         [ +  + ]:       2588 :         for (p = str; length > 0;) {
     133                 :            :                 int encoded_len, r;
     134                 :            :                 char32_t val;
     135                 :            : 
     136                 :       1492 :                 encoded_len = utf8_encoded_valid_unichar(p, length);
     137         [ +  + ]:       1492 :                 if (encoded_len < 0)
     138                 :        308 :                         return false;
     139   [ +  -  -  + ]:       1488 :                 assert(encoded_len > 0 && (size_t) encoded_len <= length);
     140                 :            : 
     141                 :       1488 :                 r = utf8_encoded_to_unichar(p, &val);
     142   [ +  -  +  + ]:       2976 :                 if (r < 0 ||
     143                 :       1488 :                     unichar_is_control(val) ||
     144   [ -  +  #  # ]:       1184 :                     (!newline && val == '\n'))
     145                 :        304 :                         return false;
     146                 :            : 
     147                 :       1184 :                 length -= encoded_len;
     148                 :       1184 :                 p += encoded_len;
     149                 :            :         }
     150                 :            : 
     151                 :       1096 :         return true;
     152                 :            : }
     153                 :            : 
     154                 :      52464 : char *utf8_is_valid(const char *str) {
     155                 :            :         const char *p;
     156                 :            : 
     157         [ -  + ]:      52464 :         assert(str);
     158                 :            : 
     159                 :      52464 :         p = str;
     160         [ +  + ]:    1078508 :         while (*p) {
     161                 :            :                 int len;
     162                 :            : 
     163                 :    1030516 :                 len = utf8_encoded_valid_unichar(p, (size_t) -1);
     164         [ +  + ]:    1030516 :                 if (len < 0)
     165                 :       4472 :                         return NULL;
     166                 :            : 
     167                 :    1026044 :                 p += len;
     168                 :            :         }
     169                 :            : 
     170                 :      47992 :         return (char*) str;
     171                 :            : }
     172                 :            : 
     173                 :         20 : char *utf8_escape_invalid(const char *str) {
     174                 :            :         char *p, *s;
     175                 :            : 
     176         [ -  + ]:         20 :         assert(str);
     177                 :            : 
     178                 :         20 :         p = s = malloc(strlen(str) * 4 + 1);
     179         [ -  + ]:         20 :         if (!p)
     180                 :          0 :                 return NULL;
     181                 :            : 
     182         [ +  + ]:        180 :         while (*str) {
     183                 :            :                 int len;
     184                 :            : 
     185                 :        160 :                 len = utf8_encoded_valid_unichar(str, (size_t) -1);
     186         [ +  + ]:        160 :                 if (len > 0) {
     187                 :        132 :                         s = mempcpy(s, str, len);
     188                 :        132 :                         str += len;
     189                 :            :                 } else {
     190                 :         28 :                         s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
     191                 :         28 :                         str += 1;
     192                 :            :                 }
     193                 :            :         }
     194                 :            : 
     195                 :         20 :         *s = '\0';
     196                 :         20 :         (void) str_realloc(&p);
     197                 :         20 :         return p;
     198                 :            : }
     199                 :            : 
     200                 :     163096 : static int utf8_char_console_width(const char *str) {
     201                 :            :         char32_t c;
     202                 :            :         int r;
     203                 :            : 
     204                 :     163096 :         r = utf8_encoded_to_unichar(str, &c);
     205         [ +  + ]:     163096 :         if (r < 0)
     206                 :          4 :                 return r;
     207                 :            : 
     208                 :            :         /* TODO: we should detect combining characters */
     209                 :            : 
     210         [ +  + ]:     163092 :         return unichar_iswide(c) ? 2 : 1;
     211                 :            : }
     212                 :            : 
     213                 :        304 : char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
     214                 :            :         char *p, *s, *prev_s;
     215                 :        304 :         size_t n = 0; /* estimated print width */
     216                 :            : 
     217         [ -  + ]:        304 :         assert(str);
     218                 :            : 
     219         [ +  + ]:        304 :         if (console_width == 0)
     220                 :         20 :                 return strdup("");
     221                 :            : 
     222                 :        284 :         p = s = prev_s = malloc(strlen(str) * 4 + 1);
     223         [ -  + ]:        284 :         if (!p)
     224                 :          0 :                 return NULL;
     225                 :            : 
     226                 :       1440 :         for (;;) {
     227                 :            :                 int len;
     228                 :       1724 :                 char *saved_s = s;
     229                 :            : 
     230         [ +  + ]:       1724 :                 if (!*str) /* done! */
     231                 :        136 :                         goto finish;
     232                 :            : 
     233                 :       1588 :                 len = utf8_encoded_valid_unichar(str, (size_t) -1);
     234         [ +  + ]:       1588 :                 if (len > 0) {
     235         [ +  + ]:       1388 :                         if (utf8_is_printable(str, len)) {
     236                 :            :                                 int w;
     237                 :            : 
     238                 :       1084 :                                 w = utf8_char_console_width(str);
     239         [ -  + ]:       1084 :                                 assert(w >= 0);
     240         [ +  + ]:       1084 :                                 if (n + w > console_width)
     241                 :         68 :                                         goto truncation;
     242                 :            : 
     243                 :       1016 :                                 s = mempcpy(s, str, len);
     244                 :       1016 :                                 str += len;
     245                 :       1016 :                                 n += w;
     246                 :            : 
     247                 :            :                         } else {
     248         [ +  + ]:        532 :                                 for (; len > 0; len--) {
     249         [ +  + ]:        304 :                                         if (n + 4 > console_width)
     250                 :         76 :                                                 goto truncation;
     251                 :            : 
     252                 :        228 :                                         *(s++) = '\\';
     253                 :        228 :                                         *(s++) = 'x';
     254                 :        228 :                                         *(s++) = hexchar((int) *str >> 4);
     255                 :        228 :                                         *(s++) = hexchar((int) *str);
     256                 :            : 
     257                 :        228 :                                         str += 1;
     258                 :        228 :                                         n += 4;
     259                 :            :                                 }
     260                 :            :                         }
     261                 :            :                 } else {
     262         [ +  + ]:        200 :                         if (n + 1 > console_width)
     263                 :          4 :                                 goto truncation;
     264                 :            : 
     265                 :        196 :                         s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
     266                 :        196 :                         str += 1;
     267                 :        196 :                         n += 1;
     268                 :            :                 }
     269                 :            : 
     270                 :       1440 :                 prev_s = saved_s;
     271                 :            :         }
     272                 :            : 
     273                 :        148 :  truncation:
     274                 :            :         /* Try to go back one if we don't have enough space for the ellipsis */
     275         [ +  + ]:        148 :         if (n + 1 >= console_width)
     276                 :        108 :                 s = prev_s;
     277                 :            : 
     278                 :        148 :         s = mempcpy(s, "…", strlen("…"));
     279                 :            : 
     280                 :        284 :  finish:
     281                 :        284 :         *s = '\0';
     282                 :        284 :         (void) str_realloc(&p);
     283                 :        284 :         return p;
     284                 :            : }
     285                 :            : 
     286                 :        228 : char *ascii_is_valid(const char *str) {
     287                 :            :         const char *p;
     288                 :            : 
     289                 :            :         /* Check whether the string consists of valid ASCII bytes,
     290                 :            :          * i.e values between 0 and 127, inclusive. */
     291                 :            : 
     292         [ -  + ]:        228 :         assert(str);
     293                 :            : 
     294         [ +  + ]:       4348 :         for (p = str; *p; p++)
     295         [ +  + ]:       4128 :                 if ((unsigned char) *p >= 128)
     296                 :          8 :                         return NULL;
     297                 :            : 
     298                 :        220 :         return (char*) str;
     299                 :            : }
     300                 :            : 
     301                 :      13064 : char *ascii_is_valid_n(const char *str, size_t len) {
     302                 :            :         size_t i;
     303                 :            : 
     304                 :            :         /* Very similar to ascii_is_valid(), but checks exactly len
     305                 :            :          * bytes and rejects any NULs in that range. */
     306                 :            : 
     307         [ -  + ]:      13064 :         assert(str);
     308                 :            : 
     309         [ +  + ]:      54736 :         for (i = 0; i < len; i++)
     310   [ +  +  +  + ]:      50616 :                 if ((unsigned char) str[i] >= 128 || str[i] == 0)
     311                 :       8944 :                         return NULL;
     312                 :            : 
     313                 :       4120 :         return (char*) str;
     314                 :            : }
     315                 :            : 
     316                 :            : /**
     317                 :            :  * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
     318                 :            :  * @out_utf8: output buffer of at least 4 bytes or NULL
     319                 :            :  * @g: UCS-4 character to encode
     320                 :            :  *
     321                 :            :  * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
     322                 :            :  * The length of the character is returned. It is not zero-terminated! If the
     323                 :            :  * output buffer is NULL, only the length is returned.
     324                 :            :  *
     325                 :            :  * Returns: The length in bytes that the UTF-8 representation does or would
     326                 :            :  *          occupy.
     327                 :            :  */
     328                 :        548 : size_t utf8_encode_unichar(char *out_utf8, char32_t g) {
     329                 :            : 
     330         [ +  + ]:        548 :         if (g < (1 << 7)) {
     331         [ +  - ]:        288 :                 if (out_utf8)
     332                 :        288 :                         out_utf8[0] = g & 0x7f;
     333                 :        288 :                 return 1;
     334         [ +  + ]:        260 :         } else if (g < (1 << 11)) {
     335         [ +  - ]:         56 :                 if (out_utf8) {
     336                 :         56 :                         out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
     337                 :         56 :                         out_utf8[1] = 0x80 | (g & 0x3f);
     338                 :            :                 }
     339                 :         56 :                 return 2;
     340         [ +  + ]:        204 :         } else if (g < (1 << 16)) {
     341         [ +  - ]:         16 :                 if (out_utf8) {
     342                 :         16 :                         out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
     343                 :         16 :                         out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
     344                 :         16 :                         out_utf8[2] = 0x80 | (g & 0x3f);
     345                 :            :                 }
     346                 :         16 :                 return 3;
     347         [ +  - ]:        188 :         } else if (g < (1 << 21)) {
     348         [ +  - ]:        188 :                 if (out_utf8) {
     349                 :        188 :                         out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
     350                 :        188 :                         out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
     351                 :        188 :                         out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
     352                 :        188 :                         out_utf8[3] = 0x80 | (g & 0x3f);
     353                 :            :                 }
     354                 :        188 :                 return 4;
     355                 :            :         }
     356                 :            : 
     357                 :          0 :         return 0;
     358                 :            : }
     359                 :            : 
     360                 :         28 : char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
     361                 :            :         const uint8_t *f;
     362                 :            :         char *r, *t;
     363                 :            : 
     364         [ -  + ]:         28 :         assert(s);
     365                 :            : 
     366                 :            :         /* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
     367                 :            :          * take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
     368         [ -  + ]:         28 :         if (length * 2 < length)
     369                 :          0 :                 return NULL; /* overflow */
     370                 :            : 
     371                 :         28 :         r = new(char, length * 2 + 1);
     372         [ -  + ]:         28 :         if (!r)
     373                 :          0 :                 return NULL;
     374                 :            : 
     375                 :         28 :         f = (const uint8_t*) s;
     376                 :         28 :         t = r;
     377                 :            : 
     378         [ +  + ]:        180 :         while (f + 1 < (const uint8_t*) s + length) {
     379                 :            :                 char16_t w1, w2;
     380                 :            : 
     381                 :            :                 /* see RFC 2781 section 2.2 */
     382                 :            : 
     383                 :        152 :                 w1 = f[1] << 8 | f[0];
     384                 :        152 :                 f += 2;
     385                 :            : 
     386         [ +  + ]:        152 :                 if (!utf16_is_surrogate(w1)) {
     387                 :        124 :                         t += utf8_encode_unichar(t, w1);
     388                 :        124 :                         continue;
     389                 :            :                 }
     390                 :            : 
     391         [ +  + ]:         28 :                 if (utf16_is_trailing_surrogate(w1))
     392                 :          4 :                         continue; /* spurious trailing surrogate, ignore */
     393                 :            : 
     394         [ -  + ]:         24 :                 if (f + 1 >= (const uint8_t*) s + length)
     395                 :          0 :                         break;
     396                 :            : 
     397                 :         24 :                 w2 = f[1] << 8 | f[0];
     398                 :         24 :                 f += 2;
     399                 :            : 
     400         [ +  + ]:         24 :                 if (!utf16_is_trailing_surrogate(w2)) {
     401                 :          4 :                         f -= 2;
     402                 :          4 :                         continue; /* surrogate missing its trailing surrogate, ignore */
     403                 :            :                 }
     404                 :            : 
     405                 :         20 :                 t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
     406                 :            :         }
     407                 :            : 
     408                 :         28 :         *t = 0;
     409                 :         28 :         return r;
     410                 :            : }
     411                 :            : 
     412                 :         64 : size_t utf16_encode_unichar(char16_t *out, char32_t c) {
     413                 :            : 
     414                 :            :         /* Note that this encodes as little-endian. */
     415                 :            : 
     416      [ +  +  - ]:         64 :         switch (c) {
     417                 :            : 
     418                 :         48 :         case 0 ... 0xd7ffU:
     419                 :            :         case 0xe000U ... 0xffffU:
     420                 :         48 :                 out[0] = htole16(c);
     421                 :         48 :                 return 1;
     422                 :            : 
     423                 :         16 :         case 0x10000U ... 0x10ffffU:
     424                 :         16 :                 c -= 0x10000U;
     425                 :         16 :                 out[0] = htole16((c >> 10) + 0xd800U);
     426                 :         16 :                 out[1] = htole16((c & 0x3ffU) + 0xdc00U);
     427                 :         16 :                 return 2;
     428                 :            : 
     429                 :          0 :         default: /* A surrogate (invalid) */
     430                 :          0 :                 return 0;
     431                 :            :         }
     432                 :            : }
     433                 :            : 
     434                 :         24 : char16_t *utf8_to_utf16(const char *s, size_t length) {
     435                 :            :         char16_t *n, *p;
     436                 :            :         size_t i;
     437                 :            :         int r;
     438                 :            : 
     439         [ -  + ]:         24 :         assert(s);
     440                 :            : 
     441                 :         24 :         n = new(char16_t, length + 1);
     442         [ -  + ]:         24 :         if (!n)
     443                 :          0 :                 return NULL;
     444                 :            : 
     445                 :         24 :         p = n;
     446                 :            : 
     447         [ +  + ]:        152 :         for (i = 0; i < length;) {
     448                 :            :                 char32_t unichar;
     449                 :            :                 size_t e;
     450                 :            : 
     451                 :        128 :                 e = utf8_encoded_expected_len(s[i]);
     452         [ +  + ]:        128 :                 if (e <= 1) /* Invalid and single byte characters are copied as they are */
     453                 :         64 :                         goto copy;
     454                 :            : 
     455         [ -  + ]:         64 :                 if (i + e > length) /* sequence longer than input buffer, then copy as-is */
     456                 :          0 :                         goto copy;
     457                 :            : 
     458                 :         64 :                 r = utf8_encoded_to_unichar(s + i, &unichar);
     459         [ -  + ]:         64 :                 if (r < 0) /* sequence invalid, then copy as-is */
     460                 :          0 :                         goto copy;
     461                 :            : 
     462                 :         64 :                 p += utf16_encode_unichar(p, unichar);
     463                 :         64 :                 i += e;
     464                 :         64 :                 continue;
     465                 :            : 
     466                 :         64 :         copy:
     467                 :         64 :                 *(p++) = htole16(s[i++]);
     468                 :            :         }
     469                 :            : 
     470                 :         24 :         *p = 0;
     471                 :         24 :         return n;
     472                 :            : }
     473                 :            : 
     474                 :         24 : size_t char16_strlen(const char16_t *s) {
     475                 :         24 :         size_t n = 0;
     476                 :            : 
     477         [ -  + ]:         24 :         assert(s);
     478                 :            : 
     479         [ +  + ]:        168 :         while (*s != 0)
     480                 :        144 :                 n++, s++;
     481                 :            : 
     482                 :         24 :         return n;
     483                 :            : }
     484                 :            : 
     485                 :            : /* expected size used to encode one unicode char */
     486                 :      31820 : static int utf8_unichar_to_encoded_len(char32_t unichar) {
     487                 :            : 
     488         [ -  + ]:      31820 :         if (unichar < 0x80)
     489                 :          0 :                 return 1;
     490         [ +  + ]:      31820 :         if (unichar < 0x800)
     491                 :      13132 :                 return 2;
     492         [ +  + ]:      18688 :         if (unichar < 0x10000)
     493                 :      18676 :                 return 3;
     494         [ +  - ]:         12 :         if (unichar < 0x200000)
     495                 :         12 :                 return 4;
     496         [ #  # ]:          0 :         if (unichar < 0x4000000)
     497                 :          0 :                 return 5;
     498                 :            : 
     499                 :          0 :         return 6;
     500                 :            : }
     501                 :            : 
     502                 :            : /* validate one encoded unicode char and return its length */
     503                 :    1056512 : int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) {
     504                 :            :         char32_t unichar;
     505                 :            :         size_t len, i;
     506                 :            :         int r;
     507                 :            : 
     508         [ -  + ]:    1056512 :         assert(str);
     509         [ -  + ]:    1056512 :         assert(length > 0);
     510                 :            : 
     511                 :            :         /* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */
     512                 :            : 
     513                 :    1056512 :         len = utf8_encoded_expected_len(str[0]);
     514         [ +  + ]:    1056512 :         if (len == 0)
     515                 :        116 :                 return -EINVAL;
     516                 :            : 
     517                 :            :         /* Do we have a truncated multi-byte character? */
     518         [ +  + ]:    1056396 :         if (len > length)
     519                 :         24 :                 return -EINVAL;
     520                 :            : 
     521                 :            :         /* ascii is valid */
     522         [ +  + ]:    1056372 :         if (len == 1)
     523                 :    1019952 :                 return 1;
     524                 :            : 
     525                 :            :         /* check if expected encoded chars are available */
     526         [ +  + ]:     125112 :         for (i = 0; i < len; i++)
     527         [ +  + ]:      93272 :                 if ((str[i] & 0x80) != 0x80)
     528                 :       4580 :                         return -EINVAL;
     529                 :            : 
     530                 :      31840 :         r = utf8_encoded_to_unichar(str, &unichar);
     531         [ +  + ]:      31840 :         if (r < 0)
     532                 :         20 :                 return r;
     533                 :            : 
     534                 :            :         /* check if encoded length matches encoded value */
     535         [ -  + ]:      31820 :         if (utf8_unichar_to_encoded_len(unichar) != (int) len)
     536                 :          0 :                 return -EINVAL;
     537                 :            : 
     538                 :            :         /* check if value has valid range */
     539         [ -  + ]:      31820 :         if (!unichar_is_valid(unichar))
     540                 :          0 :                 return -EINVAL;
     541                 :            : 
     542                 :      31820 :         return (int) len;
     543                 :            : }
     544                 :            : 
     545                 :         24 : size_t utf8_n_codepoints(const char *str) {
     546                 :         24 :         size_t n = 0;
     547                 :            : 
     548                 :            :         /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */
     549                 :            : 
     550         [ +  + ]:        136 :         while (*str != 0) {
     551                 :            :                 int k;
     552                 :            : 
     553                 :        116 :                 k = utf8_encoded_valid_unichar(str, (size_t) -1);
     554         [ +  + ]:        116 :                 if (k < 0)
     555                 :          4 :                         return (size_t) -1;
     556                 :            : 
     557                 :        112 :                 str += k;
     558                 :        112 :                 n++;
     559                 :            :         }
     560                 :            : 
     561                 :         20 :         return n;
     562                 :            : }
     563                 :            : 
     564                 :      36600 : size_t utf8_console_width(const char *str) {
     565                 :      36600 :         size_t n = 0;
     566                 :            : 
     567                 :            :         /* Returns the approximate width a string will take on screen when printed on a character cell
     568                 :            :          * terminal/console. */
     569                 :            : 
     570         [ +  + ]:     198608 :         while (*str) {
     571                 :            :                 int w;
     572                 :            : 
     573                 :     162012 :                 w = utf8_char_console_width(str);
     574         [ +  + ]:     162012 :                 if (w < 0)
     575                 :          4 :                         return (size_t) -1;
     576                 :            : 
     577                 :     162008 :                 n += w;
     578                 :     162008 :                 str = utf8_next_char(str);
     579                 :            :         }
     580                 :            : 
     581                 :      36596 :         return n;
     582                 :            : }

Generated by: LCOV version 1.14