41219641e1c5a5257b5aa8f71b06cd5a552c0e09
[smdp.git] / parser.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include "include/parser.h"
5
6 deck_t *markdown_load(FILE *input) {
7
8     int c = 0;    // char
9     int i = 0;    // increment
10     int l = 0;    // line length
11     int hc = 0;   // header count
12     int lc = 0;   // line count
13     int sc = 0;   // slide count
14     int bits = 0; // markdown bits
15
16     deck_t *deck = new_deck();
17     slide_t *slide = new_slide();
18     line_t *line;
19     cstring_t *text = cstring_init();
20
21     // assign first slide to deck
22     deck->slide = slide;
23     sc++;
24
25     while ((c = fgetc(input)) != EOF) {
26         if(c == '\n') {
27
28             // markdown analyse
29             bits = markdown_analyse(text);
30
31             // if text is markdown hr
32             if(CHECK_BIT(bits, IS_HR) &&
33                CHECK_BIT(line->bits, IS_EMPTY)) {
34
35                 slide->lines = lc;
36
37                 // clear text
38                 (text->reset)(text);
39                 l = 0;
40
41                 // create next slide
42                 slide = next_slide(slide);
43                 sc++;
44
45             } else {
46
47                 // if slide ! has line
48                 if(!slide->line) {
49
50                     // create new line
51                     line = new_line();
52                     slide->line = line;
53                     lc = 1;
54
55                 } else {
56
57                     // create next line
58                     line = next_line(line);
59                     lc++;
60
61                 }
62
63                 // add text to line
64                 line->text = text;
65
66                 // add bits to line
67                 line->bits = bits;
68
69                 // add length to line
70                 line->length = l;
71
72                 // calc offset
73                 line->offset = next_nonblank(text, 0);
74
75                 // new text
76                 text = cstring_init();
77                 l = 0;
78             }
79
80         } else if(c == '\t') {
81
82             // expand tab to spaces
83             for (i = 0;  i <= 4;  i++) {
84                 (text->expand)(text, ' ');
85                 l++;
86             }
87
88         } else if(isprint(c) || isspace(c)) {
89
90             // add char to line
91             (text->expand)(text, c);
92             l++;
93
94         } else if(is_utf8(c)) {
95
96             // add char to line
97             (text->expand)(text, c);
98
99             // if utf-8 char > 1 byte add remaing to line
100             for(i = 0; i < length_utf8(c) - 1; i++) {
101                 c = fgetc(input);
102                 (text->expand)(text, c);
103             }
104             l++;
105         }
106     }
107
108     slide->lines = lc;
109     deck->slides = sc;
110
111     // detect header
112     line = deck->slide->line;
113     if(line && line->text->size > 0 && line->text->text[0] == '%') {
114
115         // assign header to deck
116         deck->header = line;
117
118         // find first non-header line
119         while(line->text->size > 0 && line->text->text[0] == '%') {
120             hc++;
121             line = line->next;
122         }
123
124         // split linked list
125         line->prev->next = (void*)0;
126         line->prev = (void*)0;
127
128         // remove header lines from slide
129         deck->slide->line = line;
130
131         // adjust counts
132         deck->headers += hc;
133         deck->slide->lines -= hc;
134     }
135
136     // combine underlined H1/H2 in single line
137     slide = deck->slide;
138     while(slide) {
139         line = slide->line;
140         while(line) {
141             if((CHECK_BIT(line->bits, IS_H1) ||
142                 CHECK_BIT(line->bits, IS_H2)) &&
143                CHECK_BIT(line->bits, IS_EMPTY) &&
144                line->prev &&
145                !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
146
147                 // remove line from linked list
148                 line->prev->next = line->next;
149                 line->next->prev = line->prev;
150
151                 // set bits on revious line
152                 if(CHECK_BIT(line->bits, IS_H1)) {
153                     SET_BIT(line->prev->bits, IS_H1);
154                 } else {
155                     SET_BIT(line->prev->bits, IS_H2);
156                 }
157
158                 // adjust line count
159                 slide->lines -= 1;
160
161                 // delete line
162                 (line->text->delete)(line->text);
163                 free(line);
164             }
165             line = line->next;
166         }
167         slide = slide->next;
168     }
169
170     return deck;
171 }
172
173 int markdown_analyse(cstring_t *text) {
174
175     int i = 0;      // increment
176     int bits = 0;   // markdown bits
177     int offset = 0; // text offset
178     int eol    = 0; // end of line
179
180     int equals = 0, hashes = 0,
181         stars  = 0, minus  = 0,
182         spaces = 0, other  = 0; // special character counts
183
184     // count leading spaces
185     offset = next_nonblank(text, 0);
186
187     // strip trailing spaces
188     for(eol = text->size; eol > offset && isspace(text->text[eol - 1]); eol--);
189
190     for(i = offset; i < eol; i++) {
191
192         switch(text->text[i]) {
193             case '=': equals++;  break;
194             case '#': hashes++;  break;
195             case '*': stars++;   break;
196             case '-': minus++;   break;
197             case ' ': spaces++;  break;
198             default:  other++;   break;
199         }
200     }
201
202     // IS_H1
203     if((equals > 0 &&
204         hashes + stars + minus + spaces + other == 0) ||
205        (text &&
206         text->text &&
207         text->text[offset] == '#' &&
208         text->text[offset+1] != '#')) {
209
210         SET_BIT(bits, IS_H1);
211     }
212
213     // IS_H2
214     if((minus > 0 &&
215         equals + hashes + stars + spaces + other == 0) ||
216        (text &&
217         text->text &&
218         text->text[offset] == '#' &&
219         text->text[offset+1] == '#')) {
220
221         SET_BIT(bits, IS_H2);
222     }
223
224     // IS_QUOTE
225     if(text &&
226        text->text &&
227        text->text[offset] == '>') {
228
229         SET_BIT(bits, IS_QUOTE);
230     }
231
232     // IS_CODE
233     if(offset >= 4) {
234         SET_BIT(bits, IS_CODE);
235     }
236
237     // IS_HR
238     if((minus >= 3 && equals + hashes + stars + other == 0) ||
239        (stars >= 3 && equals + hashes + minus + other == 0)) {
240
241         SET_BIT(bits, IS_HR);
242     }
243
244     // IS_EMPTY
245     if(other == 0) {
246         SET_BIT(bits, IS_EMPTY);
247     }
248
249     return bits;
250 }
251
252 void markdown_debug(deck_t *deck, int debug) {
253
254     int sc = 0; // slide count
255     int lc = 0; // line count
256
257     int offset;
258     line_t *header;
259
260     if(debug == 1) {
261         fprintf(stderr, "headers: %i\nslides: %i\n", deck->headers, deck->slides);
262
263     } else if(debug > 1) {
264
265         // print header to STDERR
266         if(deck->header) {
267             header = deck->header;
268             while(header &&
269                 header->length > 0 &&
270                 header->text->text[0] == '%') {
271
272                 // skip descriptor word (e.g. %title:)
273                 offset = next_blank(header->text, 0) + 1;
274
275                 fprintf(stderr, "header: %s\n", &header->text->text[offset]);
276                 header = header->next;
277             }
278         }
279     }
280
281     slide_t *slide = deck->slide;
282     line_t *line;
283
284     // print slide/line count to STDERR
285     while(slide) {
286         sc++;
287
288         if(debug == 1) {
289             fprintf(stderr, "  slide %i: %i lines\n", sc, slide->lines);
290
291         } else if(debug > 1) {
292
293             // also print bits and line length
294             fprintf(stderr, "  slide %i:\n", sc);
295             line = slide->line;
296             lc = 0;
297             while(line) {
298                 lc++;
299                 fprintf(stderr, "    line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
300                 line = line->next;
301             }
302         }
303
304         slide = slide->next;
305     }
306 }
307
308 int is_utf8(char ch) {
309     return (ch & 0x80);
310 }
311
312 int length_utf8(char ch) {
313
314     int i = 0; // increment
315
316     while(ch & 0x80) {
317         i++;
318         ch <<= 1;
319     }
320
321     return i;
322 }
323
324 int next_nonblank(cstring_t *text, int i) {
325     while ((i < text->size) && isspace((text->text)[i]))
326         ++i;
327
328     return i;
329 };
330
331 int next_blank(cstring_t *text, int i) {
332     while ((i < text->size) && !isspace((text->text)[i]))
333         ++i;
334
335     return i;
336 };
337