add missing markdown tags to file parser
[smdp.git] / markdown_io.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include "include/cstring.h"
5 #include "include/markdown.h"
6
7 document_t *markdown_load(FILE *input) {
8
9     int c = 0, i = 0, bits = 0;
10
11     document_t *doc;
12     page_t *page;
13     line_t *line;
14     cstring_t *text;
15
16     doc = new_document();
17     page = new_page();
18     doc->page = page;
19     text = cstring_init();
20
21     while ((c = fgetc(input)) != EOF) {
22         if(c == '\n') {
23
24             // markdown analyse
25             bits = markdown_analyse(text);
26
27             // if text is markdown hr
28             if(CHECK_BIT(bits, IS_HR) &&
29                CHECK_BIT(line->bits, IS_EMPTY)) {
30
31                 // clear text
32                 (text->reset)(text);
33                 // create next page
34                 page = next_page(page);
35
36             } else {
37
38                 // if page ! has line
39                 if(!page->line) {
40
41                     // create new line
42                     line = new_line();
43                     page->line = line;
44
45                 } else {
46
47                     // create next line
48                     line = next_line(line);
49
50                 }
51
52                 // add text to line
53                 line->text = text;
54
55                 // add bits to line
56                 line->bits = bits;
57
58                 // calc offset
59                 line->offset = next_nonblank(text, 0);
60
61                 // new text
62                 text = cstring_init();
63             }
64
65         } else if(c == '\t') {
66
67             // expand tab to spaces
68             for (i = 0;  i <= 4;  i++)
69                 (text->expand)(text, ' ');
70
71         } else if(isprint(c) || isspace(c) || is_utf8(c)) {
72
73             // add char to line
74             (text->expand)(text, c);
75         }
76     }
77
78     // detect header
79     line = doc->page->line;
80     if(line && line->text->size > 0 && line->text->text[0] == '%') {
81
82         // assign header to document
83         doc->header = line;
84
85         // find first non-header line
86         while(line->text->size > 0 && line->text->text[0] == '%') {
87             line = line->next;
88         }
89
90         // split linked list
91         line->prev->next = (void*)0;
92         line->prev = (void*)0;
93
94         // remove header lines from page
95         doc->page->line = line;
96     }
97
98     // combine underlined H1/H2 in single line
99     page = doc->page;
100     while(page) {
101         line = page->line;
102         while(line) {
103             if((CHECK_BIT(line->bits, IS_H1) ||
104                 CHECK_BIT(line->bits, IS_H2)) &&
105                CHECK_BIT(line->bits, IS_EMPTY) &&
106                line->prev &&
107                !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
108
109                 // remove line from linked list
110                 line->prev->next = line->next;
111                 line->next->prev = line->prev;
112
113                 // set bits on revious line
114                 if(CHECK_BIT(line->bits, IS_H1)) {
115                     SET_BIT(line->prev->bits, IS_H1);
116                 } else {
117                     SET_BIT(line->prev->bits, IS_H2);
118                 }
119
120                 // delete line
121                 (line->text->delete)(line->text);
122                 free(line);
123             }
124             line = line->next;
125         }
126         page = page->next;
127     }
128
129     return doc;
130 }
131
132 int markdown_analyse(cstring_t *text) {
133     int i = 0, bits = 0,
134         offset = 0, eol    = 0,
135         equals = 0, hashes = 0,
136         stars  = 0, minus  = 0,
137         spaces = 0, other  = 0;
138
139     // count leading spaces
140     offset = next_nonblank(text, 0);
141
142     // strip trailing spaces
143     for(eol = text->size; eol > offset && isspace(text->text[eol - 1]); eol--);
144
145     for(i = offset; i < eol; i++) {
146
147         switch(text->text[i]) {
148             case '=': equals++;  break;
149             case '#': hashes++;  break;
150             case '*': stars++;   break;
151             case '-': minus++;   break;
152             case ' ': spaces++;  break;
153             default:  other++;   break;
154         }
155     }
156
157     // IS_H1
158     if((equals > 0 &&
159         hashes + stars + minus + spaces + other == 0) ||
160        (text &&
161         text->text &&
162         text->text[offset] == '#' &&
163         text->text[offset+1] != '#')) {
164
165         SET_BIT(bits, IS_H1);
166     }
167
168     // IS_H2
169     if((minus > 0 &&
170         equals + hashes + stars + spaces + other == 0) ||
171        (text &&
172         text->text &&
173         text->text[offset] == '#' &&
174         text->text[offset+1] == '#')) {
175
176         SET_BIT(bits, IS_H2);
177     }
178
179     // IS_QUOTE
180     if(text &&
181        text->text &&
182        text->text[offset] == '>') {
183
184         SET_BIT(bits, IS_QUOTE);
185     }
186
187     // IS_CODE
188     if(offset >= 4) {
189         SET_BIT(bits, IS_CODE);
190     }
191
192     // IS_HR
193     if((minus >= 3 && equals + hashes + stars + other == 0) ||
194        (stars >= 3 && equals + hashes + minus + other == 0)) {
195
196         SET_BIT(bits, IS_HR);
197     }
198
199     // IS_EMPTY
200     if(other == 0) {
201         SET_BIT(bits, IS_EMPTY);
202     }
203
204     return bits;
205 }
206
207 int is_utf8(char ch) {
208     return (ch & 0x80);
209 }
210
211 int next_nonblank(cstring_t *text, int i) {
212     while ((i < text->size) && isspace((text->text)[i]))
213         ++i;
214     return i;
215 };
216
217 int next_blank(cstring_t *text, int i) {
218     while ((i < text->size) && !isspace((text->text)[i]))
219         ++i;
220     return i;
221 };
222