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