no goto on our code
[smdp.git] / src / parser.c
1 /*
2  * Functions necessary to parse a file and transform its content into
3  * a deck of slides containing lines. All based on markdown formating
4  * rules.
5  * Copyright (C) 2016 Michael Goehler
6  *
7  * This file is part of mdp.
8  *
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.
13  *
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.
18  *
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/>.
21  *
22  */
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <wchar.h>
29 #include <wctype.h>
30 #include <string.h>
31
32 #include "parser.h"
33
34 // char entry translation table
35 static struct named_character_entity {
36     wchar_t        ucs;
37     const wchar_t *name;
38 } named_character_entities[] = {
39    { L'\x0022', L"quot" },
40    { L'\x0026', L"amp" },
41    { L'\x0027', L"apos" },
42    { L'\x003C', L"lt" },
43    { L'\x003E', L"gt" },
44    { L'\x00A2', L"cent" },
45    { L'\x00A3', L"pound" },
46    { L'\x00A5', L"yen" },
47    { L'\x00A7', L"sect" },
48    { L'\x00A9', L"copy" },
49    { L'\x00AA', L"laquo" },
50    { L'\x00AE', L"reg" },
51    { L'\x00B0', L"deg" },
52    { L'\x00B1', L"plusmn" },
53    { L'\x00B2', L"sup2" },
54    { L'\x00B3', L"sup3" },
55    { L'\x00B6', L"para" },
56    { L'\x00B9', L"sup1" },
57    { L'\x00BB', L"raquo" },
58    { L'\x00BC', L"frac14" },
59    { L'\x00BD', L"frac12" },
60    { L'\x00BE', L"frac34" },
61    { L'\x00D7', L"times" },
62    { L'\x00F7', L"divide" },
63    { L'\x2018', L"lsquo" },
64    { L'\x2019', L"rsquo" },
65    { L'\x201C', L"ldquo" },
66    { L'\x201D', L"rdquo" },
67    { L'\x2020', L"dagger" },
68    { L'\x2021', L"Dagger" },
69    { L'\x2022', L"bull" },
70    { L'\x2026', L"hellip" },
71    { L'\x2030', L"permil" },
72    { L'\x2032', L"prime" },
73    { L'\x2033', L"Prime" },
74    { L'\x2039', L"lsaquo" },
75    { L'\x203A', L"rsaquo" },
76    { L'\x20AC', L"euro" },
77    { L'\x2122', L"trade" },
78    { L'\x2190', L"larr" },
79    { L'\x2191', L"uarr" },
80    { L'\x2192', L"rarr" },
81    { L'\x2193', L"darr" },
82    { L'\x2194', L"harr" },
83    { L'\x21B5', L"crarr" },
84    { L'\x21D0', L"lArr" },
85    { L'\x21D1', L"uArr" },
86    { L'\x21D2', L"rArr" },
87    { L'\x21D3', L"dArr" },
88    { L'\x21D4', L"hArr" },
89    { L'\x221E', L"infin" },
90    { L'\x2261', L"equiv" },
91    { L'\x2308', L"lceil" },
92    { L'\x2309', L"rceil" },
93    { L'\x230A', L"lfloor" },
94    { L'\x230B', L"rfloor" },
95    { L'\x25CA', L"loz" },
96    { L'\x2660', L"spades" },
97    { L'\x2663', L"clubs" },
98    { L'\x2665', L"hearts" },
99    { L'\x2666', L"diams" },
100    { L'\0', NULL },
101 };
102
103 deck_t *markdown_load(FILE *input, int noexpand) {
104
105     wchar_t c = L'\0';    // char
106     int i = 0;    // increment
107     int hc = 0;   // header count
108     int lc = 0;   // line count
109     int sc = 1;   // slide count
110     int bits = 0; // markdown bits
111     int prev = 0; // markdown bits of previous line
112
113     deck_t *deck = new_deck();
114     slide_t *slide = deck->slide;
115     line_t *line = NULL;
116     line_t *tmp = NULL;
117     cstring_t *text = cstring_init();
118
119     // initialize bits as empty line
120     SET_BIT(bits, IS_EMPTY);
121
122     while ((c = fgetwc(input)) != WEOF) {
123         if (ferror(input)) {
124             fprintf(stderr, "markdown_load() failed to read input: %s\n", strerror(errno));
125             exit(EXIT_FAILURE);
126         }
127
128         if(c == L'\n') {
129
130             // markdown analyse
131             prev = bits;
132             bits = markdown_analyse(text, prev);
133
134             // if first line in file is markdown hr
135             if(!line && CHECK_BIT(bits, IS_HR)) {
136
137                 // clear text
138                 (text->reset)(text);
139
140             } else if(line && CHECK_BIT(bits, IS_STOP)) {
141
142                 // set stop bit on last line
143                 SET_BIT(line->bits, IS_STOP);
144
145                 // clear text
146                 (text->reset)(text);
147
148             // if text is markdown hr
149             } else if(CHECK_BIT(bits, IS_HR) &&
150                       CHECK_BIT(line->bits, IS_EMPTY)) {
151
152                 slide->lines = lc;
153
154                 // clear text
155                 (text->reset)(text);
156
157                 // create next slide
158                 slide = next_slide(slide);
159                 sc++;
160
161             } else if(CHECK_BIT(bits, IS_TILDE_CODE) &&
162                       CHECK_BIT(bits, IS_EMPTY)) {
163                 // remove tilde code markers
164                 (text->reset)(text);
165
166             } else {
167
168                 // if slide ! has line
169                 if(!slide->line || !line) {
170
171                     // create new line
172                     line = new_line();
173                     slide->line = line;
174                     lc = 1;
175
176                 } else {
177
178                     // create next line
179                     line = next_line(line);
180                     lc++;
181
182                 }
183
184                 // add text to line
185                 line->text = text;
186
187                 // add bits to line
188                 line->bits = bits;
189
190                 // calc offset
191                 line->offset = next_nonblank(text, 0);
192
193                 // expand character entities if enabled
194                 if(line->text->value &&
195                    !noexpand &&
196                    !CHECK_BIT(line->bits, IS_CODE))
197                     expand_character_entities(line);
198
199                 // adjust line length dynamicaly - excluding markup
200                 if(line->text->value)
201                     adjust_line_length(line);
202
203                 // new text
204                 text = cstring_init();
205             }
206
207         } else if(c == L'\t') {
208
209             // expand tab to spaces
210             for (i = 0;  i < EXPAND_TABS;  i++) {
211                 (text->expand)(text, L' ');
212             }
213
214         } else if(c == L'\\') {
215
216             // add char to line
217             (text->expand)(text, c);
218
219             // if !IS_CODE add next char to line
220             // and do not increase line count
221             if(next_nonblank(text, 0) < CODE_INDENT) {
222
223                 c = fgetwc(input);
224                 (text->expand)(text, c);
225             }
226
227         } else if(iswprint(c) || iswspace(c)) {
228
229             // add char to line
230             (text->expand)(text, c);
231         }
232     }
233     (text->delete)(text);
234
235     slide->lines = lc;
236     deck->slides = sc;
237
238     // detect header
239     line = deck->slide->line;
240     if(line && line->text->size > 0 && line->text->value[0] == L'%') {
241
242         // assign header to deck
243         deck->header = line;
244
245         // find first non-header line
246         while(line && line->text->size > 0 && line->text->value[0] == L'%') {
247             hc++;
248             line = line->next;
249         }
250
251         // only split header if any non-header line is found
252         if(line) {
253
254             // split linked list
255             line->prev->next = NULL;
256             line->prev = NULL;
257
258             // remove header lines from slide
259             deck->slide->line = line;
260
261             // adjust counts
262             deck->headers += hc;
263             deck->slide->lines -= hc;
264         } else {
265
266             // remove header from deck
267             deck->header = NULL;
268         }
269     }
270
271     slide = deck->slide;
272     while(slide) {
273         line = slide->line;
274
275         // ignore mdpress format attributes
276         if(line &&
277            slide->lines > 1 &&
278            !CHECK_BIT(line->bits, IS_EMPTY) &&
279            line->text->value[line->offset] == L'=' &&
280            line->text->value[line->offset + 1] == L' ') {
281
282             // remove line from linked list
283             slide->line = line->next;
284             line->next->prev = NULL;
285
286             // maintain loop condition
287             tmp = line;
288             line = line->next;
289
290             // adjust line count
291             slide->lines -= 1;
292
293             // delete line
294             (tmp->text->delete)(tmp->text);
295             free(tmp);
296         }
297
298         while(line) {
299             // combine underlined H1/H2 in single line
300             if((CHECK_BIT(line->bits, IS_H1) ||
301                 CHECK_BIT(line->bits, IS_H2)) &&
302                CHECK_BIT(line->bits, IS_EMPTY) &&
303                line->prev &&
304                !CHECK_BIT(line->prev->bits, IS_EMPTY)) {
305
306
307                 // remove line from linked list
308                 line->prev->next = line->next;
309                 if(line->next)
310                     line->next->prev = line->prev;
311
312                 // set bits on previous line
313                 if(CHECK_BIT(line->bits, IS_H1)) {
314                     SET_BIT(line->prev->bits, IS_H1);
315                 } else {
316                     SET_BIT(line->prev->bits, IS_H2);
317                 }
318
319                 // adjust line count
320                 slide->lines -= 1;
321
322                 // maintain loop condition
323                 tmp = line;
324                 line = line->prev;
325
326                 // delete line
327                 (tmp->text->delete)(tmp->text);
328                 free(tmp);
329
330             // pass enclosing flag IS_UNORDERED_LIST_3
331             // to nested levels for unordered lists
332             } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_3)) {
333                 tmp = line->next;
334                 line_t *list_last_level_3 = line;
335
336                 while(tmp &&
337                       CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3)) {
338                     if(CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3)) {
339                         list_last_level_3 = tmp;
340                     }
341                     tmp = tmp->next;
342                 }
343
344                 for(tmp = line; tmp != list_last_level_3; tmp = tmp->next) {
345                     SET_BIT(tmp->bits, IS_UNORDERED_LIST_3);
346                 }
347
348             // pass enclosing flag IS_UNORDERED_LIST_2
349             // to nested levels for unordered lists
350             } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_2)) {
351                 tmp = line->next;
352                 line_t *list_last_level_2 = line;
353
354                 while(tmp &&
355                       (CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_2) ||
356                        CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3))) {
357                     if(CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_2)) {
358                         list_last_level_2 = tmp;
359                     }
360                     tmp = tmp->next;
361                 }
362
363                 for(tmp = line; tmp != list_last_level_2; tmp = tmp->next) {
364                     SET_BIT(tmp->bits, IS_UNORDERED_LIST_2);
365                 }
366
367             // pass enclosing flag IS_UNORDERED_LIST_1
368             // to nested levels for unordered lists
369             } else if(CHECK_BIT(line->bits, IS_UNORDERED_LIST_1)) {
370                 tmp = line->next;
371                 line_t *list_last_level_1 = line;
372
373                 while(tmp &&
374                       (CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_1) ||
375                        CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_2) ||
376                        CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_3))) {
377                     if(CHECK_BIT(tmp->bits, IS_UNORDERED_LIST_1)) {
378                         list_last_level_1 = tmp;
379                     }
380                     tmp = tmp->next;
381                 }
382
383                 for(tmp = line; tmp != list_last_level_1; tmp = tmp->next) {
384                     SET_BIT(tmp->bits, IS_UNORDERED_LIST_1);
385                 }
386             }
387
388             line = line->next;
389         }
390         slide = slide->next;
391     }
392
393     return deck;
394 }
395
396 int markdown_analyse(cstring_t *text, int prev) {
397
398     // static variables can not be redeclaired, but changed outside of a declaration
399     // the program remembers their value on every function calls
400     static int unordered_list_level = 0;
401     static int unordered_list_level_offset[] = {-1, -1, -1, -1};
402     static int num_tilde_characters = 0;
403
404     int i = 0;      // increment
405     int bits = 0;   // markdown bits
406     int offset = 0; // text offset
407     int eol    = 0; // end of line
408
409     int equals = 0, hashes = 0,
410         stars  = 0, minus  = 0,
411         spaces = 0, other  = 0; // special character counts
412
413     const int unordered_list_offset = unordered_list_level_offset[unordered_list_level];
414
415     // return IS_EMPTY on null pointers
416     if(!text || !text->value) {
417         SET_BIT(bits, IS_EMPTY);
418
419         // continue fenced code blocks across empty lines
420         if(num_tilde_characters > 0)
421             SET_BIT(bits, IS_CODE);
422
423         return bits;
424     }
425
426     // count leading spaces
427     offset = next_nonblank(text, 0);
428
429     // IS_TILDE_CODE
430     if (wcsncmp(text->value, L"~~~", 3) == 0) {
431         int tildes_in_line = next_nontilde(text, 0);
432         if (tildes_in_line >= num_tilde_characters) {
433             if (num_tilde_characters > 0) {
434                 num_tilde_characters = 0;
435             } else {
436                 num_tilde_characters = tildes_in_line;
437             }
438             SET_BIT(bits, IS_EMPTY);
439             SET_BIT(bits, IS_TILDE_CODE);
440             return bits;
441         }
442     }
443
444     if (num_tilde_characters > 0) {
445         SET_BIT(bits, IS_CODE);
446         SET_BIT(bits, IS_TILDE_CODE);
447         return bits;
448     }
449
450     // IS_STOP
451     if((offset < CODE_INDENT || !CHECK_BIT(prev, IS_CODE)) &&
452        (!wcsncmp(&text->value[offset], L"<br>", 4) ||
453         !wcsncmp(&text->value[offset], L"<BR>", 4) ||
454         !wcsncmp(&text->value[offset], L"^", 1))) {
455         SET_BIT(bits, IS_STOP);
456         return bits;
457     }
458
459     // strip trailing spaces
460     for(eol = text->size; eol > offset && iswspace(text->value[eol - 1]); eol--);
461
462     // IS_UNORDERED_LIST_#
463     if(text->size >= offset + 2 &&
464        (text->value[offset] == L'*' || text->value[offset] == L'-') &&
465        iswspace(text->value[offset + 1])) {
466
467         // if different from last lines offset
468         if(offset != unordered_list_offset) {
469
470             // test if offset matches a lower indent level
471             for(i = unordered_list_level; i >= 0; i--) {
472                 if(unordered_list_level_offset[i] == offset) {
473                     unordered_list_level = i;
474                     break;
475                 }
476             }
477             // if offset doesn't match any previously stored indent level
478             if(i != unordered_list_level) {
479                 unordered_list_level = MIN(unordered_list_level + 1, UNORDERED_LIST_MAX_LEVEL);
480                 // memorize the offset as next bigger indent level
481                 unordered_list_level_offset[unordered_list_level] = offset;
482             }
483         }
484
485         // if no previous indent level matches, this must be the first line of the list
486         if(unordered_list_level == 0) {
487             unordered_list_level = 1;
488             unordered_list_level_offset[1] = offset;
489         }
490
491         switch(unordered_list_level) {
492             case 1: SET_BIT(bits, IS_UNORDERED_LIST_1); break;
493             case 2: SET_BIT(bits, IS_UNORDERED_LIST_2); break;
494             case 3: SET_BIT(bits, IS_UNORDERED_LIST_3); break;
495             default: break;
496         }
497     }
498
499     if(!CHECK_BIT(bits, IS_UNORDERED_LIST_1) &&
500        !CHECK_BIT(bits, IS_UNORDERED_LIST_2) &&
501        !CHECK_BIT(bits, IS_UNORDERED_LIST_3)) {
502
503         // continue list if indent level is still the same as in previous line
504         if ((CHECK_BIT(prev, IS_UNORDERED_LIST_1) ||
505              CHECK_BIT(prev, IS_UNORDERED_LIST_2) ||
506              CHECK_BIT(prev, IS_UNORDERED_LIST_3)) &&
507             offset >= unordered_list_offset) {
508
509             switch(unordered_list_level) {
510                 case 1: SET_BIT(bits, IS_UNORDERED_LIST_1); break;
511                 case 2: SET_BIT(bits, IS_UNORDERED_LIST_2); break;
512                 case 3: SET_BIT(bits, IS_UNORDERED_LIST_3); break;
513                 default: break;
514             }
515
516             // this line extends the previous list item
517             SET_BIT(bits, IS_UNORDERED_LIST_EXT);
518
519         // or reset indent level
520         } else {
521             unordered_list_level = 0;
522         }
523     }
524
525     if(!CHECK_BIT(bits, IS_UNORDERED_LIST_1) &&
526        !CHECK_BIT(bits, IS_UNORDERED_LIST_2) &&
527        !CHECK_BIT(bits, IS_UNORDERED_LIST_3)) {
528
529         // IS_CODE
530         if(offset >= CODE_INDENT &&
531            (CHECK_BIT(prev, IS_EMPTY) ||
532             CHECK_BIT(prev, IS_CODE)  ||
533             CHECK_BIT(prev, IS_STOP))) {
534             SET_BIT(bits, IS_CODE);
535
536         } else {
537
538             // IS_QUOTE
539             if(text->value[offset] == L'>') {
540                 SET_BIT(bits, IS_QUOTE);
541             }
542
543             // IS_CENTER
544             if(text->size >= offset + 3 &&
545                text->value[offset] == L'-' &&
546                text->value[offset + 1] == L'>' &&
547                iswspace(text->value[offset + 2])) {
548                 SET_BIT(bits, IS_CENTER);
549
550                 // remove start tag
551                 (text->strip)(text, offset, 3);
552                 eol -= 3;
553
554                 if(text->size >= offset + 3 &&
555                    text->value[eol - 1] == L'-' &&
556                    text->value[eol - 2] == L'<' &&
557                    iswspace(text->value[eol - 3])) {
558
559                     // remove end tags
560                     (text->strip)(text, eol - 3, 3);
561
562                     // adjust end of line
563                     for(eol = text->size; eol > offset && iswspace(text->value[eol - 1]); eol--);
564
565                 }
566             }
567
568             for(i = offset; i < eol; i++) {
569
570                 if(iswspace(text->value[i])) {
571                     spaces++;
572
573                 } else {
574                     switch(text->value[i]) {
575                         case L'=': equals++;  break;
576                         case L'#': hashes++;  break;
577                         case L'*': stars++;   break;
578                         case L'-': minus++;   break;
579                         case L'\\': other++; i++; break;
580                         default:  other++;   break;
581                     }
582                 }
583             }
584
585             // IS_H1
586             if(equals > 0 &&
587                hashes + stars + minus + spaces + other == 0) {
588                 SET_BIT(bits, IS_H1);
589             }
590             if(text->value[offset] == L'#' &&
591                iswspace(text->value[offset+1])) {
592                 SET_BIT(bits, IS_H1);
593                 SET_BIT(bits, IS_H1_ATX);
594             }
595
596             // IS_H2
597             if(minus > 0 &&
598                equals + hashes + stars + spaces + other == 0) {
599                 SET_BIT(bits, IS_H2);
600             }
601             if(text->value[offset] == L'#' &&
602                text->value[offset+1] == L'#' &&
603                iswspace(text->value[offset+2])) {
604                 SET_BIT(bits, IS_H2);
605                 SET_BIT(bits, IS_H2_ATX);
606             }
607
608             // IS_HR
609             if((minus >= 3 && equals + hashes + stars + other == 0) ||
610                (stars >= 3 && equals + hashes + minus + other == 0)) {
611
612                 SET_BIT(bits, IS_HR);
613             }
614
615             // IS_EMPTY
616             if(other == 0) {
617                 SET_BIT(bits, IS_EMPTY);
618             }
619         }
620     }
621
622     return bits;
623 }
624
625 void markdown_debug(deck_t *deck, int debug) {
626
627     int sc = 0; // slide count
628     int lc = 0; // line count
629
630     int offset;
631     line_t *header;
632
633     if(debug == 1) {
634         fwprintf(stderr, L"headers: %i\nslides: %i\n", deck->headers, deck->slides);
635
636     } else if(debug > 1) {
637
638         // print header to STDERR
639         if(deck->header) {
640             header = deck->header;
641             while(header &&
642                 header->length > 0 &&
643                 header->text->value[0] == L'%') {
644
645                 // skip descriptor word (e.g. %title:)
646                 offset = next_blank(header->text, 0) + 1;
647
648                 fwprintf(stderr, L"header: %S\n", &header->text->value[offset]);
649                 header = header->next;
650             }
651         }
652     }
653
654     slide_t *slide = deck->slide;
655     line_t *line;
656
657     // print slide/line count to STDERR
658     while(slide) {
659         sc++;
660
661         if(debug == 1) {
662             fwprintf(stderr, L"  slide %i: %i lines\n", sc, slide->lines);
663
664         } else if(debug > 1) {
665
666             // also print bits and line length
667             fwprintf(stderr, L"  slide %i:\n", sc);
668             line = slide->line;
669             lc = 0;
670             while(line) {
671                 lc++;
672                 fwprintf(stderr, L"    line %i: bits = %i, length = %i\n", lc, line->bits, line->length);
673                 line = line->next;
674             }
675         }
676
677         slide = slide->next;
678     }
679 }
680
681 void expand_character_entities(line_t *line)
682 {
683     wchar_t *ampersand;
684     wchar_t *prev, *curr;
685
686     ampersand = NULL;
687     curr = &line->text->value[0];
688
689     // for each char in line
690     for(prev = NULL; *curr; prev = curr++) {
691         if (*curr == L'&' && (prev == NULL || *prev != L'\\')) {
692             ampersand = curr;
693             continue;
694         }
695         if (ampersand == NULL) {
696             continue;
697         }
698         if (*curr == L'#') {
699             if (prev == ampersand)
700                 continue;
701             ampersand = NULL;
702             continue;
703         }
704         if (iswalpha(*curr) || iswxdigit(*curr)) {
705             continue;
706         }
707         if (*curr == L';') {
708             int cnt;
709             wchar_t ucs = L'\0';
710             if (ampersand + 1 >= curr || ampersand + 16 < curr) { // what is a good limit?
711                 ampersand = NULL;
712                 continue;
713             }
714             if (ampersand[1] == L'#') { // &#nnnn; or &#xhhhh;
715                 if (ampersand + 2 >= curr) {
716                     ampersand = NULL;
717                     continue;
718                 }
719                 if (ampersand[2] != L'x') { // &#nnnn;
720                     cnt = wcsspn(&ampersand[2], L"0123456789");
721                     if (ampersand + 2 + cnt != curr) {
722                         ampersand = NULL;
723                         continue;
724                     }
725                     ucs = wcstoul(&ampersand[2], NULL, 10);
726                 } else { // &#xhhhh;
727                     if (ampersand + 3 >= curr) {
728                         ampersand = NULL;
729                         continue;
730                     }
731                     cnt = wcsspn(&ampersand[3], L"0123456789abcdefABCDEF");
732                     if (ampersand + 3 + cnt != curr) {
733                         ampersand = NULL;
734                         continue;
735                     }
736                     ucs = wcstoul(&ampersand[3], NULL, 16);
737                 }
738             } else { // &name;
739                 for (cnt = 0; cnt < sizeof(named_character_entities)/sizeof(named_character_entities[0]); ++cnt) {
740                     if (wcsncmp(named_character_entities[cnt].name, &ampersand[1], curr - ampersand - 1))
741                         continue;
742                     ucs = named_character_entities[cnt].ucs;
743                     break;
744                 }
745                 if (ucs == L'\0') {
746                     ampersand = NULL;
747                     continue;
748                 }
749             }
750             *ampersand = ucs;
751             cstring_strip(line->text, ampersand + 1 - &line->text->value[0], curr - ampersand);
752             curr = ampersand;
753             ampersand = NULL;
754         }
755     }
756 }
757
758 void adjust_line_length(line_t *line) {
759     int l = 0;
760     const static wchar_t *special = L"\\*_`"; // list of interpreted chars
761     const wchar_t *c = &line->text->value[0];
762     cstack_t *stack = cstack_init();
763
764     // for each char in line
765     for(; *c; c++) {
766         // if char is in special char list
767         if(wcschr(special, *c)) {
768
769             // closing special char (or second backslash)
770             if((stack->top)(stack, *c)) {
771                 if(*c == L'\\') l++;
772                 (stack->pop)(stack);
773
774             // treat special as regular char
775             } else if((stack->top)(stack, L'\\')) {
776                 l++;
777                 (stack->pop)(stack);
778
779             // opening special char
780             } else {
781                 (stack->push)(stack, *c);
782             }
783
784         } else {
785             // remove backslash from stack
786             if((stack->top)(stack, L'\\'))
787                 (stack->pop)(stack);
788             l++;
789         }
790     }
791
792     if(CHECK_BIT(line->bits, IS_H1_ATX))
793         l -= 2;
794     if(CHECK_BIT(line->bits, IS_H2_ATX))
795         l -= 3;
796
797     line->length = l;
798
799     (stack->delete)(stack);
800 }
801
802 int next_nonblank(cstring_t *text, int i) {
803     while ((i < text->size) && iswspace((text->value)[i]))
804         i++;
805
806     return i;
807 }
808
809 int prev_blank(cstring_t *text, int i) {
810     while ((i > 0) && !iswspace((text->value)[i]))
811         i--;
812
813     return i;
814 }
815
816 int next_blank(cstring_t *text, int i) {
817     while ((i < text->size) && !iswspace((text->value)[i]))
818         i++;
819
820     return i;
821 }
822
823 int next_word(cstring_t *text, int i) {
824     return next_nonblank(text, next_blank(text, i));
825 }
826
827 int next_nontilde(cstring_t *text, int i) {
828     while ((i < text->size) && text->value[i] == L'~')
829         i++;
830
831     return i;
832 }
833