split up some headers + rename markdown_io to parser
[smdp.git] / parser.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include "include/parser.h"
5
6 document_t *markdown_load(FILE *input) {
7
8     int c = 0, i = 0, bits = 0;
9
10     document_t *doc;
11     page_t *page;
12     line_t *line;
13     cstring_t *text;
14
15     doc = new_document();
16     page = new_page();
17     doc->page = page;
18     text = cstring_init();
19
20     while ((c = fgetc(input)) != EOF) {
21         if(c == '\n') {
22
23             // markdown analyse
24             bits = markdown_analyse(text);
25
26             // if text is markdown hr
27             if(CHECK_BIT(bits, IS_HR) &&
28                CHECK_BIT(line->bits, IS_EMPTY)) {
29
30                 // clear text
31                 (text->reset)(text);
32                 // create next page
33                 page = next_page(page);
34
35             } else {
36
37                 // if page ! has line
38                 if(!page->line) {
39
40                     // create new line
41                     line = new_line();
42                     page->line = line;
43
44                 } else {
45
46                     // create next line
47                     line = next_line(line);
48
49                 }
50
51                 // add text to line
52                 line->text = text;
53
54                 // add bits to line
55                 line->bits = bits;
56
57                 // calc offset
58                 line->offset = next_nonblank(text, 0);
59
60                 // new text
61                 text = cstring_init();
62             }
63
64         } else if(c == '\t') {
65
66             // expand tab to spaces
67             for (i = 0;  i <= 4;  i++)
68                 (text->expand)(text, ' ');
69
70         } else if(isprint(c) || isspace(c) || is_utf8(c)) {
71
72             // add char to line
73             (text->expand)(text, c);
74         }
75     }
76
77     // detect header
78     line = doc->page->line;
79     if(line && line->text->size > 0 && line->text->text[0] == '%') {
80
81         // assign header to document
82         doc->header = line;
83
84         // find first non-header line
85         while(line->text->size > 0 && line->text->text[0] == '%') {
86             line = line->next;
87         }
88
89         // split linked list
90         line->prev->next = (void*)0;
91         line->prev = (void*)0;
92
93         // remove header lines from page
94         doc->page->line = line;
95     }
96
97     // combine underlined H1/H2 in single line
98     page = doc->page;
99     while(page) {
100         line = page->line;
101         while(line) {
102             if((CHECK_BIT(line->bits, IS_H1) ||
103                 CHECK_BIT(line->bits, IS_H2)) &&
104                CHECK_BIT(line->bits, IS_EMPTY) &&
105                line->prev &&
106                !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
107
108                 // remove line from linked list
109                 line->prev->next = line->next;
110                 line->next->prev = line->prev;
111
112                 // set bits on revious line
113                 if(CHECK_BIT(line->bits, IS_H1)) {
114                     SET_BIT(line->prev->bits, IS_H1);
115                 } else {
116                     SET_BIT(line->prev->bits, IS_H2);
117                 }
118
119                 // delete line
120                 (line->text->delete)(line->text);
121                 free(line);
122             }
123             line = line->next;
124         }
125         page = page->next;
126     }
127
128     return doc;
129 }
130
131 int markdown_analyse(cstring_t *text) {
132     int i = 0, bits = 0,
133         offset = 0, eol    = 0,
134         equals = 0, hashes = 0,
135         stars  = 0, minus  = 0,
136         spaces = 0, other  = 0;
137
138     // count leading spaces
139     offset = next_nonblank(text, 0);
140
141     // strip trailing spaces
142     for(eol = text->size; eol > offset && isspace(text->text[eol - 1]); eol--);
143
144     for(i = offset; i < eol; i++) {
145
146         switch(text->text[i]) {
147             case '=': equals++;  break;
148             case '#': hashes++;  break;
149             case '*': stars++;   break;
150             case '-': minus++;   break;
151             case ' ': spaces++;  break;
152             default:  other++;   break;
153         }
154     }
155
156     // IS_H1
157     if((equals > 0 &&
158         hashes + stars + minus + spaces + other == 0) ||
159        (text &&
160         text->text &&
161         text->text[offset] == '#' &&
162         text->text[offset+1] != '#')) {
163
164         SET_BIT(bits, IS_H1);
165     }
166
167     // IS_H2
168     if((minus > 0 &&
169         equals + hashes + stars + spaces + other == 0) ||
170        (text &&
171         text->text &&
172         text->text[offset] == '#' &&
173         text->text[offset+1] == '#')) {
174
175         SET_BIT(bits, IS_H2);
176     }
177
178     // IS_QUOTE
179     if(text &&
180        text->text &&
181        text->text[offset] == '>') {
182
183         SET_BIT(bits, IS_QUOTE);
184     }
185
186     // IS_CODE
187     if(offset >= 4) {
188         SET_BIT(bits, IS_CODE);
189     }
190
191     // IS_HR
192     if((minus >= 3 && equals + hashes + stars + other == 0) ||
193        (stars >= 3 && equals + hashes + minus + other == 0)) {
194
195         SET_BIT(bits, IS_HR);
196     }
197
198     // IS_EMPTY
199     if(other == 0) {
200         SET_BIT(bits, IS_EMPTY);
201     }
202
203     return bits;
204 }
205
206 int is_utf8(char ch) {
207     return (ch & 0x80);
208 }
209
210 int next_nonblank(cstring_t *text, int i) {
211     while ((i < text->size) && isspace((text->text)[i]))
212         ++i;
213     return i;
214 };
215
216 int next_blank(cstring_t *text, int i) {
217     while ((i < text->size) && !isspace((text->text)[i]))
218         ++i;
219     return i;
220 };
221