cgmnlm

colorful gemini line mode browser
git clone https://git.clttr.info/cgmnlm.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

parser.c (3177B)


      1 #include <assert.h>
      2 #include <ctype.h>
      3 #include <stdbool.h>
      4 #include <stddef.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <gmni/gmni.h>
      9 
     10 void
     11 gemini_parser_init(struct gemini_parser *p,
     12 	int (*read)(void *state, void *buf, size_t nbyte),
     13 	void *state)
     14 {
     15 	p->read = read;
     16 	p->state = state;
     17 	p->bufln = 0;
     18 	p->bufsz = BUFSIZ;
     19 	p->buf = malloc(p->bufsz + 1);
     20 	p->buf[0] = 0;
     21 	p->preformatted = false;
     22 }
     23 
     24 void
     25 gemini_parser_finish(struct gemini_parser *p)
     26 {
     27 	if (!p) {
     28 		return;
     29 	}
     30 	free(p->buf);
     31 }
     32 
     33 int
     34 gemini_parser_next(struct gemini_parser *p, struct gemini_token *tok)
     35 {
     36 	memset(tok, 0, sizeof(*tok));
     37 
     38 	int eof = 0;
     39 	while (!strchr(p->buf, '\n')) {
     40 		while (p->bufln >= p->bufsz - 1) {
     41 			p->bufsz *= 2;
     42 			p->buf = realloc(p->buf, p->bufsz);
     43 			assert(p->buf);
     44 		}
     45 
     46 		int n = p->read(p->state, &p->buf[p->bufln], p->bufsz - p->bufln - 1);
     47 		if (n < 1) {
     48 			eof = p->bufln == 0;
     49 			break;
     50 		}
     51 		p->bufln += n;
     52 		p->buf[p->bufln] = 0;
     53 	}
     54 
     55 	char *end;
     56 	if ((end = strchr(p->buf, '\n')) != NULL) {
     57 		*end = 0;
     58 	}
     59 
     60 	if (p->preformatted) {
     61 		if (strncmp(p->buf, "```", 3) == 0) {
     62 			tok->token = GEMINI_PREFORMATTED_END;
     63 			p->preformatted = false;
     64 		} else {
     65 			tok->token = GEMINI_PREFORMATTED_TEXT;
     66 			tok->preformatted = strdup(p->buf);
     67 		}
     68 	} else if (strncmp(p->buf, "=>", 2) == 0) {
     69 		tok->token = GEMINI_LINK;
     70 		int i = 2;
     71 		while (p->buf[i] && isspace(p->buf[i])) ++i;
     72 		tok->link.url = &p->buf[i];
     73 
     74 		for (; p->buf[i]; ++i) {
     75 			if (isspace(p->buf[i])) {
     76 				p->buf[i++] = 0;
     77 				while (isspace(p->buf[i])) ++i;
     78 				if (p->buf[i]) {
     79 					tok->link.text = strdup(&p->buf[i]);
     80 				}
     81 				break;
     82 			}
     83 		}
     84 
     85 		tok->link.url = strdup(tok->link.url);
     86 	} else if (strncmp(p->buf, "```", 3) == 0) {
     87 		tok->token = GEMINI_PREFORMATTED_BEGIN;
     88 		if (p->buf[3]) {
     89 			tok->preformatted = strdup(&p->buf[3]);
     90 		}
     91 		p->preformatted = true;
     92 	} else if (p->buf[0] == '#') {
     93 		tok->token = GEMINI_HEADING;
     94 		int level = 1;
     95 		while (p->buf[level] == '#' && level < 3) {
     96 			++level;
     97 		}
     98 		tok->heading.level = level;
     99 		tok->heading.title = strdup(&p->buf[level]);
    100 	} else if (p->buf[0] == '*') {
    101 		tok->token = GEMINI_LIST_ITEM;
    102 		tok->list_item = strdup(&p->buf[1]);
    103 	} else if (p->buf[0] == '>') {
    104 		tok->token = GEMINI_QUOTE;
    105 		tok->quote_text = strdup(&p->buf[1]);
    106 	} else {
    107 		tok->token = GEMINI_TEXT;
    108 		tok->text = strdup(p->buf);
    109 	}
    110 
    111 	if (end && end + 1 < p->buf + p->bufln) {
    112 		size_t len = end - p->buf + 1;
    113 		memmove(p->buf, end + 1, p->bufln - len);
    114 		p->bufln -= len;
    115 		p->buf[p->bufln] = 0;
    116 	} else {
    117 		p->buf[0] = 0;
    118 		p->bufln = 0;
    119 	}
    120 
    121 	return eof;
    122 }
    123 
    124 void
    125 gemini_token_finish(struct gemini_token *tok)
    126 {
    127 	if (!tok) {
    128 		return;
    129 	}
    130 
    131 	switch (tok->token) {
    132 	case GEMINI_TEXT:
    133 		free(tok->text);
    134 		break;
    135 	case GEMINI_LINK:
    136 		free(tok->link.text);
    137 		free(tok->link.url);
    138 		break;
    139 	case GEMINI_PREFORMATTED_BEGIN:
    140 		free(tok->preformatted);
    141 		break;
    142 	case GEMINI_PREFORMATTED_TEXT:
    143 		free(tok->preformatted);
    144 		break;
    145 	case GEMINI_PREFORMATTED_END:
    146 		// Nothing to free
    147 		break;
    148 	case GEMINI_HEADING:
    149 		free(tok->heading.title);
    150 		break;
    151 	case GEMINI_LIST_ITEM:
    152 		free(tok->list_item);
    153 		break;
    154 	case GEMINI_QUOTE:
    155 		free(tok->quote_text);
    156 		break;
    157 	}
    158 }