source: trunk/third/gtkhtml3/src/htmltokenizer.c @ 21116

Revision 21116, 32.2 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21115, which included commits to RCS files with non-trunk default branches.
RevLine 
[19538]1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/*
3    Copyright (C) 1997 Martin Jones (mjones@kde.org)
4              (C) 1997 Torben Weis (weis@kde.org)
5              (C) 1999 Anders Carlsson (andersca@gnu.org)
6              (C) 2000 Helix Code, Inc., Radek Doulik (rodo@helixcode.com)
7              (C) 2001 Ximian, Inc.
8
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Library General Public
11    License as published by the Free Software Foundation; either
12    version 2 of the License, or (at your option) any later version.
13
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18
19    You should have received a copy of the GNU Library General Public License
20    along with this library; see the file COPYING.LIB.  If not, write to
21    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22    Boston, MA 02111-1307, USA.
23*/
24
25/* The HTML Tokenizer */
26#include <config.h>
27#include <ctype.h>
28#include <string.h>
29#include <gnome.h>
30#include "htmltokenizer.h"
31#include "htmlentity.h"
32
33enum {
34        HTML_TOKENIZER_BEGIN_SIGNAL,
35        HTML_TOKENIZER_END_SIGNAL,
36        HTML_TOKENIZER_LAST_SIGNAL
37};
38
39static guint html_tokenizer_signals[HTML_TOKENIZER_LAST_SIGNAL] = { 0 };
40
41#define TOKEN_BUFFER_SIZE (1 << 10)
42#define INVALID_CHARACTER_MARKER '?'
43
[21115]44#define dt(x)
45
[19538]46typedef struct _HTMLBlockingToken HTMLBlockingToken;
47typedef struct _HTMLTokenBuffer   HTMLTokenBuffer;
48typedef enum { Table }            HTMLTokenType;
49
50struct _HTMLTokenBuffer {
51        gint size;
52        gint used;
53        gchar * data;
54};
55struct _HTMLTokenizerPrivate {
56
57        /* token buffers list */
58        GList *token_buffers;
59
60        /* current read_buf position in list */
61        GList *read_cur;
62
63        /* current read buffer */
64        HTMLTokenBuffer * read_buf;
65        HTMLTokenBuffer * write_buf;
66
67        /* position in the read_buf */
68        gint read_pos;
69
70        /* non-blocking and blocking unreaded tokens in tokenizer */
71        gint tokens_num;
72        gint blocking_tokens_num;
73
74        gchar *dest;
75        gchar *buffer;
76        gint size;
77
78        gboolean skipLF; /* Skip the LF par of a CRLF sequence */
79
80        gboolean tag; /* Are we in an html tag? */
81        gboolean tquote; /* Are we in quotes in an html tag? */
82        gboolean startTag;
83        gboolean comment; /* Are we in a comment block? */
84        gboolean title; /* Are we in a <title> block? */
85        gboolean style; /* Are we in a <style> block? */
86        gboolean script; /* Are we in a <script> block? */
87        gboolean textarea; /* Are we in a <textarea> block? */
88        gint     pre; /* Are we in a <pre> block? */
89        gboolean select; /* Are we in a <select> block? */
90        gboolean charEntity; /* Are we in an &... sequence? */
91        gboolean extension; /* Are we in an <!-- +GtkHTML: sequence? */
92 
93        enum {
94                NoneDiscard = 0,
95                SpaceDiscard,
96                LFDiscard
97        } discard;
98
99        enum {
100                NonePending = 0,
101                SpacePending,
102                LFPending,
103                TabPending
104        } pending;
105
106
107        gchar searchBuffer[20];
108        gint searchCount;
109        gint searchGtkHTMLCount;
110        gint searchExtensionEndCount;
111
112        gchar *scriptCode;
113        gint scriptCodeSize;
114        gint scriptCodeMaxSize;
115
116        GList *blocking; /* Blocking tokens */
117
118        const gchar *searchFor;
119        gboolean utf8;
120        gchar utf8_buffer[7];
121        gint utf8_length;
122};
123
124static const gchar *commentStart = "<!--";
125static const gchar *scriptEnd = "</script>";
126static const gchar *styleEnd = "</style>";
127static const gchar *gtkhtmlStart = "+gtkhtml:";
128
129enum quoteEnum {
130        NO_QUOTE = 0,
131        SINGLE_QUOTE,
132        DOUBLE_QUOTE
133};
134
135/* private tokenizer functions */
136static void           html_tokenizer_reset        (HTMLTokenizer *t);
137static void           html_tokenizer_add_pending  (HTMLTokenizer *t);
138static void           html_tokenizer_append_token (HTMLTokenizer *t,
139                                                   const gchar *string,
140                                                   gint len);
141static void           html_tokenizer_append_token_buffer (HTMLTokenizer *t,
142                                                          gint min_size);
143
144/* default implementations of tokenization functions */
145static void     html_tokenizer_finalize             (GObject *);
146static void     html_tokenizer_real_begin           (HTMLTokenizer *, gchar *content_type);
147static void     html_tokenizer_real_write           (HTMLTokenizer *, const gchar *str, size_t size);
148static void     html_tokenizer_real_end             (HTMLTokenizer *);
149static gchar   *html_tokenizer_real_peek_token      (HTMLTokenizer *);
150static gchar   *html_tokenizer_real_next_token      (HTMLTokenizer *);
151static gboolean html_tokenizer_real_has_more_tokens (HTMLTokenizer *);
152
153static HTMLTokenizer *html_tokenizer_real_clone     (HTMLTokenizer *);
154
155/* blocking tokens */
156static gchar             *html_tokenizer_blocking_get_name   (HTMLTokenizer  *t);
157static void               html_tokenizer_blocking_pop        (HTMLTokenizer  *t);
158static void               html_tokenizer_blocking_push       (HTMLTokenizer  *t,
159                                                              HTMLTokenType   tt);
160static void               html_tokenizer_tokenize_one_char   (HTMLTokenizer  *t,
161                                                              const gchar  **src);
162
163static void               add_unichar(HTMLTokenizer *t, gunichar wc);
164
165static GObjectClass *parent_class = NULL;
166
167static void
168html_tokenizer_class_init (HTMLTokenizerClass *klass)
169{
170        GObjectClass *object_class = (GObjectClass *) klass;
171
172        parent_class = g_type_class_ref (G_TYPE_OBJECT);
173
174        html_tokenizer_signals[HTML_TOKENIZER_BEGIN_SIGNAL] =
175                g_signal_new ("begin",
176                              G_TYPE_FROM_CLASS (klass),
177                              G_SIGNAL_RUN_LAST,
178                              G_STRUCT_OFFSET (HTMLTokenizerClass, begin),
179                              NULL, NULL,
180                              g_cclosure_marshal_VOID__POINTER,
181                              G_TYPE_NONE,
182                              1, G_TYPE_POINTER);
183
184        html_tokenizer_signals [HTML_TOKENIZER_END_SIGNAL] =
185                g_signal_new ("end",
186                              G_TYPE_FROM_CLASS (klass),
187                              G_SIGNAL_RUN_LAST,
188                              G_STRUCT_OFFSET (HTMLTokenizerClass, end),
189                              NULL, NULL,
190                              g_cclosure_marshal_VOID__VOID,
191                              G_TYPE_NONE,
192                              0);
193
194        object_class->finalize = html_tokenizer_finalize;
195
196        klass->begin      = html_tokenizer_real_begin;
197        klass->end        = html_tokenizer_real_end;
198
199        klass->write      = html_tokenizer_real_write;
200        klass->peek_token = html_tokenizer_real_peek_token;
201        klass->next_token = html_tokenizer_real_next_token;
202        klass->has_more   = html_tokenizer_real_has_more_tokens;
203        klass->clone      = html_tokenizer_real_clone;
204}
205
206static void
207html_tokenizer_init (HTMLTokenizer *t)
208{
209        struct _HTMLTokenizerPrivate *p;
210
211        t->priv = p = g_new0 (struct _HTMLTokenizerPrivate, 1);
212
213        p->token_buffers = NULL;
214        p->read_cur  = NULL;
215        p->read_buf  = NULL;
216        p->write_buf = NULL;
217        p->read_pos  = 0;
218
219        p->dest = NULL;
220        p->buffer = NULL;
221        p->size = 0;
222
223        p->skipLF = FALSE;
224        p->tag = FALSE;
225        p->tquote = FALSE;
226        p->startTag = FALSE;
227        p->comment = FALSE;
228        p->title = FALSE;
229        p->style = FALSE;
230        p->script = FALSE;
231        p->textarea = FALSE;
232        p->pre = 0;
233        p->select = FALSE;
234        p->charEntity = FALSE;
235        p->extension = FALSE;
236
237        p->discard = NoneDiscard;
238        p->pending = NonePending;
239
240        memset (p->searchBuffer, 0, sizeof (p->searchBuffer));
241        p->searchCount = 0;
242        p->searchGtkHTMLCount = 0;
243
244        p->scriptCode = NULL;
245        p->scriptCodeSize = 0;
246        p->scriptCodeMaxSize = 0;
247
248        p->blocking = NULL;
249
250        p->searchFor = NULL;
251}
252
253static void
254html_tokenizer_finalize (GObject *obj)
255{
256        HTMLTokenizer *t = HTML_TOKENIZER (obj);
257
258        html_tokenizer_reset (t);
259
260        g_free (t->priv);
261        t->priv = NULL;
262}
263
264GtkType
265html_tokenizer_get_type (void)
266{
267        static GtkType html_tokenizer_type = 0;
268
269        if (!html_tokenizer_type) {
270                static const GTypeInfo html_tokenizer_info = {
271                        sizeof (HTMLTokenizerClass),
272                        NULL,
273                        NULL,
274                        (GClassInitFunc) html_tokenizer_class_init,
275                        NULL,
276                        NULL,
277                        sizeof (HTMLTokenizer),
278                        1,
279                        (GInstanceInitFunc) html_tokenizer_init,
280                };
281                html_tokenizer_type = g_type_register_static (G_TYPE_OBJECT, "HTMLTokenizer", &html_tokenizer_info, 0);
282        }
283
284        return html_tokenizer_type;
285}
286
287static HTMLTokenBuffer *
288html_token_buffer_new (gint size)
289{
290        HTMLTokenBuffer *nb = g_new (HTMLTokenBuffer, 1);
291
292        nb->data = g_new (gchar, size);
293        nb->size = size;
294        nb->used = 0;
295
296        return nb;
297}
298
299static void
300html_token_buffer_destroy (HTMLTokenBuffer *tb)
301{
302        g_free (tb->data);
303        g_free (tb);
304}
305
306static gboolean
307html_token_buffer_append_token (HTMLTokenBuffer * buf, const gchar *token, gint len)
308{
309
310        /* check if we have enough free space */
311        if (len + 1 > buf->size - buf->used) {
312                return FALSE;
313        }
314
315        /* copy token and terminate with zero */
316        strncpy (buf->data + buf->used, token, len);
317        buf->used += len;
318        buf->data [buf->used] = 0;
319        buf->used ++;
320
[21115]321        dt(printf ("html_token_buffer_append_token: '%s'\n", buf->data + buf->used - 1 - len));
322
[19538]323        return TRUE;
324}
325
326HTMLTokenizer *
327html_tokenizer_new (void)
328{
329        return (HTMLTokenizer *) g_object_new (HTML_TYPE_TOKENIZER, NULL);
330}
331
332void
333html_tokenizer_destroy (HTMLTokenizer *t)
334{
335        g_return_if_fail (t && HTML_IS_TOKENIZER (t));
336       
337        g_object_unref (G_OBJECT (t));
338}
339
340static gchar *
341html_tokenizer_real_peek_token (HTMLTokenizer *t)
342{
343        struct _HTMLTokenizerPrivate *p = t->priv;
344        gchar *token;
345
346        g_assert (p->read_buf);
347
348        if (p->read_buf->used > p->read_pos) {
349                token = p->read_buf->data + p->read_pos;
350        } else {
351                GList *next;
352                HTMLTokenBuffer *buffer;
353
354                g_assert (p->read_cur);
355                g_assert (p->read_buf);
356
357                /* lookup for next buffer */
358                next = p->read_cur->next;
359                g_assert (next);
360
361                buffer = (HTMLTokenBuffer *) next->data;
362
363                g_return_val_if_fail (buffer->used != 0, NULL);
364
365                /* finally get first token */
366                token = buffer->data;
367        }
368       
369        return token;
370}
371       
372static gchar *
373html_tokenizer_real_next_token (HTMLTokenizer *t)
374{
375        struct _HTMLTokenizerPrivate *p = t->priv;
376        gchar *token;
377
378        g_assert (p->read_buf);
379
380        /* token is in current read_buf */
381        if (p->read_buf->used > p->read_pos) {
382                token = p->read_buf->data + p->read_pos;
383                p->read_pos += strlen (token) + 1;
384        } else {
385                GList *new;
386
387                g_assert (p->read_cur);
388                g_assert (p->read_buf);
389
390                /* lookup for next buffer */
391                new = p->read_cur->next;
392                g_assert (new);
393
394                /* destroy current buffer */
395                p->token_buffers = g_list_remove (p->token_buffers, p->read_buf);
396                html_token_buffer_destroy (p->read_buf);
397
398                p->read_cur = new;
399                p->read_buf = (HTMLTokenBuffer *) new->data;
400
401                g_return_val_if_fail (p->read_buf->used != 0, NULL);
402
403                /* finally get first token */
404                token = p->read_buf->data;
405                p->read_pos = strlen (token) + 1;
406        }
407
408        p->tokens_num--;
409        g_assert (p->tokens_num >= 0);
410
411        return token;
412}
413
414static gboolean
415html_tokenizer_real_has_more_tokens (HTMLTokenizer *t)
416{
417        return t->priv->tokens_num > 0;
418}
419
420static HTMLTokenizer *
421html_tokenizer_real_clone (HTMLTokenizer *t)
422{
423        return html_tokenizer_new ();
424}
425
426static void
427html_tokenizer_reset (HTMLTokenizer *t)
428{
429        struct _HTMLTokenizerPrivate *p = t->priv;
430        GList *cur = p->token_buffers;
431
432        /* free remaining token buffers */
433        while (cur) {
434                g_assert (cur->data);
435                html_token_buffer_destroy ((HTMLTokenBuffer *) cur->data);
436                cur = cur->next;
437        }
438
439        /* reset buffer list */
440        g_list_free (p->token_buffers);
441        p->token_buffers = p->read_cur = NULL;
442        p->read_buf = p->write_buf = NULL;
443        p->read_pos = 0;
444
445        /* reset token counters */
446        p->tokens_num = p->blocking_tokens_num = 0;
447
448        if (p->buffer)
449                g_free (p->buffer);
450        p->buffer = NULL;
451        p->dest = NULL;
452        p->size = 0;
453
454        if (p->scriptCode)
455                g_free (p->scriptCode);
456        p->scriptCode = NULL;
457}
458
459static gint
460charset_is_utf8 (gchar *content_type)
461{
462        return content_type && strstr (content_type, "charset=utf-8") != NULL;
463}
464
465static void
466html_tokenizer_real_begin (HTMLTokenizer *t, gchar *content_type)
467{
468        struct _HTMLTokenizerPrivate *p = t->priv;
469       
470        html_tokenizer_reset (t);
471
472        p->dest = p->buffer;
473        p->tag = FALSE;
474        p->pending = NonePending;
475        p->discard = NoneDiscard;
476        p->pre = 0;
477        p->script = FALSE;
478        p->style = FALSE;
479        p->skipLF = FALSE;
480        p->select = FALSE;
481        p->comment = FALSE;
482        p->textarea = FALSE;
483        p->startTag = FALSE;
484        p->extension = FALSE;
485        p->tquote = NO_QUOTE;
486        p->searchCount = 0;
487        p->searchGtkHTMLCount = 0;
488        p->title = FALSE;
489        p->charEntity = FALSE;
490       
491        p->utf8 = charset_is_utf8 (content_type);
492        p->utf8_length = 0;
493#if 0
494        if (p->utf8)
495                g_warning ("Trying UTF-8");
496        else
497                g_warning ("Trying ISO-8859-1");
498#endif
499
500}
501
502static void
503destroy_blocking (gpointer data, gpointer user_data)
504{
505        g_free (data);
506}
507
508static void
509html_tokenizer_real_end (HTMLTokenizer *t)
510{
511        struct _HTMLTokenizerPrivate *p = t->priv;
512
513        if (p->buffer == 0)
514                return;
515
516        if (p->dest > p->buffer) {
517                html_tokenizer_append_token (t, p->buffer, p->dest - p->buffer);
518        }
519
520        g_free (p->buffer);     
521
522        p->buffer = NULL;
523        p->dest = NULL;
524        p->size = 0;
525
526        if (p->blocking) {
527                g_list_foreach (p->blocking, destroy_blocking, NULL);
528                p->tokens_num += p->blocking_tokens_num;
529                p->blocking_tokens_num = 0;
530        }
531        p->blocking = NULL;
532}
533
534static void
535html_tokenizer_append_token (HTMLTokenizer *t, const gchar *string, gint len)
536{
537        struct _HTMLTokenizerPrivate *p = t->priv;
538       
539        if (len < 1)
540                return;
541
542        /* allocate first buffer */
543        if (p->write_buf == NULL)
544                html_tokenizer_append_token_buffer (t, len);
545
546        /* try append token to current buffer, if not successful, create append new token buffer */
547        if (!html_token_buffer_append_token (p->write_buf, string, len)) {
548                html_tokenizer_append_token_buffer (t, len+1);
549                /* now it must pass as we have enough space */
550                g_assert (html_token_buffer_append_token (p->write_buf, string, len));
551        }
552
553        if (p->blocking) {
554                p->blocking_tokens_num++;
555        } else {
556                p->tokens_num++;
557        }
558}
559
560static void
561html_tokenizer_append_token_buffer (HTMLTokenizer *t, gint min_size)
562{
563        struct _HTMLTokenizerPrivate *p = t->priv;
564        HTMLTokenBuffer *nb;
565        gint size = TOKEN_BUFFER_SIZE;
566
567        if (min_size > size)
568                size = min_size + (min_size >> 2);
569
570        /* create new buffer and add it to list */
571        nb = html_token_buffer_new (size);
572        p->token_buffers = g_list_append (p->token_buffers, nb);
573
574        /* this one is now write_buf */
575        p->write_buf = nb;
576
577        /* if we don't have read_buf already set it to this one */
578        if (p->read_buf == NULL) {
579                p->read_buf = nb;
580                p->read_cur = p->token_buffers;
581        }
582}
583
584/* EP CHECK: OK.  */
585static void
586html_tokenizer_add_pending (HTMLTokenizer *t)
587{
588        struct _HTMLTokenizerPrivate *p = t->priv;
589       
590        if (p->tag || p->select) {
591                add_unichar (t, ' ');
592        }
593        else if (p->textarea) {
594                if (p->pending == LFPending)
595                        add_unichar (t, '\n');
596                else
597                        add_unichar (t, ' ');
598        }
599        else if (p->pre) {
600                switch (p->pending) {
601                case SpacePending:
602                        add_unichar (t, ' ');
603                        break;
604                case LFPending:
605                        if (p->dest > p->buffer) {
606                                html_tokenizer_append_token (t, p->buffer, p->dest - p->buffer);
607                        }
608                        p->dest = p->buffer;
609                        add_unichar (t, TAG_ESCAPE);
610                        add_unichar (t, '\n');
611                        html_tokenizer_append_token (t, p->buffer, 2);
612                        p->dest = p->buffer;
613                        break;
614                case TabPending:
615                        add_unichar (t, '\t');
616                        break;
617                default:
618                        g_warning ("Unknown pending type: %d\n", (gint) p->pending);
619                        break;
620                }
621        }
622        else {
623                add_unichar (t, ' ');
624        }
625       
626        p->pending = NonePending;
627}
628
629static void
630prepare_enough_space (HTMLTokenizer *t)
631{
632        struct _HTMLTokenizerPrivate *p = t->priv;
633       
634        if ((p->dest - p->buffer + 32) > p->size) {
635                guint off = p->dest - p->buffer;
636
637                p->size  += (p->size >> 2) + 32;
638                p->buffer = g_realloc (p->buffer, p->size);
639                p->dest   = p->buffer + off;
640        }
641}
642
643static void
644in_comment (HTMLTokenizer *t, const gchar **src)
645{
646        struct _HTMLTokenizerPrivate *p = t->priv;
647
648        if (**src == '-') {                  /* Look for "-->" */
649                if (p->searchCount < 2)
650                        p->searchCount++;
651        } else if (p->searchCount == 2 && (**src == '>')) {
652                p->comment = FALSE;          /* We've got a "-->" sequence */
653        } else if (tolower (**src) == gtkhtmlStart [p->searchGtkHTMLCount]) {
654                if (p->searchGtkHTMLCount == 8) {
655                        p->extension    = TRUE;
656                        p->comment = FALSE;
657                        p->searchCount = 0;
658                        p->searchExtensionEndCount = 0;
659                        p->searchGtkHTMLCount = 0;
660                } else
661                        p->searchGtkHTMLCount ++;
662        } else {
663                p->searchGtkHTMLCount = 0;
664                if (p->searchCount < 2)
665                        p->searchCount = 0;
666        }
667
668        (*src)++;
669}
670
671static inline void
672extension_one_char (HTMLTokenizer *t, const gchar **src)
673{
674        struct _HTMLTokenizerPrivate *p = t->priv;
675       
676        p->extension = FALSE;
677        html_tokenizer_tokenize_one_char (t, src);
678        p->extension = TRUE;
679}
680
681static void
682in_extension (HTMLTokenizer *t, const gchar **src)
683{
684        struct _HTMLTokenizerPrivate *p = t->priv;
685
686        /* check for "-->" */
687        if (!p->tquote && **src == '-') {
688                if (p->searchExtensionEndCount < 2)
689                        p->searchExtensionEndCount ++;
690                (*src) ++;
691        } else if (!p->tquote && p->searchExtensionEndCount == 2 && **src == '>') {
692                p->extension = FALSE;
693                (*src) ++;
694        } else {
695                if (p->searchExtensionEndCount > 0) {
696                        if (p->extension) {
697                                const gchar *c = "-->";
698
699                                while (p->searchExtensionEndCount) {
700                                        extension_one_char (t, &c);
701                                        p->searchExtensionEndCount --;
702                                }
703                        }
704                }
705                extension_one_char (t, src);
706        }
707}
708
709static void
710in_script_or_style (HTMLTokenizer *t, const gchar **src)
711{
712        struct _HTMLTokenizerPrivate *p = t->priv;
713       
714        /* Allocate memory to store the script or style */
715        if (p->scriptCodeSize + 11 > p->scriptCodeMaxSize)
716                p->scriptCode = g_realloc (p->scriptCode, p->scriptCodeMaxSize += 1024);
717                       
718        if ((**src == '>' ) && ( p->searchFor [p->searchCount] == '>')) {
719                (*src)++;
720                p->scriptCode [p->scriptCodeSize] = 0;
721                p->scriptCode [p->scriptCodeSize + 1] = 0;
722                if (p->script) {
723                        p->script = FALSE;
724                }
725                else {
726                        p->style = FALSE;
727                }
728                g_free (p->scriptCode);
729                p->scriptCode = NULL;
730        }
731        /* Check if a </script> tag is on its way */
732        else if (p->searchCount > 0) {
733                if (tolower (**src) == p->searchFor [p->searchCount]) {
734                        p->searchBuffer [p->searchCount] = **src;
735                        p->searchCount++;
736                        (*src)++;
737                }
738                else {
739                        gchar *c;
740
741                        p->searchBuffer [p->searchCount] = 0;
742                        c = p->searchBuffer;
743                        while (*c)
744                                p->scriptCode [p->scriptCodeSize++] = *c++;
745                        p->scriptCode [p->scriptCodeSize] = **src; (*src)++;
746                        p->searchCount = 0;
747                }
748        }
749        else if (**src == '<') {
750                p->searchCount = 1;
751                p->searchBuffer [0] = '<';
752                (*src)++;
753        }
754        else {
755                p->scriptCode [p->scriptCodeSize] = **src;
756                (*src)++;
757        }
758}
759
[21115]760static gunichar win1252_to_unicode [32] = {
761        0x20ac,
762        0x81,
763        0x201a,
764        0x0192,
765        0x201e,
766        0x2026,
767        0x2020,
768        0x2021,
769        0x02c6,
770        0x2030,
771        0x0160,
772        0x2039,
773        0x0152,
774        0x8d,
775        0x017d,
776        0x8f,
777        0x90,
778        0x2018,
779        0x2019,
780        0x201c,
781        0x201d,
782        0x2022,
783        0x2013,
784        0x2014,
785        0x02dc,
786        0x2122,
787        0x0161,
788        0x203a,
789        0x0153,
790        0x9d,
791        0x017e,
792        0x0178
793};
794
[19538]795static void
796add_unichar (HTMLTokenizer *t, gunichar wc)
797{
798        struct _HTMLTokenizerPrivate *p = t->priv;
799
800        p->utf8_length = 0;
801
[21115]802        /*
803          chars in range 128 - 159 are control characters in unicode,
804          but most browsers treat them as windows 1252
805          encoded characters and translate them to unicode
806          it's broken, but we do the same here
807        */
808        if (wc > 127 && wc < 160)
809                wc = win1252_to_unicode [wc - 128];
810
[19538]811        if (wc != '\0') {
812                p->dest += g_unichar_to_utf8 (wc, p->dest);
813                *(p->dest) = 0;
814        }
815}
816
817static void
818add_byte (HTMLTokenizer *t, const gchar **src)
819{
820        gunichar wc;
821        struct _HTMLTokenizerPrivate *p = t->priv;
822
823        if (p->utf8) {
824                p->utf8_buffer[p->utf8_length] = **src;
825                p->utf8_length++;
826
827                wc = g_utf8_get_char_validated ((const gchar *)p->utf8_buffer, p->utf8_length);
828                if (wc == -1 || p->utf8_length >= (sizeof(p->utf8_buffer)/sizeof(p->utf8_buffer[0]))) {
829                        add_unichar (t, INVALID_CHARACTER_MARKER);
830                        (*src)++;
831                        return;
832                } else if (wc == -2) {
833                        /* incomplete character check again */
834                        (*src)++;
835                        return;
836                }
837        } else {
838                wc = (guchar)**src;
839        }
840
841        add_unichar (t, wc);
842        (*src)++;
843}
844
845static void
846flush_entity (HTMLTokenizer *t)
847{
848        struct _HTMLTokenizerPrivate *p = t->priv;
849        /* ignore the TAG_ESCAPE when flushing */
850        const char *str = p->searchBuffer + 1;
851
852         while (p->searchCount--) {
853                add_byte (t, &str);
854        }
855}
856
857static gboolean
858add_unichar_validated (HTMLTokenizer *t, gunichar uc)
859{
[21115]860        if (g_unichar_validate (uc)) {
[19538]861                add_unichar (t, uc);
862                return TRUE;
[21115]863        }
[19538]864               
865        g_warning ("invalid character value: x%xd", uc);
866        return FALSE;
867}
868
869static void
870in_entity (HTMLTokenizer *t, const gchar **src)
871{
872        struct _HTMLTokenizerPrivate *p = t->priv;
873        gunichar entityValue = 0;
874
875        /* See http://www.mozilla.org/newlayout/testcases/layout/entities.html for a complete entity list,
876           ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
877           (or 'man iso_8859_1') for the character encodings. */
878
879        p->searchBuffer [p->searchCount + 1] = **src;
880        p->searchBuffer [p->searchCount + 2] = '\0';
881                       
882        /* Check for &#0000 sequence */
883        if (p->searchBuffer[2] == '#') {
884                if ((p->searchCount > 1) &&
885                    (!isdigit (**src)) &&
886                    (p->searchBuffer[3] != 'x')) {
887                        /* &#123 */
888                        p->searchBuffer [p->searchCount + 1] = '\0';
889                        entityValue = strtoul (&(p->searchBuffer [3]),
890                                               NULL, 10);
891                        p->charEntity = FALSE;
892                }
893                if ((p->searchCount > 1) &&
894                    (!isalnum (**src)) &&
895                    (p->searchBuffer[3] == 'x')) {
896                        /* &x12AB */
897                        p->searchBuffer [p->searchCount + 1] = '\0';
898                       
899                        entityValue = strtoul (&(p->searchBuffer [4]),
900                                               NULL, 16);
901                        p->charEntity = FALSE;
902                }
903        }
904        else {
905                /* Check for &abc12 sequence */
906                if (!isalnum (**src)) {
907                        p->charEntity = FALSE;
908                        if ((p->searchBuffer [p->searchCount + 1] == ';') ||
909                            (!p->tag)) {
910                                char *ename = p->searchBuffer + 2;
911                                               
912                                p->searchBuffer [p->searchCount + 1] = '\0'; /* FIXME sucks */
913                                entityValue = html_entity_parse (ename, 0);
914                        }
915                }
916                               
917        }
918
919        if (p->searchCount > 13) {
920                /* Ignore this sequence since it's too long */
921                p->charEntity = FALSE;
922                flush_entity (t);
923        }
924        else if (p->charEntity) {
925                                /* Keep searching for end of character entity */
926                p->searchCount++;
927                (*src)++;
928        }
929        else {
930                /*
931                 * my reading of http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.2 makes
932                 * seem correct to always collapse entity references, even in element names
933                 * and attributes.
934                 */
935                if (entityValue) {
936                        if (entityValue != TAG_ESCAPE)
937                                /* make sure the entity value is a valid character value */
938                                if (!add_unichar_validated (t, entityValue))
939                                        add_unichar (t, INVALID_CHARACTER_MARKER);
940
941                        if (**src == ';')
942                                (*src)++;
943                } else {
944                        /* Ignore the sequence, just add it as plaintext */
945                        flush_entity (t);
946                }
947        }
948}
949
950static void
951in_tag (HTMLTokenizer *t, const gchar **src)
952{
953        struct _HTMLTokenizerPrivate *p = t->priv;
954
955        p->startTag = FALSE;
956        if (**src == '/') {
[21115]957                if (p->pending == LFPending  && !p->pre) {
[19538]958                        p->pending = NonePending;
959                }
960        }
961        else if (((**src >= 'a') && (**src <= 'z'))
962                 || ((**src >= 'A') && (**src <= 'Z'))) {
963                                /* Start of a start tag */
964        }
965        else if (**src == '!') {
966                                /* <!-- comment --> */
967        }
968        else if (**src == '?') {
969                                /* <? meta ?> */
970        }
971        else {
972                                /* Invalid tag, just add it */
973                if (p->pending)
974                        html_tokenizer_add_pending (t);
975                add_unichar (t, '<');
976                add_byte (t, src);
977                return;
978        }
979                       
980        if (p->pending)
981                html_tokenizer_add_pending (t);
982
983        if (p->dest > p->buffer) {
984                html_tokenizer_append_token (t, p->buffer, p->dest - p->buffer);
985                p->dest = p->buffer;
986        }
987        add_unichar (t, TAG_ESCAPE);
988        add_unichar (t, '<');
989        p->tag = TRUE;
990        p->searchCount = 1; /* Look for <!-- to start comment */
991}
992
993static void
994start_entity (HTMLTokenizer *t, const gchar **src)
995{
996        struct _HTMLTokenizerPrivate *p = t->priv;
997
998        (*src)++;
999                       
1000        p->discard = NoneDiscard;
1001                       
1002        if (p->pending)
1003                html_tokenizer_add_pending (t);
1004
1005        p->charEntity      = TRUE;
1006        p->searchBuffer[0] = TAG_ESCAPE;
1007        p->searchBuffer[1] = '&';
1008        p->searchCount     = 1;
1009}
1010
1011static void
1012start_tag (HTMLTokenizer *t, const gchar **src)
1013{
1014        (*src)++;
1015        t->priv->startTag = TRUE;
1016        t->priv->discard  = NoneDiscard;
1017}
1018
1019static void
1020end_tag (HTMLTokenizer *t, const gchar **src)
1021{
1022        struct _HTMLTokenizerPrivate *p = t->priv;
1023        gchar *ptr;
1024
1025        p->searchCount = 0; /* Stop looking for <!-- sequence */
1026
1027        add_unichar (t, '>');
1028       
1029        /* Make the tag lower case */
1030        ptr = p->buffer + 2;
1031        if (p->pre || *ptr == '/') {
1032                /* End tag */
1033                p->discard = NoneDiscard;
1034        }
1035        else {
1036                /* Start tag */
1037                /* Ignore CRLFs after a start tag */
1038                p->discard = LFDiscard;
1039        }
1040
1041        while (*ptr && *ptr !=' ') {
1042                *ptr = tolower (*ptr);
1043                ptr++;
1044        }
1045        html_tokenizer_append_token (t, p->buffer, p->dest - p->buffer);
1046        p->dest = p->buffer;
1047                       
1048        p->tag = FALSE;
1049        p->pending = NonePending;
1050        (*src)++;
1051                       
1052        if (strncmp (p->buffer + 2, "pre", 3) == 0) {
1053                p->pre++;
1054        }
1055        else if (strncmp (p->buffer + 2, "/pre", 4) == 0) {
1056                p->pre--;
1057        }
1058        else if (strncmp (p->buffer + 2, "textarea", 8) == 0) {
1059                p->textarea = TRUE;
1060        }
1061        else if (strncmp (p->buffer + 2, "/textarea", 9) == 0) {
1062                p->textarea = FALSE;
1063        }
1064        else if (strncmp (p->buffer + 2, "title", 5) == 0) {
1065                p->title = TRUE;
1066        }
1067        else if (strncmp (p->buffer + 2, "/title", 6) == 0) {
1068                p->title = FALSE;
1069        }
1070        else if (strncmp (p->buffer + 2, "script", 6) == 0) {
1071                p->script = TRUE;
1072                p->searchCount = 0;
1073                p->searchFor = scriptEnd;
1074                p->scriptCode = g_malloc (1024);
1075                p->scriptCodeSize = 0;
1076                p->scriptCodeMaxSize = 1024;
1077        }
1078        else if (strncmp (p->buffer + 2, "style", 5) == 0) {
1079                p->style = TRUE;
1080                p->searchCount = 0;
1081                p->searchFor = styleEnd;
1082                p->scriptCode = g_malloc (1024);
1083                p->scriptCodeSize = 0;
1084                p->scriptCodeMaxSize = 1024;
1085        }
1086        else if (strncmp (p->buffer + 2, "select", 6) == 0) {
1087                p->select = TRUE;
1088        }
1089        else if (strncmp (p->buffer + 2, "/select", 7) == 0) {
1090                p->select = FALSE;
1091        }
[21115]1092        else if (strncmp (p->buffer + 2, "tablesdkl", 9) == 0) {
[19538]1093                html_tokenizer_blocking_push (t, Table);
1094        }
1095        else {
1096                if (p->blocking) {
1097                        const gchar *bn = html_tokenizer_blocking_get_name (t);
1098
1099                        if (strncmp (p->buffer + 1, bn, strlen (bn)) == 0) {
1100                                html_tokenizer_blocking_pop (t);
1101                        }
1102                }
1103        }
1104}
1105
1106static void
1107in_crlf (HTMLTokenizer *t, const gchar **src)
1108{
1109        struct _HTMLTokenizerPrivate *p = t->priv;
1110
1111        if (p->tquote) {
1112                if (p->discard == NoneDiscard)
1113                        p->pending = SpacePending;
1114        }
1115        else if (p->tag) {
1116                p->searchCount = 0; /* Stop looking for <!-- sequence */
1117                if (p->discard == NoneDiscard)
1118                        p->pending = SpacePending; /* Treat LFs inside tags as spaces */
1119        }
1120        else if (p->pre || p->textarea) {
1121                if (p->discard == LFDiscard) {
1122                        /* Ignore this LF */
1123                        p->discard = NoneDiscard; /*  We have discarded 1 LF */
1124                } else {
1125                        /* Process this LF */
1126                        if (p->pending)
1127                                html_tokenizer_add_pending (t);
1128                        p->pending = LFPending;
1129                }
1130        }
1131        else {
1132                if (p->discard == LFDiscard) {
1133                        /* Ignore this LF */
1134                        p->discard = NoneDiscard; /* We have discarded 1 LF */
1135                } else {
1136                        /* Process this LF */
1137                        if (p->pending == NonePending)
1138                                p->pending = LFPending;
1139                }
1140        }
1141        /* Check for MS-DOS CRLF sequence */
1142        if (**src == '\r') {
1143                p->skipLF = TRUE;
1144        }
1145        (*src)++;
1146}
1147
1148static void
1149in_space_or_tab (HTMLTokenizer *t, const gchar **src)
1150{
1151        if (t->priv->tquote) {
1152                if (t->priv->discard == NoneDiscard)
1153                        t->priv->pending = SpacePending;
1154        }
1155        else if (t->priv->tag) {
1156                t->priv->searchCount = 0; /* Stop looking for <!-- sequence */
1157                if (t->priv->discard == NoneDiscard)
1158                        t->priv->pending = SpacePending;
1159        }
1160        else if (t->priv->pre || t->priv->textarea) {
1161                if (t->priv->pending)
1162                        html_tokenizer_add_pending (t);
1163                if (**src == ' ')
1164                        t->priv->pending = SpacePending;
1165                else
1166                        t->priv->pending = TabPending;
1167        }
1168        else {
1169                t->priv->pending = SpacePending;
1170        }
1171        (*src)++;
1172}
1173
1174static void
1175in_quoted (HTMLTokenizer *t, const gchar **src)
1176{
1177        /* We treat ' and " the same in tags " */
1178        t->priv->discard = NoneDiscard;
1179        if (t->priv->tag) {
1180                t->priv->searchCount = 0; /* Stop looking for <!-- sequence */
1181                if ((t->priv->tquote == SINGLE_QUOTE && **src == '\"') /* match " */
1182                    || (t->priv->tquote == DOUBLE_QUOTE && **src == '\'')) {
1183                        add_unichar (t, **src);
1184                        (*src)++;
1185                } else if (*(t->priv->dest-1) == '=' && !t->priv->tquote) {
1186                        t->priv->discard = SpaceDiscard;
1187                        t->priv->pending = NonePending;
1188                                       
1189                        if (**src == '\"') /* match " */
1190                                t->priv->tquote = DOUBLE_QUOTE;
1191                        else
1192                                t->priv->tquote = SINGLE_QUOTE;
1193                        add_unichar (t, **src);
1194                        (*src)++;
1195                }
1196                else if (t->priv->tquote) {
1197                        t->priv->tquote = NO_QUOTE;
1198                        add_byte (t, src);
1199                        t->priv->pending = SpacePending;
1200                }
1201                else {
1202                        /* Ignore stray "\'" */
1203                        (*src)++;
1204                }
1205        }
1206        else {
1207                if (t->priv->pending)
1208                        html_tokenizer_add_pending (t);
1209
1210                add_byte (t, src);
1211        }
1212}
1213
1214static void
1215in_assignment (HTMLTokenizer *t, const gchar **src)
1216{
1217        t->priv->discard = NoneDiscard;
1218        if (t->priv->tag) {
1219                t->priv->searchCount = 0; /* Stop looking for <!-- sequence */
1220                add_unichar (t, '=');
1221                if (!t->priv->tquote) {
1222                        t->priv->pending = NonePending;
1223                        t->priv->discard = SpaceDiscard;
1224                }
1225        }
1226        else {
1227                if (t->priv->pending)
1228                        html_tokenizer_add_pending (t);
1229
1230                add_unichar (t, '=');
1231        }
1232        (*src)++;
1233}
1234
1235inline static void
1236in_plain (HTMLTokenizer *t, const gchar **src)
1237{
1238        struct _HTMLTokenizerPrivate *p = t->priv;
1239       
1240        p->discard = NoneDiscard;
1241        if (p->pending)
1242                html_tokenizer_add_pending (t);
1243                       
1244        if (p->tag) {
1245                if (p->searchCount > 0) {
1246                        if (**src == commentStart[p->searchCount]) {
1247                                p->searchCount++;
1248                                if (p->searchCount == 4) {
1249                                        /* Found <!-- sequence */
1250                                        p->comment = TRUE;
1251                                        p->dest = p->buffer;
1252                                        p->tag = FALSE;
1253                                        p->searchCount = 0;
1254                                        return;
1255                                }
1256                        }
1257                        else {
1258                                p->searchCount = 0; /* Stop lookinf for <!-- sequence */
1259                        }
1260                }
1261        }
1262
1263        add_byte (t, src);
1264}
1265
1266static void
1267html_tokenizer_tokenize_one_char (HTMLTokenizer *t, const gchar **src)
1268{
1269        struct _HTMLTokenizerPrivate *p = t->priv;
1270       
1271        prepare_enough_space (t);
1272
1273        if (p->skipLF && **src != '\n')
1274                p->skipLF = FALSE;
1275
1276        if (p->skipLF)
1277                (*src) ++;
1278        else if (p->comment)
1279                in_comment (t, src);
1280        else if (p->extension)
1281                in_extension (t, src);
1282        else if (p->script || p->style)
1283                in_script_or_style (t, src);
1284        else if (p->charEntity)
1285                in_entity (t, src);
1286        else if (p->startTag)
1287                in_tag (t, src);
1288        else if (**src == '&')
1289                start_entity (t, src);
1290        else if (**src == '<' && !p->tag)
1291                start_tag (t, src);
1292        else if (**src == '>' && p->tag && !p->tquote)
1293                end_tag (t, src);
1294        else if ((**src == '\n') || (**src == '\r'))
1295                in_crlf (t, src);
1296        else if ((**src == ' ') || (**src == '\t'))
1297                in_space_or_tab (t, src);
1298        else if (**src == '\"' || **src == '\'') /* match " ' */
1299                in_quoted (t, src);
1300        else if (**src == '=')
1301                in_assignment (t, src);
1302        else
1303                in_plain (t, src);
1304}
1305
1306static void
1307html_tokenizer_real_write (HTMLTokenizer *t, const gchar *string, size_t size)
1308{
1309        const gchar *src = string;
1310
1311        while ((src - string) < size)
1312                html_tokenizer_tokenize_one_char (t, &src);
1313}
1314
1315static gchar *
1316html_tokenizer_blocking_get_name (HTMLTokenizer *t)
1317{
1318        switch (GPOINTER_TO_INT (t->priv->blocking->data)) {
1319        case Table:
[21115]1320                return "</tabledkdk";
[19538]1321        }
1322       
1323        return "";
1324}
1325
1326static void
1327html_tokenizer_blocking_push (HTMLTokenizer *t, HTMLTokenType tt)
1328{
1329        struct _HTMLTokenizerPrivate *p = t->priv;
1330       
1331        /* block tokenizer - we must block last token in buffers as it was already added */
1332        if (!p->blocking) {
1333                p->tokens_num--;
1334                p->blocking_tokens_num++;
1335        }
1336        p->blocking = g_list_prepend (p->blocking, GINT_TO_POINTER (tt));
1337}
1338
1339static void
1340html_tokenizer_blocking_pop (HTMLTokenizer *t)
1341{
1342        struct _HTMLTokenizerPrivate *p = t->priv;
1343
1344        p->blocking = g_list_remove (p->blocking, p->blocking->data);
1345
1346        /* unblock tokenizer */
1347        if (!p->blocking) {
1348                p->tokens_num += p->blocking_tokens_num;
1349                p->blocking_tokens_num = 0;
1350        }
1351}
1352
1353/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
1354
1355void
1356html_tokenizer_begin (HTMLTokenizer *t, gchar *content_type)
1357{
1358        g_return_if_fail (t && HTML_IS_TOKENIZER (t));
1359
1360        g_signal_emit (t, html_tokenizer_signals [HTML_TOKENIZER_BEGIN_SIGNAL], 0, content_type);
1361}
1362
1363void
1364html_tokenizer_end (HTMLTokenizer *t)
1365{
1366        g_return_if_fail (t && HTML_IS_TOKENIZER (t));
1367
1368        g_signal_emit (t, html_tokenizer_signals[HTML_TOKENIZER_END_SIGNAL], 0);
1369}
1370
1371void
1372html_tokenizer_write (HTMLTokenizer *t, const gchar *str, size_t size)
1373{
1374        HTMLTokenizerClass *klass;
1375
1376        g_return_if_fail (t && HTML_IS_TOKENIZER (t));
1377        klass = HTML_TOKENIZER_CLASS (G_OBJECT_GET_CLASS (t));
1378
1379        if (klass->write)
1380                klass->write (t, str, size);
1381        else
1382                g_warning ("No write method defined.");
1383}
1384
1385gchar *
1386html_tokenizer_peek_token (HTMLTokenizer *t)
1387{
1388        HTMLTokenizerClass *klass;
1389
1390        g_return_val_if_fail (t && HTML_IS_TOKENIZER (t), NULL);
1391
1392        klass = HTML_TOKENIZER_CLASS (G_OBJECT_GET_CLASS (t));
1393
1394        if (klass->peek_token)
1395                return klass->peek_token (t);
1396       
1397        g_warning ("No peek_token method defined.");
1398        return NULL;
1399
1400}
1401
1402gchar *
1403html_tokenizer_next_token (HTMLTokenizer *t)
1404{
1405        HTMLTokenizerClass *klass;
1406
1407        g_return_val_if_fail (t && HTML_IS_TOKENIZER (t), NULL);
1408
1409        klass = HTML_TOKENIZER_CLASS (G_OBJECT_GET_CLASS (t));
1410
1411        if (klass->next_token)
1412                return klass->next_token (t);
1413
1414        g_warning ("No next_token method defined.");
1415        return NULL;
1416}
1417
1418gboolean
1419html_tokenizer_has_more_tokens (HTMLTokenizer *t)
1420{
1421        HTMLTokenizerClass *klass;
1422
1423        g_return_val_if_fail (t && HTML_IS_TOKENIZER (t), FALSE);
1424
1425        klass = HTML_TOKENIZER_CLASS (G_OBJECT_GET_CLASS (t));
1426
1427        if (klass->has_more) {
1428                return klass->has_more (t);
1429        }
1430
1431        g_warning ("No has_more method defined.");
1432        return FALSE;
1433       
1434}
1435
1436HTMLTokenizer *
1437html_tokenizer_clone (HTMLTokenizer *t)
1438{
1439        HTMLTokenizerClass *klass;
1440       
1441        if (t == NULL)
1442                return NULL;
1443        g_return_val_if_fail (HTML_IS_TOKENIZER (t), NULL);
1444
1445        klass = HTML_TOKENIZER_CLASS (G_OBJECT_GET_CLASS (t));
1446
1447        if (klass->clone)
1448                return klass->clone (t);
1449       
1450        g_warning ("No clone method defined.");
1451        return NULL;
1452}
Note: See TracBrowser for help on using the repository browser.