3b56b5b347ca8a12c3fdc66db5e9be999934cbc8
[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                 line->next->prev = line->prev;
176
177                 // set bits on revious line
178                 if(CHECK_BIT(line->bits, IS_H1)) {
179                     SET_BIT(line->prev->bits, IS_H1);
180                 } else {
181                     SET_BIT(line->prev->bits, IS_H2);
182                 }
183
184                 // adjust line count
185                 slide->lines -= 1;
186
187                 // delete line
188                 (line->text->delete)(line->text);
189                 free(line);
190             }
191             line = line->next;
192         }
193         slide = slide->next;
194     }
195
196     return deck;
197 }
198
199 int markdown_analyse(cstring_t *text) {
200
201     int i = 0;      // increment
202     int bits = 0;   // markdown bits
203     int offset = 0; // text offset
204     int eol    = 0; // end of line
205
206     int equals = 0, hashes = 0,
207         stars  = 0, minus  = 0,
208         spaces = 0, other  = 0; // special character counts
209
210     // count leading spaces
211     offset = next_nonblank(text, 0);
212
213     // strip trailing spaces
214     for(eol = text->size; eol > offset && isspace(text->text[eol - 1]); eol--);
215
216     // IS_CODE
217     if(offset >= CODE_INDENT) {
218         SET_BIT(bits, IS_CODE);
219     }
220
221     for(i = offset; i < eol; i++) {
222
223         if(text->text[i] == ' ') {
224             spaces++;
225
226         } else if(CHECK_BIT(bits, IS_CODE)) {
227             other++;
228
229         } else {
230             switch(text->text[i]) {
231                 case '=': equals++;  break;
232                 case '#': hashes++;  break;
233                 case '*': stars++;   break;
234                 case '-': minus++;   break;
235                 case '\\': other++; i++; break;
236                 default:  other++;   break;
237             }
238         }
239     }
240
241     // IS_H1
242     if((equals > 0 &&
243         hashes + stars + minus + spaces + other == 0) ||
244        (text &&
245         text->text &&
246         text->text[offset] == '#' &&
247         text->text[offset+1] != '#')) {
248
249         SET_BIT(bits, IS_H1);
250     }
251
252     // IS_H2
253     if((minus > 0 &&
254         equals + hashes + stars + spaces + other == 0) ||
255        (text &&
256         text->text &&
257         text->text[offset] == '#' &&
258         text->text[offset+1] == '#')) {
259
260         SET_BIT(bits, IS_H2);
261     }
262
263     // IS_QUOTE
264     if(text &&
265        text->text &&
266        text->text[offset] == '>') {
267
268         SET_BIT(bits, IS_QUOTE);
269     }
270
271     // IS_HR
272     if((minus >= 3 && equals + hashes + stars + other == 0) ||
273        (stars >= 3 && equals + hashes + minus + other == 0)) {
274
275         SET_BIT(bits, IS_HR);
276     }
277
278     // IS_EMPTY
279     if(other == 0) {
280         SET_BIT(bits, IS_EMPTY);
281     }
282
283     return bits;
284 }
285
286 void markdown_debug(deck_t *deck, int debug) {
287
288     int sc = 0; // slide count
289     int lc = 0; // line count
290
291     int offset;
292     line_t *header;
293
294     if(debug == 1) {
295         fprintf(stderr, "headers: %i\nslides: %i\n", deck->headers, deck->slides);
296
297     } else if(debug > 1) {
298
299         // print header to STDERR
300         if(deck->header) {
301             header = deck->header;
302             while(header &&
303                 header->length > 0 &&
304                 header->text->text[0] == '%') {
305
306                 // skip descriptor word (e.g. %title:)
307                 offset = next_blank(header->text, 0) + 1;
308
309                 fprintf(stderr, "header: %s\n", &header->text->text[offset]);
310                 header = header->next;
311             }
312         }
313     }
314
315     slide_t *slide = deck->slide;
316     line_t *line;
317
318     // print slide/line count to STDERR
319     while(slide) {
320         sc++;
321
322         if(debug == 1) {
323             fprintf(stderr, "  slide %i: %i lines\n", sc, slide->lines);
324
325         } else if(debug > 1) {
326
327             // also print bits and line length
328             fprintf(stderr, "  slide %i:\n", sc);
329             line = slide->line;
330             lc = 0;
331             while(line) {
332                 lc++;
333                 fprintf(stderr, "    line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
334                 line = line->next;
335             }
336         }
337
338         slide = slide->next;
339     }
340 }
341
342 int is_utf8(char ch) {
343     return (ch & 0x80);
344 }
345
346 int length_utf8(char ch) {
347
348     int i = 0; // increment
349
350     while(ch & 0x80) {
351         i++;
352         ch <<= 1;
353     }
354
355     return i;
356 }
357
358 int next_nonblank(cstring_t *text, int i) {
359     while ((i < text->size) && isspace((text->text)[i]))
360         ++i;
361
362     return i;
363 }
364
365 int next_blank(cstring_t *text, int i) {
366     while ((i < text->size) && !isspace((text->text)[i]))
367         ++i;
368
369     return i;
370 }
371