~liuxingcs/+junk/IM

« back to all changes in this revision

Viewing changes to libpurple/protocols/gg/lib/message.c

  • Committer: liuxing
  • Date: 2013-04-25 10:41:36 UTC
  • Revision ID: liuxingcs@yeah.net-20130425104136-e5towjtz19wsz1w7
Init IM

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
 
3
 *
 
4
 *  This program is free software; you can redistribute it and/or modify
 
5
 *  it under the terms of the GNU Lesser General Public License Version
 
6
 *  2.1 as published by the Free Software Foundation.
 
7
 *
 
8
 *  This program is distributed in the hope that it will be useful,
 
9
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 *  GNU Lesser General Public License for more details.
 
12
 *
 
13
 *  You should have received a copy of the GNU Lesser General Public
 
14
 *  License along with this program; if not, write to the Free Software
 
15
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
 
16
 *  USA.
 
17
 */
 
18
 
 
19
/**
 
20
 * \file message.c
 
21
 *
 
22
 * \brief Obsługa wiadomości
 
23
 * 
 
24
 * Plik zawiera funkcje dotyczące obsługi "klasy" gg_message_t, które
 
25
 * w przyszłości zostaną dołączone do API. Obecnie używane są funkcje
 
26
 * konwersji między tekstem z atrybutami i HTML.
 
27
 */
 
28
 
 
29
#include <stdlib.h>
 
30
#include <string.h>
 
31
#include <errno.h>
 
32
#include <limits.h>
 
33
#include <ctype.h>
 
34
 
 
35
#include "message.h"
 
36
 
 
37
#if 0
 
38
 
 
39
gg_message_t *gg_message_new(void)
 
40
{
 
41
        gg_message_t *gm;
 
42
 
 
43
        gm = malloc(sizeof(gg_message_t));
 
44
 
 
45
        if (gm == NULL)
 
46
                return NULL;
 
47
 
 
48
        memset(gm, 0, sizeof(gg_message_t));
 
49
 
 
50
        gm->msgclass = GG_CLASS_CHAT;
 
51
        gm->seq = (uint32_t) -1;
 
52
 
 
53
        return gm;
 
54
}
 
55
 
 
56
int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients, size_t recipient_count, char *text, char *html, char *attributes, size_t attributes_length, int auto_convert)
 
57
{
 
58
        GG_MESSAGE_CHECK(gm, -1);
 
59
 
 
60
        memset(gm, 0, sizeof(gg_message_t));
 
61
        gm->recipients = recipients;
 
62
        gm->recipient_count = recipient_count;
 
63
        gm->text = text;
 
64
        gm->html = html;
 
65
        gm->attributes = attributes;
 
66
        gm->attributes_length = attributes_length;
 
67
        gm->msgclass = msgclass;
 
68
        gm->seq = seq;
 
69
        gm->auto_convert = auto_convert;
 
70
 
 
71
        return 0;
 
72
}
 
73
 
 
74
void gg_message_free(gg_message_t *gm)
 
75
{
 
76
        if (gm == NULL) {
 
77
                errno = EINVAL;
 
78
                return;
 
79
        }       
 
80
 
 
81
        free(gm->text);
 
82
        free(gm->text_converted);
 
83
        free(gm->html);
 
84
        free(gm->html_converted);
 
85
        free(gm->recipients);
 
86
        free(gm->attributes);
 
87
 
 
88
        free(gm);
 
89
}
 
90
 
 
91
int gg_message_set_auto_convert(gg_message_t *gm, int auto_convert)
 
92
{
 
93
        GG_MESSAGE_CHECK(gm, -1);
 
94
 
 
95
        gm->auto_convert = !!auto_convert;
 
96
 
 
97
        if (!gm->auto_convert) {
 
98
                free(gm->text_converted);
 
99
                free(gm->html_converted);
 
100
                gm->text_converted = NULL;
 
101
                gm->html_converted = NULL;
 
102
        }
 
103
 
 
104
        return 0;
 
105
}
 
106
 
 
107
int gg_message_get_auto_convert(gg_message_t *gm)
 
108
{
 
109
        GG_MESSAGE_CHECK(gm, -1);
 
110
 
 
111
        return gm->auto_convert;
 
112
}
 
113
 
 
114
int gg_message_set_recipients(gg_message_t *gm, const uin_t *recipients, size_t recipient_count)
 
115
{
 
116
        GG_MESSAGE_CHECK(gm, -1);
 
117
 
 
118
        if (recipient_count >= INT_MAX / sizeof(uin_t)) {
 
119
                errno = EINVAL;
 
120
                return -1;
 
121
        }       
 
122
 
 
123
        if ((recipients == NULL) || (recipient_count == 0)) {
 
124
                free(gm->recipients);
 
125
                gm->recipients = NULL;
 
126
                gm->recipient_count = 0;
 
127
        } else {
 
128
                uin_t *tmp;
 
129
 
 
130
                tmp = realloc(gm->recipients, recipient_count * sizeof(uin_t));
 
131
 
 
132
                if (tmp == NULL)
 
133
                        return -1;
 
134
 
 
135
                memcpy(tmp, recipients, recipient_count * sizeof(uin_t));
 
136
 
 
137
                gm->recipients = tmp;
 
138
                gm->recipient_count = recipient_count;
 
139
        }
 
140
        
 
141
        return 0;
 
142
}
 
143
 
 
144
int gg_message_set_recipient(gg_message_t *gm, uin_t recipient)
 
145
{
 
146
        return gg_message_set_recipients(gm, &recipient, 1);
 
147
}
 
148
 
 
149
int gg_message_get_recipients(gg_message_t *gm, const uin_t **recipients, size_t *recipient_count)
 
150
{
 
151
        GG_MESSAGE_CHECK(gm, -1);
 
152
 
 
153
        if (recipients != NULL)
 
154
                *recipients = gm->recipients;
 
155
 
 
156
        if (recipient_count != NULL)
 
157
                *recipient_count = gm->recipient_count;
 
158
 
 
159
        return 0;
 
160
}
 
161
 
 
162
uin_t gg_message_get_recipient(gg_message_t *gm)
 
163
{
 
164
        GG_MESSAGE_CHECK(gm, (uin_t) -1);
 
165
 
 
166
        if ((gm->recipients == NULL) || (gm->recipient_count < 1)) {
 
167
                // errno = XXX;
 
168
                return (uin_t) -1;
 
169
        }
 
170
 
 
171
        return gm->recipients[0];
 
172
}
 
173
 
 
174
int gg_message_set_class(gg_message_t *gm, uint32_t msgclass)
 
175
{
 
176
        GG_MESSAGE_CHECK(gm, -1);
 
177
 
 
178
        gm->msgclass = msgclass;
 
179
 
 
180
        return 0;
 
181
}
 
182
 
 
183
uint32_t gg_message_get_class(gg_message_t *gm)
 
184
{
 
185
        GG_MESSAGE_CHECK(gm, (uint32_t) -1);
 
186
 
 
187
        return gm->msgclass;
 
188
}
 
189
 
 
190
int gg_message_set_seq(gg_message_t *gm, uint32_t seq)
 
191
{
 
192
        GG_MESSAGE_CHECK(gm, -1);
 
193
 
 
194
        gm->seq = seq;
 
195
 
 
196
        return 0;
 
197
}
 
198
 
 
199
uint32_t gg_message_get_seq(gg_message_t *gm)
 
200
{
 
201
        GG_MESSAGE_CHECK(gm, (uint32_t) -1);
 
202
 
 
203
        return gm->seq;
 
204
}
 
205
 
 
206
int gg_message_set_text(gg_message_t *gm, const char *text)
 
207
{
 
208
        GG_MESSAGE_CHECK(gm, -1);
 
209
 
 
210
        if (text == NULL) {
 
211
                free(gm->text);
 
212
                gm->text = NULL;
 
213
        } else {
 
214
                char *tmp;
 
215
 
 
216
                tmp = strdup(text);
 
217
 
 
218
                if (tmp == NULL)
 
219
                        return -1;
 
220
 
 
221
                free(gm->text);
 
222
                gm->text = tmp;
 
223
        }
 
224
 
 
225
        free(gm->html_converted);
 
226
        gm->html_converted = NULL;
 
227
 
 
228
        return 0;
 
229
}
 
230
 
 
231
const char *gg_message_get_text(gg_message_t *gm)
 
232
{
 
233
        GG_MESSAGE_CHECK(gm, NULL);
 
234
 
 
235
        if (gm->text_converted != NULL)
 
236
                return gm->text_converted;
 
237
 
 
238
        if (gm->text == NULL && gm->html != NULL && gm->auto_convert) {
 
239
                size_t len;
 
240
 
 
241
                free(gm->text_converted);
 
242
 
 
243
                len = gg_message_html_to_text(NULL, gm->html);
 
244
 
 
245
                gm->text_converted = malloc(len + 1);
 
246
 
 
247
                if (gm->text_converted == NULL)
 
248
                        return NULL;
 
249
 
 
250
                gg_message_html_to_text(gm->text_converted, gm->html);
 
251
 
 
252
                return gm->text_converted;
 
253
        }
 
254
 
 
255
        return gm->text;
 
256
}
 
257
 
 
258
int gg_message_set_html(gg_message_t *gm, const char *html)
 
259
{
 
260
        GG_MESSAGE_CHECK(gm, -1);
 
261
 
 
262
        if (html == NULL) {
 
263
                free(gm->html);
 
264
                gm->html = NULL;
 
265
        } else {
 
266
                char *tmp;
 
267
 
 
268
                tmp = strdup(html);
 
269
 
 
270
                if (tmp == NULL)
 
271
                        return -1;
 
272
 
 
273
                free(gm->html);
 
274
                gm->html = tmp;
 
275
        }
 
276
 
 
277
        free(gm->text_converted);
 
278
        gm->text_converted = NULL;
 
279
 
 
280
        return 0;
 
281
}
 
282
 
 
283
const char *gg_message_get_html(gg_message_t *gm)
 
284
{
 
285
        GG_MESSAGE_CHECK(gm, NULL);
 
286
 
 
287
        if (gm->html_converted != NULL)
 
288
                return gm->html_converted;
 
289
 
 
290
        if (gm->html == NULL && gm->text != NULL && gm->auto_convert) {
 
291
                size_t len;
 
292
 
 
293
                free(gm->html_converted);
 
294
 
 
295
                len = gg_message_text_to_html(NULL, gm->text, gm->attributes, gm->attributes_length);
 
296
 
 
297
                gm->html_converted = malloc(len + 1);
 
298
 
 
299
                if (gm->html_converted == NULL)
 
300
                        return NULL;
 
301
 
 
302
                gg_message_text_to_html(gm->html_converted, gm->text, gm->attributes, gm->attributes_length);
 
303
 
 
304
                return gm->html_converted;
 
305
        }
 
306
 
 
307
        return gm->html;
 
308
}
 
309
 
 
310
int gg_message_set_attributes(gg_message_t *gm, const char *attributes, size_t length)
 
311
{
 
312
        GG_MESSAGE_CHECK(gm, -1);
 
313
 
 
314
        if (length > 0xfffd) {
 
315
                // errno = XXX;
 
316
                return -1;
 
317
        }
 
318
 
 
319
        if ((attributes == NULL) || (length == 0)) {
 
320
                free(gm->attributes);
 
321
                gm->attributes = NULL;
 
322
                gm->attributes_length = 0;
 
323
        } else {
 
324
                char *tmp;
 
325
 
 
326
                tmp = realloc(gm->attributes, length);
 
327
 
 
328
                if (tmp == NULL)
 
329
                        return -1;
 
330
 
 
331
                gm->attributes = tmp;
 
332
                gm->attributes_length = length;
 
333
        }
 
334
 
 
335
        free(gm->html_converted);
 
336
        gm->html_converted = NULL;
 
337
 
 
338
        return 0;
 
339
}
 
340
 
 
341
int gg_message_get_attributes(gg_message_t *gm, const char **attributes, size_t *attributes_length)
 
342
{
 
343
        GG_MESSAGE_CHECK(gm, -1);
 
344
 
 
345
        if (attributes != NULL)
 
346
                *attributes = gm->attributes;
 
347
 
 
348
        if (attributes_length != NULL)
 
349
                *attributes_length = gm->attributes_length;
 
350
 
 
351
        return 0;
 
352
}
 
353
 
 
354
#endif
 
355
 
 
356
/**
 
357
 * \internal Dodaje tekst na koniec bufora.
 
358
 * 
 
359
 * \param dst Wskaźnik na bufor roboczy
 
360
 * \param pos Wskaźnik na aktualne położenie w buforze roboczym
 
361
 * \param src Dodawany tekst
 
362
 * \param len Długość dodawanego tekstu
 
363
 */
 
364
static void gg_append(char *dst, size_t *pos, const void *src, int len)
 
365
{
 
366
        if (dst != NULL)
 
367
                memcpy(&dst[*pos], src, len);
 
368
 
 
369
        *pos += len;
 
370
}
 
371
 
 
372
/**
 
373
 * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
 
374
 *
 
375
 * \param dst Bufor wynikowy (może być \c NULL)
 
376
 * \param src Tekst źródłowy w UTF-8
 
377
 * \param format Atrybuty tekstu źródłowego
 
378
 * \param format_len Długość bloku atrybutów tekstu źródłowego
 
379
 *
 
380
 * \note Wynikowy tekst nie jest idealnym kodem HTML, ponieważ ma jak
 
381
 * dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient.
 
382
 *
 
383
 * \note Dokleja \c \\0 na końcu bufora wynikowego.
 
384
 *
 
385
 * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
 
386
 */
 
387
size_t gg_message_text_to_html(char *dst, const char *src, const char *format, size_t format_len)
 
388
{
 
389
        const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
 
390
        const int span_len = 75;
 
391
        const char img_fmt[] = "<img name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
 
392
        const int img_len = 29;
 
393
        int char_pos = 0;
 
394
        int format_idx = 0;
 
395
        unsigned char old_attr = 0;
 
396
        const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
 
397
        int i;
 
398
        size_t len;
 
399
        const unsigned char *format_ = (const unsigned char*) format;
 
400
 
 
401
        len = 0;
 
402
 
 
403
        /* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc
 
404
         * tak czy inaczej trzeba otworzyć <span>. */
 
405
 
 
406
        if (src[0] != 0 && (format_idx + 3 > format_len || (format_[format_idx] | (format_[format_idx + 1] << 8)) != 0)) {
 
407
                if (dst != NULL)
 
408
                        sprintf(&dst[len], span_fmt, 0, 0, 0);
 
409
 
 
410
                len += span_len;
 
411
        }
 
412
 
 
413
        /* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek
 
414
         * na końcu tekstu. */
 
415
 
 
416
        for (i = 0; ; i++) {
 
417
                /* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */
 
418
                for (;;) {
 
419
                        unsigned char attr;
 
420
                        int attr_pos;
 
421
 
 
422
                        if (format_idx + 3 > format_len)
 
423
                                break;
 
424
 
 
425
                        attr_pos = format_[format_idx] | (format_[format_idx + 1] << 8);
 
426
 
 
427
                        if (attr_pos != char_pos)
 
428
                                break;
 
429
 
 
430
                        attr = format_[format_idx + 2];
 
431
 
 
432
                        /* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */
 
433
 
 
434
                        if (src[i] == 0)
 
435
                                attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR);
 
436
 
 
437
                        format_idx += 3;
 
438
 
 
439
                        if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) {
 
440
                                if (char_pos != 0) {
 
441
                                        if ((old_attr & GG_FONT_UNDERLINE) != 0)
 
442
                                                gg_append(dst, &len, "</u>", 4);
 
443
 
 
444
                                        if ((old_attr & GG_FONT_ITALIC) != 0)
 
445
                                                gg_append(dst, &len, "</i>", 4);
 
446
 
 
447
                                        if ((old_attr & GG_FONT_BOLD) != 0)
 
448
                                                gg_append(dst, &len, "</b>", 4);
 
449
 
 
450
                                        if (src[i] != 0)
 
451
                                                gg_append(dst, &len, "</span>", 7);
 
452
                                }
 
453
 
 
454
                                if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
 
455
                                        color = &format_[format_idx];
 
456
                                        format_idx += 3;
 
457
                                } else {
 
458
                                        color = (unsigned char*) "\x00\x00\x00";
 
459
                                }
 
460
 
 
461
                                if (src[i] != 0) {
 
462
                                        if (dst != NULL)
 
463
                                                sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
 
464
                                        len += span_len;
 
465
                                }
 
466
                        } else if (char_pos == 0 && src[0] != 0) {
 
467
                                if (dst != NULL)
 
468
                                        sprintf(&dst[len], span_fmt, 0, 0, 0);
 
469
                                len += span_len;
 
470
                        }
 
471
 
 
472
                        if ((attr & GG_FONT_BOLD) != 0)
 
473
                                gg_append(dst, &len, "<b>", 3);
 
474
 
 
475
                        if ((attr & GG_FONT_ITALIC) != 0)
 
476
                                gg_append(dst, &len, "<i>", 3);
 
477
 
 
478
                        if ((attr & GG_FONT_UNDERLINE) != 0)
 
479
                                gg_append(dst, &len, "<u>", 3);
 
480
 
 
481
                        if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
 
482
                                if (dst != NULL) {
 
483
                                        sprintf(&dst[len], img_fmt,
 
484
                                                format_[format_idx + 9],
 
485
                                                format_[format_idx + 8], 
 
486
                                                format_[format_idx + 7],
 
487
                                                format_[format_idx + 6], 
 
488
                                                format_[format_idx + 5],
 
489
                                                format_[format_idx + 4],
 
490
                                                format_[format_idx + 3],
 
491
                                                format_[format_idx + 2]);
 
492
                                }
 
493
 
 
494
                                len += img_len;
 
495
                                format_idx += 10;
 
496
                        }
 
497
 
 
498
                        old_attr = attr;
 
499
                }
 
500
 
 
501
                /* Doklej znak zachowując htmlowe escapowanie. */
 
502
 
 
503
                switch (src[i]) {
 
504
                        case '&':
 
505
                                gg_append(dst, &len, "&amp;", 5);
 
506
                                break;
 
507
                        case '<':
 
508
                                gg_append(dst, &len, "&lt;", 4);
 
509
                                break;
 
510
                        case '>':
 
511
                                gg_append(dst, &len, "&gt;", 4);
 
512
                                break;
 
513
                        case '\'':
 
514
                                gg_append(dst, &len, "&apos;", 6);
 
515
                                break;
 
516
                        case '\"':
 
517
                                gg_append(dst, &len, "&quot;", 6);
 
518
                                break;
 
519
                        case '\n':
 
520
                                gg_append(dst, &len, "<br>", 4);
 
521
                                break;
 
522
                        case '\r':
 
523
                        case 0:
 
524
                                break;
 
525
                        default:
 
526
                                if (dst != NULL)
 
527
                                        dst[len] = src[i];
 
528
                                len++;
 
529
                }
 
530
 
 
531
                /* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */
 
532
 
 
533
                if ((src[i] & 0xc0) != 0xc0)
 
534
                        char_pos++;
 
535
 
 
536
                if (src[i] == 0)
 
537
                        break;
 
538
        }
 
539
 
 
540
        /* Zamknij tagi. */
 
541
 
 
542
        if ((old_attr & GG_FONT_UNDERLINE) != 0)
 
543
                gg_append(dst, &len, "</u>", 4);
 
544
 
 
545
        if ((old_attr & GG_FONT_ITALIC) != 0)
 
546
                gg_append(dst, &len, "</i>", 4);
 
547
 
 
548
        if ((old_attr & GG_FONT_BOLD) != 0)
 
549
                gg_append(dst, &len, "</b>", 4);
 
550
 
 
551
        if (src[0] != 0)
 
552
                gg_append(dst, &len, "</span>", 7);
 
553
 
 
554
        if (dst != NULL)
 
555
                dst[len] = 0;
 
556
 
 
557
        return len;
 
558
}
 
559
 
 
560
/**
 
561
 * \internal Zamienia tekst w formacie HTML na czysty tekst.
 
562
 *
 
563
 * \param dst Bufor wynikowy (może być \c NULL)
 
564
 * \param html Tekst źródłowy
 
565
 *
 
566
 * \note Dokleja \c \\0 na końcu bufora wynikowego.
 
567
 *
 
568
 * \note Funkcja służy do zachowania kompatybilności przy przesyłaniu
 
569
 * wiadomości HTML do klientów, które tego formatu nie obsługują. Z tego
 
570
 * powodu funkcja nie zachowuje formatowania, a jedynie usuwa tagi i
 
571
 * zamienia podstawowe encje na ich odpowiedniki ASCII.
 
572
 *
 
573
 * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
 
574
 */
 
575
size_t gg_message_html_to_text(char *dst, const char *html)
 
576
{
 
577
        const char *src, *entity, *tag;
 
578
        int in_tag, in_entity;
 
579
        size_t len;
 
580
 
 
581
        len = 0;
 
582
        in_tag = 0;
 
583
        tag = NULL;
 
584
        in_entity = 0;
 
585
        entity = NULL;
 
586
 
 
587
        for (src = html; *src != 0; src++) {
 
588
                if (in_entity && !(isalnum(*src) || *src == '#' || *src == ';')) {
 
589
                        in_entity = 0;
 
590
                        gg_append(dst, &len, entity, src - entity);
 
591
                }
 
592
 
 
593
                if (*src == '<') {
 
594
                        tag = src;
 
595
                        in_tag = 1;
 
596
                        continue;
 
597
                }
 
598
 
 
599
                if (in_tag && (*src == '>')) {
 
600
                        if (strncmp(tag, "<br", 3) == 0) {
 
601
                                if (dst != NULL)
 
602
                                        dst[len] = '\n';
 
603
                                len++;
 
604
                        }
 
605
                        in_tag = 0;
 
606
                        continue;
 
607
                }
 
608
 
 
609
                if (in_tag)
 
610
                        continue;
 
611
 
 
612
                if (*src == '&') {
 
613
                        in_entity = 1;
 
614
                        entity = src;
 
615
                        continue;
 
616
                }
 
617
 
 
618
                if (in_entity && *src == ';') {
 
619
                        in_entity = 0;
 
620
                        if (dst != NULL) {
 
621
                                if (strncmp(entity, "&lt;", 4) == 0)
 
622
                                        dst[len++] = '<';
 
623
                                else if (strncmp(entity, "&gt;", 4) == 0)
 
624
                                        dst[len++] = '>';
 
625
                                else if (strncmp(entity, "&quot;", 6) == 0)
 
626
                                        dst[len++] = '"';
 
627
                                else if (strncmp(entity, "&apos;", 6) == 0)
 
628
                                        dst[len++] = '\'';
 
629
                                else if (strncmp(entity, "&amp;", 5) == 0)
 
630
                                        dst[len++] = '&';
 
631
                                else if (strncmp(entity, "&nbsp;", 6) == 0) {
 
632
                                        dst[len++] = 0xc2;
 
633
                                        dst[len++] = 0xa0;
 
634
                                } else
 
635
                                        dst[len++] = '?';
 
636
                        } else {
 
637
                                if (strncmp(entity, "&nbsp;", 6) == 0)
 
638
                                        len += 2;
 
639
                                else
 
640
                                        len++;
 
641
                        }
 
642
 
 
643
                        continue;
 
644
                }
 
645
 
 
646
                if (in_entity && !(isalnum(*src) || *src == '#'))
 
647
                        in_entity = 0;
 
648
 
 
649
                if (in_entity)
 
650
                        continue;
 
651
 
 
652
                if (dst != NULL)
 
653
                        dst[len] = *src;
 
654
 
 
655
                len++;
 
656
        }
 
657
 
 
658
        if (dst != NULL)
 
659
                dst[len] = 0;
 
660
        
 
661
        return len;
 
662
}
 
663