blob: 077611718cb0a97336cff38bba710fabf2bb08a1 [file] [log] [blame]
Daniel Veillard260a68f1998-08-13 03:39:55 +00001/*
2 * entities.c : implementation for the XML entities handking
3 *
4 * See Copyright for the status of this software.
5 *
6 * $Id$
7 */
8
9#include <stdio.h>
Seth Alvese7f12e61998-10-01 20:51:15 +000010#include <stdlib.h>
Daniel Veillard260a68f1998-08-13 03:39:55 +000011#include <string.h>
12#include "entities.h"
13
14/*
15 * The XML predefined entities.
16 */
17
18struct xmlPredefinedEntityValue {
19 const char *name;
20 const char *value;
21};
22struct xmlPredefinedEntityValue xmlPredefinedEntityValues[] = {
23 { "lt", "<" },
24 { "gt", ">" },
25 { "apos", "'" },
26 { "quot", "\"" },
27 { "amp", "&" }
28};
29
30xmlEntitiesTablePtr xmlPredefinedEntities = NULL;
31
32/*
33 * A buffer used for converting entities to their equivalent and back.
34 */
35static int buffer_size = 0;
36static CHAR *buffer = NULL;
37
38void growBuffer(void) {
39 buffer_size *= 2;
40 buffer = (CHAR *) realloc(buffer, buffer_size * sizeof(CHAR));
41 if (buffer == NULL) {
42 perror("realloc failed");
43 exit(1);
44 }
45}
46
47/*
48 * xmlFreeEntity : clean-up an entity record.
49 */
50
51void xmlFreeEntity(xmlEntityPtr entity) {
52 if (entity == NULL) return;
53
54 if (entity->name != NULL)
55 free((char *) entity->name);
56 if (entity->ExternalID != NULL)
57 free((char *) entity->ExternalID);
58 if (entity->SystemID != NULL)
59 free((char *) entity->SystemID);
60 if (entity->content != NULL)
61 free((char *) entity->content);
62 memset(entity, -1, sizeof(xmlEntity));
63}
64
65/*
66 * xmlAddDocEntity : register a new entity for an entities table.
67 *
68 * TODO !!! We should check here that the combination of type
69 * ExternalID and SystemID is valid.
70 */
71static void xmlAddEntity(xmlEntitiesTablePtr table, const CHAR *name, int type,
72 const CHAR *ExternalID, const CHAR *SystemID, CHAR *content) {
73 int i;
74 xmlEntityPtr cur;
75 int len;
76
77 for (i = 0;i < table->nb_entities;i++) {
78 cur = &table->table[i];
79 if (!xmlStrcmp(cur->name, name)) {
80 /*
81 * The entity is already defined in this Dtd, the spec says to NOT
82 * override it ... Is it worth a Warning ??? !!!
83 */
84 return;
85 }
86 }
87 if (table->nb_entities >= table->max_entities) {
88 /*
89 * need more elements.
90 */
91 table->max_entities *= 2;
92 table->table = (xmlEntityPtr)
93 realloc(table->table, table->max_entities * sizeof(xmlEntity));
94 if (table->table) {
95 perror("realloc failed");
96 exit(1);
97 }
98 }
99 cur = &table->table[table->nb_entities];
100 cur->name = xmlStrdup(name);
101 for (len = 0;name[0] != 0;name++)len++;
102 cur->len = len;
103 cur->type = type;
104 if (ExternalID != NULL)
105 cur->ExternalID = xmlStrdup(ExternalID);
106 else
107 cur->ExternalID = NULL;
108 if (SystemID != NULL)
109 cur->SystemID = xmlStrdup(SystemID);
110 else
111 cur->SystemID = NULL;
112 if (content != NULL)
113 cur->content = xmlStrdup(content);
114 else
115 cur->content = NULL;
116 table->nb_entities++;
117}
118
119/*
120 * Set up xmlPredefinedEntities from xmlPredefinedEntityValues.
121 */
122void xmlInitializePredefinedEntities(void) {
123 int i;
124 CHAR name[50];
125 CHAR value[50];
126 const char *in;
127 CHAR *out;
128
129 if (xmlPredefinedEntities != NULL) return;
130
131 xmlPredefinedEntities = xmlCreateEntitiesTable();
132 for (i = 0;i < sizeof(xmlPredefinedEntityValues) /
133 sizeof(xmlPredefinedEntityValues[0]);i++) {
134 in = xmlPredefinedEntityValues[i].name;
135 out = &name[0];
136 for (;(*out++ = (CHAR) *in);)in++;
137 in = xmlPredefinedEntityValues[i].value;
138 out = &value[0];
139 for (;(*out++ = (CHAR) *in);)in++;
140 xmlAddEntity(xmlPredefinedEntities, (const CHAR *) &name[0],
Daniel Veillard25940b71998-10-29 05:51:30 +0000141 XML_INTERNAL_PREDEFINED_ENTITY, NULL, NULL,
Daniel Veillard260a68f1998-08-13 03:39:55 +0000142 &value[0]);
143 }
144}
145
Daniel Veillardccb09631998-10-27 06:21:04 +0000146/**
147 * xmlGetPredefinedEntity:
148 * @name: the entity name
149 *
150 * Check whether this name is an predefined entity.
151 *
152 * return values: NULL if not, othervise the entity
153 */
154xmlEntityPtr
155xmlGetPredefinedEntity(const CHAR *name) {
156 int i;
157 xmlEntityPtr cur;
158
159 if (xmlPredefinedEntities == NULL)
160 xmlInitializePredefinedEntities();
161 for (i = 0;i < xmlPredefinedEntities->nb_entities;i++) {
162 cur = &xmlPredefinedEntities->table[i];
163 if (!xmlStrcmp(cur->name, name)) return(cur);
164 }
165 return(NULL);
166}
167
168
Daniel Veillard260a68f1998-08-13 03:39:55 +0000169
170/*
171 * xmlAddDtdEntity : register a new entity for this DTD.
172 */
173void xmlAddDtdEntity(xmlDocPtr doc, const CHAR *name, int type,
174 const CHAR *ExternalID, const CHAR *SystemID, CHAR *content) {
175 xmlEntitiesTablePtr table;
176
177 if (doc->dtd == NULL) {
178 fprintf(stderr, "xmlAddDtdEntity: document without Dtd !\n");
179 return;
180 }
181 table = (xmlEntitiesTablePtr) doc->dtd->entities;
182 if (table == NULL) {
183 table = xmlCreateEntitiesTable();
184 doc->dtd->entities = table;
185 }
186 xmlAddEntity(table, name, type, ExternalID, SystemID, content);
187}
188
189/*
190 * xmlAddDocEntity : register a new entity for this document.
191 */
192void xmlAddDocEntity(xmlDocPtr doc, const CHAR *name, int type,
193 const CHAR *ExternalID, const CHAR *SystemID, CHAR *content) {
194 xmlEntitiesTablePtr table;
195
196 table = (xmlEntitiesTablePtr) doc->entities;
197 if (table == NULL) {
198 table = xmlCreateEntitiesTable();
199 doc->entities = table;
200 }
201 xmlAddEntity(doc->entities, name, type, ExternalID, SystemID, content);
202}
203
204/*
205 * xmlGetDtdEntity : do an entity lookup in the Dtd entity hash table and
206 * returns the corrsponding entity, if found, NULL otherwise.
207 */
208xmlEntityPtr xmlGetDtdEntity(xmlDocPtr doc, const CHAR *name) {
209 int i;
210 xmlEntityPtr cur;
211 xmlEntitiesTablePtr table;
212
213 if ((doc->dtd != NULL) && (doc->dtd->entities != NULL)) {
214 table = (xmlEntitiesTablePtr) doc->dtd->entities;
215 for (i = 0;i < table->nb_entities;i++) {
216 cur = &table->table[i];
217 if (!xmlStrcmp(cur->name, name)) return(cur);
218 }
219 }
220 return(NULL);
221}
222
223/*
224 * xmlGetDocEntity : do an entity lookup in the document entity hash table and
225 * returns the corrsponding entity, otherwise a lookup is done
226 * in the predefined entities too.
227 */
228xmlEntityPtr xmlGetDocEntity(xmlDocPtr doc, const CHAR *name) {
229 int i;
230 xmlEntityPtr cur;
231 xmlEntitiesTablePtr table;
232
233 if (doc->entities != NULL) {
234 table = (xmlEntitiesTablePtr) doc->entities;
235 for (i = 0;i < table->nb_entities;i++) {
236 cur = &table->table[i];
237 if (!xmlStrcmp(cur->name, name)) return(cur);
238 }
239 }
240 if (xmlPredefinedEntities == NULL)
241 xmlInitializePredefinedEntities();
242 table = xmlPredefinedEntities;
243 for (i = 0;i < table->nb_entities;i++) {
244 cur = &table->table[i];
245 if (!xmlStrcmp(cur->name, name)) return(cur);
246 }
247
248 return(NULL);
249}
250
251/*
252 * xmlEncodeEntities : do a global encoding of a string, replacing the
Daniel Veillard0ba4d531998-11-01 19:34:31 +0000253 * predefined entities and non ASCII values with their
254 * entities and CharRef counterparts.
255 * TODO !!!! Once moved to UTF-8 internal encoding, the encoding of non-ascii
256 * get erroneous.
Daniel Veillard260a68f1998-08-13 03:39:55 +0000257 */
258CHAR *xmlEncodeEntities(xmlDocPtr doc, const CHAR *input) {
259 const CHAR *cur = input;
260 CHAR *out = buffer;
261
262 if (buffer == NULL) {
263 buffer_size = 1000;
264 buffer = (CHAR *) malloc(buffer_size * sizeof(CHAR));
265 if (buffer == NULL) {
266 perror("malloc failed");
267 exit(1);
268 }
269 out = buffer;
270 }
271 while (*cur != '\0') {
272 if (out - buffer > buffer_size - 100) {
273 int index = out - buffer;
274
275 growBuffer();
276 out = &buffer[index];
277 }
278
279 /*
280 * By default one have to encode at least '<', '>', '"' and '&' !
Daniel Veillard260a68f1998-08-13 03:39:55 +0000281 */
282 if (*cur == '<') {
283 *out++ = '&';
284 *out++ = 'l';
285 *out++ = 't';
286 *out++ = ';';
287 } else if (*cur == '>') {
288 *out++ = '&';
289 *out++ = 'g';
290 *out++ = 't';
291 *out++ = ';';
292 } else if (*cur == '&') {
293 *out++ = '&';
294 *out++ = 'a';
295 *out++ = 'm';
296 *out++ = 'p';
297 *out++ = ';';
298 } else if (*cur == '"') {
299 *out++ = '&';
300 *out++ = 'q';
301 *out++ = 'u';
302 *out++ = 'o';
303 *out++ = 't';
304 *out++ = ';';
305 } else if (*cur == '\'') {
306 *out++ = '&';
307 *out++ = 'a';
308 *out++ = 'p';
309 *out++ = 'o';
310 *out++ = 's';
311 *out++ = ';';
Daniel Veillard0ba4d531998-11-01 19:34:31 +0000312#ifndef USE_UTF_8
313 } else if ((sizeof(CHAR) == 1) && (*cur >= 0x80)) {
314 char buf[10], *ptr;
Daniel Veillardda4d3c41998-11-04 20:07:05 +0000315#ifdef HAVE_SNPRINTF
316 snprintf(buf, 9, "&#%d;", *cur);
317#else
318 sprintf(buf, "&#%d;", *cur);
319#endif
Daniel Veillard0ba4d531998-11-01 19:34:31 +0000320 ptr = buf;
321 while (*ptr != 0) *out++ = *ptr++;
322#endif
Daniel Veillard260a68f1998-08-13 03:39:55 +0000323 } else {
324 /*
325 * default case, just copy !
326 */
327 *out++ = *cur;
328 }
329 cur++;
330 }
331 *out++ = 0;
332 return(buffer);
333}
334
335/*
336 * xmlCreateEntitiesTable : create and initialize an enmpty hash table
337 */
338xmlEntitiesTablePtr xmlCreateEntitiesTable(void) {
339 xmlEntitiesTablePtr ret;
340
341 ret = (xmlEntitiesTablePtr)
342 malloc(sizeof(xmlEntitiesTable));
343 if (ret == NULL) {
344 fprintf(stderr, "xmlCreateEntitiesTable : malloc(%d) failed\n",
345 sizeof(xmlEntitiesTable));
346 return(NULL);
347 }
348 ret->max_entities = XML_MIN_ENTITIES_TABLE;
349 ret->nb_entities = 0;
350 ret->table = (xmlEntityPtr )
351 malloc(ret->max_entities * sizeof(xmlEntity));
352 if (ret == NULL) {
353 fprintf(stderr, "xmlCreateEntitiesTable : malloc(%d) failed\n",
354 ret->max_entities * sizeof(xmlEntity));
355 free(ret);
356 return(NULL);
357 }
358 return(ret);
359}
360
361/*
362 * xmlFreeEntitiesTable : clean up and free an entities hash table.
363 */
364void xmlFreeEntitiesTable(xmlEntitiesTablePtr table) {
365 int i;
366
367 if (table == NULL) return;
368
369 for (i = 0;i < table->nb_entities;i++) {
370 xmlFreeEntity(&table->table[i]);
371 }
372 free(table->table);
373 free(table);
374}
375
376/*
377 * Dump the content of an entity table to the document output.
378 */
379void xmlDumpEntitiesTable(xmlEntitiesTablePtr table) {
380 int i;
381 xmlEntityPtr cur;
382
383 if (table == NULL) return;
384
385 for (i = 0;i < table->nb_entities;i++) {
386 cur = &table->table[i];
387 switch (cur->type) {
388 case XML_INTERNAL_GENERAL_ENTITY:
389 xmlBufferWriteChar("<!ENTITY ");
390 xmlBufferWriteCHAR(cur->name);
391 xmlBufferWriteChar(" \"");
392 xmlBufferWriteCHAR(cur->content);
393 xmlBufferWriteChar("\">\n");
394 break;
395 case XML_EXTERNAL_GENERAL_PARSED_ENTITY:
396 xmlBufferWriteChar("<!ENTITY ");
397 xmlBufferWriteCHAR(cur->name);
398 if (cur->ExternalID != NULL) {
399 xmlBufferWriteChar(" PUBLIC \"");
400 xmlBufferWriteCHAR(cur->ExternalID);
401 xmlBufferWriteChar("\" \"");
402 xmlBufferWriteCHAR(cur->SystemID);
403 xmlBufferWriteChar("\"");
404 } else {
405 xmlBufferWriteChar(" SYSTEM \"");
406 xmlBufferWriteCHAR(cur->SystemID);
407 xmlBufferWriteChar("\"");
408 }
409 xmlBufferWriteChar(">\n");
410 break;
411 case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY:
412 xmlBufferWriteChar("<!ENTITY ");
413 xmlBufferWriteCHAR(cur->name);
414 if (cur->ExternalID != NULL) {
415 xmlBufferWriteChar(" PUBLIC \"");
416 xmlBufferWriteCHAR(cur->ExternalID);
417 xmlBufferWriteChar("\" \"");
418 xmlBufferWriteCHAR(cur->SystemID);
419 xmlBufferWriteChar("\"");
420 } else {
421 xmlBufferWriteChar(" SYSTEM \"");
422 xmlBufferWriteCHAR(cur->SystemID);
423 xmlBufferWriteChar("\"");
424 }
425 if (cur->content != NULL) { /* Should be true ! */
426 xmlBufferWriteChar(" NDATA ");
427 xmlBufferWriteCHAR(cur->content);
428 }
429 xmlBufferWriteChar(">\n");
430 break;
431 case XML_INTERNAL_PARAMETER_ENTITY:
432 xmlBufferWriteChar("<!ENTITY % ");
433 xmlBufferWriteCHAR(cur->name);
434 xmlBufferWriteChar(" \"");
435 xmlBufferWriteCHAR(cur->content);
436 xmlBufferWriteChar("\">\n");
437 break;
438 case XML_EXTERNAL_PARAMETER_ENTITY:
439 xmlBufferWriteChar("<!ENTITY % ");
440 xmlBufferWriteCHAR(cur->name);
441 if (cur->ExternalID != NULL) {
442 xmlBufferWriteChar(" PUBLIC \"");
443 xmlBufferWriteCHAR(cur->ExternalID);
444 xmlBufferWriteChar("\" \"");
445 xmlBufferWriteCHAR(cur->SystemID);
446 xmlBufferWriteChar("\"");
447 } else {
448 xmlBufferWriteChar(" SYSTEM \"");
449 xmlBufferWriteCHAR(cur->SystemID);
450 xmlBufferWriteChar("\"");
451 }
452 xmlBufferWriteChar(">\n");
453 break;
454 default:
455 fprintf(stderr,
456 "xmlDumpEntitiesTable: internal: unknown type %d\n",
457 cur->type);
458 }
459 }
460}