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

20
.clang-format Normal file
View file

@ -0,0 +1,20 @@
---
Language: Cpp
UseTab: Always
TabWidth: 8
AlignConsecutiveAssignments:
Enabled: true
AlignConsecutiveDeclarations:
Enabled: true
IndentWidth: 8
PointerAlignment: Left
ColumnLimit: 0
AllowShortIfStatementsOnASingleLine: Always
AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: Empty
AllowShortLoopsOnASingleLine: true
SpaceBeforeParens: Never
AlignEscapedNewlines: DontAlign
SortIncludes: false
AllowShortEnumsOnASingleLine: false
#IndentPPDirectives: AfterHash

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.o
*.so
*.dll
*.core
/example

24
LICENSE Normal file
View file

@ -0,0 +1,24 @@
Copyright (c) 2025, Pyrite development team
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

28
Makefile Normal file
View file

@ -0,0 +1,28 @@
CC = gcc
CFLAGS = -I include -fPIC
LDFLAGS = -shared
LIBS =
OBJS = src/core.o src/file.o src/unicode.o src/stb_ds.o
SO = .so
.PHONY: all format clean
.SUFFIXES: .c .o
all: libxmllib$(SO)
format:
clang-format --verbose -i `find src include -name "*.c" -or -name "*.h"`
example: example.c libxmllib$(SO)
$(CC) -o $@ example.c -I include -Wl,-R. -L. -lxmllib
libxmllib$(SO): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f src/*.o *.so *.dll

31
example.c Normal file
View file

@ -0,0 +1,31 @@
#include <xmllib.h>
void recursive(xmllib_node_t* node, int indent){
int i;
xmllib_node_t* n;
for(i = 0; i < indent; i++) printf(" ");
printf("%s\n", node->name);
n = node->first_child;
while(n != NULL){
recursive(n, indent + 2);
n = n->next;
}
}
int main(int argc, char** argv){
int i;
for(i = 1; i < argc; i++){
xmllib_t* h = xmllib_open_file(argv[i]);
if(h != NULL){
printf("%s:\n", argv[i]);
if(xmllib_parse(h)){
if(h->root != NULL) recursive(h->root, 2);
}else{
printf(" Parse error\n");
}
xmllib_close(h);
}
}
}

1895
external/stb_ds.h vendored Normal file

File diff suppressed because it is too large Load diff

76
include/xmllib.h Normal file
View file

@ -0,0 +1,76 @@
#ifndef __XMLLIB_H__
#define __XMLLIB_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#define XLDECL extern
typedef struct xmllib_driver xmllib_driver_t;
typedef struct xmllib xmllib_t;
typedef struct xmllib_node xmllib_node_t;
struct xmllib_driver {
int (*open)(xmllib_t* handle);
int (*read)(xmllib_t* handle, void* data, int size);
void (*close)(xmllib_t* handle);
};
enum XMLLIB_NODE_TYPE {
XMLLIB_NODE_COMMENT = 0,
XMLLIB_NODE_NODE,
XMLLIB_NODE_PROCESS,
XMLLIB_NODE_DOCTYPE
};
struct xmllib_node {
int type;
char* name;
char* text;
xmllib_node_t* root;
xmllib_node_t* parent;
xmllib_node_t* first_child;
xmllib_node_t* prev;
xmllib_node_t* next;
};
struct xmllib {
xmllib_driver_t* driver;
void* drv_opaque;
void* drv_arg;
xmllib_node_t* root;
};
#ifdef __cplusplus
extern "C" {
#endif
/* core.c */
XLDECL xmllib_t* xmllib_open(xmllib_driver_t* driver, void* arg);
XLDECL int xmllib_parse(xmllib_t* handle);
XLDECL void xmllib_close(xmllib_t* handle);
/* file.c */
XLDECL xmllib_driver_t* xmllib_driver_file;
XLDECL xmllib_t* xmllib_open_file(const char* filename);
/* unicode.c */
XLDECL int xmllib_unicode_count(unsigned char c);
XLDECL int xmllib_unicode_8_to_32(const char* input, int* output);
XLDECL int xmllib_unicode_32_to_8(const int input, char* output);
#ifdef __cplusplus
}
#endif
#endif

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;
}
}