Fossil SCM

Add enumeration lists and indented paragraphs in the wiki.

drh 2007-09-28 15:44 trunk
Commit ab637af752641f503f69e1a12ac6c129a8758b35
3 files changed +34 -12 +1 -1 +146 -89
+34 -12
--- ideas.txt
+++ ideas.txt
@@ -43,22 +43,44 @@
4343
* Three pages: creation, display, and edit
4444
* HTML
4545
* [[field]] to substitute the appropriate form or display element
4646
4747
------------------------------------------------------------------------
48
-Wiki header format:
49
- "WikiPage"
50
- parent: UUID*
51
- title: TEXT
52
- pagename: TEXT
53
- mode: (readonly|appendonly|readwrite)
54
- attachment: UUID name description
55
-
56
- * Header ends with a blank line. wiki content follows.
57
-
58
-Need a dephantomize algorithm
59
-
48
+Change to wiki:
49
+
50
+ A uuid filename description
51
+ D datetime
52
+ P uuid ...
53
+ U user
54
+ W wikipagename uuid
55
+ Z md5sum
56
+
57
+On page:
58
+
59
+ <title>....</title>
60
+ <readonly/>
61
+
62
+Hyperlinks:
63
+
64
+ [lowercasehex] /info/lowercasehex
65
+ [attachment.gif] inline image
66
+ [tagname] /info/tagname
67
+ [wikipagename] /wiki/wikipagename
68
+ [/internal/page] /internal/page
69
+ [http:...] external link
70
+
71
+Markup:
72
+
73
+ blank-line paragraph break
74
+ _*__ bullet
75
+ __ indentation
76
+ _#.__ enumeration
77
+ *text* bold
78
+ _text_ italic
79
+
80
+
81
+------------------------------------------------------------------------
6082
6183
Random thoughts:
6284
6385
* Plink.isprim changed to record:
6486
+ child is the principal descendent of parent. (1)
6587
--- ideas.txt
+++ ideas.txt
@@ -43,22 +43,44 @@
43 * Three pages: creation, display, and edit
44 * HTML
45 * [[field]] to substitute the appropriate form or display element
46
47 ------------------------------------------------------------------------
48 Wiki header format:
49 "WikiPage"
50 parent: UUID*
51 title: TEXT
52 pagename: TEXT
53 mode: (readonly|appendonly|readwrite)
54 attachment: UUID name description
55
56 * Header ends with a blank line. wiki content follows.
57
58 Need a dephantomize algorithm
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
61 Random thoughts:
62
63 * Plink.isprim changed to record:
64 + child is the principal descendent of parent. (1)
65
--- ideas.txt
+++ ideas.txt
@@ -43,22 +43,44 @@
43 * Three pages: creation, display, and edit
44 * HTML
45 * [[field]] to substitute the appropriate form or display element
46
47 ------------------------------------------------------------------------
48 Change to wiki:
49
50 A uuid filename description
51 D datetime
52 P uuid ...
53 U user
54 W wikipagename uuid
55 Z md5sum
56
57 On page:
58
59 <title>....</title>
60 <readonly/>
61
62 Hyperlinks:
63
64 [lowercasehex] /info/lowercasehex
65 [attachment.gif] inline image
66 [tagname] /info/tagname
67 [wikipagename] /wiki/wikipagename
68 [/internal/page] /internal/page
69 [http:...] external link
70
71 Markup:
72
73 blank-line paragraph break
74 _*__ bullet
75 __ indentation
76 _#.__ enumeration
77 *text* bold
78 _text_ italic
79
80
81 ------------------------------------------------------------------------
82
83 Random thoughts:
84
85 * Plink.isprim changed to record:
86 + child is the principal descendent of parent. (1)
87
+1 -1
--- src/wiki.c
+++ src/wiki.c
@@ -109,11 +109,11 @@
109109
}
110110
111111
/* Render the page */
112112
style_header(zTitle);
113113
blob_init(&page, z, -1);
114
- wiki_convert(&page, cgi_output_blob(), WIKI_HTML);
114
+ wiki_convert(&page, 0);
115115
blob_reset(&src);
116116
}else{
117117
style_header("Unknown Wiki Page");
118118
@ The wiki page "%h(zPageName)" does not exist.
119119
}
120120
--- src/wiki.c
+++ src/wiki.c
@@ -109,11 +109,11 @@
109 }
110
111 /* Render the page */
112 style_header(zTitle);
113 blob_init(&page, z, -1);
114 wiki_convert(&page, cgi_output_blob(), WIKI_HTML);
115 blob_reset(&src);
116 }else{
117 style_header("Unknown Wiki Page");
118 @ The wiki page "%h(zPageName)" does not exist.
119 }
120
--- src/wiki.c
+++ src/wiki.c
@@ -109,11 +109,11 @@
109 }
110
111 /* Render the page */
112 style_header(zTitle);
113 blob_init(&page, z, -1);
114 wiki_convert(&page, 0);
115 blob_reset(&src);
116 }else{
117 style_header("Unknown Wiki Page");
118 @ The wiki page "%h(zPageName)" does not exist.
119 }
120
+146 -89
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -123,58 +123,58 @@
123123
** Except for MARKUP_INVALID, this must all be in alphabetical order
124124
** and in numerical sequence. The first markup type must be zero.
125125
** The value for MARKUP_XYZ must correspond to the <xyz> entry
126126
** in aAllowedMarkup[].
127127
*/
128
-#define MARKUP_INVALID 255
129
-#define MARKUP_A 0
130
-#define MARKUP_ADDRESS 1
131
-#define MARKUP_B 2
132
-#define MARKUP_BIG 3
133
-#define MARKUP_BLOCKQUOTE 4
134
-#define MARKUP_BR 5
135
-#define MARKUP_CENTER 6
136
-#define MARKUP_CITE 7
137
-#define MARKUP_CODE 8
138
-#define MARKUP_DD 9
139
-#define MARKUP_DFN 10
140
-#define MARKUP_DL 11
141
-#define MARKUP_DT 12
142
-#define MARKUP_EM 13
143
-#define MARKUP_FONT 14
144
-#define MARKUP_H1 15
145
-#define MARKUP_H2 16
146
-#define MARKUP_H3 17
147
-#define MARKUP_H4 18
148
-#define MARKUP_H5 19
149
-#define MARKUP_H6 20
150
-#define MARKUP_HR 21
151
-#define MARKUP_IMG 22
152
-#define MARKUP_I 23
153
-#define MARKUP_KBD 24
154
-#define MARKUP_LI 25
155
-#define MARKUP_NOBR 26
156
-#define MARKUP_NOWIKI 27
157
-#define MARKUP_OL 28
158
-#define MARKUP_P 29
159
-#define MARKUP_PRE 30
160
-#define MARKUP_S 31
161
-#define MARKUP_SAMP 32
162
-#define MARKUP_SMALL 33
163
-#define MARKUP_STRIKE 34
164
-#define MARKUP_STRONG 35
165
-#define MARKUP_SUB 36
166
-#define MARKUP_SUP 37
167
-#define MARKUP_TABLE 38
168
-#define MARKUP_TD 39
169
-#define MARKUP_TH 40
170
-#define MARKUP_TR 41
171
-#define MARKUP_TT 42
172
-#define MARKUP_U 43
173
-#define MARKUP_UL 44
174
-#define MARKUP_VAR 45
175
-#define MARKUP_VERBATIM 46
128
+#define MARKUP_INVALID 0
129
+#define MARKUP_A 1
130
+#define MARKUP_ADDRESS 2
131
+#define MARKUP_B 3
132
+#define MARKUP_BIG 4
133
+#define MARKUP_BLOCKQUOTE 5
134
+#define MARKUP_BR 6
135
+#define MARKUP_CENTER 7
136
+#define MARKUP_CITE 8
137
+#define MARKUP_CODE 9
138
+#define MARKUP_DD 10
139
+#define MARKUP_DFN 11
140
+#define MARKUP_DL 12
141
+#define MARKUP_DT 13
142
+#define MARKUP_EM 14
143
+#define MARKUP_FONT 15
144
+#define MARKUP_H1 16
145
+#define MARKUP_H2 17
146
+#define MARKUP_H3 18
147
+#define MARKUP_H4 19
148
+#define MARKUP_H5 20
149
+#define MARKUP_H6 21
150
+#define MARKUP_HR 22
151
+#define MARKUP_IMG 23
152
+#define MARKUP_I 24
153
+#define MARKUP_KBD 25
154
+#define MARKUP_LI 26
155
+#define MARKUP_NOBR 27
156
+#define MARKUP_NOWIKI 28
157
+#define MARKUP_OL 29
158
+#define MARKUP_P 30
159
+#define MARKUP_PRE 31
160
+#define MARKUP_S 32
161
+#define MARKUP_SAMP 33
162
+#define MARKUP_SMALL 34
163
+#define MARKUP_STRIKE 35
164
+#define MARKUP_STRONG 36
165
+#define MARKUP_SUB 37
166
+#define MARKUP_SUP 38
167
+#define MARKUP_TABLE 39
168
+#define MARKUP_TD 40
169
+#define MARKUP_TH 41
170
+#define MARKUP_TR 42
171
+#define MARKUP_TT 43
172
+#define MARKUP_U 44
173
+#define MARKUP_UL 45
174
+#define MARKUP_VAR 46
175
+#define MARKUP_VERBATIM 47
176176
177177
/*
178178
** The various markup is divided into the following types:
179179
*/
180180
#define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */
@@ -195,10 +195,11 @@
195195
const char *zName; /* Name of the markup */
196196
char iCode; /* The MARKUP_* code */
197197
short int iType; /* The MUTYPE_* code */
198198
int allowedAttr; /* Allowed attributes on this markup */
199199
} aMarkup[] = {
200
+ { 0, MARKUP_INVALID, 0, 0 },
200201
{ "a", MARKUP_A, MUTYPE_HYPERLINK, ATTR_HREF },
201202
{ "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 },
202203
{ "b", MARKUP_B, MUTYPE_FONT, 0 },
203204
{ "big", MARKUP_BIG, MUTYPE_FONT, 0 },
204205
{ "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 },
@@ -263,11 +264,11 @@
263264
/*
264265
** Use binary search to locate a tag in the aMarkup[] table.
265266
*/
266267
static int findTag(const char *z){
267268
int i, c, first, last;
268
- first = 0;
269
+ first = 1;
269270
last = sizeof(aMarkup)/sizeof(aMarkup[0]) - 1;
270271
while( first<=last ){
271272
i = (first+last)/2;
272273
c = strcmp(aMarkup[i].zName, z);
273274
if( c==0 ){
@@ -300,11 +301,28 @@
300301
*/
301302
#define AT_NEWLINE 0x001 /* At start of a line */
302303
#define AT_PARAGRAPH 0x002 /* At start of a paragraph */
303304
#define ALLOW_WIKI 0x004 /* Allow wiki markup */
304305
#define FONT_MARKUP_ONLY 0x008 /* Only allow MUTYPE_FONT markup */
305
-#define IN_LIST 0x010 /* Within <ul> */
306
+#define IN_LIST 0x010 /* Within wiki <ul> or <ol> */
307
+
308
+/*
309
+** Current state of the rendering engine
310
+*/
311
+typedef struct Renderer Renderer;
312
+struct Renderer {
313
+ Blob *pOut; /* Output appended to this blob */
314
+ int state; /* Flag that govern rendering */
315
+ int wikiList; /* Current wiki list type */
316
+ int inVerbatim; /* True in <verbatim> mode */
317
+ int preVerbState; /* Value of state prior to verbatim */
318
+ const char *zVerbatimId; /* The id= attribute of <verbatim> */
319
+ int nStack; /* Number of elements on the stack */
320
+ int nAlloc; /* Space allocated for aStack */
321
+ unsigned char *aStack; /* Open markup stack */
322
+};
323
+
306324
307325
/*
308326
** z points to a "<" character. Check to see if this is the start of
309327
** a valid markup. If it is, return the total number of characters in
310328
** the markup including the initial "<" and the terminating ">". If
@@ -410,10 +428,46 @@
410428
n++;
411429
}
412430
if( i<2 || isspace(z[n]) ) return 0;
413431
return n;
414432
}
433
+
434
+/*
435
+** Check to see if the z[] string is the beginning of a enumeration value.
436
+** If it is, return the length of the bullet text. Otherwise return 0.
437
+**
438
+** Syntax:
439
+** * a tab or two or more spaces
440
+** * one or more digits
441
+** * optional "."
442
+** * another tab or two or more additional spaces
443
+**
444
+*/
445
+static int enumLength(const char *z){
446
+ int i, n;
447
+ n = 0;
448
+ i = 0;
449
+ while( z[n]==' ' || z[n]=='\t' ){
450
+ if( z[n]=='\t' ) i++;
451
+ i++;
452
+ n++;
453
+ }
454
+ if( i<2 ) return 0;
455
+ for(i=0; isdigit(z[n]); i++, n++){}
456
+ if( i==0 ) return 0;
457
+ if( z[n]=='.' ){
458
+ n++;
459
+ }
460
+ i = 0;
461
+ while( z[n]==' ' || z[n]=='\t' ){
462
+ if( z[n]=='\t' ) i++;
463
+ i++;
464
+ n++;
465
+ }
466
+ if( i<2 || isspace(z[n]) ) return 0;
467
+ return n;
468
+}
415469
416470
/*
417471
** Check to see if the z[] string is the beginning of an indented
418472
** paragraph. If it is, return the length of the indent. Otherwise
419473
** return 0.
@@ -483,17 +537,15 @@
483537
n = bulletLength(z);
484538
if( n>0 ){
485539
*pTokenType = TOKEN_BULLET;
486540
return n;
487541
}
488
-#if 0
489542
n = enumLength(z);
490543
if( n>0 ){
491544
*pTokenType = TOKEN_ENUM;
492545
return n;
493546
}
494
-#endif
495547
}
496548
if( (state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){
497549
n = indentLength(z);
498550
if( n>0 ){
499551
*pTokenType = TOKEN_INDENT;
@@ -628,25 +680,10 @@
628680
n = strlen(z);
629681
z[n] = p->aAttr[i].cTerm;
630682
}
631683
}
632684
633
-/*
634
-** Current state of the rendering engine
635
-*/
636
-typedef struct Renderer Renderer;
637
-struct Renderer {
638
- Blob *pOut; /* Output appended to this blob */
639
- int state; /* Flag that govern rendering */
640
- int inVerbatim; /* True in <verbatim> mode */
641
- int preVerbState; /* Value of state prior to verbatim */
642
- const char *zVerbatimId; /* The id= attribute of <verbatim> */
643
- int nStack; /* Number of elements on the stack */
644
- int nAlloc; /* Space allocated for aStack */
645
- unsigned char *aStack; /* Open markup stack */
646
-};
647
-
648685
/*
649686
** Pop a single element off of the stack. As the element is popped,
650687
** output its end tag.
651688
*/
652689
static void popStack(Renderer *p){
@@ -686,23 +723,24 @@
686723
}
687724
688725
/*
689726
** Pop the stack until the top-most element of the stack
690727
** is an element that matches the type in iMask. Return
691
-** true on success. If the stack does not have an element
728
+** code of the markup element that is on left on top of the stack.
729
+** If the stack does not have an element
692730
** that matches iMask, then leave the stack unchanged and
693
-** return false.
731
+** return false (MARKUP_INVALID).
694732
*/
695733
static int backupToType(Renderer *p, int iMask){
696734
int i;
697735
for(i=p->nStack-1; i>=0 && (aMarkup[p->aStack[i]].iType&iMask)==0; i--){}
698736
if( i<0 ) return 0;
699737
i++;
700738
while( p->nStack>i ){
701739
popStack(p);
702740
}
703
- return 1;
741
+ return p->aStack[i-1];
704742
}
705743
706744
/*
707745
** Add missing markup in preparation for writing text.
708746
**
@@ -767,10 +805,14 @@
767805
while( z[0] ){
768806
n = nextToken(z, p->state, &tokenType);
769807
p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
770808
switch( tokenType ){
771809
case TOKEN_PARAGRAPH: {
810
+ if( p->wikiList ){
811
+ popStackToTag(p, p->wikiList);
812
+ p->wikiList = 0;
813
+ }
772814
blob_append(p->pOut, "\n\n<p>", -1);
773815
p->state |= AT_PARAGRAPH|AT_NEWLINE;
774816
popStackToTag(p, MARKUP_P);
775817
break;
776818
}
@@ -778,17 +820,41 @@
778820
blob_append(p->pOut, "\n", 1);
779821
p->state |= AT_NEWLINE;
780822
break;
781823
}
782824
case TOKEN_BULLET: {
783
- if( backupToType(p, MUTYPE_LIST)==0 ){
825
+ if( p->wikiList!=MARKUP_UL ){
826
+ if( p->wikiList ){
827
+ popStackToTag(p, p->wikiList);
828
+ }
784829
pushStack(p, MARKUP_UL);
785830
blob_append(p->pOut, "<ul>", 4);
831
+ p->wikiList = MARKUP_UL;
786832
}
787833
pushStack(p, MARKUP_LI);
788834
blob_append(p->pOut, "<li>", 4);
789835
break;
836
+ }
837
+ case TOKEN_ENUM: {
838
+ if( p->wikiList!=MARKUP_OL ){
839
+ if( p->wikiList ){
840
+ popStackToTag(p, p->wikiList);
841
+ }
842
+ pushStack(p, MARKUP_OL);
843
+ blob_append(p->pOut, "<ol>", 4);
844
+ p->wikiList = MARKUP_OL;
845
+ }
846
+ pushStack(p, MARKUP_LI);
847
+ blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z));
848
+ break;
849
+ }
850
+ case TOKEN_INDENT: {
851
+ assert( p->wikiList==0 );
852
+ pushStack(p, MARKUP_BLOCKQUOTE);
853
+ blob_append(p->pOut, "<blockquote>", -1);
854
+ p->wikiList = MARKUP_BLOCKQUOTE;
855
+ break;
790856
}
791857
case TOKEN_CHARACTER: {
792858
if( z[0]=='<' ){
793859
blob_append(p->pOut, "&lt;", 4);
794860
}else if( z[0]=='&' ){
@@ -904,33 +970,24 @@
904970
905971
/*
906972
** Transform the text in the pIn blob. Write the results
907973
** into the pOut blob. The pOut blob should already be
908974
** initialized. The output is merely appended to pOut.
909
-**
910
-** The transformations carried out depend on the ops flag:
911
-**
912
-** WIKI_NOFOLLOW
913
-**
914
-** * Add the nofollow attribute to external links
915
-**
916
-** WIKI_HTML
917
-**
918
-** * Convert wiki into HTML
919
-** * Remove <nowiki> and <verbatium>
920
-** * Convert & into &amp;
921
-** * Unrecognized markup and markup within <verbatim>
922
-** is converted into &lt;...&gt;
923
-** * Unauthorized attributes on markup are removed
975
+** If pOut is NULL, then the output is appended to the CGI
976
+** reply.
924977
*/
925
-void wiki_convert(Blob *pIn, Blob *pOut, int ops){
978
+void wiki_convert(Blob *pIn, Blob *pOut){
926979
char *z;
927980
Renderer renderer;
928981
929982
memset(&renderer, 0, sizeof(renderer));
930983
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
931
- renderer.pOut = pOut;
984
+ if( pOut ){
985
+ renderer.pOut = pOut;
986
+ }else{
987
+ renderer.pOut = cgi_output_blob();
988
+ }
932989
933990
z = blob_str(pIn);
934991
wiki_render(&renderer, z);
935992
while( renderer.nStack ){
936993
popStack(&renderer);
@@ -946,8 +1003,8 @@
9461003
void test_wiki_render(void){
9471004
Blob in, out;
9481005
if( g.argc!=3 ) usage("FILE");
9491006
blob_zero(&out);
9501007
blob_read_from_file(&in, g.argv[2]);
951
- wiki_convert(&in, &out, WIKI_HTML);
1008
+ wiki_convert(&in, &out);
9521009
blob_write_to_file(&out, "-");
9531010
}
9541011
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -123,58 +123,58 @@
123 ** Except for MARKUP_INVALID, this must all be in alphabetical order
124 ** and in numerical sequence. The first markup type must be zero.
125 ** The value for MARKUP_XYZ must correspond to the <xyz> entry
126 ** in aAllowedMarkup[].
127 */
128 #define MARKUP_INVALID 255
129 #define MARKUP_A 0
130 #define MARKUP_ADDRESS 1
131 #define MARKUP_B 2
132 #define MARKUP_BIG 3
133 #define MARKUP_BLOCKQUOTE 4
134 #define MARKUP_BR 5
135 #define MARKUP_CENTER 6
136 #define MARKUP_CITE 7
137 #define MARKUP_CODE 8
138 #define MARKUP_DD 9
139 #define MARKUP_DFN 10
140 #define MARKUP_DL 11
141 #define MARKUP_DT 12
142 #define MARKUP_EM 13
143 #define MARKUP_FONT 14
144 #define MARKUP_H1 15
145 #define MARKUP_H2 16
146 #define MARKUP_H3 17
147 #define MARKUP_H4 18
148 #define MARKUP_H5 19
149 #define MARKUP_H6 20
150 #define MARKUP_HR 21
151 #define MARKUP_IMG 22
152 #define MARKUP_I 23
153 #define MARKUP_KBD 24
154 #define MARKUP_LI 25
155 #define MARKUP_NOBR 26
156 #define MARKUP_NOWIKI 27
157 #define MARKUP_OL 28
158 #define MARKUP_P 29
159 #define MARKUP_PRE 30
160 #define MARKUP_S 31
161 #define MARKUP_SAMP 32
162 #define MARKUP_SMALL 33
163 #define MARKUP_STRIKE 34
164 #define MARKUP_STRONG 35
165 #define MARKUP_SUB 36
166 #define MARKUP_SUP 37
167 #define MARKUP_TABLE 38
168 #define MARKUP_TD 39
169 #define MARKUP_TH 40
170 #define MARKUP_TR 41
171 #define MARKUP_TT 42
172 #define MARKUP_U 43
173 #define MARKUP_UL 44
174 #define MARKUP_VAR 45
175 #define MARKUP_VERBATIM 46
176
177 /*
178 ** The various markup is divided into the following types:
179 */
180 #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */
@@ -195,10 +195,11 @@
195 const char *zName; /* Name of the markup */
196 char iCode; /* The MARKUP_* code */
197 short int iType; /* The MUTYPE_* code */
198 int allowedAttr; /* Allowed attributes on this markup */
199 } aMarkup[] = {
 
200 { "a", MARKUP_A, MUTYPE_HYPERLINK, ATTR_HREF },
201 { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 },
202 { "b", MARKUP_B, MUTYPE_FONT, 0 },
203 { "big", MARKUP_BIG, MUTYPE_FONT, 0 },
204 { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 },
@@ -263,11 +264,11 @@
263 /*
264 ** Use binary search to locate a tag in the aMarkup[] table.
265 */
266 static int findTag(const char *z){
267 int i, c, first, last;
268 first = 0;
269 last = sizeof(aMarkup)/sizeof(aMarkup[0]) - 1;
270 while( first<=last ){
271 i = (first+last)/2;
272 c = strcmp(aMarkup[i].zName, z);
273 if( c==0 ){
@@ -300,11 +301,28 @@
300 */
301 #define AT_NEWLINE 0x001 /* At start of a line */
302 #define AT_PARAGRAPH 0x002 /* At start of a paragraph */
303 #define ALLOW_WIKI 0x004 /* Allow wiki markup */
304 #define FONT_MARKUP_ONLY 0x008 /* Only allow MUTYPE_FONT markup */
305 #define IN_LIST 0x010 /* Within <ul> */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
307 /*
308 ** z points to a "<" character. Check to see if this is the start of
309 ** a valid markup. If it is, return the total number of characters in
310 ** the markup including the initial "<" and the terminating ">". If
@@ -410,10 +428,46 @@
410 n++;
411 }
412 if( i<2 || isspace(z[n]) ) return 0;
413 return n;
414 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
416 /*
417 ** Check to see if the z[] string is the beginning of an indented
418 ** paragraph. If it is, return the length of the indent. Otherwise
419 ** return 0.
@@ -483,17 +537,15 @@
483 n = bulletLength(z);
484 if( n>0 ){
485 *pTokenType = TOKEN_BULLET;
486 return n;
487 }
488 #if 0
489 n = enumLength(z);
490 if( n>0 ){
491 *pTokenType = TOKEN_ENUM;
492 return n;
493 }
494 #endif
495 }
496 if( (state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){
497 n = indentLength(z);
498 if( n>0 ){
499 *pTokenType = TOKEN_INDENT;
@@ -628,25 +680,10 @@
628 n = strlen(z);
629 z[n] = p->aAttr[i].cTerm;
630 }
631 }
632
633 /*
634 ** Current state of the rendering engine
635 */
636 typedef struct Renderer Renderer;
637 struct Renderer {
638 Blob *pOut; /* Output appended to this blob */
639 int state; /* Flag that govern rendering */
640 int inVerbatim; /* True in <verbatim> mode */
641 int preVerbState; /* Value of state prior to verbatim */
642 const char *zVerbatimId; /* The id= attribute of <verbatim> */
643 int nStack; /* Number of elements on the stack */
644 int nAlloc; /* Space allocated for aStack */
645 unsigned char *aStack; /* Open markup stack */
646 };
647
648 /*
649 ** Pop a single element off of the stack. As the element is popped,
650 ** output its end tag.
651 */
652 static void popStack(Renderer *p){
@@ -686,23 +723,24 @@
686 }
687
688 /*
689 ** Pop the stack until the top-most element of the stack
690 ** is an element that matches the type in iMask. Return
691 ** true on success. If the stack does not have an element
 
692 ** that matches iMask, then leave the stack unchanged and
693 ** return false.
694 */
695 static int backupToType(Renderer *p, int iMask){
696 int i;
697 for(i=p->nStack-1; i>=0 && (aMarkup[p->aStack[i]].iType&iMask)==0; i--){}
698 if( i<0 ) return 0;
699 i++;
700 while( p->nStack>i ){
701 popStack(p);
702 }
703 return 1;
704 }
705
706 /*
707 ** Add missing markup in preparation for writing text.
708 **
@@ -767,10 +805,14 @@
767 while( z[0] ){
768 n = nextToken(z, p->state, &tokenType);
769 p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
770 switch( tokenType ){
771 case TOKEN_PARAGRAPH: {
 
 
 
 
772 blob_append(p->pOut, "\n\n<p>", -1);
773 p->state |= AT_PARAGRAPH|AT_NEWLINE;
774 popStackToTag(p, MARKUP_P);
775 break;
776 }
@@ -778,17 +820,41 @@
778 blob_append(p->pOut, "\n", 1);
779 p->state |= AT_NEWLINE;
780 break;
781 }
782 case TOKEN_BULLET: {
783 if( backupToType(p, MUTYPE_LIST)==0 ){
 
 
 
784 pushStack(p, MARKUP_UL);
785 blob_append(p->pOut, "<ul>", 4);
 
786 }
787 pushStack(p, MARKUP_LI);
788 blob_append(p->pOut, "<li>", 4);
789 break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790 }
791 case TOKEN_CHARACTER: {
792 if( z[0]=='<' ){
793 blob_append(p->pOut, "&lt;", 4);
794 }else if( z[0]=='&' ){
@@ -904,33 +970,24 @@
904
905 /*
906 ** Transform the text in the pIn blob. Write the results
907 ** into the pOut blob. The pOut blob should already be
908 ** initialized. The output is merely appended to pOut.
909 **
910 ** The transformations carried out depend on the ops flag:
911 **
912 ** WIKI_NOFOLLOW
913 **
914 ** * Add the nofollow attribute to external links
915 **
916 ** WIKI_HTML
917 **
918 ** * Convert wiki into HTML
919 ** * Remove <nowiki> and <verbatium>
920 ** * Convert & into &amp;
921 ** * Unrecognized markup and markup within <verbatim>
922 ** is converted into &lt;...&gt;
923 ** * Unauthorized attributes on markup are removed
924 */
925 void wiki_convert(Blob *pIn, Blob *pOut, int ops){
926 char *z;
927 Renderer renderer;
928
929 memset(&renderer, 0, sizeof(renderer));
930 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
931 renderer.pOut = pOut;
 
 
 
 
932
933 z = blob_str(pIn);
934 wiki_render(&renderer, z);
935 while( renderer.nStack ){
936 popStack(&renderer);
@@ -946,8 +1003,8 @@
946 void test_wiki_render(void){
947 Blob in, out;
948 if( g.argc!=3 ) usage("FILE");
949 blob_zero(&out);
950 blob_read_from_file(&in, g.argv[2]);
951 wiki_convert(&in, &out, WIKI_HTML);
952 blob_write_to_file(&out, "-");
953 }
954
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -123,58 +123,58 @@
123 ** Except for MARKUP_INVALID, this must all be in alphabetical order
124 ** and in numerical sequence. The first markup type must be zero.
125 ** The value for MARKUP_XYZ must correspond to the <xyz> entry
126 ** in aAllowedMarkup[].
127 */
128 #define MARKUP_INVALID 0
129 #define MARKUP_A 1
130 #define MARKUP_ADDRESS 2
131 #define MARKUP_B 3
132 #define MARKUP_BIG 4
133 #define MARKUP_BLOCKQUOTE 5
134 #define MARKUP_BR 6
135 #define MARKUP_CENTER 7
136 #define MARKUP_CITE 8
137 #define MARKUP_CODE 9
138 #define MARKUP_DD 10
139 #define MARKUP_DFN 11
140 #define MARKUP_DL 12
141 #define MARKUP_DT 13
142 #define MARKUP_EM 14
143 #define MARKUP_FONT 15
144 #define MARKUP_H1 16
145 #define MARKUP_H2 17
146 #define MARKUP_H3 18
147 #define MARKUP_H4 19
148 #define MARKUP_H5 20
149 #define MARKUP_H6 21
150 #define MARKUP_HR 22
151 #define MARKUP_IMG 23
152 #define MARKUP_I 24
153 #define MARKUP_KBD 25
154 #define MARKUP_LI 26
155 #define MARKUP_NOBR 27
156 #define MARKUP_NOWIKI 28
157 #define MARKUP_OL 29
158 #define MARKUP_P 30
159 #define MARKUP_PRE 31
160 #define MARKUP_S 32
161 #define MARKUP_SAMP 33
162 #define MARKUP_SMALL 34
163 #define MARKUP_STRIKE 35
164 #define MARKUP_STRONG 36
165 #define MARKUP_SUB 37
166 #define MARKUP_SUP 38
167 #define MARKUP_TABLE 39
168 #define MARKUP_TD 40
169 #define MARKUP_TH 41
170 #define MARKUP_TR 42
171 #define MARKUP_TT 43
172 #define MARKUP_U 44
173 #define MARKUP_UL 45
174 #define MARKUP_VAR 46
175 #define MARKUP_VERBATIM 47
176
177 /*
178 ** The various markup is divided into the following types:
179 */
180 #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */
@@ -195,10 +195,11 @@
195 const char *zName; /* Name of the markup */
196 char iCode; /* The MARKUP_* code */
197 short int iType; /* The MUTYPE_* code */
198 int allowedAttr; /* Allowed attributes on this markup */
199 } aMarkup[] = {
200 { 0, MARKUP_INVALID, 0, 0 },
201 { "a", MARKUP_A, MUTYPE_HYPERLINK, ATTR_HREF },
202 { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 },
203 { "b", MARKUP_B, MUTYPE_FONT, 0 },
204 { "big", MARKUP_BIG, MUTYPE_FONT, 0 },
205 { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 },
@@ -263,11 +264,11 @@
264 /*
265 ** Use binary search to locate a tag in the aMarkup[] table.
266 */
267 static int findTag(const char *z){
268 int i, c, first, last;
269 first = 1;
270 last = sizeof(aMarkup)/sizeof(aMarkup[0]) - 1;
271 while( first<=last ){
272 i = (first+last)/2;
273 c = strcmp(aMarkup[i].zName, z);
274 if( c==0 ){
@@ -300,11 +301,28 @@
301 */
302 #define AT_NEWLINE 0x001 /* At start of a line */
303 #define AT_PARAGRAPH 0x002 /* At start of a paragraph */
304 #define ALLOW_WIKI 0x004 /* Allow wiki markup */
305 #define FONT_MARKUP_ONLY 0x008 /* Only allow MUTYPE_FONT markup */
306 #define IN_LIST 0x010 /* Within wiki <ul> or <ol> */
307
308 /*
309 ** Current state of the rendering engine
310 */
311 typedef struct Renderer Renderer;
312 struct Renderer {
313 Blob *pOut; /* Output appended to this blob */
314 int state; /* Flag that govern rendering */
315 int wikiList; /* Current wiki list type */
316 int inVerbatim; /* True in <verbatim> mode */
317 int preVerbState; /* Value of state prior to verbatim */
318 const char *zVerbatimId; /* The id= attribute of <verbatim> */
319 int nStack; /* Number of elements on the stack */
320 int nAlloc; /* Space allocated for aStack */
321 unsigned char *aStack; /* Open markup stack */
322 };
323
324
325 /*
326 ** z points to a "<" character. Check to see if this is the start of
327 ** a valid markup. If it is, return the total number of characters in
328 ** the markup including the initial "<" and the terminating ">". If
@@ -410,10 +428,46 @@
428 n++;
429 }
430 if( i<2 || isspace(z[n]) ) return 0;
431 return n;
432 }
433
434 /*
435 ** Check to see if the z[] string is the beginning of a enumeration value.
436 ** If it is, return the length of the bullet text. Otherwise return 0.
437 **
438 ** Syntax:
439 ** * a tab or two or more spaces
440 ** * one or more digits
441 ** * optional "."
442 ** * another tab or two or more additional spaces
443 **
444 */
445 static int enumLength(const char *z){
446 int i, n;
447 n = 0;
448 i = 0;
449 while( z[n]==' ' || z[n]=='\t' ){
450 if( z[n]=='\t' ) i++;
451 i++;
452 n++;
453 }
454 if( i<2 ) return 0;
455 for(i=0; isdigit(z[n]); i++, n++){}
456 if( i==0 ) return 0;
457 if( z[n]=='.' ){
458 n++;
459 }
460 i = 0;
461 while( z[n]==' ' || z[n]=='\t' ){
462 if( z[n]=='\t' ) i++;
463 i++;
464 n++;
465 }
466 if( i<2 || isspace(z[n]) ) return 0;
467 return n;
468 }
469
470 /*
471 ** Check to see if the z[] string is the beginning of an indented
472 ** paragraph. If it is, return the length of the indent. Otherwise
473 ** return 0.
@@ -483,17 +537,15 @@
537 n = bulletLength(z);
538 if( n>0 ){
539 *pTokenType = TOKEN_BULLET;
540 return n;
541 }
 
542 n = enumLength(z);
543 if( n>0 ){
544 *pTokenType = TOKEN_ENUM;
545 return n;
546 }
 
547 }
548 if( (state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){
549 n = indentLength(z);
550 if( n>0 ){
551 *pTokenType = TOKEN_INDENT;
@@ -628,25 +680,10 @@
680 n = strlen(z);
681 z[n] = p->aAttr[i].cTerm;
682 }
683 }
684
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685 /*
686 ** Pop a single element off of the stack. As the element is popped,
687 ** output its end tag.
688 */
689 static void popStack(Renderer *p){
@@ -686,23 +723,24 @@
723 }
724
725 /*
726 ** Pop the stack until the top-most element of the stack
727 ** is an element that matches the type in iMask. Return
728 ** code of the markup element that is on left on top of the stack.
729 ** If the stack does not have an element
730 ** that matches iMask, then leave the stack unchanged and
731 ** return false (MARKUP_INVALID).
732 */
733 static int backupToType(Renderer *p, int iMask){
734 int i;
735 for(i=p->nStack-1; i>=0 && (aMarkup[p->aStack[i]].iType&iMask)==0; i--){}
736 if( i<0 ) return 0;
737 i++;
738 while( p->nStack>i ){
739 popStack(p);
740 }
741 return p->aStack[i-1];
742 }
743
744 /*
745 ** Add missing markup in preparation for writing text.
746 **
@@ -767,10 +805,14 @@
805 while( z[0] ){
806 n = nextToken(z, p->state, &tokenType);
807 p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
808 switch( tokenType ){
809 case TOKEN_PARAGRAPH: {
810 if( p->wikiList ){
811 popStackToTag(p, p->wikiList);
812 p->wikiList = 0;
813 }
814 blob_append(p->pOut, "\n\n<p>", -1);
815 p->state |= AT_PARAGRAPH|AT_NEWLINE;
816 popStackToTag(p, MARKUP_P);
817 break;
818 }
@@ -778,17 +820,41 @@
820 blob_append(p->pOut, "\n", 1);
821 p->state |= AT_NEWLINE;
822 break;
823 }
824 case TOKEN_BULLET: {
825 if( p->wikiList!=MARKUP_UL ){
826 if( p->wikiList ){
827 popStackToTag(p, p->wikiList);
828 }
829 pushStack(p, MARKUP_UL);
830 blob_append(p->pOut, "<ul>", 4);
831 p->wikiList = MARKUP_UL;
832 }
833 pushStack(p, MARKUP_LI);
834 blob_append(p->pOut, "<li>", 4);
835 break;
836 }
837 case TOKEN_ENUM: {
838 if( p->wikiList!=MARKUP_OL ){
839 if( p->wikiList ){
840 popStackToTag(p, p->wikiList);
841 }
842 pushStack(p, MARKUP_OL);
843 blob_append(p->pOut, "<ol>", 4);
844 p->wikiList = MARKUP_OL;
845 }
846 pushStack(p, MARKUP_LI);
847 blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z));
848 break;
849 }
850 case TOKEN_INDENT: {
851 assert( p->wikiList==0 );
852 pushStack(p, MARKUP_BLOCKQUOTE);
853 blob_append(p->pOut, "<blockquote>", -1);
854 p->wikiList = MARKUP_BLOCKQUOTE;
855 break;
856 }
857 case TOKEN_CHARACTER: {
858 if( z[0]=='<' ){
859 blob_append(p->pOut, "&lt;", 4);
860 }else if( z[0]=='&' ){
@@ -904,33 +970,24 @@
970
971 /*
972 ** Transform the text in the pIn blob. Write the results
973 ** into the pOut blob. The pOut blob should already be
974 ** initialized. The output is merely appended to pOut.
975 ** If pOut is NULL, then the output is appended to the CGI
976 ** reply.
 
 
 
 
 
 
 
 
 
 
 
 
 
977 */
978 void wiki_convert(Blob *pIn, Blob *pOut){
979 char *z;
980 Renderer renderer;
981
982 memset(&renderer, 0, sizeof(renderer));
983 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
984 if( pOut ){
985 renderer.pOut = pOut;
986 }else{
987 renderer.pOut = cgi_output_blob();
988 }
989
990 z = blob_str(pIn);
991 wiki_render(&renderer, z);
992 while( renderer.nStack ){
993 popStack(&renderer);
@@ -946,8 +1003,8 @@
1003 void test_wiki_render(void){
1004 Blob in, out;
1005 if( g.argc!=3 ) usage("FILE");
1006 blob_zero(&out);
1007 blob_read_from_file(&in, g.argv[2]);
1008 wiki_convert(&in, &out);
1009 blob_write_to_file(&out, "-");
1010 }
1011

Keyboard Shortcuts

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