123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /* vim: tabstop=4 shiftwidth=4 noexpandtab
- * This file is part of ToaruOS and is released under the terms
- * of the NCSA / University of Illinois License - see LICENSE.md
- * Copyright (C) 2018 K. Lange
- *
- * Markup parser.
- */
- #include <stdio.h>
- #include <toaru/markup.h>
- struct markup_state {
- int state;
- void * user;
- markup_callback_tag_open callback_tag_open;
- markup_callback_tag_close callback_tag_close;
- markup_callback_data callback_data;
- /* Private stuff */
- struct markup_tag tag;
- size_t len;
- char data[64];
- char * attr;
- };
- struct markup_state * markup_init(void * user, markup_callback_tag_open open, markup_callback_tag_close close, markup_callback_data data) {
- struct markup_state * out = malloc(sizeof(out));
- out->state = 0;
- out->user = user;
- out->len = 0;
- out->callback_tag_open = open;
- out->callback_tag_close = close;
- out->callback_data = data;
- return out;
- }
- static void _dump_buffer(struct markup_state * state) {
- if (state->len) {
- state->data[state->len] = '\0';
- state->callback_data(state, state->user, state->data);
- state->data[0] = '\0';
- state->len = 0;
- }
- }
- static void _finish_name(struct markup_state * state) {
- state->data[state->len] = '\0';
- state->tag.name = strdup(state->data);
- state->tag.options = hashmap_create(5);
- state->data[0] = '\0';
- state->len = 0;
- state->state = 2;
- }
- static void _finish_close(struct markup_state * state) {
- state->data[state->len] = '\0';
- state->callback_tag_close(state, state->user, state->data);
- state->data[0] = '\0';
- state->len = 0;
- state->state = 0;
- }
- static void _finish_tag(struct markup_state * state) {
- state->callback_tag_open(state, state->user, &state->tag);
- state->state = 0;
- }
- static void _finish_bare_attr(struct markup_state * state) {
- state->data[state->len] = '\0';
- hashmap_set(state->tag.options, state->data, strdup(state->data));
- state->data[0] = '\0';
- state->len = 0;
- }
- static void _finish_attr(struct markup_state * state) {
- state->data[state->len] = '\0';
- state->attr = strdup(state->data);
- state->data[0] = '\0';
- state->len = 0;
- state->state = 4;
- }
- static void _finish_attr_value(struct markup_state * state) {
- state->data[state->len] = '\0';
- hashmap_set(state->tag.options, state->attr, strdup(state->data));
- free(state->attr);
- state->data[0] = '\0';
- state->len = 0;
- state->state = 2;
- }
- int markup_free_tag(struct markup_tag * tag) {
- free(tag->name);
- list_t * keys = hashmap_keys(tag->options);
- if (keys->length) {
- foreach(node, keys) {
- free(hashmap_get(tag->options, node->value));
- }
- }
- list_free(keys);
- free(keys);
- hashmap_free(tag->options);
- return 0;
- }
- int markup_parse(struct markup_state * state, char c) {
- switch (state->state) {
- case 0: /* STATE_NORMAL */
- if (state->len == 63) {
- _dump_buffer(state);
- }
- switch (c) {
- case '<':
- _dump_buffer(state);
- state->state = 1;
- return 0;
- default:
- state->data[state->len] = c;
- state->len++;
- return 0;
- }
- break;
- case 1: /* STATE_TAG_OPEN */
- switch (c) {
- case '/':
- if (state->len) {
- fprintf(stderr, "syntax error\n");
- return 1;
- }
- state->state = 3; /* STATE_TAG_CLOSE */
- return 0;
- case '>':
- _finish_name(state);
- _finish_tag(state);
- return 0;
- case ' ':
- _finish_name(state);
- return 0;
- default:
- state->data[state->len] = c;
- state->len++;
- return 0;
- }
- break;
- case 2: /* STATE_TAG_ATTRIB */
- switch (c) {
- case ' ': /* attribute has no value, end it and append it with = self */
- _finish_bare_attr(state);
- return 0;
- case '>':
- _finish_bare_attr(state);
- _finish_tag(state);
- return 0;
- case '=': /* attribute has a value, go to next mode */
- _finish_attr(state);
- return 0;
- default:
- state->data[state->len] = c;
- state->len++;
- return 0;
- }
- return 0;
- case 3: /* STATE_TAG_CLOSE */
- switch (c) {
- case '>':
- _finish_close(state);
- return 0;
- default:
- state->data[state->len] = c;
- state->len++;
- return 0;
- }
- break;
- case 4: /* STATE_ATTR_VALUE */
- switch (c) {
- case ' ':
- _finish_attr_value(state);
- return 0;
- case '>':
- _finish_attr_value(state);
- _finish_tag(state);
- return 0;
- default:
- state->data[state->len] = c;
- state->len++;
- return 0;
- }
- break;
- default:
- fprintf(stderr, "parser in unknown state\n");
- return 1;
- }
- return 0;
- }
- int markup_finish(struct markup_state * state) {
- if (state->state != 0) {
- fprintf(stderr, "unexpected end of data\n");
- return 1;
- } else {
- _dump_buffer(state);
- free(state);
- return 0;
- }
- }
|