calculate length of lines including backslashes correctly
[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 <= EXPAND_TABS;  i++) {
84                 (text->expand)(text, ' ');
85                 l++;
86             }
87
88         } else if(c == '\\') {
89
90             // add char to line
91             (text->expand)(text, c);
92             l++;
93
94             // if !IS_CODE add next char to line
95             // and do not increase line count
96             if(next_nonblank(text, 0) < CODE_INDENT) {
97
98                 c = fgetc(input);
99                 (text->expand)(text, c);
100
101                 if(is_utf8(c)) {
102
103                     // if utf-8 char > 1 byte add remaing to line
104                     for(i = 0; i < length_utf8(c) - 1; i++) {
105                         c = fgetc(input);
106                         (text->expand)(text, c);
107                     }
108                 }
109
110             }
111
112         } else if(isprint(c) || isspace(c)) {
113
114             // add char to line
115             (text->expand)(text, c);
116             l++;
117
118         } else if(is_utf8(c)) {
119
120             // add char to line
121             (text->expand)(text, c);
122
123             // if utf-8 char > 1 byte add remaing to line
124             for(i = 0; i < length_utf8(c) - 1; i++) {
125                 c = fgetc(input);
126                 (text->expand)(text, c);
127             }
128
129             l++;
130         }
131     }
132
133     slide->lines = lc;
134     deck->slides = sc;
135
136     // detect header
137     line = deck->slide->line;
138     if(line && line->text->size > 0 && line->text->text[0] == '%') {
139
140         // assign header to deck
141         deck->header = line;
142
143         // find first non-header line
144         while(line->text->size > 0 && line->text->text[0] == '%') {
145             hc++;
146             line = line->next;
147         }
148
149         // split linked list
150         line->prev->next = (void*)0;
151         line->prev = (void*)0;
152
153         // remove header lines from slide
154         deck->slide->line = line;
155
156         // adjust counts
157         deck->headers += hc;
158         deck->slide->lines -= hc;
159     }
160
161     // combine underlined H1/H2 in single line
162     slide = deck->slide;
163     while(slide) {
164         line = slide->line;
165         while(line) {
166             if((CHECK_BIT(line->bits, IS_H1) ||
167                 CHECK_BIT(line->bits, IS_H2)) &&
168                CHECK_BIT(line->bits, IS_EMPTY) &&
169                line->prev &&
170                !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
171
172                 // remove line from linked list
173                 line->prev->next = line->next;
174                 line->next->prev = line->prev;
175
176                 // set bits on revious line
177                 if(CHECK_BIT(line->bits, IS_H1)) {
178                     SET_BIT(line->prev->bits, IS_H1);
179                 } else {
180                     SET_BIT(line->prev->bits, IS_H2);
181                 }
182
183                 // adjust line count
184                 slide->lines -= 1;
185
186                 // delete line
187                 (line->text->delete)(line->text);
188                 free(line);
189             }
190             line = line->next;
191         }
192         slide = slide->next;
193     }
194
195     return deck;
196 }
197
198 int markdown_analyse(cstring_t *text) {
199
200     int i = 0;      // increment
201     int bits = 0;   // markdown bits
202     int offset = 0; // text offset
203     int eol    = 0; // end of line
204
205     int equals = 0, hashes = 0,
206         stars  = 0, minus  = 0,
207         spaces = 0, other  = 0; // special character counts
208
209     // count leading spaces
210     offset = next_nonblank(text, 0);
211
212     // strip trailing spaces
213     for(eol = text->size; eol > offset && isspace(text->text[eol - 1]); eol--);
214
215     // IS_CODE
216     if(offset >= CODE_INDENT) {
217         SET_BIT(bits, IS_CODE);
218     }
219
220     for(i = offset; i < eol; i++) {
221
222         if(text->text[i] == ' ') {
223             spaces++;
224
225         } else if(CHECK_BIT(bits, IS_CODE)) {
226             other++;
227
228         } else {
229             switch(text->text[i]) {
230                 case '=': equals++;  break;
231                 case '#': hashes++;  break;
232                 case '*': stars++;   break;
233                 case '-': minus++;   break;
234                 case '\\': other++; i++; break;
235                 default:  other++;   break;
236             }
237         }
238     }
239
240     // IS_H1
241     if((equals > 0 &&
242         hashes + stars + minus + spaces + other == 0) ||
243        (text &&
244         text->text &&
245         text->text[offset] == '#' &&
246         text->text[offset+1] != '#')) {
247
248         SET_BIT(bits, IS_H1);
249     }
250
251     // IS_H2
252     if((minus > 0 &&
253         equals + hashes + stars + spaces + other == 0) ||
254        (text &&
255         text->text &&
256         text->text[offset] == '#' &&
257         text->text[offset+1] == '#')) {
258
259         SET_BIT(bits, IS_H2);
260     }
261
262     // IS_QUOTE
263     if(text &&
264        text->text &&
265        text->text[offset] == '>') {
266
267         SET_BIT(bits, IS_QUOTE);
268     }
269
270     // IS_HR
271     if((minus >= 3 && equals + hashes + stars + other == 0) ||
272        (stars >= 3 && equals + hashes + minus + other == 0)) {
273
274         SET_BIT(bits, IS_HR);
275     }
276
277     // IS_EMPTY
278     if(other == 0) {
279         SET_BIT(bits, IS_EMPTY);
280     }
281
282     return bits;
283 }
284
285 void markdown_debug(deck_t *deck, int debug) {
286
287     int sc = 0; // slide count
288     int lc = 0; // line count
289
290     int offset;
291     line_t *header;
292
293     if(debug == 1) {
294         fprintf(stderr, "headers: %i\nslides: %i\n", deck->headers, deck->slides);
295
296     } else if(debug > 1) {
297
298         // print header to STDERR
299         if(deck->header) {
300             header = deck->header;
301             while(header &&
302                 header->length > 0 &&
303                 header->text->text[0] == '%') {
304
305                 // skip descriptor word (e.g. %title:)
306                 offset = next_blank(header->text, 0) + 1;
307
308                 fprintf(stderr, "header: %s\n", &header->text->text[offset]);
309                 header = header->next;
310             }
311         }
312     }
313
314     slide_t *slide = deck->slide;
315     line_t *line;
316
317     // print slide/line count to STDERR
318     while(slide) {
319         sc++;
320
321         if(debug == 1) {
322             fprintf(stderr, "  slide %i: %i lines\n", sc, slide->lines);
323
324         } else if(debug > 1) {
325
326             // also print bits and line length
327             fprintf(stderr, "  slide %i:\n", sc);
328             line = slide->line;
329             lc = 0;
330             while(line) {
331                 lc++;
332                 fprintf(stderr, "    line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
333                 line = line->next;
334             }
335         }
336
337         slide = slide->next;
338     }
339 }
340
341 int is_utf8(char ch) {
342     return (ch & 0x80);
343 }
344
345 int length_utf8(char ch) {
346
347     int i = 0; // increment
348
349     while(ch & 0x80) {
350         i++;
351         ch <<= 1;
352     }
353
354     return i;
355 }
356
357 int next_nonblank(cstring_t *text, int i) {
358     while ((i < text->size) && isspace((text->text)[i]))
359         ++i;
360
361     return i;
362 };
363
364 int next_blank(cstring_t *text, int i) {
365     while ((i < text->size) && !isspace((text->text)[i]))
366         ++i;
367
368     return i;
369 };
370