]> code.delx.au - pulseaudio/blob - src/polyp/utf8.c
Move utf8 to the public part (libpolyp).
[pulseaudio] / src / polyp / utf8.c
1 /* $Id */
2
3 /* This file is 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 Lesser 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 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <assert.h>
32 #include <stdlib.h>
33 #include <inttypes.h>
34 #include <string.h>
35
36 #include "utf8.h"
37 #include "xmalloc.h"
38
39 #define FILTER_CHAR '_'
40
41 static inline int is_unicode_valid(uint32_t ch) {
42 if (ch >= 0x110000) /* End of unicode space */
43 return 0;
44 if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
45 return 0;
46 if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
47 return 0;
48 if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
49 return 0;
50 return 1;
51 }
52
53 static inline int is_continuation_char(uint8_t ch) {
54 if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
55 return 0;
56 return 1;
57 }
58
59 static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
60 *u_ch <<= 6;
61 *u_ch |= ch & 0x3f;
62 }
63
64 static char* utf8_validate(const char *str, char *output) {
65 uint32_t val = 0;
66 uint32_t min = 0;
67 const uint8_t *p, *last;
68 int size;
69 uint8_t *o;
70
71 o = (uint8_t*) output;
72 for (p = (const uint8_t*) str; *p; p++) {
73 if (*p < 128) {
74 if (o)
75 *o = *p;
76 } else {
77 last = p;
78
79 if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
80 size = 2;
81 min = 128;
82 val = *p & 0x1e;
83 goto ONE_REMAINING;
84 } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
85 size = 3;
86 min = (1 << 11);
87 val = *p & 0x0f;
88 goto TWO_REMAINING;
89 } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
90 size = 4;
91 min = (1 << 16);
92 val = *p & 0x07;
93 } else {
94 size = 1;
95 goto error;
96 }
97
98 p++;
99 if (!is_continuation_char(*p))
100 goto error;
101 merge_continuation_char(&val, *p);
102
103 TWO_REMAINING:
104 p++;
105 if (!is_continuation_char(*p))
106 goto error;
107 merge_continuation_char(&val, *p);
108
109 ONE_REMAINING:
110 p++;
111 if (!is_continuation_char(*p))
112 goto error;
113 merge_continuation_char(&val, *p);
114
115 if (val < min)
116 goto error;
117
118 if (!is_unicode_valid(val))
119 goto error;
120
121 if (o) {
122 memcpy(o, last, size);
123 o += size - 1;
124 }
125
126 if (o)
127 o++;
128
129 continue;
130
131 error:
132 if (o) {
133 *o = FILTER_CHAR;
134 p = last; /* We retry at the next character */
135 } else
136 goto failure;
137 }
138
139 if (o)
140 o++;
141 }
142
143 if (o) {
144 *o = '\0';
145 return output;
146 }
147
148 return (char*) str;
149
150 failure:
151 return NULL;
152 }
153
154 const char* pa_utf8_valid (const char *str) {
155 return utf8_validate(str, NULL);
156 }
157
158 char* pa_utf8_filter (const char *str) {
159 char *new_str;
160
161 new_str = pa_xnew(char, strlen(str) + 1);
162
163 return utf8_validate(str, new_str);
164 }