1 /*
2 filters and hooks that various languages can use
3
4 Copyright (C) 2000-2003 David Necas (Yeti) <yeti@physics.muni.cz>
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of version 2 of the GNU General Public License as published
8 by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18 */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif /* HAVE_CONFIG_H */
22
23 #include <math.h>
24
25 #include "enca.h"
26 #include "internal.h"
27
28 /**
29 * EncaBoxDraw:
30 * @csname: Charset name.
31 * @isvbox: All other box drawing characters.
32 * @h1: Horizontal line character (light).
33 * @h2: Horizontal line character (heavy).
34 *
35 * Information about box-drawing characters for a charset.
36 **/
37 struct _EncaBoxDraw {
38 const char *csname;
39 const unsigned char *isvbox;
40 unsigned char h1;
41 unsigned char h2;
42 };
43
44 typedef struct _EncaBoxDraw EncaBoxDraw;
45
46 /* THIS IS A GENERATED TABLE, see tools/expand_table.pl */
47 static const unsigned char BOXVERT_IBM852[] = {
48 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
54 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1,
60 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0,
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 };
65
66 /* These are identical */
67 #define BOXVERT_IBM775 BOXVERT_IBM852
68
69 /* THIS IS A GENERATED TABLE, see tools/expand_table.pl */
70 static const unsigned char BOXVERT_KEYBCS2[] = {
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
78 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
83 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
84 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87 };
88
89 /* These are identical */
90 #define BOXVERT_IBM866 BOXVERT_KEYBCS2
91 #define BOXVERT_CP1125 BOXVERT_KEYBCS2
92
93 /* THIS IS A GENERATED TABLE, see tools/expand_table.pl */
94 static const unsigned char BOXVERT_KOI8R[] = {
95 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
106 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
107 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 };
112
113 #if 0
114 /* UNUSED */
115 /* THIS IS A GENERATED TABLE, see tools/expand_table.pl */
116 static const unsigned char BOXVERT_KOI8RU[] = {
117 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
128 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 };
134 #endif
135
136 /* THIS IS A GENERATED TABLE, see tools/expand_table.pl */
137 static const unsigned char BOXVERT_KOI8U[] = {
138 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
148 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
149 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0,
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
154 };
155
156 /* THIS IS A GENERATED TABLE, see tools/expand_table.pl */
157 static const unsigned char BOXVERT_KOI8UNI[] = {
158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
166 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
167 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
174 };
175
176 static const EncaBoxDraw BOXDRAW[] = {
177 { "cp1125", BOXVERT_CP1125, 196, 205 },
178 { "ibm775", BOXVERT_IBM775, 196, 205 },
179 { "ibm852", BOXVERT_IBM852, 196, 205 },
180 { "ibm866", BOXVERT_IBM866, 196, 205 },
181 { "keybcs2", BOXVERT_KEYBCS2, 196, 205 },
182 { "koi8r", BOXVERT_KOI8R, 128, 160 },
183 { "koi8u", BOXVERT_KOI8U, 128, 160 },
184 { "koi8uni", BOXVERT_KOI8UNI, 128, 128 }, /* there's only one */
185 #if 0
186 { "koi8ru", BOXVERT_KOI8RU, 128, 160 },
187 #endif
188 };
189
190 /* Local prototypes. */
191 static size_t filter_boxdraw_out(int charset,
192 unsigned char *buffer,
193 size_t size,
194 unsigned char fill_char);
195
196 /**
197 * enca_filter_boxdraw:
198 * @analyser: Analyser whose charsets should be considered for filtration.
199 * @fill_char: Replacement character for filtered bytes.
200 *
201 * Runs boxdrawing characters filter on @buffer for each charset in @language.
202 *
203 * Returns: Number of characters filtered out.
204 **/
205 size_t
enca_filter_boxdraw(EncaAnalyserState * analyser,unsigned char fill_char)206 enca_filter_boxdraw(EncaAnalyserState *analyser,
207 unsigned char fill_char)
208 {
209 size_t i;
210 size_t filtered = 0;
211
212 for (i = 0; i < analyser->ncharsets; i++) {
213 filtered += filter_boxdraw_out(analyser->charsets[i],
214 analyser->buffer, analyser->size,
215 fill_char);
216 }
217
218 return filtered;
219 }
220
221 /**
222 * filter_boxdraw_out:
223 * @charset: Charset whose associated filter should be applied.
224 * @buffer: Buffer to be filtered.
225 * @size: Size of @buffer.
226 * @fill_char: Replacement character for filtered bytes.
227 *
228 * Replaces box-drawing characters in @buffer with @fill_char.
229 *
230 * Not all possibly box-drawing characters are replaced, only those meeting
231 * certain conditions to reduce false filtering. It's assumed
232 * isspace(@fill_char) is true (it aborts when it isn't).
233 *
234 * It's OK to call with @charset which has no filter associated, it just
235 * returns zero then.
236 *
237 * Returns: The number of characters filtered.
238 **/
239 static size_t
filter_boxdraw_out(int charset,unsigned char * buffer,size_t size,unsigned char fill_char)240 filter_boxdraw_out(int charset,
241 unsigned char *buffer,
242 size_t size,
243 unsigned char fill_char)
244 {
245 static int charset_id[ELEMENTS(BOXDRAW)];
246 static int charset_id_initialized = 0;
247 const EncaBoxDraw *bd;
248 size_t i, n, xout;
249
250 assert(enca_isspace(fill_char));
251
252 if (!charset_id_initialized) {
253 for (i = 0; i < ELEMENTS(BOXDRAW); i++) {
254 charset_id[i] = enca_name_to_charset(BOXDRAW[i].csname);
255 assert(charset_id[i] != ENCA_CS_UNKNOWN);
256 }
257 charset_id_initialized = 1;
258 }
259
260 /* Find whether we have any filter associated with this charset. */
261 bd = NULL;
262 for (i = 0; i < ELEMENTS(BOXDRAW); i++) {
263 if (charset_id[i] == charset) {
264 bd = BOXDRAW + i;
265 break;
266 }
267 }
268 if (bd == NULL)
269 return 0;
270
271 xout = 0;
272 /* First stage:
273 * Horizontal lines, they must occur at least two in a row. */
274 i = 0;
275 while (i < size-1) {
276 if (buffer[i] == bd->h1 || buffer[i] == bd->h2) {
277 for (n = i+1; buffer[n] == buffer[i] && n < size; n++)
278 ;
279
280 if (n > i+1) {
281 memset(buffer + i, fill_char, n - i);
282 xout += n - i;
283 }
284 i = n;
285 }
286 else i++;
287 }
288
289 /* Second stage:
290 * Vertical/mixed, they must occur separated by whitespace.
291 * We assume isspace(fill_char) is true. */
292 if (size > 1
293 && bd->isvbox[buffer[0]]
294 && enca_isspace(buffer[1])) {
295 buffer[0] = fill_char;
296 xout++;
297 }
298
299 for (i = 1; i < size-1; i++) {
300 if (bd->isvbox[buffer[i]]
301 && enca_isspace(buffer[i-1])
302 && enca_isspace(buffer[i+1])) {
303 buffer[i] = fill_char;
304 xout++;
305 }
306 }
307
308 if (size > 1
309 && bd->isvbox[buffer[size-1]]
310 && enca_isspace(buffer[size-2])) {
311 buffer[size-1] = fill_char;
312 xout++;
313 }
314
315 return xout;
316 }
317
318 /**
319 * enca_language_hook_ncs:
320 * @analyser: Analyser whose charset ratings are to be modified.
321 * @ncs: The number of charsets.
322 * @hookdata: What characters of which charsets should be given the extra
323 * weight.
324 *
325 * Decide between two charsets differing only in a few characters.
326 *
327 * If the two most probable charsets correspond to @hookdata charsets,
328 * give the characters they differ half the weight of all other characters
329 * together, thus allowing to decide between the two very similar charsets.
330 *
331 * It also recomputes @order when something changes.
332 *
333 * Returns: Nonzero when @ratings were actually modified, nonzero otherwise.
334 **/
335 int
enca_language_hook_ncs(EncaAnalyserState * analyser,size_t ncs,EncaLanguageHookData1CS * hookdata)336 enca_language_hook_ncs(EncaAnalyserState *analyser,
337 size_t ncs,
338 EncaLanguageHookData1CS *hookdata)
339 {
340 const int *const ids = analyser->charsets;
341 const size_t ncharsets = analyser->ncharsets;
342 const size_t *counts = analyser->counts;
343 const size_t *const order = analyser->order;
344 double *const ratings = analyser->ratings;
345 size_t maxcnt, j, k, m;
346 double q;
347
348 assert(ncharsets > 0);
349 assert(ncs <= ncharsets);
350 if (ncs < 2)
351 return 0;
352
353 /*
354 for (j = 0; j < ncharsets; j++) {
355 fprintf(stderr, "%s:\t%g\n", enca_csname(ids[order[j]]), ratings[order[j]]);
356 }
357 */
358
359 /* Find id's and check whether they are the first */
360 for (j = 0; j < ncs; j++) {
361 EncaLanguageHookData1CS *h = hookdata + j;
362
363 /* Find charset if unknown */
364 if (h->cs == (size_t)-1) {
365 int id;
366
367 id = enca_name_to_charset(h->name);
368 assert(id != ENCA_CS_UNKNOWN);
369 k = 0;
370 while (k < ncharsets && id != ids[k])
371 k++;
372 assert(k < ncharsets);
373 h->cs = k;
374 }
375
376 /* If any charset is not between the first ncs ones, do nothing. */
377 k = 0;
378 while (k < ncs && order[k] != h->cs)
379 k++;
380 if (k == ncs)
381 return 0;
382 }
383
384 /* Sum the extra-important characters and find maximum. */
385 maxcnt = 0;
386 for (j = 0; j < ncs; j++) {
387 EncaLanguageHookData1CS const *h = hookdata + j;
388
389 for (m = k = 0; k < h->size; k++)
390 m += counts[h->list[k]];
391 if (m > maxcnt)
392 maxcnt = m;
393 }
394 if (maxcnt == 0)
395 return 0;
396
397 /* Substract something from charsets that have less than maximum. */
398 q = 0.5 * ratings[order[0]]/(maxcnt + EPSILON);
399 for (j = 0; j < ncs; j++) {
400 EncaLanguageHookData1CS const *h = hookdata + j;
401
402 m = maxcnt;
403 for (k = 0; k < h->size; k++)
404 m -= counts[h->list[k]];
405 ratings[h->cs] -= q*m;
406 }
407
408 enca_find_max_sec(analyser);
409
410 return 1;
411 }
412
413 /**
414 * enca_language_hook_eol:
415 * @analyser: Analyser whose charset ratings are to be modified.
416 * @ncs: The number of charsets.
417 * @hookdata: What characters of which charsets should be decided with based
418 * on the EOL type.
419 *
420 * Decide between two charsets differing only in EOL type or other surface.
421 *
422 * The (surface mask, charset) pairs are scanned in order. If a matching
423 * surface is found, ratings of all other charsets in the list are zeroed.
424 * So you can place a surface mask of all 1s at the end to match when nothing
425 * else matches.
426 *
427 * All the charsets have to have the same rating, or nothing happens.
428 *
429 * It also recomputes @order when something changes.
430 *
431 * Returns: Nonzero when @ratings were actually modified, nonzero otherwise.
432 **/
433 int
enca_language_hook_eol(EncaAnalyserState * analyser,size_t ncs,EncaLanguageHookDataEOL * hookdata)434 enca_language_hook_eol(EncaAnalyserState *analyser,
435 size_t ncs,
436 EncaLanguageHookDataEOL *hookdata)
437 {
438 const int *const ids = analyser->charsets;
439 const size_t ncharsets = analyser->ncharsets;
440 const size_t *const order = analyser->order;
441 double *const ratings = analyser->ratings;
442 size_t j, k;
443
444 assert(ncharsets > 0);
445 assert(ncs <= ncharsets);
446 if (ncs < 2)
447 return 0;
448
449 /* Rating equality check. */
450 for (j = 1; j < ncs; j++) {
451 if (fabs(ratings[order[j-1]] - ratings[order[j]]) > EPSILON)
452 return 0;
453 }
454
455 /* Find id's and check whether they are the first */
456 for (j = 0; j < ncs; j++) {
457 EncaLanguageHookDataEOL *h = hookdata + j;
458
459 /* Find charset if unknown */
460 if (h->cs == (size_t)-1) {
461 int id;
462
463 id = enca_name_to_charset(h->name);
464 assert(id != ENCA_CS_UNKNOWN);
465 k = 0;
466 while (k < ncharsets && id != ids[k])
467 k++;
468 assert(k < ncharsets);
469 h->cs = k;
470 }
471
472 /* If any charset is not between the first ncs ones, do nothing. */
473 k = 0;
474 while (k < ncs && order[k] != h->cs)
475 k++;
476 if (k == ncs)
477 return 0;
478 }
479
480 /* Find first matching EOL type. */
481 for (j = 0; j < ncs; j++) {
482 EncaLanguageHookDataEOL const *h = hookdata + j;
483
484 if (h->eol & analyser->result.surface) {
485 int chg = 0;
486
487 for (k = 0; k < ncs; k++) {
488 h = hookdata + k;
489
490 if (k != j && ratings[h->cs] > 0.0) {
491 ratings[h->cs] = 0.0;
492 chg = 1;
493 }
494 }
495 if (chg)
496 enca_find_max_sec(analyser);
497
498 return chg;
499 }
500 }
501
502 return 0;
503 }
504
505 /* vim: ts=2
506 */
507
508