2 * Functions necessary to parse a file and transform its content into
3 * a deck of slides containing lines. All based on markdown formating
5 * Copyright (C) 2014 Michael Goehler
7 * This file is part of mdp.
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "include/parser.h"
30 deck_t *markdown_load(FILE *input) {
33 int i = 0; // increment
34 int l = 0; // line length
35 int hc = 0; // header count
36 int lc = 0; // line count
37 int sc = 0; // slide count
38 int bits = 0; // markdown bits
40 deck_t *deck = new_deck();
41 slide_t *slide = new_slide();
44 cstring_t *text = cstring_init();
46 // assign first slide to deck
50 while ((c = fgetc(input)) != EOF) {
54 bits = markdown_analyse(text);
56 // if text is markdown hr
57 if(CHECK_BIT(bits, IS_HR) &&
58 CHECK_BIT(line->bits, IS_EMPTY)) {
67 slide = next_slide(slide);
72 // if slide ! has line
83 line = next_line(line);
98 line->offset = next_nonblank(text, 0);
101 text = cstring_init();
105 } else if(c == '\t') {
107 // expand tab to spaces
108 for (i = 0; i < EXPAND_TABS; i++) {
109 (text->expand)(text, ' ');
113 } else if(c == '\\') {
116 (text->expand)(text, c);
119 // if !IS_CODE add next char to line
120 // and do not increase line count
121 if(next_nonblank(text, 0) < CODE_INDENT) {
124 (text->expand)(text, c);
128 // if utf-8 char > 1 byte add remaing to line
129 for(i = 0; i < length_utf8(c) - 1; i++) {
131 (text->expand)(text, c);
137 } else if(isprint(c) || isspace((unsigned char) c)) {
140 (text->expand)(text, c);
143 } else if(is_utf8(c)) {
146 (text->expand)(text, c);
148 // if utf-8 char > 1 byte add remaing to line
149 for(i = 0; i < length_utf8(c) - 1; i++) {
151 (text->expand)(text, c);
162 line = deck->slide->line;
163 if(line && line->text->size > 0 && line->text->text[0] == '%') {
165 // assign header to deck
168 // find first non-header line
169 while(line->text->size > 0 && line->text->text[0] == '%') {
175 line->prev->next = (void*)0;
176 line->prev = (void*)0;
178 // remove header lines from slide
179 deck->slide->line = line;
183 deck->slide->lines -= hc;
186 // combine underlined H1/H2 in single line
191 if((CHECK_BIT(line->bits, IS_H1) ||
192 CHECK_BIT(line->bits, IS_H2)) &&
193 CHECK_BIT(line->bits, IS_EMPTY) &&
195 !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
197 // remove line from linked list
198 line->prev->next = line->next;
200 line->next->prev = line->prev;
202 // set bits on previous line
203 if(CHECK_BIT(line->bits, IS_H1)) {
204 SET_BIT(line->prev->bits, IS_H1);
206 SET_BIT(line->prev->bits, IS_H2);
212 // maintain loop condition
217 (tmp->text->delete)(tmp->text);
228 int markdown_analyse(cstring_t *text) {
230 int i = 0; // increment
231 int bits = 0; // markdown bits
232 int offset = 0; // text offset
233 int eol = 0; // end of line
235 int equals = 0, hashes = 0,
236 stars = 0, minus = 0,
237 spaces = 0, other = 0; // special character counts
239 // count leading spaces
240 offset = next_nonblank(text, 0);
242 // strip trailing spaces
243 for(eol = text->size; eol > offset && isspace((unsigned char) text->text[eol - 1]); eol--);
246 if(offset >= CODE_INDENT) {
247 SET_BIT(bits, IS_CODE);
250 for(i = offset; i < eol; i++) {
252 if(text->text[i] == ' ') {
255 } else if(CHECK_BIT(bits, IS_CODE)) {
259 switch(text->text[i]) {
260 case '=': equals++; break;
261 case '#': hashes++; break;
262 case '*': stars++; break;
263 case '-': minus++; break;
264 case '\\': other++; i++; break;
265 default: other++; break;
272 hashes + stars + minus + spaces + other == 0) ||
275 text->text[offset] == '#' &&
276 text->text[offset+1] != '#')) {
278 SET_BIT(bits, IS_H1);
283 equals + hashes + stars + spaces + other == 0) ||
286 text->text[offset] == '#' &&
287 text->text[offset+1] == '#')) {
289 SET_BIT(bits, IS_H2);
295 text->text[offset] == '>') {
297 SET_BIT(bits, IS_QUOTE);
301 if((minus >= 3 && equals + hashes + stars + other == 0) ||
302 (stars >= 3 && equals + hashes + minus + other == 0)) {
304 SET_BIT(bits, IS_HR);
309 SET_BIT(bits, IS_EMPTY);
315 void markdown_debug(deck_t *deck, int debug) {
317 int sc = 0; // slide count
318 int lc = 0; // line count
324 fprintf(stderr, "headers: %i\nslides: %i\n", deck->headers, deck->slides);
326 } else if(debug > 1) {
328 // print header to STDERR
330 header = deck->header;
332 header->length > 0 &&
333 header->text->text[0] == '%') {
335 // skip descriptor word (e.g. %title:)
336 offset = next_blank(header->text, 0) + 1;
338 fprintf(stderr, "header: %s\n", &header->text->text[offset]);
339 header = header->next;
344 slide_t *slide = deck->slide;
347 // print slide/line count to STDERR
352 fprintf(stderr, " slide %i: %i lines\n", sc, slide->lines);
354 } else if(debug > 1) {
356 // also print bits and line length
357 fprintf(stderr, " slide %i:\n", sc);
362 fprintf(stderr, " line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
371 int is_utf8(char ch) {
375 int length_utf8(char ch) {
377 int i = 0; // increment
387 int next_nonblank(cstring_t *text, int i) {
388 while ((i < text->size) && isspace((unsigned char) (text->text)[i]))
394 int next_blank(cstring_t *text, int i) {
395 while ((i < text->size) && !isspace((unsigned char) (text->text)[i]))
401 int next_word(cstring_t *text, int i) {
402 return next_nonblank(text, next_blank(text, i));