Fossil SCM

fossil-scm / src / creoleparser.c
Blame History Raw 1164 lines
1
/*
2
** Copyright (c) 2009 Robert Ledger
3
**
4
** {{{ License
5
**
6
** This program is free software; you can redistribute it and/or
7
** modify it under the terms of the GNU General Public
8
** License version 2 as published by the Free Software Foundation.
9
**
10
** This program is distributed in the hope that it will be useful,
11
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
** General Public License for more details.
14
**
15
** You should have received a copy of the GNU General Public
16
** License along with this library; if not, write to the
17
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
** Boston, MA 02111-1307, USA.
19
**
20
** Author contact information:
21
** [email protected]
22
** http://pytrash.co.uk
23
**}}}
24
*******************************************************************************
25
**
26
** This file contains code to render creole 1.0 formated text as html.
27
*/
28
#include <assert.h>
29
#include "config.h"
30
#include "creoleparser.h"
31
32
#if INTERFACE
33
#define HAVE_CREOLE_MACRO 1
34
#endif
35
36
//{{{ LOCAL INTERFACE
37
#if LOCAL_INTERFACE
38
39
#define POOL_CHUNK_SIZE 100
40
41
//{{{ KIND
42
#define KIND_ROOT 0x0000001
43
#define KIND_HORIZONTAL_RULE 0x0000002
44
#define KIND_HEADING 0x0000004
45
#define KIND_ORDERED_LIST 0x0000008
46
47
#define KIND_UNORDERED_LIST 0x0000010
48
#define KIND_PARAGRAPH 0x0000020
49
#define KIND_TABLE 0x0000040
50
#define KIND_NO_WIKI_BLOCK 0x0000080
51
52
#define KIND_PARA_BREAK 0x0000100
53
#define KIND_END_WIKI_MARKER 0x0000200
54
55
#define KIND_BOLD 0x0000400
56
#define KIND_ITALIC 0x0000800
57
#define KIND_SUPERSCRIPT 0x0001000
58
#define KIND_SUBSCRIPT 0x0002000
59
#define KIND_MONOSPACED 0x0004000
60
#define KIND_BREAK 0x0008000
61
62
#define KIND_TABLE_ROW 0x0010000
63
#define KIND_MACRO 0X0020000
64
//}}}
65
//{{{ MACRO
66
#define MACRO_NONE 0X0000000
67
#define MACRO_FOSSIL 0x0000001
68
#define MACRO_WIKI_CONTENTS 0X0000002
69
//}}}
70
//{{{ FLAG
71
// keep first four bits free (why?:)
72
#define FLAG_CENTER 0x0000100
73
#define FLAG_MACRO_BLOCK 0X0000200
74
//}}}
75
struct Node {//{{{
76
77
char *start;
78
char *end;
79
80
int kind;
81
int level;
82
int flags;
83
84
Node *parent;
85
Node *next;
86
Node *children;
87
88
};
89
//}}}
90
struct NodePool {//{{{
91
NodePool *next;
92
Node a[POOL_CHUNK_SIZE];
93
}
94
//}}}
95
struct Parser {//{{{
96
97
Blob *pOut; /* Output appended to this blob */
98
Renderer *r;
99
100
NodePool *pool;
101
int nFree;
102
103
Node *this;
104
Node *previous;
105
Node *list;
106
107
char *cursor;
108
109
int lineWasBlank;
110
int charCount;
111
112
Node *item;
113
Node *istack;
114
char *icursor;
115
char *iend;
116
117
int inLink;
118
int inTable;
119
int iesc;
120
121
Blob *iblob;
122
123
};
124
//}}}
125
126
#endif
127
128
const int KIND_LIST = (KIND_UNORDERED_LIST | KIND_ORDERED_LIST);
129
const int KIND_LIST_OR_PARAGRAPH = (KIND_PARAGRAPH | KIND_UNORDERED_LIST | KIND_ORDERED_LIST);
130
//}}}
131
132
//{{{ POOL MANAGEMENT
133
static Node *pool_new(Parser *p){
134
135
if ( p->pool == NULL || p->nFree == 0){
136
137
NodePool *temp = p->pool;
138
139
p->pool = malloc(sizeof(NodePool));
140
if( p->pool == NULL ) fossil_panic("out of memory");
141
142
p->pool->next = temp;
143
p->nFree = POOL_CHUNK_SIZE;
144
}
145
p->nFree -= 1;
146
Node *node = &(p->pool->a[p->nFree]);
147
memset(node, 0, sizeof(*node));
148
149
return node;
150
}
151
152
153
static void pool_free(Parser *p){
154
155
NodePool *temp;
156
157
while (p->pool != NULL){
158
temp = p->pool;
159
p->pool = temp->next;
160
free(temp);
161
}
162
163
}
164
//}}}
165
166
//{{{ Utility Methods
167
168
static char *cr_skipBlanks(Parser *p, char* z){//{{{
169
char *s = z;
170
while (z[0] == ' ' || z[0] == '\t') z++;
171
p->charCount = z - s;
172
return z;
173
}
174
//}}}
175
static int cr_countBlanks(Parser *p, char* z){//{{{
176
cr_skipBlanks(p, z);
177
return p->charCount;
178
}
179
//}}}
180
static char *cr_skipChars(Parser *p, char *z, char c){//{{{
181
char *s = z;
182
while (z[0] == c) z++;
183
p->charCount = z - s;
184
return z;
185
}
186
//}}}
187
static int cr_countChars(Parser *p, char *z, char c){//{{{
188
cr_skipChars(p, z, c);
189
return p->charCount;
190
}
191
//}}}
192
static char *cr_nextLine(Parser *p, char *z){//{{{
193
194
p->lineWasBlank = 1;
195
196
while (1){
197
198
switch (z[0]){
199
200
case '\r':
201
if (z[1] == '\n') {
202
z[0] = ' ';
203
return z + 2;
204
}
205
z[0] = '\n';
206
return z + 1;
207
208
case '\n':
209
return z + 1;
210
211
case '\t':
212
z[0] = ' ';
213
z++;
214
break;
215
216
case ' ':
217
z++;
218
break;
219
220
case '\0':
221
return z;
222
223
default:
224
p->lineWasBlank = 0;
225
z++;
226
}
227
}
228
}
229
//}}}
230
//}}}
231
232
233
//{{{ INLINE PARSER
234
235
static int cr_isEsc(Parser *p){//{{{
236
if (p->iesc){
237
blob_append(p->iblob, p->icursor, 1);
238
p->iesc = 0;
239
p->icursor += 1;
240
return 1;
241
}
242
return 0;
243
}
244
//}}}
245
static int cr_iOpen(Parser *p, int kind){//{{{
246
247
switch (kind){
248
249
case KIND_BOLD:
250
blob_append(p->iblob, "<strong>", 8);
251
return 1;
252
253
case KIND_ITALIC:
254
blob_append(p->iblob, "<em>", 4);
255
return 1;
256
257
case KIND_SUPERSCRIPT:
258
blob_append(p->iblob, "<sup>", 5);
259
return 1;
260
261
case KIND_SUBSCRIPT:
262
blob_append(p->iblob, "<sub>", 5);
263
return 1;
264
265
case KIND_MONOSPACED:
266
blob_append(p->iblob, "<tt>", 4);
267
return 1;
268
}
269
return 0;
270
}
271
//}}}
272
static int cr_iClose(Parser *p, int kind){//{{{
273
274
switch (kind){
275
276
case KIND_BOLD:
277
blob_append(p->iblob, "</strong>", 9);
278
return 1;
279
280
case KIND_ITALIC:
281
blob_append(p->iblob, "</em>", 5);
282
return 1;
283
284
case KIND_SUPERSCRIPT:
285
blob_append(p->iblob, "</sup>", 6);
286
return 1;
287
288
case KIND_SUBSCRIPT:
289
blob_append(p->iblob, "</sub>", 6);
290
return 1;
291
292
case KIND_MONOSPACED:
293
blob_append(p->iblob, "</tt>", 5);
294
return 1;
295
}
296
return 0;
297
}
298
//}}}
299
300
301
static void cr_iMarkup(Parser *p, int kind){//{{{
302
303
if (p->iesc) {
304
blob_append(p->iblob, p->icursor, 1);
305
p->icursor +=1;
306
p->iesc =0;
307
return;
308
}
309
310
if (p->icursor[1] != p->icursor[0]) {
311
blob_append(p->iblob, p->icursor, 1);
312
p->icursor +=1;
313
return;
314
}
315
316
p->icursor += 2;
317
318
if (kind & KIND_BREAK) {
319
blob_append(p->iblob, "<br />", 6);
320
return;
321
}
322
323
if (kind & KIND_ITALIC && p->icursor[-3] == ':'){
324
blob_append(p->iblob, "//", 2);
325
return;
326
}
327
328
Node *n = p->istack;
329
330
int found = 0;
331
while (n) {
332
if (n->kind & kind) {
333
found = 1;
334
break;
335
}
336
n = n->next;
337
}
338
339
if (!found) {
340
n = pool_new(p);
341
n->kind = kind;
342
n->next = p->istack;
343
p->istack = n;
344
345
assert(cr_iOpen(p, kind));
346
return;
347
};
348
349
n= p->istack;
350
while (n){
351
p->istack = n->next;
352
353
assert(cr_iClose(p, n->kind));
354
355
if (kind == n->kind) return;
356
n = p->istack;
357
}
358
}
359
//}}}
360
static int cr_iNoWiki(Parser *p){//{{{
361
362
if ((p->iend - p->icursor)<6) return 0;
363
364
if (p->icursor[1]!='{' || p->icursor[2]!='{')
365
return 0;
366
367
char *s = p->icursor + 3;
368
369
int count = p->iend - p->icursor - 3;
370
while (count--){
371
if (s[0]=='}' && s[1]=='}' && s[2]=='}' && s[3]!='}'){
372
blob_appendf(p->iblob, "<tt class='creole-inline-nowiki'>%s</tt>", htmlize(p->icursor + 3, s - p->icursor-3));
373
p->icursor = s + 3;
374
return 1;
375
}
376
s++;
377
}
378
return 0;
379
}
380
381
//}}}
382
static int cr_iImage(Parser *p){//{{{
383
384
if (p->inLink) return 0;
385
if ((p->iend - p->icursor)<3) return 0;
386
387
if (p->icursor[1]!='{') return 0;
388
389
char *s = p->icursor + 2;
390
char *bar = NULL;
391
392
int count = p->iend - p->icursor - 4;
393
while (count--){
394
if (s[0]=='}' && s[1]=='}'){
395
if (!bar) bar = p->icursor + 2;
396
blob_appendf(p->iblob, "<span class='creole-noimage'>%s</span>", htmlize(bar, s - bar ));
397
p->icursor = s + 2;
398
return 1;
399
}
400
if (!bar && s[0]=='|') bar=s+1;
401
s++;
402
}
403
return 0;
404
}
405
//}}}
406
static int cr_iMacro(Parser *p){//{{{
407
408
if (p->inLink) return 0;
409
if ((p->iend - p->icursor)<3) return 0;
410
411
if (p->icursor[1]!='<') return 0;
412
413
char *s = p->icursor + 2;
414
415
int count = p->iend - p->icursor - 3;
416
while (count--){
417
if (s[0]=='>' && s[1]=='>'){
418
blob_appendf(p->iblob, "<span class='creole-nomacro'>%s</span>", htmlize(p->icursor, s - p->icursor + 2));
419
p->icursor = s + 2;
420
return 1;
421
}
422
s++;
423
}
424
return 0;
425
426
}
427
//}}}
428
429
static void cr_renderLink(Parser *p, char *s, char *bar, char *e){//{{{
430
431
int tsize = bar-s;
432
int dsize = e - bar-1;
433
434
if (tsize < 1) return;
435
if (dsize < 1) dsize = 0;
436
437
char zTarget[tsize + 1];
438
memcpy(zTarget, s, tsize);
439
zTarget[tsize] = '\0';
440
441
char zClose[20];
442
443
Blob *pOut = p->r->pOut;
444
445
p->r->pOut = p->iblob;
446
wf_openHyperlink(p->r, zTarget, zClose, sizeof(zClose));
447
p->r->pOut = pOut;
448
449
if (dsize)
450
cr_parseInline(p, bar+1, e) ;
451
else
452
blob_append(p->iblob, htmlize(s, tsize), -1);
453
blob_append(p->iblob, zClose, -1);
454
}
455
//}}}
456
457
static int cr_iLink(Parser *p){//{{{
458
459
if (p->inLink) return 0;
460
if ((p->iend - p->icursor)<3) return 0;
461
462
if (p->icursor[1]!='[') return 0;
463
464
char *s = p->icursor + 2;
465
char *bar = NULL;
466
467
int count = p->iend - p->icursor -3;
468
while (count--){
469
if (s[0]==']' && s[1]==']'){
470
if (!bar) bar = s;
471
p->inLink = 1;
472
cr_renderLink(p, p->icursor+2, bar, s);
473
p->inLink = 0;
474
p->icursor = s + 2;
475
return 1;
476
}
477
if (!bar && s[0]=='|') bar=s;
478
s++;
479
}
480
return 0;
481
}
482
//}}}
483
484
LOCAL char *cr_parseInline(Parser *p, char *s, char *e){//{{{
485
486
int save_iesc = p->iesc;
487
char *save_iend = p->iend;
488
Node *save_istack = p->istack;
489
490
p->iesc = 0;
491
p->iend = e;
492
p->istack = NULL;
493
494
p->icursor = s;
495
496
char *eof = NULL;
497
while (!eof && p->icursor < p->iend ){
498
499
switch (*p->icursor) {//{{{
500
501
case '~':
502
if (p->iesc) {
503
blob_append(p->iblob, "~", 1);
504
p->iesc = 0;
505
}
506
p->iesc = !p->iesc;
507
p->icursor+=1;
508
break;
509
510
case '*':
511
cr_iMarkup(p, KIND_BOLD);
512
break;
513
514
case '/':
515
cr_iMarkup(p, KIND_ITALIC);
516
break;
517
518
case '^':
519
cr_iMarkup(p, KIND_SUPERSCRIPT);
520
break;
521
522
case ',':
523
cr_iMarkup(p, KIND_SUBSCRIPT);
524
break;
525
526
case '#':
527
cr_iMarkup(p, KIND_MONOSPACED);
528
break;
529
530
case '\\':
531
cr_iMarkup(p, KIND_BREAK);
532
break;
533
534
case '{':
535
if (cr_isEsc(p)) break;
536
if (cr_iNoWiki(p)) break;
537
if (cr_iImage(p)) break;
538
blob_append(p->iblob, p->icursor, 1);
539
p->icursor += 1;
540
break;
541
542
case '[':
543
if (cr_isEsc(p)) break;
544
if (cr_iLink(p)) break;
545
blob_append(p->iblob, p->icursor, 1);
546
p->icursor += 1;
547
break;
548
549
550
case '<':
551
if (cr_isEsc(p)) break;
552
if (cr_iMacro(p)) break;
553
554
blob_append(p->iblob, "&lt;", 4);
555
p->icursor += 1;
556
break;
557
558
case '>':
559
if (p->iesc) {
560
blob_append(p->iblob, "~", 1);
561
p->iesc = 0;
562
}
563
blob_append(p->iblob, "&gt;", 4);
564
p->icursor += 1;
565
break;
566
567
case '&':
568
if (p->iesc) {
569
blob_append(p->iblob, "~", 1);
570
p->iesc = 0;
571
}
572
blob_append(p->iblob, "&amp;", 5);
573
p->icursor += 1;
574
break;
575
576
case '|':
577
if (p->inTable){
578
if (p->iesc) {
579
blob_append(p->iblob, p->icursor, 1);
580
p->iesc = 0;
581
p->icursor += 1;
582
break;
583
}
584
eof = p->icursor + 1;
585
break;
586
}
587
// fall through to default
588
589
default:
590
if (p->iesc) {
591
blob_append(p->iblob, "~", 1);
592
p->iesc = 0;
593
}
594
blob_append(p->iblob, p->icursor, 1);
595
p->icursor +=1;
596
}//}}}
597
598
}
599
600
while (p->istack){
601
cr_iClose(p, p->istack->kind);
602
p->istack = p->istack->next;
603
}
604
605
p->iesc = save_iesc;
606
p->iend = save_iend;
607
p->istack = save_istack;
608
609
return eof;
610
611
}
612
//}}}
613
//}}}
614
615
//{{{ BLOCK PARSER
616
617
static void cr_renderListItem(Parser *p, Node *n){//{{{
618
619
620
blob_append(p->iblob, "<li>", 4);
621
cr_parseInline(p, n->start, n->end);
622
623
if (n->children){
624
625
int ord = (n->children->kind & KIND_ORDERED_LIST);
626
627
if (ord) blob_append(p->iblob, "<ol>", 4);
628
else blob_append(p->iblob, "<ul>", 4);
629
630
n = n->children;
631
while (n){
632
cr_renderListItem(p, n);
633
n = n->next;
634
}
635
636
if (ord) blob_append(p->iblob, "</ol>", 5);
637
else blob_append(p->iblob, "</ul>", 5);
638
}
639
blob_append(p->iblob, "</li>", 5);
640
}
641
//}}}
642
static void cr_renderList(Parser *p){//{{{
643
644
Node *n = p->list;
645
646
while (n->parent !=n) n = n->parent;
647
648
int ord = (n->kind & KIND_ORDERED_LIST);
649
650
if (ord) blob_append(p->iblob, "\n\n<ol>", -1);
651
else blob_append(p->iblob, "\n\n<ul>", -1);
652
653
while (n) {
654
cr_renderListItem(p, n);
655
n = n->next;
656
}
657
658
if (ord) blob_append(p->iblob, "</ol>", 5);
659
else blob_append(p->iblob, "</ul>", 5);
660
}
661
662
//}}}
663
664
static void cr_renderTableRow(Parser *p, Node *row){//{{{
665
666
char *s = row->start;
667
int th;
668
669
blob_append(p->iblob, "\n<tr>", -1);
670
671
while (s && s < row->end){
672
673
if ((th = *s == '=')) {
674
s++;
675
blob_append(p->iblob, "<th>", -1);
676
}
677
else {
678
blob_append(p->iblob, "<td>", -1);
679
}
680
681
s = cr_parseInline(p, s, row->end);
682
683
if (th)
684
blob_append(p->iblob, "</th>\n", -1);
685
else
686
blob_append(p->iblob, "</td>\n", -1);
687
688
if (!s) break;
689
}
690
blob_append(p->iblob, "</tr>", 5);
691
}
692
//}}}
693
static void cr_renderTable(Parser *p, Node *n){//{{{
694
695
Node *row = n->children;
696
697
blob_append(p->iblob, "<table class='creoletable'>", -1);
698
p->inTable = 1;
699
while (row){
700
701
cr_renderTableRow(p, row);
702
row = row->next;
703
704
}
705
blob_append(p->iblob, "</table>", -1);
706
p->inTable = 0;
707
708
}
709
//}}}
710
711
static void cr_renderMacro(Parser *p, Node *n){//{{{
712
713
switch (n->level){
714
715
case MACRO_WIKI_CONTENTS:
716
do_macro_wiki_contents(p, n);
717
break;
718
719
}
720
721
}
722
//}}}
723
724
static void cr_render(Parser *p, Node *node){//{{{
725
726
if (node->kind & KIND_PARAGRAPH){
727
blob_append(p->iblob, "\n<p>", -1);
728
cr_parseInline(p, node->start, node->end );
729
blob_append(p->iblob, "</p>\n", -1 );
730
}
731
732
if (node->kind & KIND_HEADING){
733
blob_appendf(p->iblob,
734
"\n<h%d %s>",
735
node->level,
736
(node->flags & FLAG_CENTER) ? " style='text-align:center;'" : ""
737
);
738
cr_parseInline(p, node->start, node->end);
739
blob_appendf(p->iblob, "</h%d>\n", node->level );
740
return;
741
}
742
743
if (node->kind & KIND_MACRO){
744
cr_renderMacro(p, node);
745
return;
746
}
747
748
if (node->kind & KIND_HORIZONTAL_RULE){
749
blob_append(p->iblob, "<hr />", -1);
750
return;
751
}
752
753
if (node->kind & KIND_LIST){
754
cr_renderList(p);
755
p->list = NULL;
756
return;
757
}
758
759
if (node->kind & KIND_TABLE){
760
cr_renderTable(p, node);
761
return;
762
}
763
764
if (node->kind & KIND_NO_WIKI_BLOCK){
765
blob_appendf(p->iblob,
766
"\n<pre class='creole-block-nowiki'>%s</pre>\n",
767
htmlize( node->start, node->end - node->start)
768
);
769
}
770
}
771
//}}}
772
773
static char *cr_findEndOfBlock(Parser *p, char *s, char c){//{{{
774
775
char *end;
776
while (s[0]){
777
778
end = s;
779
if (s[0] == c && s[0] == c && s[0] == c) {
780
s = cr_nextLine(p, s + 3);
781
if (p->lineWasBlank) {
782
p->cursor = s;
783
return end;
784
}
785
}
786
else {
787
s = cr_nextLine(p, s);
788
}
789
}
790
return 0;
791
}
792
//}}}
793
static int cr_addListItem(Parser *p, Node *n){//{{{
794
795
n->parent = n;
796
n->next = n->children = NULL;
797
798
if (!p->list) {
799
if (n->level != 1) return 0;
800
p->list = n;
801
return 1;
802
}
803
804
Node *list = p->list;
805
806
while (n->level < list->level){
807
list = list->parent;
808
}
809
810
if (n->level == list->level){
811
812
if (n->kind != list->kind){
813
if (n->level>1) return 0;
814
cr_renderList(p);
815
p->list = n;
816
return 1;
817
}
818
n->parent = list->parent;
819
p->list = list->next = n;
820
return 1;
821
}
822
823
if ( (n->level - list->level) > 1 ) return 0;
824
n->parent = p->list;
825
p->list->children = n;
826
p->list = n;
827
return 1;
828
829
}
830
//}}}
831
832
static int isEndWikiMarker(Parser *p){//{{{
833
834
char *s = p->cursor;
835
if (memcmp(s, "<<fossil>>", 10)) return 0;
836
p->this->start = s;
837
p->this->kind = KIND_END_WIKI_MARKER;
838
p->cursor += 10;
839
return 1;
840
}
841
//}}}
842
static int isNoWikiBlock(Parser *p){//{{{
843
844
char *s = p->cursor;
845
846
if (s[0] != '{') return 0; s++;
847
if (s[0] != '{') return 0; s++;
848
if (s[0] != '{') return 0; s++;
849
850
s = cr_nextLine(p, s);
851
if (!p->lineWasBlank) return 0;
852
853
p->this->start = s;
854
855
s = cr_findEndOfBlock(p, s, '}');
856
857
if (!s) return 0;
858
859
// p->cursor was set by findEndOfBlock
860
p->this->kind = KIND_NO_WIKI_BLOCK;
861
p->this->end = s;
862
return 1;
863
}
864
865
//}}}
866
static int isParaBreak(Parser *p){//{{{
867
868
char *s = cr_nextLine(p, p->cursor);
869
if (!p->lineWasBlank) return 0;
870
871
p->cursor = s;
872
p->this->kind = KIND_PARA_BREAK;
873
return 1;
874
}
875
//}}}
876
static int isMacro(Parser *p){//{{{
877
878
char *s = p->cursor;
879
int macroId;
880
int matchLength;
881
882
/* This is succinct but somewhat hard to follow.
883
Read as: If not '<' then return, otherwise increment s;
884
Repeat above;
885
If '<' then return;
886
*/
887
if (s[0]!='<') return 0; s++;
888
if (s[0]!='<') return 0; s++;
889
if (s[0]=='<') return 0;
890
891
matchLength = cr_has_macro(s, &macroId);
892
if (!matchLength) return 0;
893
894
s += matchLength;
895
p->this->start = s;
896
897
if (s[-1]!='>'){
898
while (s[0] && s[1] && s[0]!='\n' && !(s[0]=='>' && s[1]=='>')) s++;
899
if (!(s[0] == '>' && s[1] == '>')) return 0;
900
s +=2;
901
}
902
p->cursor = s;
903
p->this->kind = KIND_MACRO;
904
p->this->level = macroId;
905
p->this->flags &= FLAG_MACRO_BLOCK;
906
p->this->end = s-2;
907
return 1;
908
}
909
//}}}
910
static int isHeading(Parser *p){//{{{
911
912
char *s = cr_skipBlanks(p, p->cursor);
913
914
int flags = 0;
915
int level = cr_countChars(p, s, '=');
916
if (!level) return 0;
917
918
s += level;
919
920
if (s[0] == '<' && s[1] == '>') {
921
flags |= FLAG_CENTER;
922
s += 2;
923
}
924
s = cr_skipBlanks(p, s);
925
926
p->this->start = s;
927
928
s = cr_nextLine(p, s);
929
char *z = s;
930
931
if (s[-1] == '\n') s--;
932
while(s[-1] == ' ' || s[-1]=='\t') s--;
933
while(s[-1] == '=' ) s--;
934
if (p->this->start < s){
935
p->cursor = z;
936
p->this->kind = KIND_HEADING;
937
p->this->end = s;
938
p->this->level = level;
939
p->this->flags |= flags;
940
return 1;
941
}
942
return 0;
943
}
944
//}}}
945
static int isHorizontalRule(Parser *p){//{{{
946
947
char *s = cr_skipBlanks(p, p->cursor);
948
949
int level = cr_countChars(p, s, '-');
950
951
if (level < 4) return 0;
952
s = cr_nextLine(p, s + level);
953
if (!p->lineWasBlank) return 0;
954
955
p->cursor = s;
956
p->this->kind = KIND_HORIZONTAL_RULE;
957
958
return 1;
959
}
960
//}}}
961
static int isListItem(Parser *p){//{{{
962
963
char *s = cr_skipBlanks(p, p->cursor);
964
965
int level = cr_countChars(p, s, '#');
966
if (!level) level = cr_countChars(p, s, '*');
967
968
if ( !level) return 0;
969
970
p->this->kind = (s[0] == '#') ? KIND_ORDERED_LIST : KIND_UNORDERED_LIST;
971
p->this->level = level;
972
973
s = cr_skipBlanks(p, s + level);
974
p->this->start = s;
975
976
s = cr_nextLine(p, s);
977
if (p->lineWasBlank) return 0;
978
979
if (cr_addListItem(p, p->this)){
980
p->cursor = p->this->end = s;
981
return 1;
982
}
983
p->this->kind = 0;
984
return 0;
985
}
986
//}}}
987
static int isTable(Parser *p){//{{{
988
989
p->this->start = p->cursor;
990
char *s = cr_skipBlanks(p, p->cursor);
991
if (s[0] != '|') return 0;
992
s +=1;
993
p->this->kind = KIND_TABLE;
994
995
996
//p->cursor = p->this->end = cr_nextLine(p, s);
997
Node *row;
998
Node *tail = NULL;
999
1000
while (1) {
1001
1002
row = pool_new(p);
1003
row->kind = KIND_TABLE_ROW;
1004
1005
if (tail) tail = tail->next = row;
1006
else p->this->children = tail = row;
1007
1008
row->start = s;
1009
p->cursor = s = row->end = p->this->end = cr_nextLine(p, s);
1010
1011
if (row->end[-1] == '\n') row->end -= 1;
1012
while(row->end[-1] == ' ' ) row->end -= 1;
1013
if (row->end[-1] == '|') row->end -= 1;
1014
1015
if (!*s) break;
1016
1017
// blanks *not* normalized
1018
s = cr_skipBlanks(p, p->cursor);
1019
if (s[0] != '|') break;
1020
s++;
1021
1022
}
1023
return 1;
1024
1025
};
1026
//}}}
1027
static int isParagraph(Parser *p){//{{{
1028
1029
char *s = p->cursor;
1030
p->this->start = s;
1031
1032
s = cr_nextLine(p, s);
1033
p->cursor = p->this->end = s;
1034
p->this->kind = KIND_PARAGRAPH;
1035
return 1;
1036
1037
}
1038
//}}}
1039
1040
static void cr_parse(Parser *p, char* z){//{{{
1041
1042
p->previous = pool_new(p);
1043
p->previous->kind = KIND_PARA_BREAK;
1044
1045
p->this = pool_new(p);
1046
p->this->kind = KIND_PARA_BREAK;
1047
1048
p->inLink = 0;
1049
p->inTable = 0;
1050
1051
p->cursor = z;
1052
p->list = NULL;
1053
p->istack = NULL;
1054
1055
while (p->cursor[0]) {
1056
1057
while (1){
1058
1059
// must be first
1060
if (isNoWikiBlock(p)) break;
1061
if (isParaBreak(p)) break;
1062
1063
// order not important
1064
if (isMacro(p)) break;
1065
if (isHeading(p)) break;
1066
if (isHorizontalRule(p)) break;
1067
if (isListItem(p)) break;
1068
if (isTable(p)) break;
1069
1070
// here for efficiency?
1071
if (isEndWikiMarker(p)) break;
1072
1073
// must be last
1074
if (isParagraph(p)); break;
1075
1076
// doh!
1077
assert(0);
1078
}
1079
1080
int kind = p->this->kind;
1081
int prev = p->previous->kind;
1082
1083
if (kind & KIND_END_WIKI_MARKER) return;
1084
1085
if (kind == KIND_PARAGRAPH && prev & KIND_LIST_OR_PARAGRAPH) {
1086
p->previous->end = p->this->end;
1087
p->this = pool_new(p);
1088
continue;
1089
}
1090
1091
if ( !(kind & KIND_LIST && prev & KIND_LIST) )
1092
cr_render(p, p->previous);
1093
1094
p->previous = p->this;
1095
p->this = pool_new(p);
1096
1097
}
1098
}
1099
//}}}
1100
1101
//}}}
1102
1103
//{{{ MACROS
1104
LOCAL void do_macro_wiki_contents(Parser *p, Node *n){//{{{
1105
1106
Stmt q;
1107
1108
blob_append(p->iblob, "<ul>", 4);
1109
1110
db_prepare(&q,
1111
"SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'"
1112
" ORDER BY lower(tagname)"
1113
);
1114
while( db_step(&q)==SQLITE_ROW ){
1115
const char *zName = db_column_text(&q, 0);
1116
blob_appendf(p->iblob, "<li><a href=\"%s/wiki?name=%T\">%h</a></li>", g.zBaseURL, zName, zName);
1117
}
1118
db_finalize(&q);
1119
blob_append(p->iblob, "</ul>", 5);
1120
}//}}}
1121
1122
1123
static int cr_match(char *z1, char *z2, int *len){
1124
*len = strlen(z2);
1125
return !memcmp(z1, z2 ,*len);
1126
}
1127
1128
int cr_has_macro(char *z, int *tokenType){
1129
1130
int len;
1131
1132
if (cr_match(z, "wiki-contents>>", &len)) {
1133
*tokenType = MACRO_WIKI_CONTENTS;
1134
return len;
1135
}
1136
1137
tokenType = MACRO_NONE;
1138
return 0;
1139
1140
}
1141
//}}}
1142
1143
char *wiki_render_creole(Renderer *r, char *z){
1144
1145
Parser parser;
1146
Parser *p = &parser;
1147
1148
p->r = r;
1149
p->iblob = r->pOut;
1150
1151
p->nFree = 0;
1152
p->pool = NULL;
1153
1154
cr_parse(p, z);
1155
1156
cr_render(p, p->previous);
1157
1158
pool_free(p);
1159
1160
return p->cursor;
1161
1162
}
1163
1164

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button