forked from github/server
XML-Parser für resourcedateien
This commit is contained in:
parent
120e318ffe
commit
e9f1fc2207
|
@ -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 */
|
||||
}
|
|
@ -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 *);
|
||||
|
Loading…
Reference in New Issue