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 }