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[]="&lt;";
157 static char amp_escaped[]="&amp;";
158 static char gt_escaped[]="&gt;";
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, "&percnt;")) {
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,"&amp;");
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,"&lt;");
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