1 /***************************************************************************
2 * Copyright (C) 2007 by Jesus Arias Fisteus *
3 * jaf@it.uc3m.es *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21 /*
22 * procesador.c
23 *
24 * (Jes�s Arias Fisteus)
25 *
26 * Funciones que se ejecutan seg�n se van encontrando
27 * elementos... en el documento html de entrada.
28 * El analizador sint�ctico (yacc) notifica a este
29 * m�dulo los componentes que se va encontrando en
30 * el documento HTML de entrada, mediante
31 * una interfaz semejante a la de SAX.
32 *
33 * En este m�dulo se llevan a cabo todas las
34 * comprobaciones necesarias y la conversi�n,
35 * y se va generando un �rbol en memoria para
36 * almacenar el documento XHTML resultante
37 * (ver tree.c)
38 *
39 * Se implementan tambi�n las funciones necesarias
40 * para imprimir el documento XHTML a partir
41 * del �rbol.
42 *
43 * Se activa el m�dulo mediante una llamada a
44 * saxStartDocument() y se finaliza mediante saxEndDocument().
45 * Una vez finalizado, se puede volcar la salida con
46 * writeOutput().
47 *
48 */
49
50
51 #include <stdio.h>
52 #include <string.h>
53 #include <strings.h>
54 #include <stdarg.h>
55
56 #include "procesador.h"
57 #include "tree.h"
58 #include "dtd_util.h"
59 #include "mensajes.h"
60 #include "xchar.h"
61 #include "charset.h"
62 #include "params.h"
63 #include "snprintf.h"
64
65 #ifdef SELLAR
66 #define SELLO "translated by html2xhtml - http://www.it.uc3m.es/jaf/html2xhtml/"
67 #endif
68
69 #define MAX_NUM_ERRORS 20
70 #define ID_LIST_SIZE 8192
71
72 extern int is_ascii;
73
74 /* estructura principal */
75 document_t *document; /* �rbol del documento */
76
77 /* estado del m�dulo */
78 static enum {ST_START,ST_PARSING,ST_END} state= ST_START;
79
80 static int doctype_mask= 0x7;
81 static int doctype= -1;
82 static int doctype_locked= 0;
83 static int doctype_detected= 0;
84
85 static tree_node_t *actual_element= NULL; /* elemento actual */
86
87 /* new place recovery mode: variables set to non-null when this
88 mode is active */
89 static int new_place_recovery_on;
90 static tree_node_t *new_place_recovery_elm;
91 static tree_node_t *new_place_recovery_father;
92
93 static int num_errores;
94
95
96 static int doctype_scan(const xchar *data);
97 static int doctype_set(int type, int lock);
98
99 static void set_attributes(tree_node_t *elm, xchar **atts);
100 static void elm_close(tree_node_t *nodo);
101 static int insert_element(tree_node_t *nodo);
102 static void insert_chardata(const xchar *ch, int len, node_type_t type);
103 /* static void elm_meta_scan(tree_node_t *meta); */
104 static int elm_check_prohibitions(int elmid);
105 static int elm_is_father(int elmid, tree_node_t *nodo);
106 static int text_contains_special_chars(const xchar *ch, int len);
107
108 #ifdef SELLAR
109 static void sellar_documento(void);
110 #endif
111
112
113 /* funciones de gesti�n de error */
114 static void err_att_default(tree_node_t *elm, xchar **atts);
115 static int err_html_struct(document_t *document, int elm_id);
116 static int err_child_no_valid(tree_node_t* elm_ptr);
117 static int err_att_req(tree_node_t *elm, int att_id, xchar **atts);
118 static int err_att_value(tree_node_t *elm, int att_id, const xchar *value);
119 static int err_content_invalid(tree_node_t* nodo, int hijos[], int num_hijos);
120 static int err_elm_desconocido(const xchar *nombre);
121
122 /* auxiliares */
123 static tree_node_t* err_aux_insert_elm(int elm_id, const xchar *content, int len);
124 static int remove_duplicate_elm(int elmid, tree_node_t* parent, int hijos[]);
125 static xchar* check_and_fix_att_value(xchar* value);
126
127 /* new output functions */
128 static void write_document(document_t *doc);
129 static int write_node(tree_node_t *node);
130 static int write_element(tree_node_t *node);
131 static int write_chardata(tree_node_t *node);
132 static int write_cdata_sec(tree_node_t *node);
133 static int write_whitespace_or_newline_if_needed(int next_data_len);
134 static int write_chardata_space_preserve(tree_node_t *node);
135 static int write_comment(tree_node_t *node);
136 static int write_start_tag(tree_node_t* nodo);
137 static int write_end_tag(tree_node_t* nodo);
138 static int write_indent(int len, int new_line);
139 static int write_indent_internal(int len, int new_line, int ignore_xml_space);
140 static int write_plain_data(xchar* text, int len);
141 static int cprintf_init(charset_t *to_charset, FILE *file);
142 static int cprintf_close(void);
143 static int cprintf(char *format, ...);
144 static int cwrite(const char *buf, size_t num);
145 static int cputc(int c);
146 static void cflush(void);
147 static size_t ccount_utf8_chars(const char *buf, size_t num_bytes);
148
149 /* element insertion variables */
150 static tree_node_t *ins_html;
151 static tree_node_t *ins_head;
152 static tree_node_t *ins_body;
153
154
155 /* special HTML parameters specification */
156 static char lt_escaped[]="<";
157 static char amp_escaped[]="&";
158 static char gt_escaped[]=">";
159 static char lt_normal[]="<";
160 static char amp_normal[]="&";
161 static char gt_normal[]=">";
162 static char eol_unix[] = {0x0a, 0};
163 static char eol_dos[] = {0x0a, 0x0d, 0};
164
165 /* escaped or normal characters */
166 static char* lt;
167 static char* amp;
168 static char* gt;
169 static char* eol;
170 static size_t eol_len; /* initialized to strlen(eol) in writeOutput */
171
172 static int escape_chars;
173
174 /* variables para gesti�n de IDs */
175 static xchar* id_list[ID_LIST_SIZE];
176 static int id_list_num;
177 static void set_node_att(tree_node_t *nodo, int att_id, xchar *value, int is_valid);
178 static int id_search(const xchar *value);
179
180
181
182 /**
183 * inicia la conversi�n
184 *
185 */
saxStartDocument(void)186 void saxStartDocument(void)
187 {
188 EPRINTF("SAX.startDocument()\n");
189
190 if (state!=ST_START || state!=ST_END) {
191
192 state= ST_PARSING;
193
194 /* reset variables */
195 doctype= -1;
196 doctype_locked= 0;
197 doctype_detected= 0;
198 actual_element= NULL;
199 num_errores= 0;
200 ins_html= NULL;
201 ins_head= NULL;
202 ins_body= NULL;
203 id_list_num = 0;
204 new_place_recovery_on = 0;
205
206 /* tree_init(); */
207
208 document= new_tree_document(doctype, -1);
209 }
210
211 /* if (param_charset) */
212 /* strcpy(document->encoding, param_charset_out); */
213 if (param_doctype != -1)
214 doctype_set(param_doctype, 1);
215 }
216
217
218
219 /**
220 * finaliza la conversi�n
221 *
222 * si hay alg�n error, finaliza mediante la macro exit
223 */
saxEndDocument(void)224 void saxEndDocument(void)
225 {
226 EPRINTF("SAX.endDocument()\n");
227
228 if (!document) EXIT("no document found!");
229 if (!document->inicio) EXIT("document without root html element");
230
231 if (actual_element)
232 for ( ; actual_element; actual_element= actual_element->padre)
233 elm_close(actual_element);
234
235 if (!document->inicio) EXIT("document discarded");
236
237 /* if (!document->encoding[0] && !is_ascii && param_charset_default) { */
238 /* strcpy(document->encoding,param_charset_default); */
239 /* INFORM("Default charset set"); */
240 /* } */
241
242 #ifdef SELLAR
243 sellar_documento();
244 #endif
245
246 if (state== ST_PARSING) state= ST_END;
247 if (state!= ST_END) EXIT("bad state");
248 }
249
250
251 /**
252 * se recibe un tag de inicio de elemento
253 *
254 * atts ser� un array de cadenas, que sucesivamente
255 * contendr� pares nombre/valor de atributos
256 *
257 */
saxStartElement(const xchar * fullname,xchar ** atts)258 void saxStartElement(const xchar *fullname, xchar **atts)
259 {
260 int elm_ptr;
261 xchar elm_name[ELM_NAME_LEN];
262 tree_node_t *nodo;
263
264 #ifdef MSG_DEBUG
265 #ifndef PRO_NO_DEBUG
266 int i;
267
268 EPRINTF1("SAX.startElement(%s", (char *) fullname);
269 if (atts != NULL) {
270 for (i = 0;(atts[i] != NULL);i++) {
271 EPRINTF1(", %s='", atts[i++]);
272 if (atts[i] != NULL)
273 EPRINTF1("%s'", atts[i]);
274 }
275 }
276 EPRINTF(")\n");
277 #endif
278 #endif
279
280 if (state!=ST_PARSING) {
281 if (state == ST_END)
282 INFORM("Element discarded after the html end tag");
283 return;
284 }
285
286 /* se pasa a min�sculas */
287 xtolower(elm_name,fullname,ELM_NAME_LEN);
288
289 /* busca el elemento */
290 if ((elm_ptr= dtd_elm_search(elm_name))<0) {
291 if ((elm_ptr= err_elm_desconocido(elm_name))<0) {
292 INFORM("elemento no encontrado\n");
293 return;
294 }
295 }
296
297 if ((!actual_element) && (doctype<0)) doctype_set(XHTML_TRANSITIONAL, 0);
298
299
300 /* �vale para este DTD? */
301 if (!(elm_list[elm_ptr].environment & doctype_mask)) {
302 /* puede que sea algo relacionado con frames, y que no se declare
303 * como <frameset>
304 */
305 if ((elm_ptr==ELMID_FRAMESET)||(elm_ptr==ELMID_FRAME)) {
306 tree_node_t *html, *elm;
307 int res;
308
309 elm= NULL;
310
311 if (document->inicio) {
312 html= document->inicio;
313 for (elm=html->cont.elemento.hijo; elm && (ELM_ID(elm)!=ELMID_BODY);
314 elm=elm->sig);
315 }
316 /* si no existe BODY, se sit�a como frameset */
317 if (!elm) res= doctype_set(XHTML_FRAMESET,1);
318 if (elm || (res==-1)) {
319 INFORM("elemento de tipo frameset no v�lido en este DTD (descartado)");
320 return;
321 }
322 } else {
323 INFORM("elemento no v�lido en este DTD (descartado)\n");
324 return;
325 }
326 }
327
328
329 if (!actual_element) {
330 if (elm_ptr != ELMID_HTML)
331 if (!err_html_struct(document, elm_ptr))
332 EXIT("Does not begin with 'html'! Incorrect structure\n");
333 }
334
335
336 /* establece un nodo para el elemento */
337 nodo= new_tree_node(Node_element);
338 nodo->cont.elemento.elm_id= elm_ptr;
339
340
341 /* a lo mejor no se puede insertar por haber sido insertado por
342 * el conversor previamente, se usan los atributos del nuevo
343 */
344 if ((elm_ptr==ELMID_HTML)&&(ins_html)) {
345 nodo= ins_html;
346 actual_element= nodo;
347 ins_html= NULL;
348 DEBUG("insertado previamente por el conversor");
349 } else if ((elm_ptr==ELMID_HEAD)&&(ins_head)) {
350 nodo= ins_head;
351 actual_element= nodo;
352 ins_head= NULL;
353 DEBUG("insertado previamente por el conversor");
354 } else if ((elm_ptr==ELMID_BODY)&&(ins_body)) {
355 nodo= ins_body;
356 actual_element= nodo;
357 ins_body= NULL;
358 DEBUG("insertado previamente por el conversor");
359 } else
360 /* inserta el nodo */
361 if (insert_element(nodo) <0 ) {
362 INFORM("elemento no insertado");
363 return;
364 }
365
366 /* atributos */
367 set_attributes(nodo, atts);
368
369 /* If meta with att-equiv, remove it: it is unnecessary in XML */
370 if (elm_ptr == ELMID_META) {
371 att_node_t* att = tree_node_search_att(nodo, ATTID_HTTP_EQUIV_0);
372 if (!att) {
373 att = tree_node_search_att(nodo, ATTID_HTTP_EQUIV_1);
374 }
375 if (att) {
376 tree_unlink_node(nodo);
377 }
378 }
379 }
380
381
382
383 /**
384 * se encuentra un tag de finalizaci�n de elemento
385 *
386 */
saxEndElement(const xchar * name)387 void saxEndElement(const xchar *name)
388 {
389 int elm_ptr;
390 xchar elm_name[ELM_NAME_LEN];
391 tree_node_t *nodo;
392
393 EPRINTF1("SAX.endElement(%s)\n",name);
394
395 if (state!=ST_PARSING) return;
396
397 xtolower(elm_name,name,ELM_NAME_LEN);
398 if ((elm_ptr= dtd_elm_search(elm_name))<0) {
399 if ((elm_ptr= err_elm_desconocido(elm_name))<0) {
400 INFORM("elemento no encontrado\n");
401 return;
402 }
403 }
404
405 /* busca al antecesor que coincida */
406 for (nodo=actual_element; (nodo) && (ELM_ID(nodo)!=elm_ptr);
407 nodo=nodo->padre);
408 /*elm_close(nodo);*/
409
410 /* nodo= tree_search_elm_up(actual_element, elm_ptr); */
411
412 if (!nodo) DEBUG("cerrado elemento no abierto")
413 else {
414 tree_node_t *p;
415
416 /* se cierran todos los nodos hasta llegar a este */
417 for (p=actual_element; p != nodo; p=p->padre) elm_close(p);
418
419 /* cierra el nodo y actualiza actual_element */
420 elm_close(nodo);
421 if (!new_place_recovery_on || new_place_recovery_elm != actual_element) {
422 actual_element= nodo->padre;
423 } else {
424 new_place_recovery_on = 0;
425 actual_element = new_place_recovery_father;
426 }
427 if (!actual_element) state= ST_END;
428 }
429 }
430
431
432
433 /**
434 * se encuentra una referencia a car�cter o
435 * a entidad
436 *
437 */
saxReference(const xchar * name)438 void saxReference(const xchar *name)
439 {
440 EPRINTF1("SAX.reference(%s)\n",name);
441
442 if (state!=ST_PARSING || !actual_element) return;
443
444 /* si es una referencia a entidad, se comprueba que sea v�lida */
445 if (name[1]!='#')
446 if (dtd_ent_search(name)==-1) {
447 if (!strcmp(name, "%")) {
448 insert_chardata("%", 1, Node_chardata);
449 } else
450 INFORM("referencia a entidad desconocida");
451 return;
452 }
453
454 /* en este punto, o es una referencia a caracter o es
455 * una referencia a entidad conocida:
456 * se introduce en el documento xhtml
457 */
458 insert_chardata(name,xstrlen(name), Node_chardata);
459 }
460
461
462
463 /**
464 * Notificaci�n de informaci�n textual
465 * Se recibe la cadena con el texto
466 * y el n�mero de caracteres
467 *
468 */
saxCharacters(const xchar * ch,int len)469 void saxCharacters(const xchar *ch, int len)
470 {
471 EPRINTF2("SAX.characters(%d)[%s]\n",len,ch);
472
473 if (state!=ST_PARSING || !actual_element) return;
474
475 insert_chardata(ch, len, Node_chardata);
476 }
477
478 /**
479 * CDATA section (when input is XML)
480 *
481 */
saxCDataSection(const xchar * ch,int len)482 void saxCDataSection(const xchar *ch, int len)
483 {
484 node_type_t type;
485
486 EPRINTF2("SAX.cdatasection(%d)[%s]\n",len,ch);
487
488 if (state!=ST_PARSING || !actual_element) return;
489
490 /* by default, mark as CDATA section */
491 type = Node_cdata_sec;
492
493 /* but if it is inside style and does not contain & or <
494 * then insert it as normal character data
495 */
496 if (ELM_ID(actual_element) == ELMID_STYLE
497 && !text_contains_special_chars(ch, len))
498 type = Node_chardata;
499
500 insert_chardata(ch, len, type);
501 }
502
saxWhiteSpace()503 void saxWhiteSpace()
504 {
505 EPRINTF("SAX.whitespace()\n");
506
507 if (state!=ST_PARSING) return;
508
509 /* insert the whitespace only if the actual element has
510 mixed contenttype. If not, ignore it because it is irrelevant. */
511 if (actual_element
512 && ELM_PTR(actual_element).contenttype[doctype] == CONTTYPE_MIXED) {
513 xchar *data = tree_malloc(1);
514 data[0] = 0x20; /* a whitespace */
515 insert_chardata(data, 1, Node_chardata);
516 }
517 }
518
519
520 /**
521 * Se encuentra un comentario
522 * Se recibe el texto del comentario
523 *
524 */
saxComment(const xchar * value)525 void saxComment(const xchar *value)
526 {
527 /* tree_node_t *nodo; */
528
529 EPRINTF1("SAX.comment(%s)\n",value);
530
531 if (state!=ST_PARSING) {
532 INFORM("Comment discarded");
533 return;
534 }
535
536 if (actual_element)
537 tree_link_data_node(Node_comment, actual_element, value, strlen(value));
538 else
539 INFORM("comentario antes de elemento ra�z");
540 }
541
542
543
544 /**
545 * se encuentra marcaci�n <!DOCTYPE
546 * se recibe todo el contenido
547 *
548 */
saxDoctype(const xchar * data)549 void saxDoctype(const xchar *data)
550 {
551 int doc;
552
553 EPRINTF("SAX.doctype\n");
554
555 #if 0
556 fprintf(stderr, "SAX.doctype(");
557 #endif
558
559 if (state!=ST_PARSING) return;
560
561 if (!doctype_detected) {
562 doctype_detected= 1;
563 /* try to guess the document type */
564 if ((doc=doctype_scan(data))!=-1) doctype_set(doc,0);
565 } else {
566 INFORM("More than one doctype found: only the first one is processed");
567 }
568 }
569
570
571 /**
572 * XML Processing Instruction
573 *
574 * e.g. <?xml version="1.0" encoding="iso-8859-1" ?>
575 *
576 */
saxXmlProcessingInstruction(const xchar * fullname,xchar ** atts)577 void saxXmlProcessingInstruction(const xchar *fullname, xchar **atts)
578 {
579 /* xchar pi_name[ELM_NAME_LEN]; */
580 /* xchar att_name[ATT_NAME_LEN]; */
581 /* int i, j; */
582 /* int encoding_set; */
583
584 #ifdef MSG_DEBUG
585 #ifndef PRO_NO_DEBUG
586 EPRINTF1("SAX.xmlProcessingInstruction(%s", (char *) fullname);
587 if (atts != NULL) {
588 int k;
589 for (k = 0;(atts[k] != NULL);k++) {
590 EPRINTF1(", %s='", atts[k++]);
591 if (atts[k] != NULL)
592 EPRINTF1("%s'", atts[k]);
593 }
594 }
595 EPRINTF(")\n");
596 #endif
597 #endif
598
599 /* encoding_set = 0; */
600
601 /* /\* convert to lowercase the PI name *\/ */
602 /* xtolower(pi_name, fullname, ELM_NAME_LEN); */
603
604 /* if (!strncmp("xml", pi_name, 4)) { */
605 /* /\* <?xml ... ?> Look for encoding *\/ */
606 /* if (atts) { */
607 /* for (i=0; atts[i]; i+=2) { */
608 /* xtolower(att_name, atts[i], ATT_NAME_LEN); */
609 /* if (!strncmp("encoding", att_name, 9)) { */
610 /* if (!document->encoding[0] && xstrnlen(atts[i+1], 33) < 32) { */
611 /* /\* convert encoding to uppercase *\/ */
612 /* for (j=0; atts[i+1][j]; j++) { */
613 /* if ((atts[i+1][j]>='a')&&(atts[i+1][j]<='z')) */
614 /* document->encoding[j]= atts[i+1][j] - 0x20; */
615 /* else */
616 /* document->encoding[j]= atts[i+1][j]; */
617 /* } */
618 /* document->encoding[j]= 0; */
619 /* encoding_set = 1; */
620 /* DEBUG("saxXmlProcessingInstruction()"); */
621 /* EPRINTF1(" encoding=%s\n",document->encoding); */
622 /* } else { */
623 /* INFORM("Discarded encoding in saxXmlProcessingInstruction()"); */
624 /* EPRINTF1(" encoding=%s\n", atts[i+1]); */
625 /* } */
626 /* } */
627 /* } /\* for *\/ */
628 /* } /\* if (atts) *\/ */
629 /* if (!encoding_set) { */
630 /* strcpy(document->encoding, "UTF-8"); */
631 /* } */
632 /* } /\* if <?xml *\/ */
633 }
634
635
636
saxError(xchar * data)637 void saxError(xchar *data)
638 {
639 EPRINTF1("SAX.error(%s)\n",data);
640 num_errores++;
641
642 INFORM("error en el documento de entrada");
643 EPRINTF2(" texto '%s' [%x...] no emparejado\n",data,data[0]);
644
645 if (num_errores>MAX_NUM_ERRORS)
646 EXIT("too many errors in the lexer");
647 }
648
649
650 /**
651 * Write the output XHTML document
652 *
653 * returns 0 (OK) or < 0 (error)
654 */
writeOutput()655 int writeOutput()
656 {
657 if (state != ST_END) return -1;
658 if (!document) return -2;
659
660 escape_chars= param_cgi_html_output;
661 if (!param_cgi_html_output) {
662 gt= gt_normal;
663 lt= lt_normal;
664 amp= amp_normal;
665 } else {
666 gt= gt_escaped;
667 lt= lt_escaped;
668 amp= amp_escaped;
669 }
670
671 if (!param_crlf_eol)
672 eol = eol_unix;
673 else
674 eol = eol_dos;
675 eol_len = strlen(eol);
676
677 /* vuelca la salida */
678 write_document(document);
679 return 0;
680 }
681
682 /*
683 * libera la memoria asignada en la conversi�n
684 *
685 */
freeMemory()686 void freeMemory()
687 {
688 tree_free();
689 state = ST_END;
690 }
691
692
693
694
695 /*
696 * -------------------------------------------------------------------
697 * funciones internas
698 * -------------------------------------------------------------------
699 *
700 */
701
702 /*
703 * busca el tipo de salida a generar
704 * y el tipo de entrada a partir de
705 * <!DOCTYPE ...>
706 *
707 */
doctype_scan(const xchar * data)708 static int doctype_scan(const xchar *data)
709 {
710 char buffer[512];
711 int i;
712
713 /* if the document is already XHTML, maintain its doctype
714 * by default
715 */
716 strncpy(buffer, data, 512);
717 for (i = 0; i < DTD_NUM; i++) {
718 if (xsearch(buffer, dtd_string[i]))
719 return i;
720 }
721
722 /* if not, try to detect HTML doctypes */
723 xtolower(buffer, data, 512);
724 if (xsearch(buffer, "transitional")) return XHTML_TRANSITIONAL;
725 else if (xsearch(buffer, "loose.dtd")) return XHTML_TRANSITIONAL;
726 else if (xsearch(buffer, "strict")) return XHTML_STRICT;
727 else if (xsearch(buffer, "frameset")) return XHTML_FRAMESET;
728 else return -1;
729 }
730
731
732
733 /*
734 * establece doctype al valor 'type', que puede
735 * ser: XHTML_TRANSITIONAL, XHTML_STRICT, XHTML_FRAMESET
736 *
737 * si 'lock', no se permite volver a cambiarlo
738 *
739 * si hace el cambio, devuelve 0
740 * si el cambio no se puede realizar por estar bloqueado, devuelve -1
741 *
742 */
doctype_set(int type,int lock)743 static int doctype_set(int type, int lock)
744 {
745 char msg[256];
746
747 if (doctype_locked) return -1;
748
749 sprintf(msg,"establecido doctype %d",type);
750 INFORM(msg);
751
752 if (lock) doctype_locked= 1;
753 doctype= type;
754 doctype_mask= DTD_MASK(type);
755 if (document) document->xhtml_doctype= type;
756
757 return 0;
758 }
759
760
761
762
763
764 /**
765 * codifica en el nodo de elemento los
766 * atributos de la lista, que viene en pares
767 * nombre-valor
768 *
769 * por ejemplo:
770 * atts[0] -> nombre del atributo 1
771 * atts[1] -> valor del atributo 1
772 * atts[2] -> nombre del atributo 2
773 * atts[3] -> valor del atributo 2
774 * (...)
775 *
776 */
set_attributes(tree_node_t * elm,xchar ** atts)777 static void set_attributes(tree_node_t *elm, xchar **atts)
778 {
779 int elm_ptr;
780 int i;
781 xchar att_name[ATT_NAME_LEN];
782 int att_ptr;
783
784 elm_ptr= elm->cont.elemento.elm_id;
785
786 /* atributos */
787 if (atts) {
788 for (i=0; atts[i]; i+=2) {
789 xtolower(att_name,atts[i],ATT_NAME_LEN);
790 if ((att_ptr=
791 dtd_att_search_list(att_name,elm_list[elm_ptr].attlist[doctype])
792 )<0) {
793 INFORM("");
794 EPRINTF1("\"%s\" atributo no encontrado en este DTD\n",att_name);
795 }
796 else {
797 /* check that att is not already in the element */
798 if (!tree_node_search_att(elm, att_ptr)) {
799
800 /* se comprueba si el valor est� bien formado, y se corrige si no */
801 atts[i+1] = check_and_fix_att_value(atts[i+1]);
802
803 /* atributo v�lido ��comprobar valor y tipo!! */
804 switch (dtd_att_is_valid(att_ptr, atts[i+1])) {
805 case 1: /* ok */
806 set_node_att(elm, att_ptr, atts[i+1], 1);
807 break;
808 case 2: /* ok, pero en min�sculas */
809 {
810 xchar lowercase[128];
811 xtolower(lowercase, atts[i+1], 128);
812 set_node_att(elm, att_ptr, lowercase, 1);
813 }
814 break;
815 case 0:
816 /* valor incorrecto, se intenta arreglar */
817 if (!err_att_value(elm, att_ptr, atts[i+1])) {
818 INFORM("");
819 EPRINTF1("\"%s\" atributo con valor incorrecto\n",att_name);
820 }
821 break;
822 }
823 }
824 } /* else */
825 } /* for i */
826 }
827
828
829 /*
830 * ahora hay que comprobar que no falte ning�n valor que
831 * sea #REQUIRED
832 *
833 */
834 for (i=0, att_ptr= elm_list[elm_ptr].attlist[doctype][0];
835 att_ptr>=0;
836 att_ptr= elm_list[elm_ptr].attlist[doctype][++i]) {
837
838 if (att_list[att_ptr].defaultDecl== DEFDECL_REQUIRED) {
839 if (!tree_node_search_att(elm, att_ptr)) {
840 /* intenta arreglarlo */
841 if (!err_att_req(elm, att_ptr,atts)) {
842 WARNING("atributo obligatorio no especificado");
843 EPRINTF1(" \"%s\"\n",att_list[att_ptr].name);
844 }
845 }
846 } /* if REQUIRED */
847 } /* for */
848
849
850 /* rutina por defecto de detecci�n y
851 * correcci�n de otros errores
852 */
853 err_att_default(elm,atts);
854
855
856
857
858 }
859
860
861
862
863
864
865
866
867 /*
868 * inserta un nodo de elemento en el �rbol
869 * del documento
870 *
871 * comprueba si es un contenido v�lido y busca
872 * d�nde insertarlo si no lo es
873 *
874 * si no le encuentra hueco, devuelve <0
875 *
876 */
insert_element(tree_node_t * nodo)877 int insert_element(tree_node_t *nodo)
878 {
879 int elm_ptr;
880 int insertado;
881
882 elm_ptr= ELM_ID(nodo);
883
884 if (!actual_element) {
885 link_root_node(document, nodo);
886 insertado= 1;
887 }
888 else {
889 if (dtd_can_be_child(elm_ptr,ELM_ID(actual_element),doctype)) {
890 /* comprueba las prohibiciones de elementos de XHTML */
891 if (!elm_check_prohibitions(elm_ptr)) {
892 DEBUG("elemento descartado: no cumple las excepciones de XHTML");
893 insertado= 0;
894
895 } else insertado= 1;
896 }
897 else insertado= err_child_no_valid(nodo);
898
899
900
901 if (insertado == 1) {
902 link_node(nodo, actual_element, LINK_MODE_CHILD);
903 DEBUG("insert_element()");
904 EPRINTF1(" insertado elemento %s correctamente\n",ELM_PTR(nodo).name);
905 } else if (insertado == 0) {
906 DEBUG("elemento descartado:");
907 EPRINTF1(" %s\n",ELM_PTR(nodo).name);
908 }
909 /* if (insertado == 2) -> inserted by err_child_no_valid in another place */
910
911 } /* else */
912
913 /* si es un elemento vac�o, se cierra */
914 if (insertado) {
915 if (elm_list[elm_ptr].contenttype[doctype]!=CONTTYPE_EMPTY) {
916 actual_element= nodo;
917 }
918 else elm_close(nodo);
919 }
920
921 /* si es html se inserta xmlns */
922 if (insertado && elm_ptr== ELMID_HTML) {
923 set_node_att(nodo, ATTID_XMLNS,
924 dtd_att_read_buffer(att_list[ATTID_XMLNS].defaults), 1);
925 }
926
927 if (insertado) return 0;
928 else return -1;
929 }
930
931
932
933 /*
934 * realiza a cabo tareas relacionadas con el cierre de
935 * un elemento
936 *
937 *
938 */
elm_close(tree_node_t * nodo)939 static void elm_close(tree_node_t *nodo)
940 {
941 DEBUG("elm_close()");
942 EPRINTF1("cerrando elemento %s\n",ELM_PTR(nodo).name);
943
944 if (ELM_PTR(nodo).contenttype[doctype]==CONTTYPE_CHILDREN) {
945 /* si es de tipo child se comprueba su contenido */
946 int content[16384];
947 int i, num;
948 tree_node_t *elm;
949
950 for (i=0, num=0, elm= nodo->cont.elemento.hijo;
951 (i<16384) && elm;
952 i++, elm= elm->sig)
953 if (elm->tipo==Node_element) content[num++]= ELM_ID(elm);
954
955
956 if (i==16384) EXIT("internal error: variable 'i' overflow");
957 if (dtd_is_child_valid(ELM_PTR(nodo).contentspec[doctype],content,num)!=1) {
958 /* children no v�lido: a intentar corregirlo */
959 if (!err_content_invalid(nodo,content,num))
960 WARNING("invalid element content");
961 }
962 else DEBUG("child v�lido");
963 }
964 }
965
966
967
968
969
insert_chardata(const xchar * ch,int len,node_type_t type)970 static void insert_chardata(const xchar *ch, int len, node_type_t type)
971 {
972 /* tree_node_t *nodo; */
973
974 if (!actual_element) {
975 num_errores++;
976 if (num_errores>MAX_NUM_ERRORS) EXIT("too many errors");
977 return;
978 }
979
980 /* �si el elemento no es de tipo mixed? */
981 if (ELM_PTR(actual_element).contenttype[doctype]!=CONTTYPE_MIXED) {
982 if (dtd_can_be_child(ELMID_P, ELM_ID(actual_element),doctype)) {
983 /* inserta un elemento <p> como contenedor */
984 tree_node_t *p;
985
986 p= new_tree_node(Node_element);
987 p->cont.elemento.elm_id= ELMID_P;
988 link_node(p, actual_element, LINK_MODE_CHILD);
989 actual_element= p;
990 DEBUG("[ERR] insertado elemento <p> para contener PCDATA");
991 } else
992 /* si el padre es <ul> o <ol>, se inserta <li> */
993 if((ELM_ID(actual_element)==ELMID_UL)
994 ||(ELM_ID(actual_element)==ELMID_OL)) {
995 actual_element= err_aux_insert_elm(ELMID_LI,NULL,0);
996 DEBUG("[ERR] insertado elemento li");
997 } else {
998 INFORM("intento de introducir datos en tipo no mixed");
999 return;
1000 }
1001 }
1002
1003 tree_link_data_node(type, actual_element, ch, len);
1004
1005 /* /\* se crea un nodo para los datos *\/ */
1006 /* nodo= new_tree_node(Node_chardata); */
1007
1008 /* link_node(nodo, actual_element, LINK_MODE_CHILD); */
1009
1010 /* /\* le pasa los datos al nodo *\/ */
1011 /* tree_set_node_data(nodo, ch, len); */
1012 }
1013
1014
1015
1016
1017 /*
1018 * Returns 1 if at least one '&' or one '<' appear in text
1019 * or 0 otherwise
1020 */
text_contains_special_chars(const xchar * ch,int len)1021 static int text_contains_special_chars(const xchar *ch, int len)
1022 {
1023 int i;
1024
1025 for (i = 0; i < len; i++) {
1026 if (ch[i] == '&' || ch[i] == '<')
1027 return 1;
1028 }
1029 return 0;
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040 /*
1041 * recibe un elemento de tipo META
1042 *
1043 * - si contiene http-equiv (Content-Type) se busca
1044 * informaci�n sobre el sistema de codificaci�n de
1045 * caracteres
1046 *
1047 */
1048 /* static void elm_meta_scan(tree_node_t *meta) */
1049 /* { */
1050 /* att_node_t *att; */
1051
1052 /* DEBUG("elm_meta_scan()"); */
1053
1054 /* /\* busca http-equiv *\/ */
1055 /* att= tree_node_search_att(meta,ATTID_HTTP_EQUIV_0); */
1056 /* if (!att) att= tree_node_search_att(meta,ATTID_HTTP_EQUIV_1); */
1057
1058 /* if (att) { */
1059 /* char *attval,*cad; */
1060 /* att_node_t *content; */
1061
1062 /* attval= tree_index_to_ptr(att->valor); */
1063 /* if (attval && !strcasecmp(attval,"Content-Type") */
1064 /* && ((content=tree_node_search_att(meta,ATTID_CONTENT)))){ */
1065
1066 /* attval= tree_index_to_ptr(content->valor); */
1067 /* if (attval && ((cad=strstr(attval,"charset=")))) { */
1068 /* for ( ; *cad!='='; cad++); */
1069 /* if (!document->encoding[0] && (xstrnlen(cad, 33)<32)) { */
1070 /* int i; */
1071 /* cad++; */
1072 /* /\* pasa a may�sculas *\/ */
1073 /* for (i=0; cad[i]; i++) */
1074 /* if ((cad[i]>='a')&&(cad[i]<='z')) */
1075 /* document->encoding[i]= cad[i] - 0x20; */
1076 /* else document->encoding[i]= cad[i]; */
1077 /* document->encoding[i]= 0; */
1078
1079 /* DEBUG("elm_meta_scan()"); */
1080 /* EPRINTF1(" encoding=%s\n",document->encoding); */
1081 /* } else { */
1082 /* INFORM("elm_meta_scan()"); */
1083 /* EPRINTF1(" se descarta encoding=%s\n",cad); */
1084 /* } */
1085 /* } */
1086 /* } */
1087 /* } /\* if http-equiv *\/ */
1088 /* } */
1089
1090
1091
1092
1093 #if 0
1094 static void sellar_documento(void)
1095 {
1096 tree_node_t *sello;
1097 int att_ptr;
1098
1099 sello= new_tree_node(Node_element);
1100 sello->cont.elemento.elm_id= ELMID_META;
1101
1102 att_ptr=dtd_att_search_list("name",elm_list[ELMID_META].attlist[doctype]);
1103 set_node_att(sello,att_ptr,"filter",1);
1104
1105 att_ptr=dtd_att_search_list("content",elm_list[ELMID_META].attlist[doctype]);
1106 #ifdef SELLAR
1107 set_node_att(sello,att_ptr,SELLO,1);
1108 #endif
1109
1110 if (document->inicio && document->inicio->cont.elemento.hijo &&
1111 ELM_ID(document->inicio->cont.elemento.hijo)==ELMID_HEAD)
1112 link_node(sello,document->inicio->cont.elemento.hijo,LINK_MODE_CHILD);
1113 }
1114 #endif
1115
1116
1117
1118
1119 /*
1120 * chequea si se cumplen las excepciones recogidas en la norma
1121 * de XHTML en el ap�ndice B de la norma de XHTML 1.0
1122 *
1123 * devuelve 1 si es correcto o 0 si incumple la norma
1124 *
1125 */
elm_check_prohibitions(int elmid)1126 static int elm_check_prohibitions(int elmid)
1127 {
1128 switch (elmid) {
1129 case ELMID_A:
1130 return !elm_is_father(ELMID_A,actual_element);
1131 case ELMID_IMG:
1132 case ELMID_OBJECT:
1133 case ELMID_BIG:
1134 case ELMID_SMALL:
1135 case ELMID_SUB:
1136 case ELMID_SUP:
1137 return !elm_is_father(ELMID_PRE,actual_element);
1138 case ELMID_INPUT:
1139 case ELMID_SELECT:
1140 case ELMID_TEXTAREA:
1141 case ELMID_BUTTON:
1142 case ELMID_FIELDSET:
1143 case ELMID_IFRAME:
1144 case ELMID_ISINDEX:
1145 return !elm_is_father(ELMID_BUTTON,actual_element);
1146 case ELMID_LABEL:
1147 return ((!elm_is_father(ELMID_LABEL,actual_element))
1148 &&(!elm_is_father(ELMID_BUTTON,actual_element)));
1149 case ELMID_FORM:
1150 return ((!elm_is_father(ELMID_BUTTON,actual_element))
1151 &&(!elm_is_father(ELMID_FORM,actual_element)));
1152 default:
1153 return 1;
1154 }
1155 }
1156
1157
1158 /*
1159 * comprueba si alg�n elemento que contenga a 'nodo',
1160 * o el propio elemento 'nodo', tiene identificador 'elmid'
1161 *
1162 * devuelve 1 si es as�, o 0 si no
1163 *
1164 */
elm_is_father(int elmid,tree_node_t * nodo)1165 static int elm_is_father(int elmid, tree_node_t *nodo)
1166 {
1167 for ( ; nodo ; nodo= nodo->padre)
1168 if (ELM_ID(nodo)==elmid) return 1;
1169
1170 return 0;
1171 }
1172
1173
1174
set_node_att(tree_node_t * nodo,int att_id,xchar * value,int is_valid)1175 void set_node_att(tree_node_t *nodo, int att_id, xchar *value, int is_valid)
1176 {
1177 xchar* new_value = value;
1178 xchar *tmp_value;
1179 int len = strlen(value);
1180
1181 if (att_list[att_id].attType== ATTTYPE_ID) {
1182 /* si es ID, comprueba que no se repita y registra el valor */
1183 while (id_search(new_value)) {
1184 len++;
1185 tmp_value = (xchar*) tree_malloc(len + 1);
1186 strcpy(tmp_value, new_value);
1187 tmp_value[len - 1] = '_';
1188 tmp_value[len] = 0;
1189 new_value = tmp_value;
1190 }
1191
1192 /* se a�ade el valor a la lista */
1193 if (id_list_num < ID_LIST_SIZE - 1)
1194 id_list[id_list_num++] = new_value;
1195 else
1196 WARNING("lista de atributos id desbordada");
1197 /* jjjjjjjj */
1198 }
1199
1200 tree_set_node_att(nodo, att_id, new_value, is_valid);
1201 }
1202
id_search(const xchar * value)1203 int id_search(const xchar* value)
1204 {
1205 int i;
1206
1207 for (i = 0; i < id_list_num; i++) {
1208 if (!strcmp(id_list[i], value))
1209 return 1;
1210 }
1211
1212 return 0;
1213 }
1214
1215
1216
1217
1218
1219
1220
1221 /*
1222 * ===================================================================
1223 * FUNCIONES PARA GESTI�N DE ERRORES
1224 * ===================================================================
1225 *
1226 */
1227
1228 /*
1229 * Funciones que se ejecutan para atender
1230 * casos de error en el documento HTML
1231 * e intentar arreglarlos
1232 *
1233 * En general, estas funciones devuelven 0 si
1234 * no arreglaron el error o >0 si lo hicieron
1235 *
1236 * Estas funciones ser�n invocadas cuando, procesando
1237 * el documento de entrada, el programa se encuentre
1238 * con problemas que no puede solucionar con los
1239 * procedimientos est�ndar
1240 *
1241 * Por ejemplo, si un atributo obligatorio no
1242 * aparece en el documento HTML, la funci�n err_att_req
1243 * intentar� obtener un valor v�lido para ella.
1244 * Si lo consigue, inserta en el �rbol del documento
1245 * el atributo con dicho valor. Si no, no hace nada
1246 * y devuelve 0 para indicarlo a quien la invoc�
1247 *
1248 * Cuando, probando, se encuentren casos que el
1249 * procedimiento est�ndar no soluciona, debe modificarse
1250 * en este fichero la funci�n adecuada para gestionar
1251 * dichos casos
1252 *
1253 */
1254
1255
1256
1257
1258
1259 /**
1260 * si un elemento es de tipo children y su contenido no verifica
1261 * el modelo, se intenta corregir en esta funci�n
1262 *
1263 * devuelve 0 si no lo consigue y 1 si lo hace
1264 *
1265 */
err_content_invalid(tree_node_t * nodo,int hijos[],int num_hijos)1266 static int err_content_invalid(tree_node_t* nodo, int hijos[], int num_hijos)
1267 {
1268 tree_node_t* actual_bak= actual_element;
1269 int corregido= 0;
1270 int check_again = 0;
1271
1272 DEBUG("[ERR] err_content_invalid()");
1273
1274 switch (ELM_ID(nodo)) {
1275 case ELMID_HTML:
1276 {
1277 int in_body;
1278
1279 in_body= ELMID_PRE;
1280 if (doctype==XHTML_FRAMESET) in_body= ELMID_FRAME;
1281
1282 /* si est� mal HTML, intenta arreglarlo */
1283 if (!num_hijos) {
1284 /* faltan HEAD y BODY */
1285 actual_element= nodo;
1286 corregido= err_html_struct(document,in_body);;
1287 }
1288 else if ((num_hijos==1)&&(hijos[0]==ELMID_HEAD)) {
1289 /* falta BODY */
1290 actual_element= nodo->cont.elemento.hijo;
1291 corregido= err_html_struct(document,in_body);
1292 }
1293 else if ((num_hijos==1)&&
1294 ((hijos[0]==ELMID_BODY)||(hijos[0]==ELMID_FRAMESET))) {
1295 /* falta HEAD */
1296 tree_node_t *body;
1297 if (!(body= tree_search_elm_child(nodo,ELMID_BODY)))
1298 body= tree_search_elm_child(nodo,ELMID_FRAMESET);
1299 if (body) {
1300 tree_unlink_node(body);
1301 actual_element= nodo;
1302 corregido= err_html_struct(document,ELMID_BODY);
1303 link_node(body,nodo,LINK_MODE_CHILD);
1304 }
1305 }
1306
1307
1308 actual_element= actual_bak;
1309 break;
1310 }
1311
1312 case ELMID_HEAD:
1313 {
1314 int num_title = 0;
1315 int num_base = 0;
1316 int i;
1317 int* h;
1318
1319 /* Count the number of title and base elements */
1320 for (i = 0; i < num_hijos; i++) {
1321 if (hijos[i] == ELMID_TITLE)
1322 num_title++;
1323 else if (hijos[i] == ELMID_BASE) {
1324 num_base++;
1325 }
1326 }
1327
1328 if (num_title == 0) {
1329 /* Element title missing */
1330 h= tree_malloc((num_hijos + 1)*sizeof(int));
1331 for (i = 0; i < num_hijos; i++)
1332 h[i + 1] = hijos[i];
1333 h[0]= ELMID_TITLE;
1334 if (dtd_is_child_valid(ELM_PTR(nodo).contentspec[doctype],
1335 h, num_hijos + 1) == 1) {
1336 tree_node_t* title;
1337 title = new_tree_node(Node_element);
1338 title->cont.elemento.elm_id = ELMID_TITLE;
1339 link_node(title, nodo, LINK_MODE_FIRST_CHILD);
1340 corregido = 1;
1341 }
1342 } else if (num_title > 1) {
1343 /* More than one title, keep only the first one */
1344 num_hijos -= remove_duplicate_elm(ELMID_TITLE, nodo, hijos);
1345 check_again = 1;
1346 }
1347
1348 if (num_base > 1) {
1349 /* More than one base, keep only the first one */
1350 num_hijos -= remove_duplicate_elm(ELMID_BASE, nodo, hijos);
1351 check_again = 1;
1352 }
1353 break;
1354 }
1355
1356 case ELMID_OL:
1357 case ELMID_UL:
1358 {
1359 /* se descarta, porque no contiene informaci�n */
1360 tree_unlink_node(nodo);
1361 INFORM("[ERR] descartado el elemento");
1362 corregido=1;
1363 break;
1364 }
1365
1366 case ELMID_TR:
1367 {
1368 /* se inserta TD a pesar de que no contiene
1369 * informaci�n, porque puede propagar el problema
1370 * de contenido inv�lido hacia sus padres
1371 */
1372 tree_node_t *td;
1373 td= new_tree_node(Node_element);
1374 td->cont.elemento.elm_id= ELMID_TD;
1375 link_node(td,nodo,LINK_MODE_CHILD);
1376 corregido= 1;
1377 INFORM("insertado elemento td");
1378 break;
1379 }
1380 case ELMID_TABLE:
1381 case ELMID_TBODY:
1382 case ELMID_THEAD:
1383 {
1384 /* si no tiene contenido, se cierra */
1385 /* se intenta solucionar con <TR> al final */
1386 int *hijos2= tree_malloc((num_hijos+1)*sizeof(int));
1387 tree_node_t *tr,*td;
1388
1389 if (!num_hijos) {
1390 tree_unlink_node(nodo);
1391 corregido= 1;
1392 INFORM("[ERR] descartado el elemento");
1393 } else {
1394 memcpy(hijos2,hijos,num_hijos*sizeof(int));
1395 hijos2[num_hijos]= ELMID_TR;
1396 if (dtd_is_child_valid(ELM_PTR(nodo).contentspec[doctype],
1397 hijos2,num_hijos+1)==1) {
1398 /* OK, se inserta */
1399 tr= new_tree_node(Node_element);
1400 tr->cont.elemento.elm_id= ELMID_TR;
1401 link_node(tr,nodo,LINK_MODE_CHILD);
1402 td= new_tree_node(Node_element);
1403 td->cont.elemento.elm_id= ELMID_TD;
1404 link_node(td,tr,LINK_MODE_CHILD);
1405 corregido= 1;
1406 INFORM("[ERR] insertados <TR><TD></TD></TR>");
1407 }
1408 }
1409
1410 /* free(hijos2); */
1411 break;
1412 }
1413 }
1414
1415 if (!corregido && check_again) {
1416 corregido = dtd_is_child_valid(ELM_PTR(nodo).contentspec[doctype],
1417 hijos, num_hijos);
1418 }
1419
1420 if (!corregido && param_strict) {
1421 /* se descarta el elemento */
1422 if (nodo!=document->inicio) tree_unlink_node(nodo);
1423 else document->inicio= NULL;
1424 INFORM("se descarta el elemento por no tener contenido v�lido");
1425 corregido= 1;
1426 }
1427
1428 return corregido;
1429 }
1430
remove_duplicate_elm(int elmid,tree_node_t * parent,int hijos[])1431 static int remove_duplicate_elm(int elmid, tree_node_t* parent, int hijos[])
1432 {
1433 tree_node_t* p;
1434 tree_node_t* next;
1435 int keep_node;
1436 int removed;
1437 int i;
1438
1439 removed = 0;
1440 keep_node = 1;
1441 for (i = 0, p = parent->cont.elemento.hijo; p; i++) {
1442 next = p->sig;
1443 if (p->tipo == Node_element && ELM_ID(p) == elmid) {
1444 if (!keep_node) {
1445 tree_unlink_node(p);
1446 removed++;
1447 } else {
1448 keep_node = 0;
1449 }
1450 } else {
1451 hijos[i - removed] = hijos[i];
1452 }
1453 p = next;
1454 }
1455
1456 return removed;
1457 }
1458
1459
1460
1461
1462 /**
1463 * el elemento no es un hijo v�lido para el nodo actual
1464 * se intenta solucionar esto
1465 *
1466 */
err_child_no_valid(tree_node_t * nodo)1467 static int err_child_no_valid(tree_node_t* nodo)
1468 {
1469 int insertado;
1470 tree_node_t* actual;
1471
1472 EPRINTF1("err_child_no_valid(%s)\n", ELM_PTR(nodo).name);
1473
1474 /* si el padre es <ul> o <ol>, se inserta <li> */
1475 if (((ELM_ID(actual_element)==ELMID_OL)||(ELM_ID(actual_element)==ELMID_UL))
1476 && (dtd_can_be_child(ELM_ID(nodo),ELMID_LI,doctype))){
1477 actual_element= err_aux_insert_elm(ELMID_LI,NULL,0);
1478 DEBUG("[ERR] insertado elemento li");
1479 return 1;
1480 }
1481
1482 /* si el padre es <table> y se inserta <th> o <td>, se inserta
1483 * antes <tr>
1484 */
1485 if (((ELM_ID(actual_element)==ELMID_TABLE)||
1486 (ELM_ID(actual_element)==ELMID_THEAD)||
1487 (ELM_ID(actual_element)==ELMID_TBODY) ) &&
1488 ((ELM_ID(nodo)==ELMID_TH)||(ELM_ID(nodo)==ELMID_TD))) {
1489 actual_element= err_aux_insert_elm(ELMID_TR,NULL,0);
1490 DEBUG("[ERR] insertado elemento tr");
1491 return 1;
1492 }
1493
1494 /* si el elemento est� dentro de TABLE, THEAD, TBODY o TR se descarta */
1495 if ((ELM_ID(actual_element)==ELMID_TABLE)||
1496 (ELM_ID(actual_element)==ELMID_THEAD)||
1497 (ELM_ID(actual_element)==ELMID_TBODY)||
1498 (ELM_ID(actual_element)==ELMID_TR)) {
1499 INFORM("[ERR] el elemento se descarta por estar en una tabla");
1500 return 0;
1501 }
1502
1503
1504
1505 /* en principio, se asciende en la jerarqu�a hasta
1506 * que sea un hijo v�lido
1507 */
1508 for (insertado=0, actual= actual_element; actual; actual= actual->padre) {
1509 if (dtd_can_be_child(ELM_ID(nodo),ELM_ID(actual),doctype)) {
1510 insertado= 1;
1511 break;
1512 }
1513 } /* for */
1514
1515 /* si se le encontr� hueco, se cierran los elementos hasta �l */
1516 if (insertado) {
1517 /* control de estructura */
1518 if ((ELM_ID(nodo)==ELMID_HEAD)
1519 || (ELM_ID(nodo)==ELMID_BODY)
1520 || (ELM_ID(nodo)==ELMID_FRAMESET)) insertado=0;
1521 else for ( ; actual_element != actual; actual_element= actual_element->padre)
1522 elm_close(actual_element);
1523 } else {
1524 /* puede ser un caso de estructura incorrecta */
1525 insertado= err_html_struct(document, ELM_ID(nodo));
1526 if (!insertado && actual_element &&
1527 dtd_can_be_child(ELM_ID(nodo),ELMID_P,doctype) &&
1528 dtd_can_be_child(ELMID_P,ELM_ID(actual_element),doctype)) {
1529 /* se inserta en <p> */
1530 tree_node_t *p;
1531
1532 p= new_tree_node(Node_element);
1533 p->cont.elemento.elm_id= ELMID_P;
1534 link_node(p, actual_element, LINK_MODE_CHILD);
1535 actual_element= p;
1536 insertado= 1;
1537 DEBUG("[ERR] insertado elemento p como padre");
1538 } else if (ELM_ID(actual_element) == ELMID_HTML
1539 && (actual = tree_search_elm_child(actual_element, ELMID_BODY))) {
1540 if (dtd_can_be_child(ELM_ID(nodo), ELMID_BODY, doctype)) {
1541 /* Insert the new element inside the body element */
1542 actual_element = actual;
1543 insertado = 1;
1544 } else if (dtd_can_be_child(ELM_ID(nodo), ELMID_P, doctype)) {
1545 /* Insert the new element inside a new p element inside the
1546 * body element.
1547 */
1548 tree_node_t *p;
1549 p= new_tree_node(Node_element);
1550 p->cont.elemento.elm_id= ELMID_P;
1551 link_node(p, actual, LINK_MODE_CHILD);
1552 actual_element = p;
1553 insertado = 1;
1554 }
1555 }
1556 }
1557
1558 if (!insertado && ELM_ID(nodo) == ELMID_STYLE) {
1559 /* si es un elemento style, lo metemos dentro de head */
1560
1561 /* el primer hijo de <html> es <head> */
1562 tree_node_t* head = document->inicio->cont.elemento.hijo;
1563 link_node(nodo, head, LINK_MODE_CHILD);
1564 insertado = 2;
1565
1566 /* activate the new place recovery mode */
1567 new_place_recovery_on = 1;
1568 new_place_recovery_elm = nodo;
1569 new_place_recovery_father = actual_element;
1570
1571 DEBUG("Inserted style in head");
1572 }
1573
1574 return insertado;
1575 }
1576
1577
1578
1579 /**
1580 * intenta corregir los casos en que el valor de un atributo no es
1581 * correcto
1582 *
1583 * devuelve 0 si no lo consigue
1584 *
1585 */
err_att_value(tree_node_t * elm,int att_id,const xchar * value)1586 static int err_att_value(tree_node_t *elm, int att_id, const xchar *value)
1587 {
1588 char *correcto= NULL;
1589
1590 /* CASO 1: valor 'center' en lugar de 'middle' */
1591 if (!strcasecmp(value,"center") && (dtd_att_is_valid(att_id, "middle")))
1592 correcto= tree_strdup("middle");
1593
1594 /* CASO 2: valor contiene '<' o '&' */
1595 else if (dtd_att_val_search_errors(value)!=-1) {
1596 int pos;
1597 char *tmp;
1598 correcto= tree_strdup(value);
1599 while ((pos=dtd_att_val_search_errors(correcto))!=-1) {
1600 if (correcto[pos]=='&') {
1601 tmp= tree_malloc(strlen(correcto)+1+4);
1602 memcpy(tmp,correcto,pos);
1603 tmp[pos]= 0;
1604 strcat(tmp,"&");
1605 strcat(tmp,&correcto[pos+1]);
1606 /* free(correcto); */
1607 correcto= tmp;
1608 } else if (correcto[pos]=='<') {
1609 tmp= tree_malloc(strlen(correcto)+1+3);
1610 memcpy(tmp,correcto,pos);
1611 tmp[pos]= 0;
1612 strcat(tmp,"<");
1613 strcat(tmp,&correcto[pos+1]);
1614 /* free(correcto); */
1615 correcto= tmp;
1616 } else {
1617 /* free(correcto); */
1618 correcto= NULL;
1619 break;
1620 }
1621 }
1622 }
1623
1624 /* se cambia el valor si se tom� la decisi�n */
1625 if (correcto) {
1626 set_node_att(elm, att_id, correcto, 1);
1627 DEBUG("err_att_value");
1628 EPRINTF3(" [ERR] valor atributo %s cambiado de '%s' a '%s'\n",
1629 att_list[att_id].name, value, correcto);
1630 /* free(correcto); */
1631 return 1;
1632 }
1633 else return 0;
1634 }
1635
1636
1637
1638
1639
1640
1641 /**
1642 * intenta corregir los casos en que no aparece un atributo
1643 * obligatorio
1644 *
1645 * devuelve 0 si no lo consigue
1646 *
1647 */
err_att_req(tree_node_t * elm,int att_id,xchar ** atts)1648 static int err_att_req(tree_node_t *elm, int att_id, xchar **atts)
1649 {
1650 char *valor= NULL;
1651
1652 /* CASO 3: si el elemento es 'script' y el atributo 'type'
1653 * se pone el tipo MIME del lenguaje: en principio, se soporta
1654 * JavaScript. Si es otro, se toma del atributo 'language' o
1655 * se deja vac�o
1656 */
1657 if ((ELM_ID(elm)==ELMID_SCRIPT) && (!strcmp(att_list[att_id].name,"type"))
1658 && atts) {
1659 /* busca atributo 'language' */
1660 int i;
1661 for (i=0; atts[i] && strcasecmp(atts[i],"language"); i+=2);
1662 if (atts[i]) {
1663 if ((xsearch(atts[i+1],"javascript"))||(xsearch(atts[i+1],"Javascript"))||
1664 (xsearch(atts[i+1],"JavaScript")))
1665 valor= tree_strdup("text/javascript");
1666 else valor= tree_strdup(atts[i+1]);
1667 }
1668 else valor= tree_strdup("text/javascript");
1669 }
1670
1671 /* CASO 4: si el elemento es 'style' y el atributo 'type'
1672 * se pone el tipo MIME del lenguaje: en principio, se
1673 * coloca text/css, pero se podr�a mejorar este comportamiento
1674 * tomando el de http-equiv en META
1675 *
1676 */
1677 else if ((ELM_ID(elm)==ELMID_STYLE) && (!strcmp(att_list[att_id].name,"type"))) {
1678 valor= tree_strdup("text/css");
1679 }
1680
1681
1682
1683
1684 /* CASO 1: es de tipo CDATA -> se pone valor vac�o "" */
1685 else if (att_list[att_id].attType== ATTTYPE_CDATA) {
1686 valor= tree_strdup("");
1687 }
1688
1689 /* CASO 2: es de tipo ID -> se toma el de 'name' si lo hay */
1690 else if ((att_list[att_id].attType== ATTTYPE_ID) && atts) {
1691 int i;
1692 for (i=0; atts[i] && strcasecmp(atts[i],"name"); i+=2);
1693 if (atts[i] && dtd_att_is_valid(att_id,atts[i+1]))
1694 valor= tree_strdup(atts[i+1]);
1695 }
1696
1697
1698 /* se establece el atributo si se tom� la decisi�n */
1699 if (valor) {
1700 set_node_att(elm, att_id, valor, 1);
1701 DEBUG("err_att_req");
1702 EPRINTF2(" [ERR] atributo obligatorio '%s' inclu�do con valor '%s'\n",
1703 att_list[att_id].name, valor);
1704 /* free(valor); */
1705 return 1;
1706 }
1707 else return 0;
1708 }
1709
1710
1711
1712
1713
1714 /**
1715 * intenta corregir los casos en que haya errores o situaciones
1716 * especiales en atributos, no detectadas por otros m�todos
1717 *
1718 * se ejecuta por defecto siempre
1719 *
1720 */
err_att_default(tree_node_t * elm,xchar ** atts)1721 static void err_att_default(tree_node_t *elm, xchar **atts)
1722 {
1723
1724 /* si posee atributo 'name', se pasa su valor a 'id' */
1725 switch (ELM_ID(elm)) {
1726 case ELMID_A:
1727 case ELMID_APPLET:
1728 case ELMID_FORM:
1729 case ELMID_FRAME:
1730 case ELMID_IFRAME:
1731 case ELMID_IMG:
1732 case ELMID_MAP:
1733 {
1734 int i;
1735 int att_ptr;
1736
1737 if (atts) {
1738 for (i=0; atts[i]; i+=2)
1739 if (!strcmp(atts[i],"name")) {
1740 att_ptr= dtd_att_search_list("id",ELM_PTR(elm).attlist[doctype]);
1741 if ((att_ptr != -1) && (!tree_node_search_att(elm,att_ptr))) {
1742 if (dtd_att_is_valid(att_ptr,atts[i+1])) {
1743 set_node_att(elm, att_ptr, atts[i+1], 1);
1744 DEBUG("");
1745 EPRINTF1(" [ERR] insertado atributo 'id' con valor '%s'\n",
1746 atts[i+1]);
1747 }
1748 else INFORM("se descarta atributo ID con valor incorrecto");
1749 break;
1750 }
1751 }
1752 break;
1753 } /* if atts */
1754 break;
1755 }
1756 case ELMID_PRE:
1757 case ELMID_SCRIPT:
1758 case ELMID_STYLE:
1759 if (!tree_node_search_att(elm, ATTID_XML_SPACE)
1760 && dtd_att_search_list_id(ATTID_XML_SPACE,
1761 ELM_PTR(elm).attlist[doctype]) >= 0) {
1762 set_node_att(elm,ATTID_XML_SPACE, "preserve", 1);
1763 }
1764 break;
1765 } /* switch */
1766 }
1767
1768
1769
1770
1771
1772 /**
1773 * intenta corregir situaciones en que no se respeta la estructura
1774 * b�sica de HTML: <html><head>...</head><body>...</body>
1775 *
1776 * devuelve 0 si no lo consigue
1777 *
1778 *
1779 * [] si no empez� el documento
1780 * - si recibe <head>, inserta <html>
1781 * - si recibe <body>, inserta <html> <head> </head>
1782 * - si recibe elemento de <head>, inserta <html> <head>
1783 * - si recibe elemento de <body>, inserta <html> <head> </head> <body>
1784 *
1785 */
err_html_struct(document_t * document,int elm_id)1786 static int err_html_struct(document_t *document, int elm_id)
1787 {
1788 int ok= 0;
1789 tree_node_t *html=NULL, *head=NULL, *body=NULL;
1790 tree_node_t *nodo;
1791
1792 html= document->inicio;
1793 if (html) {
1794 head= tree_search_elm_child(html,ELMID_HEAD);
1795 if (doctype!=XHTML_FRAMESET)
1796 body= tree_search_elm_child(html,ELMID_BODY);
1797 else
1798 body= tree_search_elm_child(html,ELMID_FRAMESET);
1799 }
1800
1801
1802 switch (elm_id) {
1803 case ELMID_HEAD:
1804 if (html) break;
1805 /* establece un nodo para el elemento html */
1806 if (!html) html= err_aux_insert_elm(ELMID_HTML,NULL,0);
1807 actual_element= html;
1808 ok= 1;
1809 DEBUG("err_html_struct()");
1810 EPRINTF(" [ERR] introducido elemento <html>\n");
1811 break;
1812
1813 case ELMID_BODY:
1814 case ELMID_FRAMESET:
1815 if (html && head) break;
1816 /* establece un nodo para el elemento html */
1817 if (!html) html= err_aux_insert_elm(ELMID_HTML,NULL,0);
1818 /* establece un nodo para el elemento head */
1819 if (!head) {
1820 actual_element= html;
1821 head= err_aux_insert_elm(ELMID_HEAD,NULL,0);
1822 /* establece un nodo para el elemento title */
1823 nodo= err_aux_insert_elm(ELMID_TITLE,"****",4);
1824 /* cierra title y head */
1825 elm_close(nodo);
1826 elm_close(head);
1827 }
1828 for (nodo=actual_element; nodo && (nodo!=html); nodo=nodo->padre)
1829 elm_close(nodo);
1830 actual_element= html;
1831 ok=1;
1832 DEBUG("err_html_struct()");
1833 EPRINTF(" [ERR] introducido <html> <head> <title> </title> </head> \n");
1834 break;
1835
1836
1837 default:
1838 /* elemento de HEAD */
1839 if (!head && !body && dtd_can_be_child(elm_id,ELMID_HEAD,doctype)) {
1840 /* se abre <html><head> */
1841 /* establece un nodo para el elemento html */
1842 if (!html) html= err_aux_insert_elm(ELMID_HTML,NULL,0);
1843 /* establece un nodo para el elemento head */
1844 head= err_aux_insert_elm(ELMID_HEAD,NULL,0);
1845 ok=1;
1846 DEBUG("err_html_struct()");
1847 EPRINTF(" [ERR] introducido <html> <head>\n");
1848
1849 /* elemento de BODY */
1850 } else if (!body && (doctype!=XHTML_FRAMESET) &&
1851 dtd_can_be_child(elm_id,ELMID_BODY,doctype)) {
1852 /* establece un nodo para el elemento html */
1853 if (!html) html= err_aux_insert_elm(ELMID_HTML,NULL,0);
1854 /* establece un nodo para el elemento head */
1855 if (!head) {
1856 head= err_aux_insert_elm(ELMID_HEAD,NULL,0);
1857 /* establece un nodo para el elemento title */
1858 nodo= err_aux_insert_elm(ELMID_TITLE,"****",4);
1859 /* cierra title y head */
1860 elm_close(nodo);
1861 elm_close(head);
1862 }
1863
1864 for(nodo=actual_element; nodo && (nodo!=html); nodo=nodo->padre)
1865 elm_close(nodo);
1866 actual_element= html;
1867
1868 /* establece un nodo para el elemento body */
1869 body= err_aux_insert_elm(ELMID_BODY,NULL,0);
1870 ok=1;
1871 DEBUG("err_html_struct()");
1872 EPRINTF(" [ERR] introducido <html> <head> <title> </title> </head> <body>\n");
1873
1874 } else if (!body && (doctype==XHTML_FRAMESET) &&
1875 dtd_can_be_child(elm_id,ELMID_FRAMESET,doctype)) {
1876 /* establece un nodo para el elemento html */
1877 if (!html) html= err_aux_insert_elm(ELMID_HTML,NULL,0);
1878 /* establece un nodo para el elemento head */
1879 if (!head) {
1880 head= err_aux_insert_elm(ELMID_HEAD,NULL,0);
1881 /* establece un nodo para el elemento title */
1882 nodo= err_aux_insert_elm(ELMID_TITLE,"****",4);
1883 /* cierra title y head */
1884 elm_close(nodo);
1885 elm_close(head);
1886 }
1887
1888 for(nodo=actual_element; nodo && (nodo!=html); nodo=nodo->padre)
1889 elm_close(nodo);
1890 actual_element= html;
1891
1892 /* establece un nodo para el elemento body */
1893 body= err_aux_insert_elm(ELMID_FRAMESET,NULL,0);
1894 ok=1;
1895 DEBUG("err_html_struct()");
1896 EPRINTF(" [ERR] introducido <html> <head> <title> </title> </head> <frameset>\n");
1897 }
1898 } /* switch */
1899
1900
1901 return ok;
1902 }
1903
1904
1905 /*
1906 * si se intenta insertar un elemento con nombre desconocido
1907 *
1908 */
err_elm_desconocido(const xchar * nombre)1909 static int err_elm_desconocido(const xchar *nombre)
1910 {
1911 /* se mira si es alg�n elemento no normativo u obsoleto conocido */
1912 if (!strcmp(nombre,"listing") || !strcmp(nombre,"plaintext") ||
1913 !strcmp(nombre,"xmp")) {
1914 /* se debe insertar <pre> */
1915 return ELMID_PRE;
1916 }/* else if (!strcmp(nombre,"layer") || !strcmp(nombre,"ilayer"))
1917 return ELMID_IFRAME;*/
1918
1919 return -1;
1920 }
1921
1922
1923
1924
1925 /* funciones auxiliares del subm�dulo */
err_aux_insert_elm(int elm_id,const xchar * content,int len)1926 static tree_node_t* err_aux_insert_elm(int elm_id, const xchar *content, int len)
1927 {
1928 tree_node_t *elm;
1929 /* tree_node_t *data; */
1930
1931 /* crea e inserta el nodo de elemento */
1932 elm= new_tree_node(Node_element);
1933 elm->cont.elemento.elm_id= elm_id;
1934 insert_element(elm);
1935 set_attributes(elm,NULL);
1936
1937 /* si tiene contenido, se lo inserta */
1938 if (content && (len>0)) {
1939 tree_link_data_node(Node_chardata, elm, content, len);
1940 }
1941
1942 /* si es HTML, HEAD o BODY, marca las variables de inserci�n */
1943 switch (elm_id) {
1944 case ELMID_HTML:
1945 ins_html= elm;
1946 break;
1947 case ELMID_HEAD:
1948 ins_head= elm;
1949 break;
1950 case ELMID_BODY:
1951 ins_body= elm;
1952 break;
1953 }
1954
1955 /* gesti�n de atributos */
1956 set_attributes(elm,NULL);
1957
1958
1959 return elm;
1960 }
1961
1962
1963 /*
1964 * Comprueba si el valor de un atributo est� bien formado:
1965 * - no contiene "<" ni "&" sin pertenecer a ref. a entidad
1966 * - nota: las comillas vienen ya filtradas por el parser, as�
1967 * que no se comprueban aqu�.
1968 *
1969 * Devuelve:
1970 * - si est� bien formado: puntero a la misma cadena
1971 * - si no: puntero a una nueva cadena corregida
1972 */
check_and_fix_att_value(xchar * value)1973 xchar* check_and_fix_att_value(xchar* value)
1974 {
1975 xchar* fixed = value;
1976 int i, k;
1977 int size_inc = 0;
1978 xchar tmpchar;
1979
1980 /* special marks: in values, characters with code 1 and 2 are
1981 * introduced for signaling, respectively, '<' or '&' that
1982 * need to be fixed
1983 */
1984
1985 /* first round: detect errors and count the increment of size
1986 * necessary to correct them
1987 */
1988 for (i = 0; value[i]; i++) {
1989 if (value[i] == '&' && value[i+1] == '#' && value[i+2] == 'x') {
1990 /* character reference (hexadecimal) */
1991 for (k = i + 3;
1992 (value[k] >= '0' && value[k] <= '9')
1993 || (value[k] >= 'a' && value[k] <= 'f')
1994 || (value[k] >= 'A' && value[k] <= 'F');
1995 k++);
1996 if (value[k] != ';') {
1997 /* needs to be fixed insert "amp;" */
1998 size_inc += 4;
1999 value[i] = 2;
2000 i = k - 1; /* next iteration at index k */
2001 } else {
2002 i = k; /* next iteration at index k + 1 */
2003 }
2004
2005 } else if (value[i] == '&' && value[i+1] == '#') {
2006 /* character reference */
2007 for (k = i + 2; value[k] >= '0' && value[k] <= '9'; k++);
2008 if (value[k] != ';') {
2009 /* needs to be fixed insert "amp;" */
2010 size_inc += 4;
2011 value[i] = 2;
2012 i = k - 1; /* next iteration at index k */
2013 } else {
2014 i = k; /* next iteration at index k + 1 */
2015 }
2016
2017 } else if (value[i] == '&') {
2018 /* entity reference */
2019 /* LETTER ([\x41-\x5a]|[\x61-\x7a]|[\xc0-\xd6]|[\xd8-\xf6]|[\xf8-\xff]) */
2020 for (k = i + 1;
2021 (value[k] >= 0x41 && value[k] <= 0x5a)
2022 || (value[k] >= 0x61 && value[k] <= 0x7a)
2023 || ((unsigned)value[k] >= 0xc0 && (unsigned)value[k] <= 0xd6)
2024 || ((unsigned)value[k] >= 0xd8 && (unsigned)value[k] <= 0xf6)
2025 || ((unsigned)value[k] >= 0xf8);
2026 k++);
2027 if (value[k] != ';') {
2028 /* needs to be fixed insert "amp;" */
2029 size_inc += 4;
2030 value[i] = 2;
2031 i = k - 1; /* next iteration at index k */
2032 } else {
2033 /* check the entity name */
2034 tmpchar = value[k+1];
2035 value[k+1] = 0;
2036 if (dtd_ent_search(&value[i]) == -1) {
2037 /* needs to be fixed insert "amp;" */
2038 size_inc += 4;
2039 value[i] = 2;
2040 i = k - 1; /* next iteration at index k */
2041 } else {
2042 i = k; /* next iteration at index k + 1 */
2043 }
2044 value[k+1] = tmpchar;
2045 }
2046 } else if (value[i] == '<') {
2047 size_inc += 3;
2048 value[i] = 1;
2049 }
2050 }
2051
2052 /* fix marked entries */
2053 if (size_inc > 0) {
2054 fixed = (xchar*) tree_malloc(strlen(value) + 1 + size_inc);
2055
2056 for (i = 0, k = 0; value[i]; i++, k++) {
2057 /* copy the value and replace marked characters */
2058 if (value[i] == 1) {
2059 fixed[k ] = '&';
2060 fixed[k + 1] = 'l';
2061 fixed[k + 2] = 't';
2062 fixed[k + 3] = ';';
2063 k += 3;
2064 } else if (value[i] == 2) {
2065 fixed[k ] = '&';
2066 fixed[k + 1] = 'a';
2067 fixed[k + 2] = 'm';
2068 fixed[k + 3] = 'p';
2069 fixed[k + 4] = ';';
2070 k += 4;
2071 } else {
2072 fixed[k] = value[i];
2073 }
2074 }
2075 fixed[k] = 0;
2076 }
2077
2078 return fixed;
2079 }
2080
2081
2082
2083 /*
2084 * ===================================================================
2085 * Write the XHTML output
2086 * ===================================================================
2087 *
2088 */
2089
2090 /* internal variables of the output module */
2091 static int xml_space_on;
2092 static int inline_on;
2093 static int indent;
2094 static int chars_in_line;
2095 static int whitespace_needed;
2096 static int inside_cdata_sec;
2097
2098 #define CBUFFER_SIZE 32768
2099 static char cbuffer[CBUFFER_SIZE];
2100 static int cbuffer_pos;
2101 static int cbuffer_avail;
2102
2103 /*
2104 * Writes to output the document
2105 *
2106 */
write_document(document_t * doc)2107 static void write_document(document_t *doc)
2108 {
2109 tree_node_t *p;
2110 tree_node_t *body;
2111
2112 xml_space_on = 0;
2113 inside_cdata_sec = 0;
2114
2115 cprintf_init(param_charset_out, param_outputf);
2116
2117 if (!param_generate_snippet) {
2118 /* write <?xml... */
2119 cprintf("%s?xml version=\"1.0\"", lt);
2120 /* if (document->encoding[0]) */
2121 /* cprintf(" encoding=\"%s\"",document->encoding); */
2122 cprintf(" encoding=\"%s\"", param_charset_out->preferred_name);
2123 cprintf("?%s%s%s", gt, eol, eol);
2124
2125 /* write <!DOCTYPE... */
2126 cprintf("%s!DOCTYPE html%s %s%s \"%s\" %s%s",
2127 lt, eol, doctype_string[doctype], eol, dtd_string[doctype], gt, eol);
2128 }
2129 p = doc->inicio;
2130 if (!param_generate_snippet) {
2131 write_node(p);
2132 cprintf(eol);
2133 } else {
2134 body = tree_search_elm_child(doc->inicio, ELMID_BODY);
2135 if (body) {
2136 for (p = body->cont.elemento.hijo; p; p = p->sig) {
2137 write_node(p);
2138 }
2139 cprintf(eol);
2140 }
2141 }
2142
2143 cprintf_close();
2144 }
2145
write_node(tree_node_t * node)2146 static int write_node(tree_node_t *node)
2147 {
2148 int len = 0;
2149
2150 switch (node->tipo) {
2151 case Node_element:
2152 len = write_element(node);
2153 break;
2154 case Node_chardata:
2155 if (!xml_space_on)
2156 len = write_chardata(node);
2157 else
2158 len = write_chardata_space_preserve(node);
2159 break;
2160 case Node_cdata_sec:
2161 len = write_cdata_sec(node);
2162 break;
2163 case Node_comment:
2164 len = write_comment(node);
2165 break;
2166 }
2167
2168 return len;
2169 }
2170
2171
write_element(tree_node_t * elm)2172 static int write_element(tree_node_t *elm)
2173 {
2174 int len = 0;
2175 tree_node_t *n;
2176 int is_block;
2177 int xml_space_activated;
2178
2179 is_block = dtd_elm_is_block(ELM_ID(elm));
2180
2181 /* activate "xml:space preserve" if necessary */
2182 if (!xml_space_on
2183 && (tree_node_search_att(elm, ATTID_XML_SPACE)
2184 || ELM_ID(elm) == ELMID_SCRIPT
2185 || ELM_ID(elm) == ELMID_STYLE)) {
2186 if (ELM_ID(elm) == ELMID_STYLE)
2187 len += write_indent(indent, 1);
2188 else if (ELM_ID(elm) != ELMID_SCRIPT && is_block)
2189 len += cprintf(eol);
2190 xml_space_activated = 1;
2191 xml_space_on = 1;
2192 } else {
2193 xml_space_activated = 0;
2194 }
2195
2196 /* write start tag */
2197 if (is_block) {
2198 len += write_indent(indent, 1);
2199 inline_on = 0;
2200 } else {
2201 if (!inline_on) {
2202 inline_on = 1;
2203 whitespace_needed = 0;
2204 if (!param_compact_block_elms)
2205 len += write_indent(indent, 1);
2206 }
2207 }
2208 len += write_start_tag(elm);
2209
2210 /* process children nodes */
2211 n = elm->cont.elemento.hijo;
2212 if (is_block)
2213 indent += param_tab_len;
2214 while (n) {
2215 len += write_node(n);
2216 n = n->sig;
2217 }
2218 if (is_block)
2219 indent -= param_tab_len;
2220
2221 /* write end tag if not empty */
2222 if (elm->cont.elemento.hijo) {
2223 if (is_block) {
2224 if (inline_on) {
2225 inline_on = 0;
2226 if (!param_compact_block_elms)
2227 len += write_indent(indent, 1);
2228 } else {
2229 len += write_indent(indent, 1);
2230 }
2231 }
2232 len += write_end_tag(elm);
2233 } else if (!param_empty_tags
2234 && elm_list[ELM_ID(elm)].contenttype[doctype] != CONTTYPE_EMPTY) {
2235 len += write_end_tag(elm);
2236 }
2237
2238 /* deactivate "xml:space preserve" if activated */
2239 if (xml_space_activated) {
2240 xml_space_on = 0;
2241 }
2242
2243 return len;
2244 }
2245
2246
write_chardata(tree_node_t * node)2247 static int write_chardata(tree_node_t *node)
2248 {
2249 int i;
2250 int pos;
2251 int data_len;
2252 xchar *data;
2253 int bytes_to_print;
2254 int chars_to_print;
2255 int num;
2256 int printed;
2257
2258 data = (char*)tree_index_to_ptr(node->cont.chardata.data);
2259 data_len = node->cont.chardata.data_len;
2260 pos = 0;
2261 num = 0;
2262
2263 while (pos < data_len) {
2264 /* filter blanks at the beginning of this data block */
2265 for (i = pos;
2266 i < data_len
2267 && (data[i] == 0x0a || data[i] == 0x0d
2268 || data[i] == ' ' || data[i] == '\t');
2269 i++);
2270
2271 if (!inline_on && pos == 0 && i < data_len) {
2272 inline_on = 1;
2273 whitespace_needed = 0;
2274 if (!param_compact_block_elms)
2275 num += write_indent(indent, 1);
2276 }
2277
2278 /* put one whitespace instead, but only if no new line is printed */
2279 if (i != pos && chars_in_line != indent)
2280 whitespace_needed = 1;
2281
2282 /* find the next breakpoint */
2283 pos = i;
2284 chars_to_print = 0;
2285 bytes_to_print = 0;
2286 for ( ;
2287 i < data_len && data[i] != 0x0a && data[i] != 0x0d
2288 && data[i] != ' ' && data[i] != '\t';
2289 i++, bytes_to_print++) {
2290 if ((char)(data[i] & 0xC0) != (char) 0x80)
2291 chars_to_print++;
2292 }
2293
2294 if (bytes_to_print) {
2295 num += write_whitespace_or_newline_if_needed(chars_to_print);
2296 printed = write_plain_data(&data[pos], bytes_to_print);
2297 num += printed;
2298 pos += bytes_to_print;
2299 }
2300 }
2301
2302 return num;
2303 }
2304
write_cdata_sec(tree_node_t * node)2305 static int write_cdata_sec(tree_node_t *node)
2306 {
2307 int len;
2308
2309 len = 0;
2310
2311 if (!inline_on && !xml_space_on) {
2312 inline_on = 1;
2313 whitespace_needed = 0;
2314 len += write_indent(indent, 1);
2315 }
2316
2317 /* write opening markup only if previous node was not
2318 * another CDATA section
2319 */
2320 if (!inside_cdata_sec) {
2321 /* write the opening markup (with // if xml_space_on <- (script|style) */
2322 if (param_protect_cdata && xml_space_on) {
2323 len += cprintf("//%s![CDATA[", lt);
2324 chars_in_line += 11;
2325 } else {
2326 write_whitespace_or_newline_if_needed(9);
2327 len += cprintf("%s![CDATA[", lt);
2328 chars_in_line += 9;
2329 }
2330 }
2331
2332 /* write the data node itself */
2333 len += write_chardata_space_preserve(node);
2334
2335 /* write the closing markup if next node is not a CDATA
2336 * section
2337 */
2338 if (!node->sig || node->sig->tipo != Node_cdata_sec) {
2339 /* write the closing markup */
2340 if (param_protect_cdata && xml_space_on) {
2341 len += cprintf("//]]%s", gt);
2342 chars_in_line += 5;
2343 } else {
2344 len += cprintf("]]%s", gt);
2345 chars_in_line += 3;
2346 }
2347 inside_cdata_sec = 0;
2348 } else {
2349 inside_cdata_sec = 1;
2350 }
2351
2352 return len;
2353 }
2354
write_chardata_space_preserve(tree_node_t * node)2355 static int write_chardata_space_preserve(tree_node_t *node)
2356 {
2357 int data_len;
2358 xchar *data;
2359 int num;
2360
2361 data = (char*)tree_index_to_ptr(node->cont.chardata.data);
2362 data_len = node->cont.chardata.data_len;
2363 num = 0;
2364
2365 num += write_plain_data(data, data_len);
2366
2367 return num;
2368 }
2369
write_comment(tree_node_t * comm)2370 static int write_comment(tree_node_t *comm)
2371 {
2372 int num;
2373 int prev_inline;
2374
2375 num = write_indent(indent, 1);
2376
2377 write_whitespace_or_newline_if_needed(4); /* strlen("<!--") */
2378
2379 if (param_pre_comments) {
2380 num += cprintf("%s!--", lt);
2381 chars_in_line += 4;
2382 num += write_chardata_space_preserve(comm);
2383 } else {
2384 num += cprintf("%s!-- ", lt);
2385 chars_in_line += 5;
2386 prev_inline = inline_on;
2387 inline_on = 1;
2388 indent += param_tab_len;
2389 num += write_chardata(comm);
2390 indent -= param_tab_len;
2391 inline_on = prev_inline;
2392 }
2393
2394 if (param_pre_comments) {
2395 num += cprintf("--%s", gt);
2396 chars_in_line += 3;
2397 } else {
2398 num += cprintf(" --%s", gt);
2399 chars_in_line += 4;
2400 }
2401
2402 return num;
2403 }
2404
write_start_tag(tree_node_t * nodo)2405 static int write_start_tag(tree_node_t* nodo)
2406 {
2407 att_node_t *att;
2408 char limit;
2409 char *value;
2410 int num;
2411 char *text;
2412 int chars_to_print;
2413 int i;
2414 char *elm_name;
2415 int elm_name_len;
2416 int printed;
2417
2418
2419 elm_name = elm_list[ELM_ID(nodo)].name;
2420 elm_name_len = strlen(elm_name);
2421 num = 0;
2422
2423 if (nodo->cont.elemento.hijo)
2424 num += write_whitespace_or_newline_if_needed(1 + elm_name_len);
2425 else
2426 num += write_whitespace_or_newline_if_needed(3 + elm_name_len);
2427
2428 printed = cprintf("%s%s", lt, elm_name);
2429 num += printed;
2430 chars_in_line += printed;
2431
2432 for (att= nodo->cont.elemento.attlist; att; att= att->sig)
2433 if (att->es_valido) {
2434 value= (char*)tree_index_to_ptr(att->valor);
2435
2436 if (xsearch(value,"\"")) limit= '\'';
2437 else limit= '\"';
2438
2439 /* does this attribute fit in this line? */
2440 if (inline_on
2441 && (3 + strlen(att_list[att->att_id].name)
2442 + strlen(value) + chars_in_line) > param_chars_per_line) {
2443 num += write_indent_internal(indent, 1, 1);
2444 } else {
2445 cputc(' ');
2446 num++;
2447 chars_in_line++;
2448 }
2449
2450 /* write name=(single|double)quote */
2451 printed = cprintf("%s=%c", att_list[att->att_id].name, limit);
2452 num += printed;
2453 chars_in_line += printed;
2454
2455 chars_to_print = strlen(value);
2456 text = value;
2457 while (chars_to_print > 0) {
2458 if (text[0] == '&') {
2459 num += cprintf("%s", amp);
2460 chars_in_line++;
2461 text++;
2462 chars_to_print--;
2463 } else if (text[0] == '<') {
2464 num += cprintf("%s", lt);
2465 chars_in_line++;
2466 text++;
2467 chars_to_print--;
2468 } else if (text[0] == 0x0a) {
2469 num += cprintf(" ");
2470 chars_in_line++;
2471 text++;
2472 chars_to_print--;
2473 } else if (text[0] == 0x0d) {
2474 num += cprintf(" ");
2475 chars_in_line++;
2476 if (chars_to_print > 1 && text[1] == 0x0a) {
2477 text += 2;
2478 chars_to_print -= 2;
2479 } else {
2480 text++;
2481 chars_to_print--;
2482 }
2483 }
2484
2485 for (i=0; i < chars_to_print; i++)
2486 if (text[i] == '&' || text[i] == '<'
2487 || text[i] == 0x0a || text[i] == 0x0d) {
2488 break;
2489 }
2490
2491 /* print this fragment of text */
2492 printed = cwrite(text, i);
2493 num += printed;
2494 chars_in_line += printed;
2495 text += i;
2496 chars_to_print -= i;
2497 }
2498
2499 num += cprintf("%c", limit);
2500 chars_in_line++;
2501 }
2502
2503 if ((param_empty_tags && !nodo->cont.elemento.hijo)
2504 || elm_list[ELM_ID(nodo)].contenttype[doctype] == CONTTYPE_EMPTY) {
2505 if (!param_compact_empty_elm_tags) {
2506 num += cprintf(" /");
2507 } else {
2508 num += cprintf("/");
2509 }
2510 chars_in_line++;
2511 }
2512
2513 num+=cprintf(gt);
2514 chars_in_line++;
2515
2516 return num;
2517 }
2518
write_end_tag(tree_node_t * nodo)2519 static int write_end_tag(tree_node_t* nodo)
2520 {
2521 int num;
2522 int printed;
2523 char *elm_name;
2524 int elm_name_len;
2525
2526 /* if (!nodo->cont.elemento.hijo) */
2527 /* return 0; */
2528
2529 elm_name = elm_list[nodo->cont.elemento.elm_id].name;
2530 elm_name_len = strlen(elm_name);
2531 num = 0;
2532
2533 num += write_whitespace_or_newline_if_needed(3 + elm_name_len);
2534
2535 printed = cprintf("%s/%s%s",lt,
2536 elm_list[nodo->cont.elemento.elm_id].name, gt);
2537 num += printed;
2538 chars_in_line += printed;
2539
2540 return num;
2541 }
2542
2543 /*
2544 * In inline mode, if a whitespace is needed, writes it or,
2545 * if the text following it doesn't fit in the line, writes
2546 * a new line instead. If no whitespace needs to be written,
2547 * then the current line must be continued, even if it is
2548 * overflown.
2549 *
2550 */
write_whitespace_or_newline_if_needed(int next_data_len)2551 static int write_whitespace_or_newline_if_needed(int next_data_len)
2552 {
2553 int num = 0;
2554
2555 if (inline_on && whitespace_needed) {
2556 /* does the next text fit in this line */
2557 if ((chars_in_line + next_data_len + 1) > param_chars_per_line) {
2558 /* write a new line */
2559 num += write_indent(indent, 1);
2560 } else {
2561 /* write a whitespace only */
2562 cputc(' ');
2563 num++;
2564 chars_in_line++;
2565 }
2566
2567 whitespace_needed = 0;
2568 }
2569
2570 return num;
2571 }
2572
write_plain_data(xchar * text,int len)2573 static int write_plain_data(xchar* text, int len)
2574 {
2575 int num;
2576 int i;
2577 int pos;
2578 int wrote;
2579
2580 i = 0;
2581 num = 0;
2582 while (i < len) {
2583 if (text[i] == '&') {
2584 num += cprintf("%s", amp);
2585 i++;
2586 chars_in_line++;
2587 } else if (text[i] == '<') {
2588 num += cprintf("%s", lt);
2589 i++;
2590 chars_in_line++;
2591 } else if (text[i] == 0x0a) {
2592 cwrite(eol, eol_len);
2593 num++;
2594 i++;
2595 chars_in_line = 0;
2596 } else if (text[i] == 0x0d) {
2597 cwrite(eol, eol_len);
2598 num++;
2599 chars_in_line = 0;
2600 if (i + 1 < len && text[i + 1] == 0x0a) {
2601 i += 2;
2602 } else {
2603 i++;
2604 }
2605 }
2606
2607 pos = i;
2608 for ( ; i < len; i++) {
2609 if (text[i] == '&' || text[i] == '<'
2610 || text[i] == 0x0d || text[i] == 0x0a) {
2611 break;
2612 }
2613 }
2614
2615 /* print the fragment */
2616 if (i > pos) {
2617 wrote = cwrite(&text[pos], i - pos);
2618 num += wrote;
2619 chars_in_line += wrote;
2620 }
2621 }
2622
2623 return num;
2624 }
2625
write_indent(int len,int new_line)2626 static int write_indent(int len, int new_line)
2627 {
2628 return write_indent_internal(len, new_line, 0);
2629 }
2630
write_indent_internal(int len,int new_line,int ignore_xml_space)2631 static int write_indent_internal(int len, int new_line, int ignore_xml_space)
2632 {
2633 int i;
2634
2635 if (xml_space_on && !ignore_xml_space) {
2636 return 0;
2637 }
2638
2639 chars_in_line = indent;
2640
2641 if (new_line) {
2642 cprintf(eol);
2643 }
2644
2645 for (i = 0; i < len; i++)
2646 cputc(' ');
2647
2648 if (new_line)
2649 return len + 1;
2650 else
2651 return len;
2652 }
2653
cprintf_init(charset_t * to_charset,FILE * file)2654 static int cprintf_init(charset_t *to_charset, FILE *file)
2655 {
2656 cbuffer_pos = 0;
2657 cbuffer_avail = CBUFFER_SIZE;
2658 charset_init_output(to_charset, file);
2659 return 0;
2660 }
2661
cprintf_close()2662 static int cprintf_close()
2663 {
2664 /* write the last block */
2665 if (cbuffer_pos > 0)
2666 cflush();
2667
2668 /* close the charset converter */
2669 charset_close();
2670 return 0;
2671 }
2672
cprintf(char * format,...)2673 static int cprintf(char *format, ...)
2674 {
2675 va_list ap;
2676 int written;
2677 int chars_written;
2678
2679 va_start(ap, format);
2680 written = vsnprintf(&cbuffer[cbuffer_pos], cbuffer_avail, format, ap);
2681 va_end(ap);
2682
2683 /* buffer overflow? */
2684 if (written >= cbuffer_avail) {
2685 if (written >= CBUFFER_SIZE) {
2686 EXIT("Output buffer overflow");
2687 }
2688 /* write the current buffer, excluding the new data */
2689 cflush();
2690
2691 /* write again the new data to the buffer, but now at the beginning */
2692 va_start(ap, format);
2693 written = vsnprintf(&cbuffer[cbuffer_pos], cbuffer_avail, format, ap);
2694 va_end(ap);
2695 }
2696
2697 if (written < 0) {
2698 perror("vsnprintf()");
2699 EXIT("Error when writing output");
2700 }
2701
2702 /* count the number of UTF-8 chars (written is in bytes, not chars) */
2703 chars_written = ccount_utf8_chars(&cbuffer[cbuffer_pos], written);
2704
2705 cbuffer_pos += written;
2706 cbuffer_avail -= written;
2707
2708 return (int) chars_written;
2709 }
2710
cwrite(const char * buf,size_t num)2711 static int cwrite(const char *buf, size_t num)
2712 {
2713 size_t chars_written;
2714
2715 if (num > CBUFFER_SIZE) {
2716 EXIT("Output buffer overflow");
2717 }
2718
2719 if (num > cbuffer_avail) {
2720 cflush();
2721 }
2722
2723 /* count the number of UTF-8 chars (written is in bytes, not chars) */
2724 chars_written = ccount_utf8_chars(buf, num);
2725
2726 memcpy(&cbuffer[cbuffer_pos], buf, num);
2727 cbuffer_pos += num;
2728 cbuffer_avail -= num;
2729
2730 return (int) chars_written;
2731 }
2732
cputc(int c)2733 static int cputc(int c)
2734 {
2735 if (cbuffer_avail == 0)
2736 cflush();
2737 cbuffer[cbuffer_pos++] = (char) c;
2738 cbuffer_avail--;
2739 return 1;
2740 }
2741
cflush()2742 static void cflush()
2743 {
2744 int wrote = charset_write(cbuffer, cbuffer_pos);
2745 if (wrote < cbuffer_pos) {
2746 /* feed again unwrote bytes */
2747 memmove(cbuffer, &cbuffer[wrote], cbuffer_pos - wrote);
2748 cbuffer_pos = cbuffer_pos - wrote;
2749 cbuffer_avail = CBUFFER_SIZE - cbuffer_pos;
2750 } else {
2751 /* all the output bytes have been wrote */
2752 cbuffer_pos = 0;
2753 cbuffer_avail = CBUFFER_SIZE;
2754 }
2755 }
2756
ccount_utf8_chars(const char * buf,size_t num_bytes)2757 static size_t ccount_utf8_chars(const char *buf, size_t num_bytes)
2758 {
2759 size_t num_chars = 0;
2760 int i;
2761
2762 for (i = 0; i < num_bytes; i++) {
2763 if ((char)(buf[i] & 0xC0) != (char)0x80)
2764 num_chars++;
2765 }
2766
2767 return num_chars;
2768 }
2769