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();
43 cstring_t *text = cstring_init();
45 // assign first slide to deck
49 while ((c = fgetc(input)) != EOF) {
53 bits = markdown_analyse(text);
55 // if text is markdown hr
56 if(CHECK_BIT(bits, IS_HR) &&
57 CHECK_BIT(line->bits, IS_EMPTY)) {
66 slide = next_slide(slide);
71 // if slide ! has line
82 line = next_line(line);
97 line->offset = next_nonblank(text, 0);
100 text = cstring_init();
104 } else if(c == '\t') {
106 // expand tab to spaces
107 for (i = 0; i < EXPAND_TABS; i++) {
108 (text->expand)(text, ' ');
112 } else if(c == '\\') {
115 (text->expand)(text, c);
118 // if !IS_CODE add next char to line
119 // and do not increase line count
120 if(next_nonblank(text, 0) < CODE_INDENT) {
123 (text->expand)(text, c);
127 // if utf-8 char > 1 byte add remaing to line
128 for(i = 0; i < length_utf8(c) - 1; i++) {
130 (text->expand)(text, c);
136 } else if(isprint(c) || isspace(c)) {
139 (text->expand)(text, c);
142 } else if(is_utf8(c)) {
145 (text->expand)(text, c);
147 // if utf-8 char > 1 byte add remaing to line
148 for(i = 0; i < length_utf8(c) - 1; i++) {
150 (text->expand)(text, c);
161 line = deck->slide->line;
162 if(line && line->text->size > 0 && line->text->text[0] == '%') {
164 // assign header to deck
167 // find first non-header line
168 while(line->text->size > 0 && line->text->text[0] == '%') {
174 line->prev->next = (void*)0;
175 line->prev = (void*)0;
177 // remove header lines from slide
178 deck->slide->line = line;
182 deck->slide->lines -= hc;
185 // combine underlined H1/H2 in single line
190 if((CHECK_BIT(line->bits, IS_H1) ||
191 CHECK_BIT(line->bits, IS_H2)) &&
192 CHECK_BIT(line->bits, IS_EMPTY) &&
194 !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
196 // remove line from linked list
197 line->prev->next = line->next;
199 line->next->prev = line->prev;
201 // set bits on revious line
202 if(CHECK_BIT(line->bits, IS_H1)) {
203 SET_BIT(line->prev->bits, IS_H1);
205 SET_BIT(line->prev->bits, IS_H2);
212 (line->text->delete)(line->text);
223 int markdown_analyse(cstring_t *text) {
225 int i = 0; // increment
226 int bits = 0; // markdown bits
227 int offset = 0; // text offset
228 int eol = 0; // end of line
230 int equals = 0, hashes = 0,
231 stars = 0, minus = 0,
232 spaces = 0, other = 0; // special character counts
234 // count leading spaces
235 offset = next_nonblank(text, 0);
237 // strip trailing spaces
238 for(eol = text->size; eol > offset && isspace(text->text[eol - 1]); eol--);
241 if(offset >= CODE_INDENT) {
242 SET_BIT(bits, IS_CODE);
245 for(i = offset; i < eol; i++) {
247 if(text->text[i] == ' ') {
250 } else if(CHECK_BIT(bits, IS_CODE)) {
254 switch(text->text[i]) {
255 case '=': equals++; break;
256 case '#': hashes++; break;
257 case '*': stars++; break;
258 case '-': minus++; break;
259 case '\\': other++; i++; break;
260 default: other++; break;
267 hashes + stars + minus + spaces + other == 0) ||
270 text->text[offset] == '#' &&
271 text->text[offset+1] != '#')) {
273 SET_BIT(bits, IS_H1);
278 equals + hashes + stars + spaces + other == 0) ||
281 text->text[offset] == '#' &&
282 text->text[offset+1] == '#')) {
284 SET_BIT(bits, IS_H2);
290 text->text[offset] == '>') {
292 SET_BIT(bits, IS_QUOTE);
296 if((minus >= 3 && equals + hashes + stars + other == 0) ||
297 (stars >= 3 && equals + hashes + minus + other == 0)) {
299 SET_BIT(bits, IS_HR);
304 SET_BIT(bits, IS_EMPTY);
310 void markdown_debug(deck_t *deck, int debug) {
312 int sc = 0; // slide count
313 int lc = 0; // line count
319 fprintf(stderr, "headers: %i\nslides: %i\n", deck->headers, deck->slides);
321 } else if(debug > 1) {
323 // print header to STDERR
325 header = deck->header;
327 header->length > 0 &&
328 header->text->text[0] == '%') {
330 // skip descriptor word (e.g. %title:)
331 offset = next_blank(header->text, 0) + 1;
333 fprintf(stderr, "header: %s\n", &header->text->text[offset]);
334 header = header->next;
339 slide_t *slide = deck->slide;
342 // print slide/line count to STDERR
347 fprintf(stderr, " slide %i: %i lines\n", sc, slide->lines);
349 } else if(debug > 1) {
351 // also print bits and line length
352 fprintf(stderr, " slide %i:\n", sc);
357 fprintf(stderr, " line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
366 int is_utf8(char ch) {
370 int length_utf8(char ch) {
372 int i = 0; // increment
382 int next_nonblank(cstring_t *text, int i) {
383 while ((i < text->size) && isspace((text->text)[i]))
389 int next_blank(cstring_t *text, int i) {
390 while ((i < text->size) && !isspace((text->text)[i]))
396 int next_word(cstring_t *text, int i) {
397 return next_nonblank(text, next_blank(text, i));