LCOV - code coverage report
Current view: top level - basic - utf8.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 170 192 88.5 %
Date: 2015-07-29 18:47:03 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2008-2011 Kay Sievers
       7             :   Copyright 2012 Lennart Poettering
       8             : 
       9             :   systemd is free software; you can redistribute it and/or modify it
      10             :   under the terms of the GNU Lesser General Public License as published by
      11             :   the Free Software Foundation; either version 2.1 of the License, or
      12             :   (at your option) any later version.
      13             : 
      14             :   systemd is distributed in the hope that it will be useful, but
      15             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      17             :   Lesser General Public License for more details.
      18             : 
      19             :   You should have received a copy of the GNU Lesser General Public License
      20             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      21             : ***/
      22             : 
      23             : /* Parts of this file are based on the GLIB utf8 validation functions. The
      24             :  * original license text follows. */
      25             : 
      26             : /* gutf8.c - Operations on UTF-8 strings.
      27             :  *
      28             :  * Copyright (C) 1999 Tom Tromey
      29             :  * Copyright (C) 2000 Red Hat, Inc.
      30             :  *
      31             :  * This library is free software; you can redistribute it and/or
      32             :  * modify it under the terms of the GNU Library General Public
      33             :  * License as published by the Free Software Foundation; either
      34             :  * version 2 of the License, or (at your option) any later version.
      35             :  *
      36             :  * This library is distributed in the hope that it will be useful,
      37             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      38             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      39             :  * Library General Public License for more details.
      40             :  *
      41             :  * You should have received a copy of the GNU Library General Public
      42             :  * License along with this library; if not, write to the Free Software
      43             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
      44             :  */
      45             : 
      46             : #include <errno.h>
      47             : #include <stdlib.h>
      48             : #include <inttypes.h>
      49             : #include <string.h>
      50             : #include <stdbool.h>
      51             : 
      52             : #include "utf8.h"
      53             : #include "util.h"
      54             : 
      55          62 : bool unichar_is_valid(uint32_t ch) {
      56             : 
      57          62 :         if (ch >= 0x110000) /* End of unicode space */
      58           0 :                 return false;
      59          62 :         if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
      60           0 :                 return false;
      61          62 :         if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
      62           0 :                 return false;
      63          62 :         if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
      64           0 :                 return false;
      65             : 
      66          62 :         return true;
      67             : }
      68             : 
      69          64 : static bool unichar_is_control(uint32_t ch) {
      70             : 
      71             :         /*
      72             :           0 to ' '-1 is the C0 range.
      73             :           DEL=0x7F, and DEL+1 to 0x9F is C1 range.
      74             :           '\t' is in C0 range, but more or less harmless and commonly used.
      75             :         */
      76             : 
      77          73 :         return (ch < ' ' && ch != '\t' && ch != '\n') ||
      78           9 :                 (0x7F <= ch && ch <= 0x9F);
      79             : }
      80             : 
      81             : /* count of characters used to encode one unicode char */
      82       20859 : static int utf8_encoded_expected_len(const char *str) {
      83             :         unsigned char c;
      84             : 
      85       20859 :         assert(str);
      86             : 
      87       20859 :         c = (unsigned char) str[0];
      88       20859 :         if (c < 0x80)
      89       20528 :                 return 1;
      90         331 :         if ((c & 0xe0) == 0xc0)
      91          72 :                 return 2;
      92         259 :         if ((c & 0xf0) == 0xe0)
      93         210 :                 return 3;
      94          49 :         if ((c & 0xf8) == 0xf0)
      95          40 :                 return 4;
      96           9 :         if ((c & 0xfc) == 0xf8)
      97           0 :                 return 5;
      98           9 :         if ((c & 0xfe) == 0xfc)
      99           0 :                 return 6;
     100             : 
     101           9 :         return 0;
     102             : }
     103             : 
     104             : /* decode one unicode char */
     105         367 : int utf8_encoded_to_unichar(const char *str) {
     106             :         int unichar, len, i;
     107             : 
     108         367 :         assert(str);
     109             : 
     110         367 :         len = utf8_encoded_expected_len(str);
     111             : 
     112         367 :         switch (len) {
     113             :         case 1:
     114         116 :                 return (int)str[0];
     115             :         case 2:
     116          48 :                 unichar = str[0] & 0x1f;
     117          48 :                 break;
     118             :         case 3:
     119         163 :                 unichar = (int)str[0] & 0x0f;
     120         163 :                 break;
     121             :         case 4:
     122          40 :                 unichar = (int)str[0] & 0x07;
     123          40 :                 break;
     124             :         case 5:
     125           0 :                 unichar = (int)str[0] & 0x03;
     126           0 :                 break;
     127             :         case 6:
     128           0 :                 unichar = (int)str[0] & 0x01;
     129           0 :                 break;
     130             :         default:
     131           0 :                 return -EINVAL;
     132             :         }
     133             : 
     134         741 :         for (i = 1; i < len; i++) {
     135         494 :                 if (((int)str[i] & 0xc0) != 0x80)
     136           4 :                         return -EINVAL;
     137         490 :                 unichar <<= 6;
     138         490 :                 unichar |= (int)str[i] & 0x3f;
     139             :         }
     140             : 
     141         247 :         return unichar;
     142             : }
     143             : 
     144          43 : bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
     145             :         const char *p;
     146             : 
     147          43 :         assert(str);
     148             : 
     149         141 :         for (p = str; length;) {
     150             :                 int encoded_len, val;
     151             : 
     152          65 :                 encoded_len = utf8_encoded_valid_unichar(p);
     153         129 :                 if (encoded_len < 0 ||
     154          64 :                     (size_t) encoded_len > length)
     155           1 :                         return false;
     156             : 
     157          64 :                 val = utf8_encoded_to_unichar(p);
     158         128 :                 if (val < 0 ||
     159         119 :                     unichar_is_control(val) ||
     160          55 :                     (!newline && val == '\n'))
     161           9 :                         return false;
     162             : 
     163          55 :                 length -= encoded_len;
     164          55 :                 p += encoded_len;
     165             :         }
     166             : 
     167          33 :         return true;
     168             : }
     169             : 
     170         832 : const char *utf8_is_valid(const char *str) {
     171             :         const uint8_t *p;
     172             : 
     173         832 :         assert(str);
     174             : 
     175       21780 :         for (p = (const uint8_t*) str; *p; ) {
     176             :                 int len;
     177             : 
     178       20117 :                 len = utf8_encoded_valid_unichar((const char *)p);
     179       20117 :                 if (len < 0)
     180           1 :                         return NULL;
     181             : 
     182       20116 :                 p += len;
     183             :         }
     184             : 
     185         831 :         return str;
     186             : }
     187             : 
     188           7 : char *utf8_escape_invalid(const char *str) {
     189             :         char *p, *s;
     190             : 
     191           7 :         assert(str);
     192             : 
     193           7 :         p = s = malloc(strlen(str) * 4 + 1);
     194           7 :         if (!p)
     195           0 :                 return NULL;
     196             : 
     197          91 :         while (*str) {
     198             :                 int len;
     199             : 
     200          77 :                 len = utf8_encoded_valid_unichar(str);
     201          77 :                 if (len > 0) {
     202          71 :                         s = mempcpy(s, str, len);
     203          71 :                         str += len;
     204             :                 } else {
     205           6 :                         s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
     206           6 :                         str += 1;
     207             :                 }
     208             :         }
     209             : 
     210           7 :         *s = '\0';
     211             : 
     212           7 :         return p;
     213             : }
     214             : 
     215           6 : char *utf8_escape_non_printable(const char *str) {
     216             :         char *p, *s;
     217             : 
     218           6 :         assert(str);
     219             : 
     220           6 :         p = s = malloc(strlen(str) * 4 + 1);
     221           6 :         if (!p)
     222           0 :                 return NULL;
     223             : 
     224          63 :         while (*str) {
     225             :                 int len;
     226             : 
     227          51 :                 len = utf8_encoded_valid_unichar(str);
     228          51 :                 if (len > 0) {
     229          39 :                         if (utf8_is_printable(str, len)) {
     230          30 :                                 s = mempcpy(s, str, len);
     231          30 :                                 str += len;
     232             :                         } else {
     233          27 :                                 while (len > 0) {
     234           9 :                                         *(s++) = '\\';
     235           9 :                                         *(s++) = 'x';
     236           9 :                                         *(s++) = hexchar((int) *str >> 4);
     237           9 :                                         *(s++) = hexchar((int) *str);
     238             : 
     239           9 :                                         str += 1;
     240           9 :                                         len --;
     241             :                                 }
     242             :                         }
     243             :                 } else {
     244          12 :                         s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
     245          12 :                         str += 1;
     246             :                 }
     247             :         }
     248             : 
     249           6 :         *s = '\0';
     250             : 
     251           6 :         return p;
     252             : }
     253             : 
     254          79 : char *ascii_is_valid(const char *str) {
     255             :         const char *p;
     256             : 
     257          79 :         assert(str);
     258             : 
     259        1879 :         for (p = str; *p; p++)
     260        1808 :                 if ((unsigned char) *p >= 128)
     261           8 :                         return NULL;
     262             : 
     263          71 :         return (char*) str;
     264             : }
     265             : 
     266             : /**
     267             :  * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
     268             :  * @out_utf8: output buffer of at least 4 bytes or NULL
     269             :  * @g: UCS-4 character to encode
     270             :  *
     271             :  * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
     272             :  * The length of the character is returned. It is not zero-terminated! If the
     273             :  * output buffer is NULL, only the length is returned.
     274             :  *
     275             :  * Returns: The length in bytes that the UTF-8 representation does or would
     276             :  *          occupy.
     277             :  */
     278          13 : size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
     279             : 
     280          13 :         if (g < (1 << 7)) {
     281           3 :                 if (out_utf8)
     282           3 :                         out_utf8[0] = g & 0x7f;
     283           3 :                 return 1;
     284          10 :         } else if (g < (1 << 11)) {
     285           5 :                 if (out_utf8) {
     286           5 :                         out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
     287           5 :                         out_utf8[1] = 0x80 | (g & 0x3f);
     288             :                 }
     289           5 :                 return 2;
     290           5 :         } else if (g < (1 << 16)) {
     291           1 :                 if (out_utf8) {
     292           1 :                         out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
     293           1 :                         out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
     294           1 :                         out_utf8[2] = 0x80 | (g & 0x3f);
     295             :                 }
     296           1 :                 return 3;
     297           4 :         } else if (g < (1 << 21)) {
     298           4 :                 if (out_utf8) {
     299           4 :                         out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
     300           4 :                         out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
     301           4 :                         out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
     302           4 :                         out_utf8[3] = 0x80 | (g & 0x3f);
     303             :                 }
     304           4 :                 return 4;
     305             :         }
     306             : 
     307           0 :         return 0;
     308             : }
     309             : 
     310           1 : char *utf16_to_utf8(const void *s, size_t length) {
     311             :         const uint8_t *f;
     312             :         char *r, *t;
     313             : 
     314           1 :         r = new(char, (length * 4 + 1) / 2 + 1);
     315           1 :         if (!r)
     316           0 :                 return NULL;
     317             : 
     318           1 :         f = s;
     319           1 :         t = r;
     320             : 
     321           8 :         while (f < (const uint8_t*) s + length) {
     322             :                 uint16_t w1, w2;
     323             : 
     324             :                 /* see RFC 2781 section 2.2 */
     325             : 
     326           6 :                 w1 = f[1] << 8 | f[0];
     327           6 :                 f += 2;
     328             : 
     329           6 :                 if (!utf16_is_surrogate(w1)) {
     330           3 :                         t += utf8_encode_unichar(t, w1);
     331             : 
     332           3 :                         continue;
     333             :                 }
     334             : 
     335           3 :                 if (utf16_is_trailing_surrogate(w1))
     336           1 :                         continue;
     337           2 :                 else if (f >= (const uint8_t*) s + length)
     338           0 :                         break;
     339             : 
     340           2 :                 w2 = f[1] << 8 | f[0];
     341           2 :                 f += 2;
     342             : 
     343           2 :                 if (!utf16_is_trailing_surrogate(w2)) {
     344           1 :                         f -= 2;
     345           1 :                         continue;
     346             :                 }
     347             : 
     348           1 :                 t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
     349             :         }
     350             : 
     351           1 :         *t = 0;
     352           1 :         return r;
     353             : }
     354             : 
     355             : /* expected size used to encode one unicode char */
     356          62 : static int utf8_unichar_to_encoded_len(int unichar) {
     357             : 
     358          62 :         if (unichar < 0x80)
     359           4 :                 return 1;
     360          58 :         if (unichar < 0x800)
     361          24 :                 return 2;
     362          34 :         if (unichar < 0x10000)
     363          34 :                 return 3;
     364           0 :         if (unichar < 0x200000)
     365           0 :                 return 4;
     366           0 :         if (unichar < 0x4000000)
     367           0 :                 return 5;
     368             : 
     369           0 :         return 6;
     370             : }
     371             : 
     372             : /* validate one encoded unicode char and return its length */
     373       20492 : int utf8_encoded_valid_unichar(const char *str) {
     374             :         int len, unichar, i;
     375             : 
     376       20492 :         assert(str);
     377             : 
     378       20492 :         len = utf8_encoded_expected_len(str);
     379       20492 :         if (len == 0)
     380           9 :                 return -EINVAL;
     381             : 
     382             :         /* ascii is valid */
     383       20483 :         if (len == 1)
     384       20412 :                 return 1;
     385             : 
     386             :         /* check if expected encoded chars are available */
     387         251 :         for (i = 0; i < len; i++)
     388         189 :                 if ((str[i] & 0x80) != 0x80)
     389           9 :                         return -EINVAL;
     390             : 
     391          62 :         unichar = utf8_encoded_to_unichar(str);
     392             : 
     393             :         /* check if encoded length matches encoded value */
     394          62 :         if (utf8_unichar_to_encoded_len(unichar) != len)
     395           4 :                 return -EINVAL;
     396             : 
     397             :         /* check if value has valid range */
     398          58 :         if (!unichar_is_valid(unichar))
     399           0 :                 return -EINVAL;
     400             : 
     401          58 :         return len;
     402             : }

Generated by: LCOV version 1.11