New modules 'unistr/u8-check', 'unistr/u16-check', 'unistr/u32-check'.
[gnulib.git] / lib / unistr / u8-check.c
1 /* Check UTF-8 string.
2    Copyright (C) 2002, 2006 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2002.
4
5    This program is free software; you can redistribute it and/or modify it
6    under the terms of the GNU Library General Public License as published
7    by the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18    USA.  */
19
20 #include <config.h>
21
22 /* Specification.  */
23 #include "unistr.h"
24
25 const uint8_t *
26 u8_check (const uint8_t *s, size_t n)
27 {
28   const uint8_t *s_end = s + n;
29
30   while (s < s_end)
31     {
32       /* Keep in sync with utf8-ucs4.h and utf8-ucs4.c.  */
33       uint8_t c = *s;
34
35       if (c < 0x80)
36         {
37           s++;
38           continue;
39         }
40       if (c >= 0xc2)
41         {
42           if (c < 0xe0)
43             {
44               if (s + 2 <= s_end
45                   && (s[1] ^ 0x80) < 0x40)
46                 {
47                   s += 2;
48                   continue;
49                 }
50             }
51           else if (c < 0xf0)
52             {
53               if (s + 3 <= s_end
54                   && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
55                   && (c >= 0xe1 || s[1] >= 0xa0)
56                   && (c != 0xed || s[1] < 0xa0))
57                 {
58                   s += 3;
59                   continue;
60                 }
61             }
62           else if (c < 0xf8)
63             {
64               if (s + 4 <= s_end
65                   && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
66                   && (s[3] ^ 0x80) < 0x40
67                   && (c >= 0xf1 || s[1] >= 0x90)
68 #if 1
69                   && (c < 0xf4 || (c == 0xf4 && s[1] < 0x90))
70 #endif
71                  )
72                 {
73                   s += 4;
74                   continue;
75                 }
76             }
77 #if 0
78           else if (c < 0xfc)
79             {
80               if (s + 5 <= s_end
81                   && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
82                   && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40
83                   && (c >= 0xf9 || s[1] >= 0x88))
84                 {
85                   s += 5;
86                   continue;
87                 }
88             }
89           else if (c < 0xfe)
90             {
91               if (s + 6 <= s_end
92                   && (s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40
93                   && (s[3] ^ 0x80) < 0x40 && (s[4] ^ 0x80) < 0x40
94                   && (s[5] ^ 0x80) < 0x40
95                   && (c >= 0xfd || s[1] >= 0x84))
96                 {
97                   s += 6;
98                   continue;
99                 }
100             }
101 #endif
102         }
103       /* invalid or incomplete multibyte character */
104       return s;
105     }
106   return NULL;
107 }