genx4r 0.04
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +31 -0
- data/LICENSE +22 -0
- data/README +31 -0
- data/TODO +6 -0
- data/ext/genx4r/MANIFEST +5 -0
- data/ext/genx4r/charProps.c +378 -0
- data/ext/genx4r/extconf.rb +5 -0
- data/ext/genx4r/genx.c +1940 -0
- data/ext/genx4r/genx.h +287 -0
- data/ext/genx4r/genx4r.c +584 -0
- data/genx4r.gemspec +29 -0
- data/setup.rb +1346 -0
- data/test/basics.rb +93 -0
- data/test/declare.rb +59 -0
- data/test/non-io.rb +41 -0
- metadata +55 -0
data/ext/genx4r/genx.c
ADDED
|
@@ -0,0 +1,1940 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2004 by Tim Bray and Sun Microsystems. For copying
|
|
3
|
+
* permission, see http://www.tbray.org/ongoing/genx/COPYING
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
#define GENX_VERSION "beta5"
|
|
7
|
+
|
|
8
|
+
#include <stdlib.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
|
|
11
|
+
#include "genx.h"
|
|
12
|
+
|
|
13
|
+
#define Boolean int
|
|
14
|
+
#define True 1
|
|
15
|
+
#define False 0
|
|
16
|
+
#define STRLEN_XMLNS_COLON 6
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/*******************************
|
|
20
|
+
* writer state
|
|
21
|
+
*/
|
|
22
|
+
typedef enum
|
|
23
|
+
{
|
|
24
|
+
SEQUENCE_NO_DOC,
|
|
25
|
+
SEQUENCE_PRE_DOC,
|
|
26
|
+
SEQUENCE_POST_DOC,
|
|
27
|
+
SEQUENCE_START_TAG,
|
|
28
|
+
SEQUENCE_ATTRIBUTES,
|
|
29
|
+
SEQUENCE_CONTENT
|
|
30
|
+
} writerSequence;
|
|
31
|
+
|
|
32
|
+
/*******************************
|
|
33
|
+
* generic pointer list
|
|
34
|
+
*/
|
|
35
|
+
typedef struct
|
|
36
|
+
{
|
|
37
|
+
genxWriter writer;
|
|
38
|
+
int count;
|
|
39
|
+
int space;
|
|
40
|
+
void * * pointers;
|
|
41
|
+
} plist;
|
|
42
|
+
|
|
43
|
+
/*******************************
|
|
44
|
+
* text collector, for attribute values
|
|
45
|
+
*/
|
|
46
|
+
typedef struct
|
|
47
|
+
{
|
|
48
|
+
utf8 buf;
|
|
49
|
+
int used;
|
|
50
|
+
int space;
|
|
51
|
+
} collector;
|
|
52
|
+
|
|
53
|
+
/*******************************
|
|
54
|
+
* Structs with opaquely-exposed handles
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* This one's tricky, to handle stacking namespaces
|
|
59
|
+
* 'declaration' is the current attribute which would be used to
|
|
60
|
+
* declare the currently-effective prefix
|
|
61
|
+
* 'defDeclaration' is a appropriate declaration when this is being
|
|
62
|
+
* used with the default prefix as passed to genxDeclareNamespace
|
|
63
|
+
* baroque is true if this namespace has been used with more than one
|
|
64
|
+
* prefix, or is the default namespace but has been unset
|
|
65
|
+
*/
|
|
66
|
+
struct genxNamespace_rec
|
|
67
|
+
{
|
|
68
|
+
genxWriter writer;
|
|
69
|
+
utf8 name;
|
|
70
|
+
int declCount;
|
|
71
|
+
Boolean baroque;
|
|
72
|
+
genxAttribute declaration;
|
|
73
|
+
genxAttribute defaultDecl;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
struct genxElement_rec
|
|
77
|
+
{
|
|
78
|
+
genxWriter writer;
|
|
79
|
+
utf8 type;
|
|
80
|
+
genxNamespace ns;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
typedef enum
|
|
84
|
+
{
|
|
85
|
+
ATTR_NSDECL,
|
|
86
|
+
ATTR_NAKED,
|
|
87
|
+
ATTR_PREFIXED
|
|
88
|
+
} attrType;
|
|
89
|
+
|
|
90
|
+
struct genxAttribute_rec
|
|
91
|
+
{
|
|
92
|
+
genxWriter writer;
|
|
93
|
+
utf8 name;
|
|
94
|
+
genxNamespace ns;
|
|
95
|
+
collector value;
|
|
96
|
+
int provided; /* provided for current element? */
|
|
97
|
+
attrType atype;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/*******************************
|
|
101
|
+
* genx's sandbox
|
|
102
|
+
*/
|
|
103
|
+
struct genxWriter_rec
|
|
104
|
+
{
|
|
105
|
+
FILE * file;
|
|
106
|
+
genxSender * sender;
|
|
107
|
+
genxStatus status;
|
|
108
|
+
writerSequence sequence;
|
|
109
|
+
char xmlChars[0x10000];
|
|
110
|
+
void * userData;
|
|
111
|
+
int nextPrefix;
|
|
112
|
+
utf8 empty;
|
|
113
|
+
Boolean defaultNsDeclared;
|
|
114
|
+
genxAttribute xmlnsEquals;
|
|
115
|
+
genxElement nowStarting;
|
|
116
|
+
plist namespaces;
|
|
117
|
+
plist elements;
|
|
118
|
+
plist attributes;
|
|
119
|
+
plist prefixes;
|
|
120
|
+
plist stack;
|
|
121
|
+
struct genxAttribute_rec arec;
|
|
122
|
+
char * etext[100];
|
|
123
|
+
void * (* alloc)(void * userData, int bytes);
|
|
124
|
+
void (* dealloc)(void * userData, void * data);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/*******************************
|
|
128
|
+
* Forward declarations
|
|
129
|
+
*/
|
|
130
|
+
static genxAttribute declareAttribute(genxWriter w, genxNamespace ns,
|
|
131
|
+
constUtf8 name, constUtf8 valuestr,
|
|
132
|
+
genxStatus * statusP);
|
|
133
|
+
static genxStatus addNamespace(genxNamespace ns, utf8 prefix);
|
|
134
|
+
static genxStatus unsetDefaultNamespace(genxWriter w);
|
|
135
|
+
static genxStatus addAttribute(genxAttribute a, constUtf8 valuestr);
|
|
136
|
+
void genxSetCharProps(char * p);
|
|
137
|
+
|
|
138
|
+
/*******************************
|
|
139
|
+
* End of declarations
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/*******************************
|
|
143
|
+
* private memory utilities
|
|
144
|
+
*/
|
|
145
|
+
static void * allocate(genxWriter w, int bytes)
|
|
146
|
+
{
|
|
147
|
+
if (w->alloc)
|
|
148
|
+
return (void *) (*w->alloc)(w->userData, bytes);
|
|
149
|
+
else
|
|
150
|
+
return (void *) malloc(bytes);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static void deallocate(genxWriter w, void * data)
|
|
154
|
+
{
|
|
155
|
+
if (w->dealloc)
|
|
156
|
+
(*w->dealloc)(w->userData, data);
|
|
157
|
+
else if (w->alloc == NULL)
|
|
158
|
+
free(data);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static utf8 copy(genxWriter w, constUtf8 from)
|
|
162
|
+
{
|
|
163
|
+
utf8 temp;
|
|
164
|
+
|
|
165
|
+
if ((temp = (utf8) allocate(w, strlen((const char *) from) + 1)) == NULL)
|
|
166
|
+
return NULL;
|
|
167
|
+
strcpy((char *) temp, (const char *) from);
|
|
168
|
+
return temp;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static genxStatus initCollector(genxWriter w, collector * c)
|
|
172
|
+
{
|
|
173
|
+
c->space = 100;
|
|
174
|
+
if ((c->buf = (utf8) allocate(w, c->space)) == NULL)
|
|
175
|
+
return GENX_ALLOC_FAILED;
|
|
176
|
+
c->used = 0;
|
|
177
|
+
return GENX_SUCCESS;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static genxStatus growCollector(genxWriter w, collector * c, int size)
|
|
181
|
+
{
|
|
182
|
+
utf8 newSpace;
|
|
183
|
+
|
|
184
|
+
c->space = size * 2;
|
|
185
|
+
if ((newSpace = (utf8) allocate(w, c->space)) == NULL)
|
|
186
|
+
return GENX_ALLOC_FAILED;
|
|
187
|
+
|
|
188
|
+
strncpy((char *) newSpace, (const char *) c->buf, c->used);
|
|
189
|
+
newSpace[c->used] = 0;
|
|
190
|
+
deallocate(w, c->buf);
|
|
191
|
+
c->buf = newSpace;
|
|
192
|
+
return GENX_SUCCESS;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
static void startCollect(collector * c)
|
|
196
|
+
{
|
|
197
|
+
c->used = 0;
|
|
198
|
+
}
|
|
199
|
+
static void endCollect(collector * c)
|
|
200
|
+
{
|
|
201
|
+
c->buf[c->used] = 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static genxStatus collectString(genxWriter w, collector * c, constUtf8 string)
|
|
205
|
+
{
|
|
206
|
+
int sl = strlen((const char *) string);
|
|
207
|
+
|
|
208
|
+
if (sl >= c->space)
|
|
209
|
+
if ((w->status = growCollector(w, c, sl)) != GENX_SUCCESS)
|
|
210
|
+
return GENX_ALLOC_FAILED;
|
|
211
|
+
|
|
212
|
+
strcpy((char *) c->buf, (const char *) string);
|
|
213
|
+
return GENX_SUCCESS;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#define collectPiece(w,c,d,size) {if (((c)->used+(size))>=(c)->space){if (((w)->status=growCollector(w,c,(c)->used+(size)))!=GENX_SUCCESS) return (w)->status;}strncpy((char *)(c)->buf+(c)->used,d,size);(c)->used+=size;}
|
|
217
|
+
|
|
218
|
+
/*******************************
|
|
219
|
+
* private list utilities
|
|
220
|
+
*/
|
|
221
|
+
static genxStatus initPlist(genxWriter w, plist * pl)
|
|
222
|
+
{
|
|
223
|
+
pl->writer = w;
|
|
224
|
+
pl->count = 0;
|
|
225
|
+
pl->space = 10;
|
|
226
|
+
pl->pointers = (void * *) allocate(w, pl->space * sizeof(void *));
|
|
227
|
+
if (pl->pointers == NULL)
|
|
228
|
+
return GENX_ALLOC_FAILED;
|
|
229
|
+
|
|
230
|
+
return GENX_SUCCESS;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/*
|
|
234
|
+
* make room in a plist
|
|
235
|
+
*/
|
|
236
|
+
static Boolean checkExpand(plist * pl)
|
|
237
|
+
{
|
|
238
|
+
void * * newlist;
|
|
239
|
+
int i;
|
|
240
|
+
|
|
241
|
+
if (pl->count < pl->space)
|
|
242
|
+
return True;
|
|
243
|
+
|
|
244
|
+
pl->space *= 2;
|
|
245
|
+
newlist = (void * *) allocate(pl->writer, pl->space * sizeof(void *));
|
|
246
|
+
if (newlist == NULL)
|
|
247
|
+
return False;
|
|
248
|
+
for (i = 0; i < pl->count; i++)
|
|
249
|
+
newlist[i] = pl->pointers[i];
|
|
250
|
+
deallocate(pl->writer, pl->pointers);
|
|
251
|
+
pl->pointers = newlist;
|
|
252
|
+
|
|
253
|
+
return True;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/*
|
|
257
|
+
* stick something on the end of a plist
|
|
258
|
+
*/
|
|
259
|
+
static genxStatus listAppend(plist * pl, void * pointer)
|
|
260
|
+
{
|
|
261
|
+
if (!checkExpand(pl))
|
|
262
|
+
return GENX_ALLOC_FAILED;
|
|
263
|
+
|
|
264
|
+
pl->pointers[pl->count++] = pointer;
|
|
265
|
+
return GENX_SUCCESS;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/*
|
|
269
|
+
* insert in place, shuffling up
|
|
270
|
+
*/
|
|
271
|
+
static genxStatus listInsert(plist * pl, void * pointer, int at)
|
|
272
|
+
{
|
|
273
|
+
int i;
|
|
274
|
+
|
|
275
|
+
if (!checkExpand(pl))
|
|
276
|
+
return GENX_ALLOC_FAILED;
|
|
277
|
+
|
|
278
|
+
for (i = pl->count; i > at; i--)
|
|
279
|
+
pl->pointers[i] = pl->pointers[i - 1];
|
|
280
|
+
pl->count++;
|
|
281
|
+
|
|
282
|
+
pl->pointers[at] = pointer;
|
|
283
|
+
return GENX_SUCCESS;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/*******************************
|
|
287
|
+
* list lookups
|
|
288
|
+
*/
|
|
289
|
+
|
|
290
|
+
static genxNamespace findNamespace(plist * pl, constUtf8 uri)
|
|
291
|
+
{
|
|
292
|
+
int i;
|
|
293
|
+
genxNamespace * nn = (genxNamespace *) pl->pointers;
|
|
294
|
+
|
|
295
|
+
for (i = 0; i < pl->count; i++)
|
|
296
|
+
if (strcmp((char *) uri, (const char *) nn[i]->name) == 0)
|
|
297
|
+
return nn[i];
|
|
298
|
+
|
|
299
|
+
return NULL;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
static genxElement findElement(plist * pl, constUtf8 xmlns, constUtf8 type)
|
|
303
|
+
{
|
|
304
|
+
int i;
|
|
305
|
+
genxElement * ee = (genxElement *) pl->pointers;
|
|
306
|
+
|
|
307
|
+
for (i = 0; i < pl->count; i++)
|
|
308
|
+
{
|
|
309
|
+
if (xmlns == NULL)
|
|
310
|
+
{
|
|
311
|
+
if (ee[i]->ns == NULL && strcmp((const char *) type,
|
|
312
|
+
(const char *) ee[i]->type) == 0)
|
|
313
|
+
return ee[i];
|
|
314
|
+
}
|
|
315
|
+
else
|
|
316
|
+
{
|
|
317
|
+
if (ee[i]->ns != NULL &&
|
|
318
|
+
strcmp((const char *) xmlns, (const char *) ee[i]->ns->name) == 0 &&
|
|
319
|
+
strcmp((const char *) type, (const char *) ee[i]->type) == 0)
|
|
320
|
+
return ee[i];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return NULL;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/*
|
|
328
|
+
* store & intern a prefix, after giving it the
|
|
329
|
+
* "xmlns:" prefix. Don't allow storing the same one twice unless 'force'
|
|
330
|
+
* is set.
|
|
331
|
+
*/
|
|
332
|
+
static utf8 storePrefix(genxWriter w, constUtf8 prefix, Boolean force)
|
|
333
|
+
{
|
|
334
|
+
int high, low;
|
|
335
|
+
utf8 * pp = (utf8 *) w->prefixes.pointers;
|
|
336
|
+
unsigned char buf[1024];
|
|
337
|
+
|
|
338
|
+
if (prefix[0] == 0)
|
|
339
|
+
prefix = (utf8) "xmlns";
|
|
340
|
+
else
|
|
341
|
+
{
|
|
342
|
+
sprintf((char *) buf, "xmlns:%s", prefix);
|
|
343
|
+
prefix = buf;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
high = w->prefixes.count; low = -1;
|
|
347
|
+
while (high - low > 1)
|
|
348
|
+
{
|
|
349
|
+
int probe = (high + low) / 2;
|
|
350
|
+
if (strcmp((const char *) prefix, (const char *) pp[probe]) < 0)
|
|
351
|
+
high = probe;
|
|
352
|
+
else
|
|
353
|
+
low = probe;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/* already there? */
|
|
357
|
+
if (low != -1 && strcmp((const char *) prefix, (const char *) pp[low]) == 0)
|
|
358
|
+
{
|
|
359
|
+
if (force)
|
|
360
|
+
return pp[low];
|
|
361
|
+
|
|
362
|
+
w->status = GENX_DUPLICATE_PREFIX;
|
|
363
|
+
return NULL;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* copy & insert */
|
|
367
|
+
if ((prefix = copy(w, prefix)) == NULL)
|
|
368
|
+
{
|
|
369
|
+
w->status = GENX_ALLOC_FAILED;
|
|
370
|
+
return NULL;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
w->status = listInsert(&w->prefixes, (void *) prefix, high);
|
|
374
|
+
if (w->status != GENX_SUCCESS)
|
|
375
|
+
return NULL;
|
|
376
|
+
|
|
377
|
+
return (utf8) prefix;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/*******************************
|
|
381
|
+
* UTF8 bit-banging
|
|
382
|
+
*/
|
|
383
|
+
|
|
384
|
+
/*
|
|
385
|
+
* Retrieve the character pointed at, and advance the pointer; return -1 on
|
|
386
|
+
* error
|
|
387
|
+
*/
|
|
388
|
+
int genxNextUnicodeChar(constUtf8 * sp)
|
|
389
|
+
{
|
|
390
|
+
utf8 s = (utf8) *sp;
|
|
391
|
+
int c;
|
|
392
|
+
|
|
393
|
+
if (*s == 0)
|
|
394
|
+
return -1;
|
|
395
|
+
|
|
396
|
+
if (*s < 0x80)
|
|
397
|
+
c = *s++;
|
|
398
|
+
|
|
399
|
+
/* all this encoding sanity-checking taken from section 3.10 of Unicode 4 */
|
|
400
|
+
else if (*s < 0xc2)
|
|
401
|
+
goto malformed;
|
|
402
|
+
|
|
403
|
+
/* 2-byte encodings, first byte c2 .. df */
|
|
404
|
+
else if (*s < 0xe0)
|
|
405
|
+
{
|
|
406
|
+
c = (*s++ & 0x1f) << 6;
|
|
407
|
+
|
|
408
|
+
/*
|
|
409
|
+
* for this common idiom, if ((c & 0xc0) != 0x80) is slightly faster
|
|
410
|
+
* on MacOS (PPC)
|
|
411
|
+
*/
|
|
412
|
+
if (*s < 0x80 || *s > 0xbf)
|
|
413
|
+
goto malformed;
|
|
414
|
+
|
|
415
|
+
c |= *s++ & 0x3f;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* 3-byte encodings, first byte e0 .. ef */
|
|
419
|
+
else if (*s < 0xf0)
|
|
420
|
+
{
|
|
421
|
+
int b0 = *s;
|
|
422
|
+
c = (*s++ & 0x0f) << 12;
|
|
423
|
+
|
|
424
|
+
if ((b0 == 0xe0 && (*s < 0xa0 || *s > 0xbf)) ||
|
|
425
|
+
(b0 < 0xed && (*s < 0x80 || *s > 0xbf)) ||
|
|
426
|
+
(b0 == 0xed && (*s < 0x80 || *s > 0x9f)) ||
|
|
427
|
+
(b0 > 0xed && (*s < 0x80 || *s > 0xbf)))
|
|
428
|
+
goto malformed;
|
|
429
|
+
|
|
430
|
+
c |= (*s++ & 0x3f) << 6;
|
|
431
|
+
|
|
432
|
+
if (*s < 0x80 || *s > 0xbf)
|
|
433
|
+
goto malformed;
|
|
434
|
+
|
|
435
|
+
c |= *s++ & 0x3f;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* 4-byte encodings, first byte f0 .. f4 */
|
|
439
|
+
else if (*s < 0xf5)
|
|
440
|
+
{
|
|
441
|
+
int b0 = *s;
|
|
442
|
+
c = (*s++ & 0x07) << 18;
|
|
443
|
+
|
|
444
|
+
if ((b0 == 0xf0 && (*s < 0x90 || *s > 0xbf)) ||
|
|
445
|
+
(b0 < 0xf4 && (*s < 0x80 || *s > 0xbf)) ||
|
|
446
|
+
(b0 >= 0xf4 && (*s < 0x80 || *s > 0x8f)))
|
|
447
|
+
goto malformed;
|
|
448
|
+
|
|
449
|
+
c |= (*s++ & 0x3f) << 12;
|
|
450
|
+
|
|
451
|
+
if (*s < 0x80 || *s > 0xbf)
|
|
452
|
+
goto malformed;
|
|
453
|
+
|
|
454
|
+
c |= (*s++ & 0x3f) << 6;
|
|
455
|
+
|
|
456
|
+
if (*s < 0x80 || *s > 0xbf)
|
|
457
|
+
goto malformed;
|
|
458
|
+
|
|
459
|
+
c |= *s++ & 0x3f;
|
|
460
|
+
}
|
|
461
|
+
else
|
|
462
|
+
goto malformed;
|
|
463
|
+
|
|
464
|
+
*sp = s;
|
|
465
|
+
return c;
|
|
466
|
+
|
|
467
|
+
/*
|
|
468
|
+
* this is needed by scrubText, which wants to get the pointer moved
|
|
469
|
+
* past the problem area.
|
|
470
|
+
*/
|
|
471
|
+
malformed:
|
|
472
|
+
if (*s)
|
|
473
|
+
++s;
|
|
474
|
+
*sp = s;
|
|
475
|
+
return -1;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
static Boolean isXMLChar(genxWriter w, int c)
|
|
479
|
+
{
|
|
480
|
+
if (c < 0)
|
|
481
|
+
return False;
|
|
482
|
+
else if (c < 0x10000)
|
|
483
|
+
return (int) w->xmlChars[c];
|
|
484
|
+
else
|
|
485
|
+
return (c <= 0x10ffff);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
static Boolean isLetter(genxWriter w, int c)
|
|
489
|
+
{
|
|
490
|
+
if (c < 0 || c > 0xffff)
|
|
491
|
+
return False;
|
|
492
|
+
else
|
|
493
|
+
return w->xmlChars[c] & GENX_LETTER;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
static Boolean isNameChar(genxWriter w, int c)
|
|
497
|
+
{
|
|
498
|
+
if (c < 0 || c > 0xffff)
|
|
499
|
+
return False;
|
|
500
|
+
else
|
|
501
|
+
return w->xmlChars[c] & GENX_NAMECHAR;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/*******************************
|
|
505
|
+
* Constructors, setters/getters
|
|
506
|
+
*/
|
|
507
|
+
|
|
508
|
+
/*
|
|
509
|
+
* Construct a new genxWriter
|
|
510
|
+
*/
|
|
511
|
+
genxWriter genxNew(void * (* alloc)(void * userData, int bytes),
|
|
512
|
+
void (* dealloc)(void * userData, void * data),
|
|
513
|
+
void * userData)
|
|
514
|
+
{
|
|
515
|
+
genxWriter w;
|
|
516
|
+
genxNamespace xml;
|
|
517
|
+
|
|
518
|
+
if (alloc)
|
|
519
|
+
w = (genxWriter) (*alloc)(userData, sizeof(struct genxWriter_rec));
|
|
520
|
+
else
|
|
521
|
+
w = (genxWriter) malloc(sizeof(struct genxWriter_rec));
|
|
522
|
+
|
|
523
|
+
if (w == NULL)
|
|
524
|
+
return NULL;
|
|
525
|
+
|
|
526
|
+
w->status = GENX_SUCCESS;
|
|
527
|
+
w->alloc = alloc;
|
|
528
|
+
w->dealloc = dealloc;
|
|
529
|
+
w->userData = userData;
|
|
530
|
+
w->sequence = SEQUENCE_NO_DOC;
|
|
531
|
+
|
|
532
|
+
if (initPlist(w, &w->namespaces) != GENX_SUCCESS ||
|
|
533
|
+
initPlist(w, &w->elements) != GENX_SUCCESS ||
|
|
534
|
+
initPlist(w, &w->attributes) != GENX_SUCCESS ||
|
|
535
|
+
initPlist(w, &w->prefixes) != GENX_SUCCESS ||
|
|
536
|
+
initPlist(w, &w->stack) != GENX_SUCCESS)
|
|
537
|
+
return NULL;
|
|
538
|
+
|
|
539
|
+
if ((w->status = initCollector(w, &w->arec.value)) != GENX_SUCCESS)
|
|
540
|
+
return NULL;
|
|
541
|
+
|
|
542
|
+
if ((w->empty = copy(w, (utf8) "")) == NULL)
|
|
543
|
+
{
|
|
544
|
+
w->status = GENX_ALLOC_FAILED;
|
|
545
|
+
return NULL;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
w->xmlnsEquals = declareAttribute(w, NULL, (utf8) "xmlns", NULL, &w->status);
|
|
549
|
+
if (w->xmlnsEquals == NULL || w->status != GENX_SUCCESS)
|
|
550
|
+
return NULL;
|
|
551
|
+
w->defaultNsDeclared = False;
|
|
552
|
+
|
|
553
|
+
w->nextPrefix = 1;
|
|
554
|
+
|
|
555
|
+
genxSetCharProps(w->xmlChars);
|
|
556
|
+
|
|
557
|
+
w->etext[GENX_SUCCESS] = "Success";
|
|
558
|
+
w->etext[GENX_BAD_UTF8] = "Bad UTF8";
|
|
559
|
+
w->etext[GENX_NON_XML_CHARACTER] = "Non XML Character";
|
|
560
|
+
w->etext[GENX_BAD_NAME] = "Bad NAME";
|
|
561
|
+
w->etext[GENX_ALLOC_FAILED] = "Memory allocation failed";
|
|
562
|
+
w->etext[GENX_BAD_NAMESPACE_NAME] = "Bad namespace name";
|
|
563
|
+
w->etext[GENX_INTERNAL_ERROR] = "Internal error";
|
|
564
|
+
w->etext[GENX_DUPLICATE_PREFIX] = "Duplicate prefix";
|
|
565
|
+
w->etext[GENX_SEQUENCE_ERROR] = "Call out of sequence";
|
|
566
|
+
w->etext[GENX_NO_START_TAG] = "No Start-tag for EndElement call";
|
|
567
|
+
w->etext[GENX_IO_ERROR] = "I/O error";
|
|
568
|
+
w->etext[GENX_MISSING_VALUE] = "Missing attribute value";
|
|
569
|
+
w->etext[GENX_MALFORMED_COMMENT] = "Malformed comment body";
|
|
570
|
+
w->etext[GENX_MALFORMED_PI] = "?> in PI";
|
|
571
|
+
w->etext[GENX_XML_PI_TARGET] = "Target of PI matches [xX][mM][lL]";
|
|
572
|
+
w->etext[GENX_DUPLICATE_ATTRIBUTE] =
|
|
573
|
+
"Same attribute specified more than once";
|
|
574
|
+
w->etext[GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE] =
|
|
575
|
+
"Attribute cannot be in default namespace";
|
|
576
|
+
w->etext[GENX_DUPLICATE_NAMESPACE] =
|
|
577
|
+
"Declared namespace twice with different prefixes on one element.";
|
|
578
|
+
w->etext[GENX_BAD_DEFAULT_DECLARATION] =
|
|
579
|
+
"Declared a default namespace on an element which is in no namespace";
|
|
580
|
+
|
|
581
|
+
/* the xml: namespace is pre-wired */
|
|
582
|
+
xml = genxDeclareNamespace(w, (utf8) "http://www.w3.org/XML/1998/namespace",
|
|
583
|
+
(utf8) "xml", &w->status);
|
|
584
|
+
if (xml == NULL)
|
|
585
|
+
return NULL;
|
|
586
|
+
xml->declCount = 1;
|
|
587
|
+
xml->declaration = xml->defaultDecl;
|
|
588
|
+
|
|
589
|
+
return w;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/*
|
|
593
|
+
* get/set userData
|
|
594
|
+
*/
|
|
595
|
+
void genxSetUserData(genxWriter w, void * userData)
|
|
596
|
+
{
|
|
597
|
+
w->userData = userData;
|
|
598
|
+
}
|
|
599
|
+
void * genxGetUserData(genxWriter w)
|
|
600
|
+
{
|
|
601
|
+
return w->userData;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/*
|
|
605
|
+
* get/set allocator
|
|
606
|
+
*/
|
|
607
|
+
void genxSetAlloc(genxWriter w, void * (* alloc)(void * userData, int bytes))
|
|
608
|
+
{
|
|
609
|
+
w->alloc = alloc;
|
|
610
|
+
}
|
|
611
|
+
void genxSetDealloc(genxWriter w,
|
|
612
|
+
void (* dealloc)(void * userData, void * data))
|
|
613
|
+
{
|
|
614
|
+
w->dealloc = dealloc;
|
|
615
|
+
}
|
|
616
|
+
void * (* genxGetAlloc(genxWriter w))(void * userData, int bytes)
|
|
617
|
+
{
|
|
618
|
+
return w->alloc;
|
|
619
|
+
}
|
|
620
|
+
void (* genxGetDealloc(genxWriter w))(void * userData, void * data)
|
|
621
|
+
{
|
|
622
|
+
return w->dealloc;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/*
|
|
626
|
+
* Clean up
|
|
627
|
+
*/
|
|
628
|
+
void genxDispose(genxWriter w)
|
|
629
|
+
{
|
|
630
|
+
int i;
|
|
631
|
+
genxNamespace * nn = (genxNamespace *) w->namespaces.pointers;
|
|
632
|
+
genxElement * ee = (genxElement *) w->elements.pointers;
|
|
633
|
+
genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
|
|
634
|
+
utf8 * pp = (utf8 *) w->prefixes.pointers;
|
|
635
|
+
|
|
636
|
+
for (i = 0; i < w->namespaces.count; i++)
|
|
637
|
+
{
|
|
638
|
+
deallocate(w, nn[i]->name);
|
|
639
|
+
deallocate(w, nn[i]);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
for (i = 0; i < w->elements.count; i++)
|
|
643
|
+
{
|
|
644
|
+
deallocate(w, ee[i]->type);
|
|
645
|
+
deallocate(w, ee[i]);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
for (i = 0; i < w->attributes.count; i++)
|
|
649
|
+
{
|
|
650
|
+
deallocate(w, aa[i]->name);
|
|
651
|
+
deallocate(w, aa[i]->value.buf);
|
|
652
|
+
deallocate(w, aa[i]);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
for(i = 0; i < w->prefixes.count; i++)
|
|
656
|
+
deallocate(w, pp[i]);
|
|
657
|
+
|
|
658
|
+
deallocate(w, w->namespaces.pointers);
|
|
659
|
+
deallocate(w, w->elements.pointers);
|
|
660
|
+
deallocate(w, w->attributes.pointers);
|
|
661
|
+
deallocate(w, w->prefixes.pointers);
|
|
662
|
+
deallocate(w, w->stack.pointers);
|
|
663
|
+
|
|
664
|
+
deallocate(w, w->arec.value.buf);
|
|
665
|
+
|
|
666
|
+
deallocate(w, w->empty);
|
|
667
|
+
|
|
668
|
+
/* how Oscar dealt with Igli */
|
|
669
|
+
deallocate(w, w);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/*******************************
|
|
673
|
+
* External utility routines
|
|
674
|
+
*/
|
|
675
|
+
|
|
676
|
+
/*
|
|
677
|
+
* scan a buffer and report problems with UTF-8 encoding or non-XML characters
|
|
678
|
+
*/
|
|
679
|
+
genxStatus genxCheckText(genxWriter w, constUtf8 s)
|
|
680
|
+
{
|
|
681
|
+
while (*s)
|
|
682
|
+
{
|
|
683
|
+
int c = genxNextUnicodeChar(&s);
|
|
684
|
+
if (c == -1)
|
|
685
|
+
return GENX_BAD_UTF8;
|
|
686
|
+
|
|
687
|
+
if (!isXMLChar(w, c))
|
|
688
|
+
return GENX_NON_XML_CHARACTER;
|
|
689
|
+
}
|
|
690
|
+
return GENX_SUCCESS;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/*
|
|
694
|
+
* Purify some text
|
|
695
|
+
*/
|
|
696
|
+
int genxScrubText(genxWriter w, constUtf8 in, utf8 out)
|
|
697
|
+
{
|
|
698
|
+
int problems = 0;
|
|
699
|
+
constUtf8 last = in;
|
|
700
|
+
|
|
701
|
+
while (*in)
|
|
702
|
+
{
|
|
703
|
+
int c = genxNextUnicodeChar(&in);
|
|
704
|
+
if (c == -1)
|
|
705
|
+
{
|
|
706
|
+
problems++;
|
|
707
|
+
last = in;
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (!isXMLChar(w, c))
|
|
712
|
+
{
|
|
713
|
+
problems++;
|
|
714
|
+
last = in;
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
while (last < in)
|
|
719
|
+
*out++ = *last++;
|
|
720
|
+
}
|
|
721
|
+
*out = 0;
|
|
722
|
+
return problems;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/*
|
|
726
|
+
* check one character
|
|
727
|
+
*/
|
|
728
|
+
int genxCharClass(genxWriter w, int c)
|
|
729
|
+
{
|
|
730
|
+
int ret = 0;
|
|
731
|
+
|
|
732
|
+
if (isXMLChar(w, c))
|
|
733
|
+
ret |= GENX_XML_CHAR;
|
|
734
|
+
if (isNameChar(w, c))
|
|
735
|
+
ret |= GENX_NAMECHAR;
|
|
736
|
+
if (isLetter(w, c))
|
|
737
|
+
ret |= GENX_LETTER;
|
|
738
|
+
return ret;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
static genxStatus checkNCName(genxWriter w, constUtf8 name)
|
|
742
|
+
{
|
|
743
|
+
int c;
|
|
744
|
+
|
|
745
|
+
if (name == NULL || *name == 0)
|
|
746
|
+
return GENX_BAD_NAME;
|
|
747
|
+
|
|
748
|
+
c = genxNextUnicodeChar(&name);
|
|
749
|
+
if (!isLetter(w, c) && c != ':' && c != '_')
|
|
750
|
+
return GENX_BAD_NAME;
|
|
751
|
+
|
|
752
|
+
while (*name)
|
|
753
|
+
{
|
|
754
|
+
c = genxNextUnicodeChar(&name);
|
|
755
|
+
if (c == -1)
|
|
756
|
+
return GENX_BAD_UTF8;
|
|
757
|
+
if (!isNameChar(w, c))
|
|
758
|
+
return GENX_BAD_NAME;
|
|
759
|
+
}
|
|
760
|
+
return GENX_SUCCESS;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
char * genxGetErrorMessage(genxWriter w, genxStatus status)
|
|
764
|
+
{
|
|
765
|
+
return w->etext[status];
|
|
766
|
+
}
|
|
767
|
+
char * genxLastErrorMessage(genxWriter w)
|
|
768
|
+
{
|
|
769
|
+
return w->etext[w->status];
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/*******************************
|
|
773
|
+
* Declarations: namespace/element/attribute
|
|
774
|
+
*/
|
|
775
|
+
|
|
776
|
+
/*
|
|
777
|
+
* DeclareNamespace - by far the most complex routine in Genx
|
|
778
|
+
*/
|
|
779
|
+
genxNamespace genxDeclareNamespace(genxWriter w, constUtf8 uri,
|
|
780
|
+
constUtf8 defaultPref,
|
|
781
|
+
genxStatus * statusP)
|
|
782
|
+
{
|
|
783
|
+
genxNamespace ns;
|
|
784
|
+
genxAttribute defaultDecl;
|
|
785
|
+
unsigned char newPrefix[100];
|
|
786
|
+
|
|
787
|
+
if (uri == NULL || uri[0] == 0)
|
|
788
|
+
{
|
|
789
|
+
w->status = GENX_BAD_NAMESPACE_NAME;
|
|
790
|
+
goto busted;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if ((w->status = genxCheckText(w, uri)) != GENX_SUCCESS)
|
|
794
|
+
goto busted;
|
|
795
|
+
|
|
796
|
+
/* if a prefix is provided, it has to be an NCname */
|
|
797
|
+
if (defaultPref != NULL && defaultPref[0] != 0 &&
|
|
798
|
+
(w->status = checkNCName(w, defaultPref)) != GENX_SUCCESS)
|
|
799
|
+
goto busted;
|
|
800
|
+
|
|
801
|
+
/* previously declared? */
|
|
802
|
+
if ((ns = findNamespace(&w->namespaces, uri)))
|
|
803
|
+
{
|
|
804
|
+
/* just a lookup, really */
|
|
805
|
+
if ((defaultPref == NULL) ||
|
|
806
|
+
(defaultPref[0] == 0 && ns->defaultDecl == w->xmlnsEquals) ||
|
|
807
|
+
(strcmp((const char *) ns->defaultDecl->name + STRLEN_XMLNS_COLON,
|
|
808
|
+
(const char *) defaultPref) == 0))
|
|
809
|
+
{
|
|
810
|
+
w->status = *statusP = GENX_SUCCESS;
|
|
811
|
+
return ns;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/* wasn't already declared */
|
|
816
|
+
else
|
|
817
|
+
{
|
|
818
|
+
|
|
819
|
+
/* make a default prefix if none provided */
|
|
820
|
+
if (defaultPref == NULL)
|
|
821
|
+
{
|
|
822
|
+
sprintf((char *) newPrefix, "g%d", w->nextPrefix++);
|
|
823
|
+
defaultPref = newPrefix;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
ns = (genxNamespace) allocate(w, sizeof(struct genxNamespace_rec));
|
|
827
|
+
if (ns == NULL)
|
|
828
|
+
{
|
|
829
|
+
w->status = GENX_ALLOC_FAILED;
|
|
830
|
+
goto busted;
|
|
831
|
+
}
|
|
832
|
+
ns->writer = w;
|
|
833
|
+
ns->baroque = False;
|
|
834
|
+
|
|
835
|
+
if ((ns->name = copy(w, uri)) == NULL)
|
|
836
|
+
{
|
|
837
|
+
w->status = GENX_ALLOC_FAILED;
|
|
838
|
+
goto busted;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if ((w->status = listAppend(&w->namespaces, ns)) != GENX_SUCCESS)
|
|
842
|
+
goto busted;
|
|
843
|
+
ns->defaultDecl = ns->declaration = NULL;
|
|
844
|
+
ns->declCount = 0;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (defaultPref[0] == 0)
|
|
848
|
+
{
|
|
849
|
+
if (w->defaultNsDeclared)
|
|
850
|
+
{
|
|
851
|
+
w->status = GENX_DUPLICATE_PREFIX;
|
|
852
|
+
goto busted;
|
|
853
|
+
}
|
|
854
|
+
defaultDecl = w->xmlnsEquals;
|
|
855
|
+
w->defaultNsDeclared = True;
|
|
856
|
+
}
|
|
857
|
+
else
|
|
858
|
+
{
|
|
859
|
+
/* this catches dupes too */
|
|
860
|
+
if ((defaultPref = storePrefix(w, defaultPref, False)) == NULL)
|
|
861
|
+
goto busted;
|
|
862
|
+
|
|
863
|
+
defaultDecl = declareAttribute(w, NULL, defaultPref, ns->name, statusP);
|
|
864
|
+
if (defaultDecl == NULL || *statusP != GENX_SUCCESS)
|
|
865
|
+
{
|
|
866
|
+
w->status = *statusP;
|
|
867
|
+
return NULL;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (ns->defaultDecl != NULL && defaultDecl != ns->defaultDecl)
|
|
872
|
+
ns->baroque = True;
|
|
873
|
+
ns->defaultDecl = defaultDecl;
|
|
874
|
+
|
|
875
|
+
*statusP = GENX_SUCCESS;
|
|
876
|
+
return ns;
|
|
877
|
+
|
|
878
|
+
busted:
|
|
879
|
+
*statusP = w->status;
|
|
880
|
+
return NULL;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/*
|
|
884
|
+
* get namespace prefix
|
|
885
|
+
*/
|
|
886
|
+
utf8 genxGetNamespacePrefix(genxNamespace ns)
|
|
887
|
+
{
|
|
888
|
+
if (ns->declaration == NULL)
|
|
889
|
+
return NULL;
|
|
890
|
+
|
|
891
|
+
if (ns->declaration == ns->writer->xmlnsEquals)
|
|
892
|
+
return ns->writer->empty;
|
|
893
|
+
|
|
894
|
+
return ns->declaration->name + STRLEN_XMLNS_COLON;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/*
|
|
898
|
+
* DeclareElement - see genx.h for details
|
|
899
|
+
*/
|
|
900
|
+
genxElement genxDeclareElement(genxWriter w,
|
|
901
|
+
genxNamespace ns, constUtf8 type,
|
|
902
|
+
genxStatus * statusP)
|
|
903
|
+
{
|
|
904
|
+
genxElement old;
|
|
905
|
+
genxElement el;
|
|
906
|
+
|
|
907
|
+
if ((w->status = checkNCName(w, type)) != GENX_SUCCESS)
|
|
908
|
+
{
|
|
909
|
+
*statusP = w->status;
|
|
910
|
+
return NULL;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/* already declared? */
|
|
914
|
+
old = findElement(&w->elements, (ns == NULL) ? NULL : ns->name, type);
|
|
915
|
+
if (old)
|
|
916
|
+
return old;
|
|
917
|
+
|
|
918
|
+
if ((el = (genxElement) allocate(w, sizeof(struct genxElement_rec))) == NULL)
|
|
919
|
+
{
|
|
920
|
+
w->status = *statusP = GENX_ALLOC_FAILED;
|
|
921
|
+
return NULL;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
el->writer = w;
|
|
925
|
+
el->ns = ns;
|
|
926
|
+
if ((el->type = copy(w, type)) == NULL)
|
|
927
|
+
{
|
|
928
|
+
w->status = *statusP = GENX_ALLOC_FAILED;
|
|
929
|
+
return NULL;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if ((w->status = listAppend(&w->elements, el)) != GENX_SUCCESS)
|
|
933
|
+
{
|
|
934
|
+
*statusP = w->status;
|
|
935
|
+
return NULL;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
*statusP = GENX_SUCCESS;
|
|
939
|
+
return el;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/*
|
|
943
|
+
* C14n ordering for attributes:
|
|
944
|
+
* - first, namespace declarations by the prefix being declared
|
|
945
|
+
* - second, unprefixed attributes by attr name
|
|
946
|
+
* - third, prefixed attrs by ns uri then local part
|
|
947
|
+
*/
|
|
948
|
+
static int orderAttributes(genxAttribute a1, genxAttribute a2)
|
|
949
|
+
{
|
|
950
|
+
if (a1->atype == a2->atype)
|
|
951
|
+
{
|
|
952
|
+
if (a1->atype == ATTR_PREFIXED && a1->ns != a2->ns)
|
|
953
|
+
return strcmp((const char *) a1->ns->name, (const char *) a2->ns->name);
|
|
954
|
+
else
|
|
955
|
+
return strcmp((const char *) a1->name, (const char *) a2->name);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
else if (a1->atype == ATTR_NSDECL)
|
|
959
|
+
return -1;
|
|
960
|
+
|
|
961
|
+
else if (a1->atype == ATTR_NAKED)
|
|
962
|
+
{
|
|
963
|
+
if (a2->atype == ATTR_NSDECL)
|
|
964
|
+
return 1;
|
|
965
|
+
else
|
|
966
|
+
return -1;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
else
|
|
970
|
+
return 1;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/*
|
|
974
|
+
* internal declare-attribute. This one allows colonized values for
|
|
975
|
+
* names, so that you can declare xmlns:-type attributes
|
|
976
|
+
*/
|
|
977
|
+
static genxAttribute declareAttribute(genxWriter w, genxNamespace ns,
|
|
978
|
+
constUtf8 name, constUtf8 valuestr,
|
|
979
|
+
genxStatus * statusP)
|
|
980
|
+
{
|
|
981
|
+
int high, low;
|
|
982
|
+
genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
|
|
983
|
+
genxAttribute a;
|
|
984
|
+
|
|
985
|
+
w->arec.ns = ns;
|
|
986
|
+
w->arec.name = (utf8) name;
|
|
987
|
+
|
|
988
|
+
if (ns)
|
|
989
|
+
w->arec.atype = ATTR_PREFIXED;
|
|
990
|
+
else if (strncmp((const char *) name, "xmlns", STRLEN_XMLNS_COLON - 1) == 0)
|
|
991
|
+
w->arec.atype = ATTR_NSDECL;
|
|
992
|
+
else
|
|
993
|
+
w->arec.atype = ATTR_NAKED;
|
|
994
|
+
|
|
995
|
+
if (ns && (ns->defaultDecl == w->xmlnsEquals))
|
|
996
|
+
{
|
|
997
|
+
w->status = GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE;
|
|
998
|
+
goto busted;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/* attribute list has to be kept sorted per c14n rules */
|
|
1002
|
+
high = w->attributes.count; low = -1;
|
|
1003
|
+
while (high - low > 1)
|
|
1004
|
+
{
|
|
1005
|
+
int probe = (high + low) / 2;
|
|
1006
|
+
if (orderAttributes(&w->arec, aa[probe]) < 0)
|
|
1007
|
+
high = probe;
|
|
1008
|
+
else
|
|
1009
|
+
low = probe;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/* if it was already there */
|
|
1013
|
+
if (low != -1 && orderAttributes(&w->arec, aa[low]) == 0)
|
|
1014
|
+
return aa[low];
|
|
1015
|
+
|
|
1016
|
+
/* not there, build it */
|
|
1017
|
+
a = (genxAttribute) allocate(w, sizeof(struct genxAttribute_rec));
|
|
1018
|
+
if (a == NULL)
|
|
1019
|
+
{
|
|
1020
|
+
w->status = GENX_ALLOC_FAILED;
|
|
1021
|
+
goto busted;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
a->writer = w;
|
|
1025
|
+
a->ns = ns;
|
|
1026
|
+
a->provided = False;
|
|
1027
|
+
a->atype = w->arec.atype;
|
|
1028
|
+
|
|
1029
|
+
if ((a->name = copy(w, name)) == NULL)
|
|
1030
|
+
{
|
|
1031
|
+
w->status = GENX_ALLOC_FAILED;
|
|
1032
|
+
goto busted;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if ((w->status = initCollector(w, &a->value)) != GENX_SUCCESS)
|
|
1036
|
+
goto busted;
|
|
1037
|
+
|
|
1038
|
+
if (valuestr)
|
|
1039
|
+
if ((w->status = collectString(w, &a->value, valuestr)) != GENX_SUCCESS)
|
|
1040
|
+
goto busted;
|
|
1041
|
+
|
|
1042
|
+
w->status = listInsert(&w->attributes, a, high);
|
|
1043
|
+
if (w->status != GENX_SUCCESS)
|
|
1044
|
+
goto busted;
|
|
1045
|
+
|
|
1046
|
+
*statusP = GENX_SUCCESS;
|
|
1047
|
+
return a;
|
|
1048
|
+
|
|
1049
|
+
busted:
|
|
1050
|
+
*statusP = w->status;
|
|
1051
|
+
return NULL;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/*
|
|
1055
|
+
* genxDeclareAttribute - see genx.h for details
|
|
1056
|
+
*/
|
|
1057
|
+
genxAttribute genxDeclareAttribute(genxWriter w,
|
|
1058
|
+
genxNamespace ns, constUtf8 name,
|
|
1059
|
+
genxStatus * statusP)
|
|
1060
|
+
{
|
|
1061
|
+
if ((w->status = checkNCName(w, name)) != GENX_SUCCESS)
|
|
1062
|
+
{
|
|
1063
|
+
*statusP = w->status;
|
|
1064
|
+
return NULL;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
return declareAttribute(w, ns, name, NULL, statusP);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/*******************************
|
|
1071
|
+
* I/O
|
|
1072
|
+
*/
|
|
1073
|
+
static genxStatus sendx(genxWriter w, constUtf8 s)
|
|
1074
|
+
{
|
|
1075
|
+
if (w->sender)
|
|
1076
|
+
return (*w->sender->send)(w->userData, s);
|
|
1077
|
+
else
|
|
1078
|
+
{
|
|
1079
|
+
if (fputs((const char *) s, w->file) == -1)
|
|
1080
|
+
return GENX_IO_ERROR;
|
|
1081
|
+
else
|
|
1082
|
+
return GENX_SUCCESS;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
static genxStatus sendxBounded(genxWriter w, constUtf8 start, constUtf8 end)
|
|
1087
|
+
{
|
|
1088
|
+
if (w->sender)
|
|
1089
|
+
return (*w->sender->sendBounded)(w->userData, start, end);
|
|
1090
|
+
else
|
|
1091
|
+
if (fwrite(start, 1, end - start, w->file) != (unsigned) (end - start))
|
|
1092
|
+
return GENX_IO_ERROR;
|
|
1093
|
+
else
|
|
1094
|
+
return GENX_SUCCESS;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
#define SendCheck(w,s) if ((w->status=sendx(w,(utf8)s))!=GENX_SUCCESS) return w->status;
|
|
1098
|
+
|
|
1099
|
+
/*******************************
|
|
1100
|
+
* XML writing routines. The semantics of the externally-facing ones are
|
|
1101
|
+
* written up in genx.h. Commentary here is implementation notes and
|
|
1102
|
+
* for internal routines.
|
|
1103
|
+
*/
|
|
1104
|
+
|
|
1105
|
+
/*
|
|
1106
|
+
* Start a document
|
|
1107
|
+
*/
|
|
1108
|
+
genxStatus genxStartDocFile(genxWriter w, FILE * file)
|
|
1109
|
+
{
|
|
1110
|
+
if (w->sequence != SEQUENCE_NO_DOC)
|
|
1111
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1112
|
+
|
|
1113
|
+
w->sequence = SEQUENCE_PRE_DOC;
|
|
1114
|
+
w->file = file;
|
|
1115
|
+
w->sender = NULL;
|
|
1116
|
+
return GENX_SUCCESS;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
genxStatus genxStartDocSender(genxWriter w, genxSender * sender)
|
|
1120
|
+
{
|
|
1121
|
+
if (w->sequence != SEQUENCE_NO_DOC)
|
|
1122
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1123
|
+
|
|
1124
|
+
w->sequence = SEQUENCE_PRE_DOC;
|
|
1125
|
+
w->file = NULL;
|
|
1126
|
+
w->sender = sender;
|
|
1127
|
+
return GENX_SUCCESS;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/*
|
|
1131
|
+
* Write out the attributes we've been gathering up for an element. We save
|
|
1132
|
+
* them until we've gathered them all so they can be writen in canonical
|
|
1133
|
+
* order.
|
|
1134
|
+
* Also, we end the start-tag.
|
|
1135
|
+
* The trick here is that we keep the attribute list properly sorted as
|
|
1136
|
+
* we build it, then as each attribute is added, we fill in its value and
|
|
1137
|
+
* mark the fact that it's been added, in the "provided" field.
|
|
1138
|
+
*/
|
|
1139
|
+
static genxStatus writeStartTag(genxWriter w)
|
|
1140
|
+
{
|
|
1141
|
+
int i;
|
|
1142
|
+
genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
|
|
1143
|
+
genxElement e = w->nowStarting;
|
|
1144
|
+
|
|
1145
|
+
/*
|
|
1146
|
+
* make sure the right namespace decls are in effect;
|
|
1147
|
+
* if they are these might create an error, so ignore it
|
|
1148
|
+
*/
|
|
1149
|
+
if (e->ns)
|
|
1150
|
+
addNamespace(e->ns, NULL);
|
|
1151
|
+
else
|
|
1152
|
+
unsetDefaultNamespace(w);
|
|
1153
|
+
w->status = GENX_SUCCESS;
|
|
1154
|
+
|
|
1155
|
+
SendCheck(w, "<");
|
|
1156
|
+
if (e->ns && (e->ns->declaration != w->xmlnsEquals))
|
|
1157
|
+
{
|
|
1158
|
+
SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
|
|
1159
|
+
SendCheck(w, ":");
|
|
1160
|
+
}
|
|
1161
|
+
SendCheck(w, e->type);
|
|
1162
|
+
|
|
1163
|
+
for (i = 0; i < w->attributes.count; i++)
|
|
1164
|
+
{
|
|
1165
|
+
if (aa[i]->provided)
|
|
1166
|
+
{
|
|
1167
|
+
if (aa[i]->ns && aa[i]->ns->baroque &&
|
|
1168
|
+
aa[i]->ns->declaration == w->xmlnsEquals)
|
|
1169
|
+
return w->status = GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE;
|
|
1170
|
+
|
|
1171
|
+
SendCheck(w, " ");
|
|
1172
|
+
|
|
1173
|
+
if (aa[i]->ns)
|
|
1174
|
+
{
|
|
1175
|
+
SendCheck(w, aa[i]->ns->declaration->name + STRLEN_XMLNS_COLON)
|
|
1176
|
+
SendCheck(w, ":");
|
|
1177
|
+
}
|
|
1178
|
+
SendCheck(w, aa[i]->name);
|
|
1179
|
+
SendCheck(w, "=\"");
|
|
1180
|
+
SendCheck(w, aa[i]->value.buf);
|
|
1181
|
+
SendCheck(w, "\"");
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
SendCheck(w, ">");
|
|
1185
|
+
return GENX_SUCCESS;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/*
|
|
1189
|
+
* internal clear-er; no sequence checking
|
|
1190
|
+
*/
|
|
1191
|
+
static genxStatus unsetDefaultNamespace(genxWriter w)
|
|
1192
|
+
{
|
|
1193
|
+
int i;
|
|
1194
|
+
Boolean found = False;
|
|
1195
|
+
|
|
1196
|
+
/* don't put it in if not needed */
|
|
1197
|
+
i = w->stack.count - 1;
|
|
1198
|
+
while (found == False && i > 0)
|
|
1199
|
+
{
|
|
1200
|
+
while (w->stack.pointers[i] != NULL)
|
|
1201
|
+
{
|
|
1202
|
+
genxAttribute decl = (genxAttribute) w->stack.pointers[i--];
|
|
1203
|
+
genxNamespace ns = (genxNamespace) w->stack.pointers[i--];
|
|
1204
|
+
|
|
1205
|
+
/* if already unset */
|
|
1206
|
+
if (ns == NULL)
|
|
1207
|
+
return w->status = GENX_SUCCESS;
|
|
1208
|
+
|
|
1209
|
+
/*
|
|
1210
|
+
* the default namespace was declared. This namespace now
|
|
1211
|
+
* becomes baroque
|
|
1212
|
+
*/
|
|
1213
|
+
if (decl == w->xmlnsEquals)
|
|
1214
|
+
{
|
|
1215
|
+
ns->baroque = True;
|
|
1216
|
+
found = True;
|
|
1217
|
+
break;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
i -= 2;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
if (!found)
|
|
1224
|
+
return GENX_SUCCESS;
|
|
1225
|
+
|
|
1226
|
+
/*
|
|
1227
|
+
* push a signal on the stack
|
|
1228
|
+
*/
|
|
1229
|
+
if ((w->status = listAppend(&w->stack, NULL)) != GENX_SUCCESS)
|
|
1230
|
+
return w->status;
|
|
1231
|
+
w->status = listAppend(&w->stack, w->xmlnsEquals);
|
|
1232
|
+
if (w->status != GENX_SUCCESS)
|
|
1233
|
+
return w->status;
|
|
1234
|
+
|
|
1235
|
+
/* add the xmlns= attribute, it must be the first one */
|
|
1236
|
+
return addAttribute(w->xmlnsEquals, w->empty);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/*
|
|
1240
|
+
* clear the default namespace declaration
|
|
1241
|
+
*/
|
|
1242
|
+
genxStatus genxUnsetDefaultNamespace(genxWriter w)
|
|
1243
|
+
{
|
|
1244
|
+
|
|
1245
|
+
/* can only do this while in start-tag mode */
|
|
1246
|
+
if (w->sequence != SEQUENCE_START_TAG)
|
|
1247
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1248
|
+
|
|
1249
|
+
return unsetDefaultNamespace(w);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
genxStatus genxStartElement(genxElement e)
|
|
1253
|
+
{
|
|
1254
|
+
genxWriter w = e->writer;
|
|
1255
|
+
int i;
|
|
1256
|
+
|
|
1257
|
+
switch (w->sequence)
|
|
1258
|
+
{
|
|
1259
|
+
case SEQUENCE_NO_DOC:
|
|
1260
|
+
case SEQUENCE_POST_DOC:
|
|
1261
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1262
|
+
case SEQUENCE_START_TAG:
|
|
1263
|
+
case SEQUENCE_ATTRIBUTES:
|
|
1264
|
+
if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
|
|
1265
|
+
return w->status;
|
|
1266
|
+
break;
|
|
1267
|
+
case SEQUENCE_PRE_DOC:
|
|
1268
|
+
case SEQUENCE_CONTENT:
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
w->sequence = SEQUENCE_START_TAG;
|
|
1273
|
+
|
|
1274
|
+
/* clear provided attributes */
|
|
1275
|
+
for (i = 0; i < w->attributes.count; i++)
|
|
1276
|
+
((genxAttribute) w->attributes.pointers[i])->provided = 0;
|
|
1277
|
+
|
|
1278
|
+
/*
|
|
1279
|
+
* push the stack. We push a NULL after a pointer to this element
|
|
1280
|
+
* because the stack will also contain pointers to the namespace
|
|
1281
|
+
* attributes that got declared here, so we can keep track of what's
|
|
1282
|
+
* in effect. I.e. a single stack entry consists logically of a pointer
|
|
1283
|
+
* to an element object, a NULL, then zero or more pairs of pointers to
|
|
1284
|
+
* namespace objects/declarations
|
|
1285
|
+
*/
|
|
1286
|
+
if ((w->status = listAppend(&w->stack, e)) != GENX_SUCCESS)
|
|
1287
|
+
return w->status;
|
|
1288
|
+
if ((w->status = listAppend(&w->stack, NULL)) != GENX_SUCCESS)
|
|
1289
|
+
return w->status;
|
|
1290
|
+
|
|
1291
|
+
w->nowStarting = e;
|
|
1292
|
+
|
|
1293
|
+
return GENX_SUCCESS;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/*
|
|
1297
|
+
* internal namespace adder; no sequence checking
|
|
1298
|
+
*/
|
|
1299
|
+
static genxStatus addNamespace(genxNamespace ns, utf8 prefix)
|
|
1300
|
+
{
|
|
1301
|
+
genxWriter w = ns->writer;
|
|
1302
|
+
genxAttribute decl;
|
|
1303
|
+
int i;
|
|
1304
|
+
genxElement e;
|
|
1305
|
+
|
|
1306
|
+
/*
|
|
1307
|
+
* first, we'll find the declaring attribute
|
|
1308
|
+
*/
|
|
1309
|
+
if (prefix == NULL)
|
|
1310
|
+
decl = ns->defaultDecl;
|
|
1311
|
+
else
|
|
1312
|
+
{
|
|
1313
|
+
if (prefix[0] == 0)
|
|
1314
|
+
decl = w->xmlnsEquals;
|
|
1315
|
+
else
|
|
1316
|
+
{
|
|
1317
|
+
if ((prefix = storePrefix(w, prefix, True)) == NULL)
|
|
1318
|
+
return w->status;
|
|
1319
|
+
decl = declareAttribute(w, NULL, prefix, ns->name, &w->status);
|
|
1320
|
+
if (decl == NULL || w->status != GENX_SUCCESS)
|
|
1321
|
+
return w->status;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
if (decl != ns->defaultDecl)
|
|
1326
|
+
ns->baroque = True;
|
|
1327
|
+
|
|
1328
|
+
/*
|
|
1329
|
+
* avoid doing anything if this namespace is already declared. If
|
|
1330
|
+
* they've shown good taste we can do this cheaply
|
|
1331
|
+
*/
|
|
1332
|
+
if (!ns->baroque)
|
|
1333
|
+
{
|
|
1334
|
+
if (ns->declCount > 0)
|
|
1335
|
+
return w->status = GENX_SUCCESS;
|
|
1336
|
+
}
|
|
1337
|
+
else
|
|
1338
|
+
{
|
|
1339
|
+
|
|
1340
|
+
/*
|
|
1341
|
+
* First, we'll run all the way up the stack to see if there is
|
|
1342
|
+
* another declaration for this namespace/prefix in scope, in which
|
|
1343
|
+
* case it's a no-op; or, if there's another declaration for this
|
|
1344
|
+
* prefix on another namespace, in which case we have to over-ride
|
|
1345
|
+
*/
|
|
1346
|
+
i = w->stack.count - 1;
|
|
1347
|
+
while (i > 0)
|
|
1348
|
+
{
|
|
1349
|
+
while (w->stack.pointers[i] != NULL)
|
|
1350
|
+
{
|
|
1351
|
+
genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
|
|
1352
|
+
genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];
|
|
1353
|
+
|
|
1354
|
+
if (ns == otherNs)
|
|
1355
|
+
{
|
|
1356
|
+
if (decl == otherDecl)
|
|
1357
|
+
return w->status = GENX_SUCCESS;
|
|
1358
|
+
else
|
|
1359
|
+
{
|
|
1360
|
+
i = 0;
|
|
1361
|
+
break;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
else
|
|
1365
|
+
{
|
|
1366
|
+
/* different namespace, same prefix? */
|
|
1367
|
+
if (decl == otherDecl)
|
|
1368
|
+
{
|
|
1369
|
+
i = 0;
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
i -= 2;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/*
|
|
1379
|
+
* If this namespace is already declared on
|
|
1380
|
+
* this element (with different prefix/decl) which is an error.
|
|
1381
|
+
*/
|
|
1382
|
+
i = w->stack.count - 1;
|
|
1383
|
+
while (w->stack.pointers[i] != NULL)
|
|
1384
|
+
{
|
|
1385
|
+
genxNamespace otherNs;
|
|
1386
|
+
i--; /* don't need declaration */
|
|
1387
|
+
otherNs = (genxNamespace) w->stack.pointers[i--];
|
|
1388
|
+
|
|
1389
|
+
if (ns == otherNs)
|
|
1390
|
+
return w->status = GENX_DUPLICATE_NAMESPACE;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/* move pointer from NULL to element */
|
|
1394
|
+
--i;
|
|
1395
|
+
|
|
1396
|
+
/*
|
|
1397
|
+
* It's also an error if this is a default-namespace declaration and the
|
|
1398
|
+
* element is in no namespace.
|
|
1399
|
+
*/
|
|
1400
|
+
e = (genxElement) w->stack.pointers[i];
|
|
1401
|
+
if (e->ns == NULL && decl == w->xmlnsEquals)
|
|
1402
|
+
return w->status = GENX_BAD_DEFAULT_DECLARATION;
|
|
1403
|
+
|
|
1404
|
+
if ((w->status = listAppend(&w->stack, ns)) != GENX_SUCCESS)
|
|
1405
|
+
return w->status;
|
|
1406
|
+
if ((w->status = listAppend(&w->stack, decl)) != GENX_SUCCESS)
|
|
1407
|
+
return w->status;
|
|
1408
|
+
|
|
1409
|
+
ns->declaration = decl;
|
|
1410
|
+
ns->declCount++;
|
|
1411
|
+
return addAttribute(decl, ns->name);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/*
|
|
1415
|
+
* Add a namespace declaration
|
|
1416
|
+
*/
|
|
1417
|
+
genxStatus genxAddNamespace(genxNamespace ns, utf8 prefix)
|
|
1418
|
+
{
|
|
1419
|
+
if (ns->writer->sequence != SEQUENCE_START_TAG)
|
|
1420
|
+
return ns->writer->status = GENX_SEQUENCE_ERROR;
|
|
1421
|
+
|
|
1422
|
+
return addNamespace(ns, prefix);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
/*
|
|
1426
|
+
* Private attribute-adding code
|
|
1427
|
+
* most of the work here is normalizing the value, which is the same
|
|
1428
|
+
* as regular normalization except for " is replaced by """
|
|
1429
|
+
*/
|
|
1430
|
+
static genxStatus addAttribute(genxAttribute a, constUtf8 valuestr)
|
|
1431
|
+
{
|
|
1432
|
+
utf8 lastv = (utf8) valuestr;
|
|
1433
|
+
genxWriter w = a->writer;
|
|
1434
|
+
|
|
1435
|
+
/* if valuestr not provided, this is an xmlns with a pre-cooked value */
|
|
1436
|
+
if (valuestr)
|
|
1437
|
+
{
|
|
1438
|
+
startCollect(&a->value);
|
|
1439
|
+
while (*valuestr)
|
|
1440
|
+
{
|
|
1441
|
+
int c = genxNextUnicodeChar(&valuestr);
|
|
1442
|
+
|
|
1443
|
+
if (c == -1)
|
|
1444
|
+
return w->status = GENX_BAD_UTF8;
|
|
1445
|
+
|
|
1446
|
+
if (!isXMLChar(w, c))
|
|
1447
|
+
return w->status = GENX_NON_XML_CHARACTER;
|
|
1448
|
+
|
|
1449
|
+
switch(c)
|
|
1450
|
+
{
|
|
1451
|
+
case 9:
|
|
1452
|
+
collectPiece(w, &a->value, "	", 5);
|
|
1453
|
+
break;
|
|
1454
|
+
case 0xa:
|
|
1455
|
+
collectPiece(w, &a->value, "
", 5);
|
|
1456
|
+
break;
|
|
1457
|
+
case 0xd:
|
|
1458
|
+
collectPiece(w, &a->value, "
", 5);
|
|
1459
|
+
break;
|
|
1460
|
+
case '"':
|
|
1461
|
+
collectPiece(w, &a->value, """, 6);
|
|
1462
|
+
break;
|
|
1463
|
+
case '<':
|
|
1464
|
+
collectPiece(w, &a->value, "<", 4);
|
|
1465
|
+
break;
|
|
1466
|
+
case '&':
|
|
1467
|
+
collectPiece(w, &a->value, "&", 5);
|
|
1468
|
+
break;
|
|
1469
|
+
/*
|
|
1470
|
+
case '>':
|
|
1471
|
+
collectPiece(w, &a->value, ">", 4);
|
|
1472
|
+
break;
|
|
1473
|
+
*/
|
|
1474
|
+
default:
|
|
1475
|
+
collectPiece(w, &a->value, (const char *) lastv, valuestr - lastv);
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
lastv = (utf8) valuestr;
|
|
1479
|
+
}
|
|
1480
|
+
endCollect(&a->value);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
/* now add the namespace attribute; might fail if it's bee hand-declared */
|
|
1484
|
+
if (a->ns)
|
|
1485
|
+
addNamespace(a->ns, NULL);
|
|
1486
|
+
|
|
1487
|
+
if (valuestr && a->provided)
|
|
1488
|
+
return w->status = GENX_DUPLICATE_ATTRIBUTE;
|
|
1489
|
+
a->provided = 1;
|
|
1490
|
+
|
|
1491
|
+
return GENX_SUCCESS;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
/*
|
|
1495
|
+
* public attribute adder.
|
|
1496
|
+
* The only difference is that it doesn't allow a NULL value
|
|
1497
|
+
*/
|
|
1498
|
+
genxStatus genxAddAttribute(genxAttribute a, constUtf8 valuestr)
|
|
1499
|
+
{
|
|
1500
|
+
if (a->writer->sequence != SEQUENCE_START_TAG &&
|
|
1501
|
+
a->writer->sequence != SEQUENCE_ATTRIBUTES)
|
|
1502
|
+
return a->writer->status = GENX_SEQUENCE_ERROR;
|
|
1503
|
+
a->writer->sequence = SEQUENCE_ATTRIBUTES;
|
|
1504
|
+
|
|
1505
|
+
if (valuestr == NULL)
|
|
1506
|
+
return a->writer->status = GENX_MISSING_VALUE;
|
|
1507
|
+
|
|
1508
|
+
return addAttribute(a, valuestr);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
genxStatus genxEndElement(genxWriter w)
|
|
1512
|
+
{
|
|
1513
|
+
genxElement e;
|
|
1514
|
+
int i;
|
|
1515
|
+
|
|
1516
|
+
switch (w->sequence)
|
|
1517
|
+
{
|
|
1518
|
+
case SEQUENCE_NO_DOC:
|
|
1519
|
+
case SEQUENCE_PRE_DOC:
|
|
1520
|
+
case SEQUENCE_POST_DOC:
|
|
1521
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1522
|
+
case SEQUENCE_START_TAG:
|
|
1523
|
+
case SEQUENCE_ATTRIBUTES:
|
|
1524
|
+
if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
|
|
1525
|
+
return w->status;
|
|
1526
|
+
break;
|
|
1527
|
+
case SEQUENCE_CONTENT:
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/*
|
|
1532
|
+
* first peek into the stack to find the right namespace declaration
|
|
1533
|
+
* (if any) so we can properly prefix the end-tag. Have to do this
|
|
1534
|
+
* before unwinding the stack because that might reset some xmlns
|
|
1535
|
+
* prefixes to the context in the parent element
|
|
1536
|
+
*/
|
|
1537
|
+
for (i = w->stack.count - 1; w->stack.pointers[i] != NULL; i -= 2)
|
|
1538
|
+
;
|
|
1539
|
+
e = (genxElement) w->stack.pointers[--i];
|
|
1540
|
+
|
|
1541
|
+
SendCheck(w, "</");
|
|
1542
|
+
if (e->ns && e->ns->declaration != w->xmlnsEquals)
|
|
1543
|
+
{
|
|
1544
|
+
SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
|
|
1545
|
+
SendCheck(w, ":");
|
|
1546
|
+
}
|
|
1547
|
+
SendCheck(w, e->type);
|
|
1548
|
+
SendCheck(w, ">");
|
|
1549
|
+
|
|
1550
|
+
/*
|
|
1551
|
+
* pop zero or more namespace declarations, then a null, then the
|
|
1552
|
+
* start-element declaration off the stack
|
|
1553
|
+
*/
|
|
1554
|
+
w->stack.count--;
|
|
1555
|
+
while (w->stack.pointers[w->stack.count] != NULL)
|
|
1556
|
+
{
|
|
1557
|
+
genxNamespace ns = (genxNamespace) w->stack.pointers[--w->stack.count];
|
|
1558
|
+
w->stack.count--; /* don't need decl */
|
|
1559
|
+
|
|
1560
|
+
/* if not a fake unset-default namespace */
|
|
1561
|
+
if (ns)
|
|
1562
|
+
{
|
|
1563
|
+
/*
|
|
1564
|
+
* if they've stupidly jammed in their own namespace-prefix
|
|
1565
|
+
* declarations, we have to go looking to see if there's another
|
|
1566
|
+
* one in effect
|
|
1567
|
+
*/
|
|
1568
|
+
if (ns->baroque)
|
|
1569
|
+
{
|
|
1570
|
+
i = w->stack.count;
|
|
1571
|
+
while (i > 0)
|
|
1572
|
+
{
|
|
1573
|
+
while (w->stack.pointers[i] != NULL)
|
|
1574
|
+
{
|
|
1575
|
+
genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
|
|
1576
|
+
genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];
|
|
1577
|
+
|
|
1578
|
+
if (otherNs == ns)
|
|
1579
|
+
{
|
|
1580
|
+
ns->declaration = otherDecl;
|
|
1581
|
+
i = 0;
|
|
1582
|
+
break;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
/* skip NULL & element */
|
|
1587
|
+
i -= 2;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
ns->declCount--;
|
|
1591
|
+
if (ns->declCount == 0)
|
|
1592
|
+
ns->baroque = False;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
/* pop the NULL */
|
|
1597
|
+
--w->stack.count;
|
|
1598
|
+
if (w->stack.count < 0)
|
|
1599
|
+
return w->status = GENX_NO_START_TAG;
|
|
1600
|
+
|
|
1601
|
+
if (w->stack.count == 0)
|
|
1602
|
+
w->sequence = SEQUENCE_POST_DOC;
|
|
1603
|
+
else
|
|
1604
|
+
w->sequence = SEQUENCE_CONTENT;
|
|
1605
|
+
|
|
1606
|
+
return GENX_SUCCESS;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/*
|
|
1610
|
+
* Internal character-adder. It tries to keep the number of sendx()
|
|
1611
|
+
* calls down by looking at each character but only doing the output
|
|
1612
|
+
* when it has to escape something; ordinary text gets saved up in
|
|
1613
|
+
* chunks the start of which is indicated by *breaker.
|
|
1614
|
+
* c is the character, next points to the UTF8 representing the next
|
|
1615
|
+
* lastsP indirectly points to the UTF8 representing the
|
|
1616
|
+
* character, breakerP* indirectly points to the last place genx
|
|
1617
|
+
* changed the UTF8, e.g. by escaping a '<'
|
|
1618
|
+
*/
|
|
1619
|
+
static genxStatus addChar(genxWriter w, int c, constUtf8 next,
|
|
1620
|
+
constUtf8 * lastsP, constUtf8 * breakerP)
|
|
1621
|
+
{
|
|
1622
|
+
if (c == -1)
|
|
1623
|
+
return GENX_BAD_UTF8;
|
|
1624
|
+
|
|
1625
|
+
if (!isXMLChar(w, c))
|
|
1626
|
+
return GENX_NON_XML_CHARACTER;
|
|
1627
|
+
|
|
1628
|
+
switch(c)
|
|
1629
|
+
{
|
|
1630
|
+
case 0xd:
|
|
1631
|
+
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
|
|
1632
|
+
return w->status;
|
|
1633
|
+
*breakerP = next;
|
|
1634
|
+
sendx(w, (utf8) "
");
|
|
1635
|
+
break;
|
|
1636
|
+
case '<':
|
|
1637
|
+
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
|
|
1638
|
+
return w->status;
|
|
1639
|
+
*breakerP = next;
|
|
1640
|
+
sendx(w, (utf8) "<");
|
|
1641
|
+
break;
|
|
1642
|
+
case '&':
|
|
1643
|
+
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
|
|
1644
|
+
return w->status;
|
|
1645
|
+
*breakerP = next;
|
|
1646
|
+
sendx(w, (utf8) "&");
|
|
1647
|
+
break;
|
|
1648
|
+
case '>':
|
|
1649
|
+
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
|
|
1650
|
+
return w->status;
|
|
1651
|
+
*breakerP = next;
|
|
1652
|
+
sendx(w, (utf8) ">");
|
|
1653
|
+
break;
|
|
1654
|
+
default:
|
|
1655
|
+
break;
|
|
1656
|
+
}
|
|
1657
|
+
*lastsP = next;
|
|
1658
|
+
return GENX_SUCCESS;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
genxStatus genxAddText(genxWriter w, constUtf8 start)
|
|
1662
|
+
{
|
|
1663
|
+
constUtf8 lasts = start;
|
|
1664
|
+
constUtf8 breaker = start;
|
|
1665
|
+
|
|
1666
|
+
if (w->sequence == SEQUENCE_START_TAG ||
|
|
1667
|
+
w->sequence == SEQUENCE_ATTRIBUTES)
|
|
1668
|
+
{
|
|
1669
|
+
if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
|
|
1670
|
+
return w->status;
|
|
1671
|
+
w->sequence = SEQUENCE_CONTENT;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
if (w->sequence != SEQUENCE_CONTENT)
|
|
1675
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1676
|
+
|
|
1677
|
+
while (*start)
|
|
1678
|
+
{
|
|
1679
|
+
int c = genxNextUnicodeChar(&start);
|
|
1680
|
+
|
|
1681
|
+
w->status = addChar(w, c, start, &lasts, &breaker);
|
|
1682
|
+
if (w->status != GENX_SUCCESS)
|
|
1683
|
+
return w->status;
|
|
1684
|
+
}
|
|
1685
|
+
return sendxBounded(w, breaker, (utf8) start);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
genxStatus genxAddBoundedText(genxWriter w, constUtf8 start, constUtf8 end)
|
|
1689
|
+
{
|
|
1690
|
+
constUtf8 lasts = start;
|
|
1691
|
+
constUtf8 breaker = start;
|
|
1692
|
+
|
|
1693
|
+
if (w->sequence == SEQUENCE_START_TAG ||
|
|
1694
|
+
w->sequence == SEQUENCE_ATTRIBUTES)
|
|
1695
|
+
{
|
|
1696
|
+
if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
|
|
1697
|
+
return w->status;
|
|
1698
|
+
w->sequence = SEQUENCE_CONTENT;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
if (w->sequence != SEQUENCE_CONTENT)
|
|
1702
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1703
|
+
|
|
1704
|
+
while (start < end)
|
|
1705
|
+
{
|
|
1706
|
+
int c = genxNextUnicodeChar(&start);
|
|
1707
|
+
|
|
1708
|
+
w->status = addChar(w, c, (utf8) start, &lasts, &breaker);
|
|
1709
|
+
if (w->status != GENX_SUCCESS)
|
|
1710
|
+
return w->status;
|
|
1711
|
+
}
|
|
1712
|
+
return sendxBounded(w, breaker, (utf8) start);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
genxStatus genxAddCountedText(genxWriter w, constUtf8 start, int byteCount)
|
|
1716
|
+
{
|
|
1717
|
+
utf8 end = (utf8) (start + byteCount);
|
|
1718
|
+
|
|
1719
|
+
return genxAddBoundedText(w, start, end);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
genxStatus genxAddCharacter(genxWriter w, int c)
|
|
1723
|
+
{
|
|
1724
|
+
unsigned char cUTF8[10];
|
|
1725
|
+
utf8 lasts, breaker, next;
|
|
1726
|
+
|
|
1727
|
+
if (w->sequence == SEQUENCE_START_TAG ||
|
|
1728
|
+
w->sequence == SEQUENCE_ATTRIBUTES)
|
|
1729
|
+
{
|
|
1730
|
+
if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
|
|
1731
|
+
return w->status;
|
|
1732
|
+
w->sequence = SEQUENCE_CONTENT;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
if (w->sequence != SEQUENCE_CONTENT)
|
|
1736
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1737
|
+
|
|
1738
|
+
if (!isXMLChar(w, c))
|
|
1739
|
+
return w->status = GENX_NON_XML_CHARACTER;
|
|
1740
|
+
|
|
1741
|
+
/* make UTF8 representation of character */
|
|
1742
|
+
lasts = breaker = next = cUTF8;
|
|
1743
|
+
|
|
1744
|
+
if (c < 0x80)
|
|
1745
|
+
*next++ = c;
|
|
1746
|
+
else if (c < 0x800)
|
|
1747
|
+
{
|
|
1748
|
+
*next++ = 0xc0 | (c >> 6);
|
|
1749
|
+
*next++ = 0x80 | (c & 0x3f);
|
|
1750
|
+
}
|
|
1751
|
+
else if (c < 0x10000)
|
|
1752
|
+
{
|
|
1753
|
+
*next++ = 0xe0 | (c >> 12);
|
|
1754
|
+
*next++ = 0x80 | ((c & 0xfc0) >> 6);
|
|
1755
|
+
*next++ = 0x80 | (c & 0x3f);
|
|
1756
|
+
}
|
|
1757
|
+
else
|
|
1758
|
+
{
|
|
1759
|
+
*next++ = 0xf0 | (c >> 18);
|
|
1760
|
+
*next++ = 0x80 | ((c & 0x3f000) >> 12);
|
|
1761
|
+
*next++ = 0x80 | ((c & 0xfc0) >> 6);
|
|
1762
|
+
*next++ = 0x80 | (c & 0x3f);
|
|
1763
|
+
}
|
|
1764
|
+
*next = 0;
|
|
1765
|
+
|
|
1766
|
+
w->status =
|
|
1767
|
+
addChar(w, c, next, (constUtf8 *) &lasts, (constUtf8 *) &breaker);
|
|
1768
|
+
if (w->status != GENX_SUCCESS)
|
|
1769
|
+
return w->status;
|
|
1770
|
+
|
|
1771
|
+
return sendxBounded(w, breaker, next);
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
genxStatus genxEndDocument(genxWriter w)
|
|
1775
|
+
{
|
|
1776
|
+
if (w->sequence != SEQUENCE_POST_DOC)
|
|
1777
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1778
|
+
|
|
1779
|
+
if (w->file)
|
|
1780
|
+
fflush(w->file);
|
|
1781
|
+
else
|
|
1782
|
+
if ((w->status = (*w->sender->flush)(w->userData)) != GENX_SUCCESS)
|
|
1783
|
+
return w->status;
|
|
1784
|
+
|
|
1785
|
+
w->sequence = SEQUENCE_NO_DOC;
|
|
1786
|
+
return GENX_SUCCESS;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
genxStatus genxComment(genxWriter w, constUtf8 text)
|
|
1790
|
+
{
|
|
1791
|
+
int i;
|
|
1792
|
+
|
|
1793
|
+
if (w->sequence == SEQUENCE_NO_DOC)
|
|
1794
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1795
|
+
|
|
1796
|
+
if ((w->status = genxCheckText(w, text)) != GENX_SUCCESS)
|
|
1797
|
+
return w->status;
|
|
1798
|
+
|
|
1799
|
+
/* no leading '-', no trailing '-', no '--' */
|
|
1800
|
+
if (text[0] == '-')
|
|
1801
|
+
return w->status = GENX_MALFORMED_COMMENT;
|
|
1802
|
+
for (i = 0; text[i]; i++)
|
|
1803
|
+
if (text[i] == '-' && (text[i + 1] == '-' || text[i + 1] == 0))
|
|
1804
|
+
return w->status = GENX_MALFORMED_COMMENT;
|
|
1805
|
+
|
|
1806
|
+
if (w->sequence == SEQUENCE_START_TAG ||
|
|
1807
|
+
w->sequence == SEQUENCE_ATTRIBUTES)
|
|
1808
|
+
{
|
|
1809
|
+
if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
|
|
1810
|
+
return w->status;
|
|
1811
|
+
w->sequence = SEQUENCE_CONTENT;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
else if (w->sequence == SEQUENCE_POST_DOC)
|
|
1815
|
+
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
|
|
1816
|
+
return w->status;
|
|
1817
|
+
|
|
1818
|
+
if ((w->status = sendx(w, (utf8) "<!--")) != GENX_SUCCESS)
|
|
1819
|
+
return w->status;
|
|
1820
|
+
if ((w->status = sendx(w, (utf8) text)) != GENX_SUCCESS)
|
|
1821
|
+
return w->status;
|
|
1822
|
+
if ((w->status = sendx(w, (utf8) "-->")) != GENX_SUCCESS)
|
|
1823
|
+
return w->status;
|
|
1824
|
+
|
|
1825
|
+
if (w->sequence == SEQUENCE_PRE_DOC)
|
|
1826
|
+
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
|
|
1827
|
+
return w->status;
|
|
1828
|
+
|
|
1829
|
+
return GENX_SUCCESS;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text)
|
|
1833
|
+
{
|
|
1834
|
+
int i;
|
|
1835
|
+
|
|
1836
|
+
if (w->sequence == SEQUENCE_NO_DOC)
|
|
1837
|
+
return w->status = GENX_SEQUENCE_ERROR;
|
|
1838
|
+
|
|
1839
|
+
if ((w->status = genxCheckText(w, target)) != GENX_SUCCESS)
|
|
1840
|
+
return w->status;
|
|
1841
|
+
if ((w->status = checkNCName(w, target)) != GENX_SUCCESS)
|
|
1842
|
+
return w->status;
|
|
1843
|
+
if ((strlen((const char *) target) >= 3) &&
|
|
1844
|
+
(target[0] == 'x' || target[0] == 'X') &&
|
|
1845
|
+
(target[1] == 'm' || target[1] == 'M') &&
|
|
1846
|
+
(target[2] == 'l' || target[2] == 'L') &&
|
|
1847
|
+
(target[3] == 0))
|
|
1848
|
+
return w->status = GENX_XML_PI_TARGET;
|
|
1849
|
+
|
|
1850
|
+
if ((w->status = genxCheckText(w, text)) != GENX_SUCCESS)
|
|
1851
|
+
return w->status;
|
|
1852
|
+
|
|
1853
|
+
/* no ?> within */
|
|
1854
|
+
for (i = 1; text[i]; i++)
|
|
1855
|
+
if (text[i] == '>' && text[i - 1] == '?')
|
|
1856
|
+
return w->status = GENX_MALFORMED_PI;
|
|
1857
|
+
|
|
1858
|
+
if (w->sequence == SEQUENCE_START_TAG ||
|
|
1859
|
+
w->sequence == SEQUENCE_ATTRIBUTES)
|
|
1860
|
+
{
|
|
1861
|
+
if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
|
|
1862
|
+
return w->status;
|
|
1863
|
+
w->sequence = SEQUENCE_CONTENT;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
else if (w->sequence == SEQUENCE_POST_DOC)
|
|
1867
|
+
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
|
|
1868
|
+
return w->status;
|
|
1869
|
+
|
|
1870
|
+
if ((w->status = sendx(w, (utf8) "<?")) != GENX_SUCCESS)
|
|
1871
|
+
return w->status;
|
|
1872
|
+
if ((w->status = sendx(w, target)) != GENX_SUCCESS)
|
|
1873
|
+
return w->status;
|
|
1874
|
+
if (text[0])
|
|
1875
|
+
{
|
|
1876
|
+
if ((w->status = sendx(w, (utf8) " ")) != GENX_SUCCESS)
|
|
1877
|
+
return w->status;
|
|
1878
|
+
if ((w->status = sendx(w, text)) != GENX_SUCCESS)
|
|
1879
|
+
return w->status;
|
|
1880
|
+
}
|
|
1881
|
+
if ((w->status = sendx(w, (utf8) "?>")) != GENX_SUCCESS)
|
|
1882
|
+
return w->status;
|
|
1883
|
+
|
|
1884
|
+
if (w->sequence == SEQUENCE_PRE_DOC)
|
|
1885
|
+
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
|
|
1886
|
+
return w->status;
|
|
1887
|
+
|
|
1888
|
+
return GENX_SUCCESS;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
/*******************************
|
|
1892
|
+
* Literal versions of the writing routines
|
|
1893
|
+
*/
|
|
1894
|
+
genxStatus genxStartElementLiteral(genxWriter w,
|
|
1895
|
+
constUtf8 xmlns, constUtf8 type)
|
|
1896
|
+
{
|
|
1897
|
+
genxNamespace ns = NULL;
|
|
1898
|
+
genxElement e;
|
|
1899
|
+
|
|
1900
|
+
if (xmlns)
|
|
1901
|
+
{
|
|
1902
|
+
ns = genxDeclareNamespace(w, xmlns, NULL, &w->status);
|
|
1903
|
+
if (ns == NULL || w->status != GENX_SUCCESS)
|
|
1904
|
+
return w->status;
|
|
1905
|
+
}
|
|
1906
|
+
e = genxDeclareElement(w, ns, type, &w->status);
|
|
1907
|
+
if (e == NULL || w->status != GENX_SUCCESS)
|
|
1908
|
+
return w->status;
|
|
1909
|
+
|
|
1910
|
+
return genxStartElement(e);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
genxStatus genxAddAttributeLiteral(genxWriter w, constUtf8 xmlns,
|
|
1914
|
+
constUtf8 name, constUtf8 value)
|
|
1915
|
+
{
|
|
1916
|
+
genxNamespace ns = NULL;
|
|
1917
|
+
genxAttribute a;
|
|
1918
|
+
|
|
1919
|
+
if (xmlns)
|
|
1920
|
+
{
|
|
1921
|
+
ns = genxDeclareNamespace(w, xmlns, NULL, &w->status);
|
|
1922
|
+
if (ns == NULL && w->status != GENX_SUCCESS)
|
|
1923
|
+
return w->status;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
a = genxDeclareAttribute(w, ns, name, &w->status);
|
|
1927
|
+
if (a == NULL || w->status != GENX_SUCCESS)
|
|
1928
|
+
return w->status;
|
|
1929
|
+
|
|
1930
|
+
return genxAddAttribute(a, value);
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
/*
|
|
1934
|
+
* return version
|
|
1935
|
+
*/
|
|
1936
|
+
char * genxGetVersion()
|
|
1937
|
+
{
|
|
1938
|
+
return GENX_VERSION;
|
|
1939
|
+
}
|
|
1940
|
+
|