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