XML-Parser für resourcedateien

This commit is contained in:
Enno Rehling 2001-03-25 07:42:34 +00:00
parent 120e318ffe
commit e9f1fc2207
2 changed files with 270 additions and 0 deletions

237
src/common/util/xml.c Normal file
View File

@ -0,0 +1,237 @@
#include <config.h>
#include "xml.h"
/* libc includes */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
static int
__cberror(const struct xml_stack * stack, const char* parsed, unsigned int line, int error)
{
fprintf(stderr, "Error #%d in line %u while parsing \"%s\"\n", -error, line, parsed);
return error;
}
static xml_tag *
make_tag(const char * name)
{
xml_tag * tag = calloc(sizeof(xml_tag), 1);
tag->name = strdup(name);
return tag;
}
static void
push_tag(xml_stack ** ostack, xml_tag * tag)
{
xml_stack * stack = calloc(sizeof(xml_stack), 1);
stack->next = *ostack;
stack->tag = tag;
*ostack = stack;
}
static void
free_attribs(xml_attrib * xa)
{
free(xa->name);
free(xa->value);
free(xa);
}
static void
free_tag(xml_tag * tag)
{
while (tag->attribs) {
xml_attrib * p = tag->attribs;
tag->attribs = tag->attribs->next;
free_attribs(p);
}
free(tag->name);
free(tag);
}
static xml_attrib *
make_attrib(xml_tag * tag, const char * name)
{
xml_attrib * xa = calloc(sizeof(xml_attrib), 1);
xa->name = strdup(name);
xa->next = tag->attribs;
return tag->attribs = xa;
}
static xml_tag *
pop_tag(xml_stack ** ostack)
{
xml_stack * stack = *ostack;
xml_tag * tag = stack->tag;
*ostack = stack->next;
free(stack);
return tag;
}
int
xml_parse(FILE * stream, struct xml_callbacks * cb)
{
xml_stack * stack = NULL;
enum { TAG, ENDTAG, ATNAME, ATVALUE, PLAIN } state = PLAIN;
char tokbuffer[1024];
char * pos = tokbuffer;
int quoted = 0;
unsigned int line = 0;
xml_tag * tag = NULL;
xml_attrib * attrib = NULL;
int (*cb_error)(const struct xml_stack*, const char*, unsigned int, int) = __cberror;
if (cb && cb->error) cb_error = cb->error;
for (;;) {
int reparse;
int c = fgetc(stream);
if (c=='\n') {
++line;
} else if (c==EOF) {
if (state==PLAIN) {
*pos='\0';
if (cb && cb->plaintext && pos!=tokbuffer) cb->plaintext(stack, tokbuffer);
break;
} else {
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_BROKENSTREAM);
}
}
do {
reparse = 0;
switch (state) {
case ATVALUE:
switch (c) {
case '<':
case '/':
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_INVALIDCHAR);
case '"':
quoted = !quoted;
break;
case '>':
if (quoted) {
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_INVALIDCHAR);
}
state = TAG;
/* intentional fallthrough */
default:
if (quoted) *pos++ = (char)c;
else {
if (isspace(c) || c=='>') {
assert(attrib || !"internal error");
*pos='\0';
attrib->value = strdup(tokbuffer);
state = TAG;
pos = tokbuffer;
if (c=='>') reparse = 1;
}
}
}
break; /* case ATVALUE */
case ATNAME:
switch (c) {
case '=':
*pos='\0';
assert(tag || !"internal error");
attrib = make_attrib(tag, tokbuffer);
state = ATVALUE;
pos = tokbuffer;
break;
case '>':
case '<':
case '/':
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_INVALIDCHAR);
default:
if (isspace(c)) {
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_INVALIDCHAR);
}
*pos++ = (char)c;
}
break; /* case ATNAME */
case PLAIN:
switch (c) {
case '<':
if (cb && cb->plaintext && pos!=tokbuffer) {
*pos = '\0';
cb->plaintext(stack, tokbuffer);
}
state = TAG;
tag = NULL;
pos = tokbuffer;
break;
case '>':
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_INVALIDCHAR);
default:
*pos++ = (char)c;
}
break; /* case PLAIN */
case TAG:
switch (c) {
case '/':
if (pos==tokbuffer) state = ENDTAG;
else {
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_INVALIDCHAR);
}
break;
case '>':
if (tag==NULL) {
*pos='\0';
push_tag(&stack, make_tag(tokbuffer));
}
if (cb && cb->tagbegin) cb->tagbegin(stack);
state = PLAIN;
pos = tokbuffer;
break;
default:
if (isspace(c)) {
if (tag==NULL) {
*pos='\0';
push_tag(&stack, tag = make_tag(tokbuffer));
state = ATNAME;
pos = tokbuffer;
}
} else {
if (tag!=NULL) {
state = ATNAME;
pos = tokbuffer;
reparse = 1;
}
else *pos++ = (char)c;
}
}
break; /* case TAG */
case ENDTAG:
switch (c) {
case '>':
*pos = '\0';
while (stack && strcmp(stack->tag->name, tokbuffer)!=0) free_tag(pop_tag(&stack));
if (stack==NULL) return cb_error(stack, tokbuffer, line, XML_NESTINGERROR);
if (cb && cb->tagend) cb->tagend(stack);
free_tag(pop_tag(&stack));
state = PLAIN;
pos = tokbuffer;
break;
case '<':
case ' ':
case '=':
case '/':
*pos='\0';
return cb_error(stack, tokbuffer, line, XML_INVALIDCHAR);
default:
*pos++ = (char)c;
}
break; /* case ENDTAG */
} /* switch(state) */
} while (reparse);
} /* for(;;) */
return XML_OK; /* SUCCESS */
}

33
src/common/util/xml.h Normal file
View File

@ -0,0 +1,33 @@
typedef struct xml_stack {
struct xml_stack * next;
struct xml_tag * tag;
} xml_stack;
typedef struct xml_tag {
char * name;
struct xml_attrib * attribs;
} xml_tag;
typedef struct xml_attrib {
struct xml_attrib * next;
char * name;
char * value;
} xml_attrib;
#define XML_OK 0
#define XML_INVALIDCHAR -1
#define XML_NESTINGERROR -2
#define XML_BROKENSTREAM -3
/* callbacks */
typedef struct xml_callbacks {
int (*plaintext)(const struct xml_stack *, const char*);
int (*tagbegin)(const struct xml_stack *);
int (*tagend)(const struct xml_stack *);
int (*error)(const struct xml_stack *, const char*, unsigned int, int);
} xml_callbacks;
/* parser */
#include <stdio.h>
extern int xml_parse(FILE * stream, struct xml_callbacks *);