|
dvander@27
|
1 |
/* Codepage translation to Unicode, and UTF-8 support |
|
dvander@27
|
2 |
* |
|
dvander@27
|
3 |
* The translation is based on codepage mapping files that are distributed |
|
dvander@27
|
4 |
* by the Unicode consortium, see ftp://ftp.unicode.org/Public/MAPPINGS/. |
|
dvander@27
|
5 |
* |
|
dvander@27
|
6 |
* Character sets with a maximum of 256 codes are translated via a lookup |
|
dvander@27
|
7 |
* table (these are Single-Byte Character Sets). Character sets like Shift-JIS |
|
dvander@27
|
8 |
* with single-byte characters and multi-byte characters (introduced by a |
|
dvander@27
|
9 |
* leader byte) are split into two tables: the 256-entry lookup table for |
|
dvander@27
|
10 |
* the single-byte characters and an extended table for the multi-byte |
|
dvander@27
|
11 |
* characters. The extended table is allocated dynamically; the lookup table |
|
dvander@27
|
12 |
* is allocated statically, so loading SBCS tables cannot fail (if the tables |
|
dvander@27
|
13 |
* themselves are valid, of course). |
|
dvander@27
|
14 |
* |
|
dvander@27
|
15 |
* Copyright (c) ITB CompuPhase, 2004-2006 |
|
dvander@27
|
16 |
* |
|
dvander@27
|
17 |
* This software is provided "as-is", without any express or implied warranty. |
|
dvander@27
|
18 |
* In no event will the authors be held liable for any damages arising from |
|
dvander@27
|
19 |
* the use of this software. |
|
dvander@27
|
20 |
* |
|
dvander@27
|
21 |
* Permission is granted to anyone to use this software for any purpose, |
|
dvander@27
|
22 |
* including commercial applications, and to alter it and redistribute it |
|
dvander@27
|
23 |
* freely, subject to the following restrictions: |
|
dvander@27
|
24 |
* |
|
dvander@27
|
25 |
* 1. The origin of this software must not be misrepresented; you must not |
|
dvander@27
|
26 |
* claim that you wrote the original software. If you use this software in |
|
dvander@27
|
27 |
* a product, an acknowledgment in the product documentation would be |
|
dvander@27
|
28 |
* appreciated but is not required. |
|
dvander@27
|
29 |
* 2. Altered source versions must be plainly marked as such, and must not be |
|
dvander@27
|
30 |
* misrepresented as being the original software. |
|
dvander@27
|
31 |
* 3. This notice may not be removed or altered from any source distribution. |
|
dvander@27
|
32 |
* |
|
dvander@365
|
33 |
* Version: $Id$ |
|
dvander@27
|
34 |
*/ |
|
dvander@27
|
35 |
#include <assert.h> |
|
dvander@27
|
36 |
#include <stdio.h> |
|
dvander@27
|
37 |
#include <stddef.h> |
|
dvander@27
|
38 |
#include <stdlib.h> |
|
dvander@27
|
39 |
#include <string.h> |
|
dvander@27
|
40 |
#include "sc.h" |
|
dvander@27
|
41 |
|
|
dvander@27
|
42 |
#if !defined TRUE |
|
dvander@27
|
43 |
#define FALSE 0 |
|
dvander@27
|
44 |
#define TRUE 1 |
|
dvander@27
|
45 |
#endif |
|
dvander@27
|
46 |
#if !defined _MAX_PATH |
|
dvander@27
|
47 |
#define _MAX_PATH 250 |
|
dvander@27
|
48 |
#endif |
|
dvander@27
|
49 |
#if !defined DIRSEP_CHAR |
|
dvander@27
|
50 |
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ |
|
dvander@27
|
51 |
#define DIRSEP_CHAR '/' |
|
dvander@27
|
52 |
#elif defined macintosh |
|
dvander@27
|
53 |
#define DIRSEP_CHAR ':' |
|
dvander@27
|
54 |
#else |
|
dvander@27
|
55 |
#define DIRSEP_CHAR '\\' |
|
dvander@27
|
56 |
#endif |
|
dvander@27
|
57 |
#endif |
|
dvander@27
|
58 |
|
|
dvander@27
|
59 |
#if !defined ELEMENTS |
|
dvander@27
|
60 |
#define ELEMENTS(array) (sizeof(array) / sizeof(array[0])) |
|
dvander@27
|
61 |
#endif |
|
dvander@27
|
62 |
|
|
dvander@27
|
63 |
#if !defined NO_CODEPAGE |
|
dvander@27
|
64 |
|
|
dvander@27
|
65 |
#if !defined MAXCODEPAGE |
|
dvander@27
|
66 |
#define MAXCODEPAGE 12 /* typically "cp" + 4 digits + ".txt" */ |
|
dvander@27
|
67 |
#endif |
|
dvander@27
|
68 |
#define INVALID 0xffffu /* 0xffff and 0xfffe are invalid Unicode characters */ |
|
dvander@27
|
69 |
#define LEADBYTE 0xfffeu |
|
dvander@27
|
70 |
|
|
dvander@27
|
71 |
struct wordpair { |
|
dvander@27
|
72 |
unsigned short index; |
|
dvander@27
|
73 |
wchar_t code; |
|
dvander@27
|
74 |
}; |
|
dvander@27
|
75 |
static char cprootpath[_MAX_PATH] = { DIRSEP_CHAR, '\0' }; |
|
dvander@27
|
76 |
static wchar_t bytetable[256]; |
|
dvander@27
|
77 |
static struct wordpair *wordtable = NULL; |
|
dvander@27
|
78 |
static unsigned wordtablesize = 0; |
|
dvander@27
|
79 |
static unsigned wordtabletop = 0; |
|
dvander@27
|
80 |
|
|
dvander@27
|
81 |
|
|
dvander@27
|
82 |
/* read in a line delimited by '\r' or '\n'; do NOT store the '\r' or '\n' into |
|
dvander@27
|
83 |
* the string and ignore empty lines |
|
dvander@27
|
84 |
* returns 1 for success and 0 for failure |
|
dvander@27
|
85 |
*/ |
|
dvander@27
|
86 |
static int cp_readline(FILE *fp,char *string,size_t size) |
|
dvander@27
|
87 |
{ |
|
dvander@27
|
88 |
size_t count=0; |
|
dvander@27
|
89 |
int c; |
|
dvander@27
|
90 |
assert(size>1); |
|
dvander@27
|
91 |
while ((c=fgetc(fp))!=EOF && count<size-1) { |
|
dvander@27
|
92 |
if (c=='\r' || c=='\n') { |
|
dvander@27
|
93 |
if (count>0) /* '\r' or '\n' ends a string */ |
|
dvander@27
|
94 |
break; |
|
dvander@27
|
95 |
/* if count==0, the line started with a '\r' or '\n', or perhaps line |
|
dvander@27
|
96 |
* ends in the file are '\r\n' and we read and stopped on the '\r' of |
|
dvander@27
|
97 |
* the preceding line |
|
dvander@27
|
98 |
*/ |
|
dvander@27
|
99 |
} else { |
|
dvander@27
|
100 |
string[count++]=(char)c; |
|
dvander@27
|
101 |
} /* if */ |
|
dvander@27
|
102 |
} /* while */ |
|
dvander@27
|
103 |
string[count]='\0'; |
|
dvander@27
|
104 |
return count>0; |
|
dvander@27
|
105 |
} |
|
dvander@27
|
106 |
|
|
dvander@27
|
107 |
/* cp_path() sets the directory where all codepage files must be found (if |
|
dvander@27
|
108 |
* the parameter to cp_set() specifies a full path, that is used instead). |
|
dvander@27
|
109 |
* The path is specified into two parts: root and directory; the full path |
|
dvander@27
|
110 |
* for the codepage direcory is just the concatenation of the two, with a |
|
dvander@27
|
111 |
* directory separator in between. The directory is given in two parts, |
|
dvander@27
|
112 |
* because often a program already retrieves its "home" directory and the |
|
dvander@27
|
113 |
* codepages are most conveniently stored in a subdirectory of this home |
|
dvander@27
|
114 |
* directory. |
|
dvander@27
|
115 |
*/ |
|
dvander@27
|
116 |
SC_FUNC int cp_path(const char *root, const char *directory) |
|
dvander@27
|
117 |
{ |
|
dvander@27
|
118 |
size_t len1,len2; |
|
dvander@27
|
119 |
int add_slash1,add_slash2; |
|
dvander@27
|
120 |
|
|
dvander@27
|
121 |
len1= (root!=NULL) ? strlen(root) : 0; |
|
dvander@27
|
122 |
add_slash1= (len1==0 || root[len1-1]!=DIRSEP_CHAR); |
|
dvander@27
|
123 |
len2= (directory!=NULL) ? strlen(directory) : 0; |
|
dvander@27
|
124 |
add_slash2= (len2>0 && root[len2-1]!=DIRSEP_CHAR); |
|
dvander@27
|
125 |
if (len1+add_slash1+len2+add_slash2>=(_MAX_PATH-MAXCODEPAGE)) |
|
dvander@27
|
126 |
return FALSE; /* full filename may not fit */ |
|
dvander@27
|
127 |
if (root!=NULL) |
|
dvander@27
|
128 |
strcpy(cprootpath,root); |
|
dvander@27
|
129 |
if (add_slash1) { |
|
dvander@27
|
130 |
assert(len1==0 || cprootpath[len1]=='\0'); |
|
dvander@27
|
131 |
cprootpath[len1]=DIRSEP_CHAR; |
|
dvander@27
|
132 |
cprootpath[len1+1]='\0'; |
|
dvander@27
|
133 |
} /* if */ |
|
dvander@27
|
134 |
if (directory!=NULL) |
|
dvander@27
|
135 |
strcat(cprootpath,directory); |
|
dvander@27
|
136 |
if (add_slash2) { |
|
dvander@27
|
137 |
assert(cprootpath[len1+add_slash1+len2]=='\0'); |
|
dvander@27
|
138 |
cprootpath[len1+add_slash1+len2]=DIRSEP_CHAR; |
|
dvander@27
|
139 |
cprootpath[len1+add_slash1+len2+1]='\0'; |
|
dvander@27
|
140 |
} /* if */ |
|
dvander@27
|
141 |
cp_set(NULL); /* start with a "linear" table (no translation) */ |
|
dvander@27
|
142 |
return TRUE; |
|
dvander@27
|
143 |
} |
|
dvander@27
|
144 |
|
|
dvander@27
|
145 |
/* cp_set() loads a codepage from a file. The name parameter may be a |
|
dvander@27
|
146 |
* filename (including a full path) or it may be a partial codepage name. |
|
dvander@27
|
147 |
* If the name parameter is NULL, the codepage is cleared to be a "linear" |
|
dvander@27
|
148 |
* table (no translation). |
|
dvander@27
|
149 |
* The following files are attempted to open (where <name> specifies the |
|
dvander@27
|
150 |
* value of the parameter): |
|
dvander@27
|
151 |
* <name> |
|
dvander@27
|
152 |
* <cprootpath>/<name> |
|
dvander@27
|
153 |
* <cprootpath>/<name>.txt |
|
dvander@27
|
154 |
* <cprootpath>/cp<name> |
|
dvander@27
|
155 |
* <cprootpath>/cp<name>.txt |
|
dvander@27
|
156 |
*/ |
|
dvander@27
|
157 |
SC_FUNC int cp_set(const char *name) |
|
dvander@27
|
158 |
{ |
|
dvander@27
|
159 |
char filename[_MAX_PATH]; |
|
dvander@27
|
160 |
FILE *fp=NULL; |
|
dvander@27
|
161 |
unsigned index; |
|
dvander@27
|
162 |
|
|
dvander@27
|
163 |
/* for name==NULL, set up an identity table */ |
|
dvander@27
|
164 |
if (name==NULL || *name=='\0') { |
|
dvander@27
|
165 |
if (wordtable!=NULL) { |
|
dvander@27
|
166 |
free(wordtable); |
|
dvander@27
|
167 |
wordtable=NULL; |
|
dvander@27
|
168 |
wordtablesize=0; |
|
dvander@27
|
169 |
wordtabletop=0; |
|
dvander@27
|
170 |
} /* if */ |
|
dvander@27
|
171 |
for (index=0; index<ELEMENTS(bytetable); index++) |
|
dvander@27
|
172 |
bytetable[index]=(wchar_t)index; |
|
dvander@27
|
173 |
return TRUE; |
|
dvander@27
|
174 |
} /* if */ |
|
dvander@27
|
175 |
|
|
dvander@27
|
176 |
/* try to open the file as-is */ |
|
dvander@27
|
177 |
if (strchr(name,DIRSEP_CHAR)!=NULL) |
|
dvander@27
|
178 |
fp=fopen(name,"rt"); |
|
dvander@27
|
179 |
if (fp==NULL) { |
|
dvander@27
|
180 |
/* try opening the file in the "root path" for codepages */ |
|
dvander@27
|
181 |
if (strlen(name)>MAXCODEPAGE) |
|
dvander@27
|
182 |
return 0; |
|
dvander@27
|
183 |
assert(strlen(name)+strlen(cprootpath)<_MAX_PATH); |
|
dvander@27
|
184 |
strcpy(filename,cprootpath); |
|
dvander@27
|
185 |
strcat(filename,name); |
|
dvander@27
|
186 |
fp=fopen(filename,"rt"); |
|
dvander@27
|
187 |
} /* if */ |
|
dvander@27
|
188 |
if (fp==NULL) { |
|
dvander@27
|
189 |
/* try opening the file in the "root path" for codepages, with a ".txt" extension */ |
|
dvander@27
|
190 |
if (strlen(name)+4>=MAXCODEPAGE) |
|
dvander@27
|
191 |
return 0; |
|
dvander@27
|
192 |
assert(strlen(filename)+4<_MAX_PATH); |
|
dvander@27
|
193 |
strcat(filename,".txt"); |
|
dvander@27
|
194 |
fp=fopen(filename,"rt"); |
|
dvander@27
|
195 |
} /* if */ |
|
dvander@27
|
196 |
if (fp==NULL) { |
|
dvander@27
|
197 |
/* try opening the file in the "root path" for codepages, with "cp" prefixed before the name */ |
|
dvander@27
|
198 |
if (strlen(name)+2>MAXCODEPAGE) |
|
dvander@27
|
199 |
return 0; |
|
dvander@27
|
200 |
assert(2+strlen(name)+strlen(cprootpath)<_MAX_PATH); |
|
dvander@27
|
201 |
strcpy(filename,cprootpath); |
|
dvander@27
|
202 |
strcat(filename,"cp"); |
|
dvander@27
|
203 |
strcat(filename,name); |
|
dvander@27
|
204 |
fp=fopen(filename,"rt"); |
|
dvander@27
|
205 |
} /* if */ |
|
dvander@27
|
206 |
if (fp==NULL) { |
|
dvander@27
|
207 |
/* try opening the file in the "root path" for codepages, with "cp" prefixed an ".txt" appended */ |
|
dvander@27
|
208 |
if (strlen(name)+2+4>MAXCODEPAGE) |
|
dvander@27
|
209 |
return 0; |
|
dvander@27
|
210 |
assert(strlen(filename)+4<_MAX_PATH); |
|
dvander@27
|
211 |
strcat(filename,".txt"); |
|
dvander@27
|
212 |
fp=fopen(filename,"rt"); |
|
dvander@27
|
213 |
} /* if */ |
|
dvander@27
|
214 |
if (fp==NULL) |
|
dvander@27
|
215 |
return FALSE; /* all failed */ |
|
dvander@27
|
216 |
|
|
dvander@27
|
217 |
/* clear the tables */ |
|
dvander@27
|
218 |
for (index=0; index<ELEMENTS(bytetable); index++) |
|
dvander@27
|
219 |
bytetable[index]=INVALID; /* special code meaning "not found" */ |
|
dvander@27
|
220 |
assert(wordtablesize==0 && wordtabletop==0 && wordtable==NULL |
|
dvander@27
|
221 |
|| wordtablesize>0 && wordtable!=NULL); |
|
dvander@27
|
222 |
if (wordtable!=NULL) { |
|
dvander@27
|
223 |
free(wordtable); |
|
dvander@27
|
224 |
wordtable=NULL; |
|
dvander@27
|
225 |
wordtablesize=0; |
|
dvander@27
|
226 |
wordtabletop=0; |
|
dvander@27
|
227 |
} /* if */ |
|
dvander@27
|
228 |
|
|
dvander@27
|
229 |
/* read in the table */ |
|
dvander@27
|
230 |
while (cp_readline(fp,filename,sizeof filename)) { |
|
dvander@27
|
231 |
char *ptr; |
|
dvander@27
|
232 |
if ((ptr=strchr(filename,'#'))!=NULL) |
|
dvander@27
|
233 |
*ptr='\0'; /* strip of comment */ |
|
dvander@27
|
234 |
for (ptr=filename; *ptr>0 && *ptr<' '; ptr++) |
|
dvander@27
|
235 |
/* nothing */; /* skip leading whitespace */ |
|
dvander@27
|
236 |
if (*ptr!='\0') { |
|
dvander@27
|
237 |
/* content on line */ |
|
dvander@27
|
238 |
unsigned code=LEADBYTE; |
|
dvander@27
|
239 |
int num=sscanf(ptr,"%i %i",&index,&code); |
|
dvander@27
|
240 |
/* if sscanf() returns 1 and the index is in range 0..255, then the |
|
dvander@27
|
241 |
* code is a DBCS lead byte; if sscanf() returns 2 and index>=256, this |
|
dvander@27
|
242 |
* is a double byte pair (lead byte + follower) |
|
dvander@27
|
243 |
*/ |
|
dvander@27
|
244 |
if (num>=1 && index<256) { |
|
dvander@27
|
245 |
bytetable[index]=(wchar_t)code; |
|
dvander@27
|
246 |
} else if (num==2 && index>=256 && index<LEADBYTE) { |
|
dvander@27
|
247 |
/* store the DBCS character in wordtable */ |
|
dvander@27
|
248 |
if (wordtabletop>=wordtablesize) { |
|
dvander@27
|
249 |
/* grow the list */ |
|
dvander@27
|
250 |
int newsize; |
|
dvander@27
|
251 |
struct wordpair *newblock; |
|
dvander@27
|
252 |
newsize= (wordtablesize==0) ? 128 : 2*wordtablesize; |
|
dvander@27
|
253 |
newblock=(struct wordpair *)malloc(newsize*sizeof(*wordtable)); |
|
dvander@27
|
254 |
if (newblock!=NULL) { |
|
dvander@27
|
255 |
memcpy(newblock,wordtable,wordtabletop*sizeof(*wordtable)); |
|
dvander@27
|
256 |
free(wordtable); |
|
dvander@27
|
257 |
wordtable=newblock; |
|
dvander@27
|
258 |
wordtablesize=newsize; |
|
dvander@27
|
259 |
} /* if */ |
|
dvander@27
|
260 |
} /* if */ |
|
dvander@27
|
261 |
if (wordtabletop<wordtablesize) { |
|
dvander@27
|
262 |
/* insert at sorted position */ |
|
dvander@27
|
263 |
int pos=wordtabletop; |
|
dvander@27
|
264 |
assert(wordtable!=NULL); |
|
dvander@27
|
265 |
while (pos>0 && (unsigned)wordtable[pos-1].index>index) { |
|
dvander@27
|
266 |
wordtable[pos]=wordtable[pos-1]; |
|
dvander@27
|
267 |
pos--; |
|
dvander@27
|
268 |
} /* while */ |
|
dvander@27
|
269 |
wordtable[pos].index=(unsigned short)index; |
|
dvander@27
|
270 |
wordtable[pos].code=(wchar_t)code; |
|
dvander@27
|
271 |
} /* if */ |
|
dvander@27
|
272 |
} /* if */ |
|
dvander@27
|
273 |
} /* if */ |
|
dvander@27
|
274 |
} /* while */ |
|
dvander@27
|
275 |
|
|
dvander@27
|
276 |
fclose(fp); |
|
dvander@27
|
277 |
return TRUE; |
|
dvander@27
|
278 |
} |
|
dvander@27
|
279 |
|
|
dvander@27
|
280 |
SC_FUNC cell cp_translate(const unsigned char *string,const unsigned char **endptr) |
|
dvander@27
|
281 |
{ |
|
dvander@27
|
282 |
wchar_t result; |
|
dvander@27
|
283 |
|
|
dvander@27
|
284 |
result=bytetable[*string++]; |
|
dvander@27
|
285 |
/* check whether this is a leader code */ |
|
dvander@27
|
286 |
if ((unsigned)result==LEADBYTE && wordtable!=NULL) { |
|
dvander@27
|
287 |
/* look up the code via binary search */ |
|
dvander@27
|
288 |
int low,high,mid; |
|
dvander@27
|
289 |
unsigned short index=(unsigned short)(((*(string-1)) << 8) | *string); |
|
dvander@27
|
290 |
string++; |
|
dvander@27
|
291 |
assert(wordtabletop>0); |
|
dvander@27
|
292 |
low=0; |
|
dvander@27
|
293 |
high=wordtabletop-1; |
|
dvander@27
|
294 |
while (low<high) { |
|
dvander@27
|
295 |
mid=(low+high)/2; |
|
dvander@27
|
296 |
assert(low<=mid && mid<high); |
|
dvander@27
|
297 |
if (index>wordtable[mid].index) |
|
dvander@27
|
298 |
low=mid+1; |
|
dvander@27
|
299 |
else |
|
dvander@27
|
300 |
high=mid; |
|
dvander@27
|
301 |
} /* while */ |
|
dvander@27
|
302 |
assert(low==high); |
|
dvander@27
|
303 |
if (wordtable[low].index==index) |
|
dvander@27
|
304 |
result=wordtable[low].code; |
|
dvander@27
|
305 |
} /* if */ |
|
dvander@27
|
306 |
|
|
dvander@27
|
307 |
if (endptr!=NULL) |
|
dvander@27
|
308 |
*endptr=string; |
|
dvander@27
|
309 |
return (cell)result; |
|
dvander@27
|
310 |
} |
|
dvander@27
|
311 |
|
|
dvander@27
|
312 |
#endif /* NO_CODEPAGE */ |
|
dvander@27
|
313 |
|
|
dvander@27
|
314 |
#if !defined NO_UTF8 |
|
dvander@27
|
315 |
SC_FUNC cell get_utf8_char(const unsigned char *string,const unsigned char **endptr) |
|
dvander@27
|
316 |
{ |
|
dvander@27
|
317 |
int follow=0; |
|
dvander@27
|
318 |
long lowmark=0; |
|
dvander@27
|
319 |
unsigned char ch; |
|
dvander@27
|
320 |
cell result=0; |
|
dvander@27
|
321 |
|
|
dvander@27
|
322 |
if (endptr!=NULL) |
|
dvander@27
|
323 |
*endptr=string; |
|
dvander@27
|
324 |
|
|
dvander@27
|
325 |
for ( ;; ) { |
|
dvander@27
|
326 |
ch=*string++; |
|
dvander@27
|
327 |
|
|
dvander@27
|
328 |
if (follow>0 && (ch & 0xc0)==0x80) { |
|
dvander@27
|
329 |
/* leader code is active, combine with earlier code */ |
|
dvander@27
|
330 |
result=(result << 6) | (ch & 0x3f); |
|
dvander@27
|
331 |
if (--follow==0) { |
|
dvander@27
|
332 |
/* encoding a character in more bytes than is strictly needed, |
|
dvander@27
|
333 |
* is not really valid UTF-8; we are strict here to increase |
|
dvander@27
|
334 |
* the chance of heuristic dectection of non-UTF-8 text |
|
dvander@27
|
335 |
* (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) |
|
dvander@27
|
336 |
*/ |
|
dvander@27
|
337 |
if (result<lowmark) |
|
dvander@27
|
338 |
return -1; |
|
dvander@27
|
339 |
/* the code positions 0xd800--0xdfff and 0xfffe & 0xffff do not |
|
dvander@27
|
340 |
* exist in UCS-4 (and hence, they do not exist in Unicode) |
|
dvander@27
|
341 |
*/ |
|
dvander@27
|
342 |
if (result>=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff) |
|
dvander@27
|
343 |
return -1; |
|
dvander@27
|
344 |
} /* if */ |
|
dvander@27
|
345 |
break; |
|
dvander@27
|
346 |
} else if (follow==0 && (ch & 0x80)==0x80) { |
|
dvander@27
|
347 |
/* UTF-8 leader code */ |
|
dvander@27
|
348 |
if ((ch & 0xe0)==0xc0) { |
|
dvander@27
|
349 |
/* 110xxxxx 10xxxxxx */ |
|
dvander@27
|
350 |
follow=1; |
|
dvander@27
|
351 |
lowmark=0x80L; |
|
dvander@27
|
352 |
result=ch & 0x1f; |
|
dvander@27
|
353 |
} else if ((ch & 0xf0)==0xe0) { |
|
dvander@27
|
354 |
/* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ |
|
dvander@27
|
355 |
follow=2; |
|
dvander@27
|
356 |
lowmark=0x800L; |
|
dvander@27
|
357 |
result=ch & 0x0f; |
|
dvander@27
|
358 |
} else if ((ch & 0xf8)==0xf0) { |
|
dvander@27
|
359 |
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
|
dvander@27
|
360 |
follow=3; |
|
dvander@27
|
361 |
lowmark=0x10000L; |
|
dvander@27
|
362 |
result=ch & 0x07; |
|
dvander@27
|
363 |
} else if ((ch & 0xfc)==0xf8) { |
|
dvander@27
|
364 |
/* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
|
dvander@27
|
365 |
follow=4; |
|
dvander@27
|
366 |
lowmark=0x200000L; |
|
dvander@27
|
367 |
result=ch & 0x03; |
|
dvander@27
|
368 |
} else if ((ch & 0xfe)==0xfc) { |
|
dvander@27
|
369 |
/* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (32 bits) */ |
|
dvander@27
|
370 |
follow=5; |
|
dvander@27
|
371 |
lowmark=0x4000000L; |
|
dvander@27
|
372 |
result=ch & 0x01; |
|
dvander@27
|
373 |
} else { |
|
dvander@27
|
374 |
/* this is invalid UTF-8 */ |
|
dvander@27
|
375 |
return -1; |
|
dvander@27
|
376 |
} /* if */ |
|
dvander@27
|
377 |
} else if (follow==0 && (ch & 0x80)==0x00) { |
|
dvander@27
|
378 |
/* 0xxxxxxx (US-ASCII) */ |
|
dvander@27
|
379 |
result=ch; |
|
dvander@27
|
380 |
break; |
|
dvander@27
|
381 |
} else { |
|
dvander@27
|
382 |
/* this is invalid UTF-8 */ |
|
dvander@27
|
383 |
return -1; |
|
dvander@27
|
384 |
} /* if */ |
|
dvander@27
|
385 |
|
|
dvander@27
|
386 |
} /* for */ |
|
dvander@27
|
387 |
|
|
dvander@27
|
388 |
if (endptr!=NULL) |
|
dvander@27
|
389 |
*endptr=string; |
|
dvander@27
|
390 |
return result; |
|
dvander@27
|
391 |
} |
|
dvander@27
|
392 |
#endif |
|
dvander@27
|
393 |
|
|
dvander@27
|
394 |
SC_FUNC int scan_utf8(FILE *fp,const char *filename) |
|
dvander@27
|
395 |
{ |
|
dvander@27
|
396 |
#if defined NO_UTF8 |
|
dvander@27
|
397 |
return 0; |
|
dvander@27
|
398 |
#else |
|
dvander@27
|
399 |
void *resetpos=pc_getpossrc(fp); |
|
dvander@27
|
400 |
int utf8=TRUE; |
|
dvander@27
|
401 |
int firstchar=TRUE,bom_found=FALSE; |
|
dvander@27
|
402 |
const unsigned char *ptr; |
|
dvander@27
|
403 |
|
|
dvander@27
|
404 |
while (utf8 && pc_readsrc(fp,pline,sLINEMAX)!=NULL) { |
|
dvander@27
|
405 |
ptr=pline; |
|
dvander@27
|
406 |
if (firstchar) { |
|
dvander@27
|
407 |
/* check whether the very first character on the very first line |
|
dvander@27
|
408 |
* starts with a BYTE order mark |
|
dvander@27
|
409 |
*/ |
|
dvander@27
|
410 |
cell c=get_utf8_char(ptr,&ptr); |
|
dvander@27
|
411 |
bom_found= (c==0xfeff); |
|
dvander@27
|
412 |
utf8= (c>=0); |
|
dvander@27
|
413 |
firstchar=FALSE; |
|
dvander@27
|
414 |
} /* if */ |
|
dvander@27
|
415 |
while (utf8 && *ptr!='\0') |
|
dvander@27
|
416 |
utf8= (get_utf8_char(ptr,&ptr)>=0); |
|
dvander@27
|
417 |
} /* while */ |
|
dvander@27
|
418 |
pc_resetsrc(fp,resetpos); |
|
dvander@27
|
419 |
if (bom_found) { |
|
dvander@27
|
420 |
unsigned char bom[3]; |
|
dvander@27
|
421 |
if (!utf8) |
|
dvander@27
|
422 |
error(77,filename); /* malformed UTF-8 encoding */ |
|
dvander@27
|
423 |
pc_readsrc(fp,bom,3); |
|
dvander@27
|
424 |
assert(bom[0]==0xef && bom[1]==0xbb && bom[2]==0xbf); |
|
dvander@27
|
425 |
} /* if */ |
|
dvander@27
|
426 |
return utf8; |
|
dvander@27
|
427 |
#endif /* NO_UTF8 */ |
|
dvander@27
|
428 |
} |