This commit is contained in:
Nishi 2025-12-22 18:22:46 +09:00
commit 32ef62bd20
Signed by: nishi
GPG key ID: 27EF69B208EB9343
11 changed files with 2354 additions and 0 deletions

168
src/core.c Normal file
View file

@ -0,0 +1,168 @@
#include <xmllib.h>
#include "../external/stb_ds.h"
xmllib_t* xmllib_open(xmllib_driver_t* driver, void* arg) {
xmllib_t* handle = malloc(sizeof(*handle));
memset(handle, 0, sizeof(*handle));
handle->driver = driver;
handle->drv_arg = arg;
if(!handle->driver->open(handle)) {
free(handle);
return NULL;
}
return handle;
}
enum STATES {
STATE_INITIAL = 0,
STATE_TAG,
STATE_STRING,
STATE_SPECIAL,
STATE_COMMENT,
STATE_DOCTYPE,
};
#define TAKE_AS_NODE(x) \
{ \
char* old = node; \
char buf[4]; \
int new = xmllib_unicode_32_to_8((x), buf); \
\
node = malloc(strlen(old) + new + 1); \
strcpy(node, old); \
memcpy(node + strlen(old), buf, new); \
node[strlen(old) + new] = 0; \
node_count++; \
}
#define CLEANUP \
{ \
if(node != NULL) free(node); \
arrfree(state); \
}
#define ERROR \
{ \
printf("%d\n", __LINE__); \
CLEANUP; \
longjmp(err, 1); \
}
#define STATE state[arrlen(state) - 1]
int xmllib_parse(xmllib_t* handle) {
jmp_buf err;
int* state = NULL;
char* node = NULL;
int node_count = 0;
int nest_level = 0;
if(setjmp(err)) {
return 0;
}
arrput(state, STATE_INITIAL);
while(1) {
char in[4];
int c;
int cp;
if(handle->driver->read(handle, &in[0], 1) < 1) ERROR;
c = xmllib_unicode_count(in[0]);
if((c > 1) && handle->driver->read(handle, &in[1], c - 1) < (c - 1)) longjmp(err, 1);
xmllib_unicode_8_to_32(in, &cp);
if(cp == '<' && STATE != STATE_COMMENT && STATE != STATE_DOCTYPE) {
if(STATE == STATE_INITIAL) {
arrput(state, STATE_TAG);
node = malloc(1);
node[0] = 0;
node_count = 0;
} else if(STATE == STATE_STRING) {
TAKE_AS_NODE(cp);
} else {
ERROR;
}
} else if(cp == '>') {
if(STATE == STATE_TAG || STATE == STATE_DOCTYPE || STATE == STATE_COMMENT) {
if((STATE != STATE_COMMENT || node[strlen(node) - 1] == '-') && (STATE != STATE_DOCTYPE || node[strlen(node) - 1] == ']')) {
arrpop(state);
if(STATE == STATE_INITIAL) {
/* parsed tag */
if(node[0] == '!') {
/* special tag */
if(strlen(node) > 1 && node[1] == '-') {
/* comment */
} else {
/* doctype or something - we ignore this for now */
}
} else if(node[0] == '?') {
/* process */
} else {
/* normal tag */
if(node[0] == '/') {
/* close tag */
nest_level--;
} else if(node[strlen(node) - 1] == '/') {
/* self-closing tag */
nest_level++;
}
if(nest_level < 0) ERROR;
if(nest_level == 0) break;
}
free(node);
node = NULL;
}
} else {
TAKE_AS_NODE(cp);
}
} else if(STATE == STATE_STRING) {
TAKE_AS_NODE(cp);
} else {
ERROR;
}
} else if(cp == '"' && STATE != STATE_COMMENT && STATE != STATE_DOCTYPE) {
if(STATE == STATE_TAG) {
arrput(state, STATE_STRING);
} else if(STATE == STATE_STRING) {
arrpop(state);
}
TAKE_AS_NODE(cp);
} else if(STATE == STATE_TAG || STATE == STATE_STRING || STATE == STATE_SPECIAL || STATE == STATE_COMMENT || STATE == STATE_DOCTYPE) {
if(node_count == 0 && cp == '!') {
arrpop(state);
arrput(state, STATE_SPECIAL);
} else if(node_count == 1 && STATE == STATE_SPECIAL && cp == '-') {
arrpop(state);
arrput(state, STATE_COMMENT);
} else if(node_count == 1 && STATE == STATE_SPECIAL) {
arrpop(state);
arrput(state, STATE_DOCTYPE);
}
TAKE_AS_NODE(cp);
}
}
CLEANUP;
return 1;
}
void xmllib_close(xmllib_t* handle) {
handle->driver->close(handle);
free(handle);
}

26
src/file.c Normal file
View file

@ -0,0 +1,26 @@
#include <xmllib.h>
static int xmllib_driver_file_open(xmllib_t* handle) {
if((handle->drv_opaque = fopen(handle->drv_arg, "rb")) == NULL) return 0;
return 1;
}
static int xmllib_driver_file_read(xmllib_t* handle, void* data, int size) {
return fread(data, 1, size, handle->drv_opaque);
}
static void xmllib_driver_file_close(xmllib_t* handle) {
fclose(handle->drv_opaque);
}
xmllib_driver_t xmllib_driver_file_rec = {
xmllib_driver_file_open,
xmllib_driver_file_read,
xmllib_driver_file_close,
};
xmllib_driver_t* xmllib_driver_file = &xmllib_driver_file_rec;
xmllib_t* xmllib_open_file(const char* filename) {
return xmllib_open(xmllib_driver_file, (void*)filename);
}

2
src/stb_ds.c Normal file
View file

@ -0,0 +1,2 @@
#define STB_DS_IMPLEMENTATION
#include "../external/stb_ds.h"

79
src/unicode.c Normal file
View file

@ -0,0 +1,79 @@
#include <xmllib.h>
#define CAST_I32(x) ((int)(x))
int xmllib_unicode_count(unsigned char c) {
if(c < 0x80) {
return 1;
}
if(0xc2 <= c && c < 0xe0) {
return 2;
}
if(0xe0 <= c && c < 0xf0) {
return 3;
}
if(0xf0 <= c && c < 0xf8) {
return 4;
}
return 0;
}
static int unicode_later(unsigned char c) {
return 0x80 <= c && c < 0xc0;
}
int xmllib_unicode_8_to_32(const char* input, int* output) {
const unsigned char* inbuf = (const unsigned char*)input;
int b = xmllib_unicode_count(inbuf[0]);
if(b == 0) return 0;
if(b == 1) *output = inbuf[0];
if(b == 2) {
if(!unicode_later(inbuf[1])) return 0;
if((inbuf[0] & 0x1e) == 0) return 0;
*output = CAST_I32(inbuf[0] & 0x1f) << 6;
*output |= CAST_I32(inbuf[1] & 0x3f);
}
if(b == 3) {
if(!unicode_later(inbuf[1]) || !unicode_later(inbuf[2])) return 0;
if((inbuf[0] & 0x0f) == 0 && (inbuf[1] & 0x20) == 0) return 0;
*output = CAST_I32(inbuf[0] & 0x0f) << 12;
*output |= CAST_I32(inbuf[1] & 0x3f) << 6;
*output |= CAST_I32(inbuf[2] & 0x3f);
}
if(b == 4) {
if(!unicode_later(inbuf[1]) || !unicode_later(inbuf[2]) || !unicode_later(inbuf[3])) return 0;
if((inbuf[0] & 0x07) == 0 && (inbuf[1] & 0x30) == 0) return 0;
*output = CAST_I32(inbuf[0] & 0x07) << 18;
*output |= CAST_I32(inbuf[1] & 0x3f) << 12;
*output |= CAST_I32(inbuf[2] & 0x3f) << 6;
*output |= CAST_I32(inbuf[3] & 0x3f);
}
return b;
}
int xmllib_unicode_32_to_8(const int input, char* output) {
if(input < 128) {
output[0] = input;
return 1;
} else if(input < 2048) {
output[0] = 0xc0 | (input >> 6);
output[1] = 0x80 | (input & 0x3f);
return 2;
} else if(input < 65536) {
output[0] = 0xe0 | (input >> 12);
output[1] = 0x80 | ((input >> 6) & 0x3f);
output[2] = 0x80 | (input & 0x3f);
return 3;
} else {
output[0] = 0xf0 | (input >> 18);
output[1] = 0x80 | ((input >> 12) & 0x3f);
output[2] = 0x80 | ((input >> 6) & 0x3f);
output[3] = 0x80 | (input & 0x3f);
return 4;
}
}