source: trunk/third/gtkhtml/src/htmltokenizer.c @ 18136

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