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/>.
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 first line in file is markdown hr
57 if(!line && CHECK_BIT(bits, IS_HR)) {
62 // if text is markdown hr
63 } else if(CHECK_BIT(bits, IS_HR) &&
64 CHECK_BIT(line->bits, IS_EMPTY)) {
73 slide = next_slide(slide);
78 // if slide ! has line
89 line = next_line(line);
100 // add length to line
104 line->offset = next_nonblank(text, 0);
107 text = cstring_init();
111 } else if(c == '\t') {
113 // expand tab to spaces
114 for (i = 0; i < EXPAND_TABS; i++) {
115 (text->expand)(text, ' ');
119 } else if(c == '\\') {
122 (text->expand)(text, c);
125 // if !IS_CODE add next char to line
126 // and do not increase line count
127 if(next_nonblank(text, 0) < CODE_INDENT) {
130 (text->expand)(text, c);
134 // if utf-8 char > 1 byte add remaing to line
135 for(i = 0; i < length_utf8(c) - 1; i++) {
137 (text->expand)(text, c);
143 } else if(isprint(c) || isspace((unsigned char) c)) {
146 (text->expand)(text, c);
149 } else if(is_utf8(c)) {
152 (text->expand)(text, c);
154 // if utf-8 char > 1 byte add remaing to line
155 for(i = 0; i < length_utf8(c) - 1; i++) {
157 (text->expand)(text, c);
168 line = deck->slide->line;
169 if(line && line->text->size > 0 && line->text->text[0] == '%') {
171 // assign header to deck
174 // find first non-header line
175 while(line->text->size > 0 && line->text->text[0] == '%') {
181 line->prev->next = (void*)0;
182 line->prev = (void*)0;
184 // remove header lines from slide
185 deck->slide->line = line;
189 deck->slide->lines -= hc;
192 // combine underlined H1/H2 in single line
197 if((CHECK_BIT(line->bits, IS_H1) ||
198 CHECK_BIT(line->bits, IS_H2)) &&
199 CHECK_BIT(line->bits, IS_EMPTY) &&
201 !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
203 // remove line from linked list
204 line->prev->next = line->next;
206 line->next->prev = line->prev;
208 // set bits on previous line
209 if(CHECK_BIT(line->bits, IS_H1)) {
210 SET_BIT(line->prev->bits, IS_H1);
212 SET_BIT(line->prev->bits, IS_H2);
218 // maintain loop condition
223 (tmp->text->delete)(tmp->text);
234 int markdown_analyse(cstring_t *text) {
236 int i = 0; // increment
237 int bits = 0; // markdown bits
238 int offset = 0; // text offset
239 int eol = 0; // end of line
241 int equals = 0, hashes = 0,
242 stars = 0, minus = 0,
243 spaces = 0, other = 0; // special character counts
245 // count leading spaces
246 offset = next_nonblank(text, 0);
248 // strip trailing spaces
249 for(eol = text->size; eol > offset && isspace((unsigned char) text->text[eol - 1]); eol--);
252 if(offset >= CODE_INDENT) {
253 SET_BIT(bits, IS_CODE);
256 for(i = offset; i < eol; i++) {
258 if(text->text[i] == ' ') {
261 } else if(CHECK_BIT(bits, IS_CODE)) {
265 switch(text->text[i]) {
266 case '=': equals++; break;
267 case '#': hashes++; break;
268 case '*': stars++; break;
269 case '-': minus++; break;
270 case '\\': other++; i++; break;
271 default: other++; break;
278 hashes + stars + minus + spaces + other == 0) ||
281 text->text[offset] == '#' &&
282 text->text[offset+1] != '#')) {
284 SET_BIT(bits, IS_H1);
289 equals + hashes + stars + spaces + other == 0) ||
292 text->text[offset] == '#' &&
293 text->text[offset+1] == '#')) {
295 SET_BIT(bits, IS_H2);
301 text->text[offset] == '>') {
303 SET_BIT(bits, IS_QUOTE);
307 if((minus >= 3 && equals + hashes + stars + other == 0) ||
308 (stars >= 3 && equals + hashes + minus + other == 0)) {
310 SET_BIT(bits, IS_HR);
315 SET_BIT(bits, IS_EMPTY);
321 void markdown_debug(deck_t *deck, int debug) {
323 int sc = 0; // slide count
324 int lc = 0; // line count
330 fprintf(stderr, "headers: %i\nslides: %i\n", deck->headers, deck->slides);
332 } else if(debug > 1) {
334 // print header to STDERR
336 header = deck->header;
338 header->length > 0 &&
339 header->text->text[0] == '%') {
341 // skip descriptor word (e.g. %title:)
342 offset = next_blank(header->text, 0) + 1;
344 fprintf(stderr, "header: %s\n", &header->text->text[offset]);
345 header = header->next;
350 slide_t *slide = deck->slide;
353 // print slide/line count to STDERR
358 fprintf(stderr, " slide %i: %i lines\n", sc, slide->lines);
360 } else if(debug > 1) {
362 // also print bits and line length
363 fprintf(stderr, " slide %i:\n", sc);
368 fprintf(stderr, " line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
377 int is_utf8(char ch) {
381 int length_utf8(char ch) {
383 int i = 0; // increment
393 int next_nonblank(cstring_t *text, int i) {
394 while ((i < text->size) && isspace((unsigned char) (text->text)[i]))
400 int next_blank(cstring_t *text, int i) {
401 while ((i < text->size) && !isspace((unsigned char) (text->text)[i]))
407 int next_word(cstring_t *text, int i) {
408 return next_nonblank(text, next_blank(text, i));