Fossil SCM

Enhance the wiki rendering to support <div> markup. The closing </div> tag with and id= attribute can close off <verbatim> and <nowiki>. The wiki append page uses this to prevent unclosed tags from messing up the rendering of subsequent comments.

drh 2008-07-17 15:49 trunk
Commit e01aa8cb4b23e512b16f6d0bd05646b578e5531e
2 files changed +9 -6 +282 -139
+9 -6
--- src/wiki.c
+++ src/wiki.c
@@ -292,11 +292,11 @@
292292
}
293293
for(n=2, z=zBody; z[0]; z++){
294294
if( z[0]=='\n' ) n++;
295295
}
296296
if( n<20 ) n = 20;
297
- if( n>200 ) n = 200;
297
+ if( n>40 ) n = 40;
298298
@ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
299299
@ <input type="hidden" name="name" value="%h(zPageName)">
300300
@ <textarea name="w" class="wikiedit" cols="80"
301301
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
302302
@ <br>
@@ -315,20 +315,23 @@
315315
*/
316316
static void appendRemark(Blob *p){
317317
char *zDate;
318318
const char *zUser;
319319
const char *zRemark;
320
+ char *zId;
320321
321322
zDate = db_text(0, "SELECT datetime('now')");
322
- blob_appendf(p, "\n\n<hr><i>On %s UTC %h", zDate, g.zLogin);
323
+ zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
324
+ blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h",
325
+ zId, zDate, g.zLogin);
323326
free(zDate);
324327
zUser = PD("u",g.zLogin);
325328
if( zUser[0] && strcmp(zUser,g.zLogin) ){
326329
blob_appendf(p, " (claiming to be %h)", zUser);
327330
}
328331
zRemark = PD("r","");
329
- blob_appendf(p, " added:</i><br />\n%s", zRemark);
332
+ blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
330333
}
331334
332335
/*
333336
** WEBPAGE: wikiappend
334337
** URL: /wikiappend?name=PAGENAME
@@ -352,11 +355,11 @@
352355
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
353356
" ORDER BY mtime DESC", zTag
354357
);
355358
free(zTag);
356359
if( !rid ){
357
- cgi_redirect("index");
360
+ fossil_redirect_home();
358361
return;
359362
}
360363
}
361364
if( !g.okApndWiki ){
362365
login_needed();
@@ -378,11 +381,11 @@
378381
db_set("sandbox", blob_str(&body), 0);
379382
}else{
380383
content_get(rid, &content);
381384
manifest_parse(&m, &content);
382385
if( m.type==CFTYPE_WIKI ){
383
- blob_appendf(&body, m.zWiki, -1);
386
+ blob_append(&body, m.zWiki, -1);
384387
}
385388
manifest_clear(&m);
386389
blob_zero(&wiki);
387390
db_begin_transaction();
388391
zDate = db_text(0, "SELECT datetime('now')");
@@ -395,11 +398,10 @@
395398
free(zUuid);
396399
}
397400
if( g.zLogin ){
398401
blob_appendf(&wiki, "U %F\n", g.zLogin);
399402
}
400
- blob_appendf(&body, "\n<hr>\n");
401403
appendRemark(&body);
402404
blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
403405
md5sum_blob(&wiki, &cksum);
404406
blob_appendf(&wiki, "Z %b\n", &cksum);
405407
blob_reset(&cksum);
@@ -555,10 +557,11 @@
555557
@ &lt;center&gt;
556558
@ &lt;cite&gt;
557559
@ &lt;code&gt;
558560
@ &lt;dd&gt;
559561
@ &lt;dfn&gt;
562
+ @ &lt;div&gt;
560563
@ &lt;dl&gt;
561564
@ &lt;dt&gt;
562565
@ &lt;em&gt;
563566
@ &lt;font&gt;
564567
@ &lt;h1&gt;
565568
--- src/wiki.c
+++ src/wiki.c
@@ -292,11 +292,11 @@
292 }
293 for(n=2, z=zBody; z[0]; z++){
294 if( z[0]=='\n' ) n++;
295 }
296 if( n<20 ) n = 20;
297 if( n>200 ) n = 200;
298 @ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
299 @ <input type="hidden" name="name" value="%h(zPageName)">
300 @ <textarea name="w" class="wikiedit" cols="80"
301 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
302 @ <br>
@@ -315,20 +315,23 @@
315 */
316 static void appendRemark(Blob *p){
317 char *zDate;
318 const char *zUser;
319 const char *zRemark;
 
320
321 zDate = db_text(0, "SELECT datetime('now')");
322 blob_appendf(p, "\n\n<hr><i>On %s UTC %h", zDate, g.zLogin);
 
 
323 free(zDate);
324 zUser = PD("u",g.zLogin);
325 if( zUser[0] && strcmp(zUser,g.zLogin) ){
326 blob_appendf(p, " (claiming to be %h)", zUser);
327 }
328 zRemark = PD("r","");
329 blob_appendf(p, " added:</i><br />\n%s", zRemark);
330 }
331
332 /*
333 ** WEBPAGE: wikiappend
334 ** URL: /wikiappend?name=PAGENAME
@@ -352,11 +355,11 @@
352 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
353 " ORDER BY mtime DESC", zTag
354 );
355 free(zTag);
356 if( !rid ){
357 cgi_redirect("index");
358 return;
359 }
360 }
361 if( !g.okApndWiki ){
362 login_needed();
@@ -378,11 +381,11 @@
378 db_set("sandbox", blob_str(&body), 0);
379 }else{
380 content_get(rid, &content);
381 manifest_parse(&m, &content);
382 if( m.type==CFTYPE_WIKI ){
383 blob_appendf(&body, m.zWiki, -1);
384 }
385 manifest_clear(&m);
386 blob_zero(&wiki);
387 db_begin_transaction();
388 zDate = db_text(0, "SELECT datetime('now')");
@@ -395,11 +398,10 @@
395 free(zUuid);
396 }
397 if( g.zLogin ){
398 blob_appendf(&wiki, "U %F\n", g.zLogin);
399 }
400 blob_appendf(&body, "\n<hr>\n");
401 appendRemark(&body);
402 blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
403 md5sum_blob(&wiki, &cksum);
404 blob_appendf(&wiki, "Z %b\n", &cksum);
405 blob_reset(&cksum);
@@ -555,10 +557,11 @@
555 @ &lt;center&gt;
556 @ &lt;cite&gt;
557 @ &lt;code&gt;
558 @ &lt;dd&gt;
559 @ &lt;dfn&gt;
 
560 @ &lt;dl&gt;
561 @ &lt;dt&gt;
562 @ &lt;em&gt;
563 @ &lt;font&gt;
564 @ &lt;h1&gt;
565
--- src/wiki.c
+++ src/wiki.c
@@ -292,11 +292,11 @@
292 }
293 for(n=2, z=zBody; z[0]; z++){
294 if( z[0]=='\n' ) n++;
295 }
296 if( n<20 ) n = 20;
297 if( n>40 ) n = 40;
298 @ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
299 @ <input type="hidden" name="name" value="%h(zPageName)">
300 @ <textarea name="w" class="wikiedit" cols="80"
301 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
302 @ <br>
@@ -315,20 +315,23 @@
315 */
316 static void appendRemark(Blob *p){
317 char *zDate;
318 const char *zUser;
319 const char *zRemark;
320 char *zId;
321
322 zDate = db_text(0, "SELECT datetime('now')");
323 zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
324 blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h",
325 zId, zDate, g.zLogin);
326 free(zDate);
327 zUser = PD("u",g.zLogin);
328 if( zUser[0] && strcmp(zUser,g.zLogin) ){
329 blob_appendf(p, " (claiming to be %h)", zUser);
330 }
331 zRemark = PD("r","");
332 blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
333 }
334
335 /*
336 ** WEBPAGE: wikiappend
337 ** URL: /wikiappend?name=PAGENAME
@@ -352,11 +355,11 @@
355 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
356 " ORDER BY mtime DESC", zTag
357 );
358 free(zTag);
359 if( !rid ){
360 fossil_redirect_home();
361 return;
362 }
363 }
364 if( !g.okApndWiki ){
365 login_needed();
@@ -378,11 +381,11 @@
381 db_set("sandbox", blob_str(&body), 0);
382 }else{
383 content_get(rid, &content);
384 manifest_parse(&m, &content);
385 if( m.type==CFTYPE_WIKI ){
386 blob_append(&body, m.zWiki, -1);
387 }
388 manifest_clear(&m);
389 blob_zero(&wiki);
390 db_begin_transaction();
391 zDate = db_text(0, "SELECT datetime('now')");
@@ -395,11 +398,10 @@
398 free(zUuid);
399 }
400 if( g.zLogin ){
401 blob_appendf(&wiki, "U %F\n", g.zLogin);
402 }
 
403 appendRemark(&body);
404 blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
405 md5sum_blob(&wiki, &cksum);
406 blob_appendf(&wiki, "Z %b\n", &cksum);
407 blob_reset(&cksum);
@@ -555,10 +557,11 @@
557 @ &lt;center&gt;
558 @ &lt;cite&gt;
559 @ &lt;code&gt;
560 @ &lt;dd&gt;
561 @ &lt;dfn&gt;
562 @ &lt;div&gt;
563 @ &lt;dl&gt;
564 @ &lt;dt&gt;
565 @ &lt;em&gt;
566 @ &lt;font&gt;
567 @ &lt;h1&gt;
568
+282 -139
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -39,66 +39,91 @@
3939
4040
4141
/*
4242
** These are the only markup attributes allowed.
4343
*/
44
-#define ATTR_ALIGN 0x0000001
45
-#define ATTR_ALT 0x0000002
46
-#define ATTR_BGCOLOR 0x0000004
47
-#define ATTR_BORDER 0x0000008
48
-#define ATTR_CELLPADDING 0x0000010
49
-#define ATTR_CELLSPACING 0x0000020
50
-#define ATTR_CLEAR 0x0000040
51
-#define ATTR_COLOR 0x0000080
52
-#define ATTR_COLSPAN 0x0000100
53
-#define ATTR_COMPACT 0x0000200
54
-#define ATTR_FACE 0x0000400
55
-#define ATTR_HEIGHT 0x0000800
56
-#define ATTR_HREF 0x0001000
57
-#define ATTR_HSPACE 0x0002000
58
-#define ATTR_ID 0x0004000
59
-#define ATTR_NAME 0x0008000
60
-#define ATTR_ROWSPAN 0x0010000
61
-#define ATTR_SIZE 0x0020000
62
-#define ATTR_SRC 0x0040000
63
-#define ATTR_START 0x0080000
64
-#define ATTR_TYPE 0x0100000
65
-#define ATTR_VALIGN 0x0200000
66
-#define ATTR_VALUE 0x0400000
67
-#define ATTR_VSPACE 0x0800000
68
-#define ATTR_WIDTH 0x1000000
44
+#define ATTR_ALIGN 1
45
+#define ATTR_ALT 2
46
+#define ATTR_BGCOLOR 3
47
+#define ATTR_BORDER 4
48
+#define ATTR_CELLPADDING 5
49
+#define ATTR_CELLSPACING 6
50
+#define ATTR_CLEAR 7
51
+#define ATTR_COLOR 8
52
+#define ATTR_COLSPAN 9
53
+#define ATTR_COMPACT 10
54
+#define ATTR_FACE 11
55
+#define ATTR_HEIGHT 12
56
+#define ATTR_HREF 13
57
+#define ATTR_HSPACE 14
58
+#define ATTR_ID 15
59
+#define ATTR_NAME 16
60
+#define ATTR_ROWSPAN 17
61
+#define ATTR_SIZE 18
62
+#define ATTR_SRC 19
63
+#define ATTR_START 20
64
+#define ATTR_TYPE 21
65
+#define ATTR_VALIGN 22
66
+#define ATTR_VALUE 23
67
+#define ATTR_VSPACE 24
68
+#define ATTR_WIDTH 25
69
+#define AMSK_ALIGN 0x0000001
70
+#define AMSK_ALT 0x0000002
71
+#define AMSK_BGCOLOR 0x0000004
72
+#define AMSK_BORDER 0x0000008
73
+#define AMSK_CELLPADDING 0x0000010
74
+#define AMSK_CELLSPACING 0x0000020
75
+#define AMSK_CLEAR 0x0000040
76
+#define AMSK_COLOR 0x0000080
77
+#define AMSK_COLSPAN 0x0000100
78
+#define AMSK_COMPACT 0x0000200
79
+#define AMSK_FACE 0x0000400
80
+#define AMSK_HEIGHT 0x0000800
81
+#define AMSK_HREF 0x0001000
82
+#define AMSK_HSPACE 0x0002000
83
+#define AMSK_ID 0x0004000
84
+#define AMSK_NAME 0x0008000
85
+#define AMSK_ROWSPAN 0x0010000
86
+#define AMSK_SIZE 0x0020000
87
+#define AMSK_SRC 0x0040000
88
+#define AMSK_START 0x0080000
89
+#define AMSK_TYPE 0x0100000
90
+#define AMSK_VALIGN 0x0200000
91
+#define AMSK_VALUE 0x0400000
92
+#define AMSK_VSPACE 0x0800000
93
+#define AMSK_WIDTH 0x1000000
6994
7095
static const struct AllowedAttribute {
7196
const char *zName;
7297
unsigned int iMask;
7398
} aAttribute[] = {
7499
{ 0, 0 },
75
- { "align", ATTR_ALIGN, },
76
- { "alt", ATTR_ALT, },
77
- { "bgcolor", ATTR_BGCOLOR, },
78
- { "border", ATTR_BORDER, },
79
- { "cellpadding", ATTR_CELLPADDING, },
80
- { "cellspacing", ATTR_CELLSPACING, },
81
- { "clear", ATTR_CLEAR, },
82
- { "color", ATTR_COLOR, },
83
- { "colspan", ATTR_COLSPAN, },
84
- { "compact", ATTR_COMPACT, },
85
- { "face", ATTR_FACE, },
86
- { "height", ATTR_HEIGHT, },
87
- { "href", ATTR_HREF, },
88
- { "hspace", ATTR_HSPACE, },
89
- { "id", ATTR_ID, },
90
- { "name", ATTR_NAME, },
91
- { "rowspan", ATTR_ROWSPAN, },
92
- { "size", ATTR_SIZE, },
93
- { "src", ATTR_SRC, },
94
- { "start", ATTR_START, },
95
- { "type", ATTR_TYPE, },
96
- { "valign", ATTR_VALIGN, },
97
- { "value", ATTR_VALUE, },
98
- { "vspace", ATTR_VSPACE, },
99
- { "width", ATTR_WIDTH, },
100
+ { "align", AMSK_ALIGN, },
101
+ { "alt", AMSK_ALT, },
102
+ { "bgcolor", AMSK_BGCOLOR, },
103
+ { "border", AMSK_BORDER, },
104
+ { "cellpadding", AMSK_CELLPADDING, },
105
+ { "cellspacing", AMSK_CELLSPACING, },
106
+ { "clear", AMSK_CLEAR, },
107
+ { "color", AMSK_COLOR, },
108
+ { "colspan", AMSK_COLSPAN, },
109
+ { "compact", AMSK_COMPACT, },
110
+ { "face", AMSK_FACE, },
111
+ { "height", AMSK_HEIGHT, },
112
+ { "href", AMSK_HREF, },
113
+ { "hspace", AMSK_HSPACE, },
114
+ { "id", AMSK_ID, },
115
+ { "name", AMSK_NAME, },
116
+ { "rowspan", AMSK_ROWSPAN, },
117
+ { "size", AMSK_SIZE, },
118
+ { "src", AMSK_SRC, },
119
+ { "start", AMSK_START, },
120
+ { "type", AMSK_TYPE, },
121
+ { "valign", AMSK_VALIGN, },
122
+ { "value", AMSK_VALUE, },
123
+ { "vspace", AMSK_VSPACE, },
124
+ { "width", AMSK_WIDTH, },
100125
};
101126
102127
/*
103128
** Use binary search to locate a tag in the aAttribute[] table.
104129
*/
@@ -140,46 +165,47 @@
140165
#define MARKUP_CENTER 7
141166
#define MARKUP_CITE 8
142167
#define MARKUP_CODE 9
143168
#define MARKUP_DD 10
144169
#define MARKUP_DFN 11
145
-#define MARKUP_DL 12
146
-#define MARKUP_DT 13
147
-#define MARKUP_EM 14
148
-#define MARKUP_FONT 15
149
-#define MARKUP_H1 16
150
-#define MARKUP_H2 17
151
-#define MARKUP_H3 18
152
-#define MARKUP_H4 19
153
-#define MARKUP_H5 20
154
-#define MARKUP_H6 21
155
-#define MARKUP_HR 22
156
-#define MARKUP_I 23
157
-#define MARKUP_IMG 24
158
-#define MARKUP_KBD 25
159
-#define MARKUP_LI 26
160
-#define MARKUP_NOBR 27
161
-#define MARKUP_NOWIKI 28
162
-#define MARKUP_OL 29
163
-#define MARKUP_P 30
164
-#define MARKUP_PRE 31
165
-#define MARKUP_S 32
166
-#define MARKUP_SAMP 33
167
-#define MARKUP_SMALL 34
168
-#define MARKUP_STRIKE 35
169
-#define MARKUP_STRONG 36
170
-#define MARKUP_SUB 37
171
-#define MARKUP_SUP 38
172
-#define MARKUP_TABLE 39
173
-#define MARKUP_TD 40
174
-#define MARKUP_TH 41
175
-#define MARKUP_TR 42
176
-#define MARKUP_TT 43
177
-#define MARKUP_U 44
178
-#define MARKUP_UL 45
179
-#define MARKUP_VAR 46
180
-#define MARKUP_VERBATIM 47
170
+#define MARKUP_DIV 12
171
+#define MARKUP_DL 13
172
+#define MARKUP_DT 14
173
+#define MARKUP_EM 15
174
+#define MARKUP_FONT 16
175
+#define MARKUP_H1 17
176
+#define MARKUP_H2 18
177
+#define MARKUP_H3 19
178
+#define MARKUP_H4 20
179
+#define MARKUP_H5 21
180
+#define MARKUP_H6 22
181
+#define MARKUP_HR 23
182
+#define MARKUP_I 24
183
+#define MARKUP_IMG 25
184
+#define MARKUP_KBD 26
185
+#define MARKUP_LI 27
186
+#define MARKUP_NOBR 28
187
+#define MARKUP_NOWIKI 29
188
+#define MARKUP_OL 30
189
+#define MARKUP_P 31
190
+#define MARKUP_PRE 32
191
+#define MARKUP_S 33
192
+#define MARKUP_SAMP 34
193
+#define MARKUP_SMALL 35
194
+#define MARKUP_STRIKE 36
195
+#define MARKUP_STRONG 37
196
+#define MARKUP_SUB 38
197
+#define MARKUP_SUP 39
198
+#define MARKUP_TABLE 40
199
+#define MARKUP_TD 41
200
+#define MARKUP_TH 42
201
+#define MARKUP_TR 43
202
+#define MARKUP_TT 44
203
+#define MARKUP_U 45
204
+#define MARKUP_UL 46
205
+#define MARKUP_VAR 47
206
+#define MARKUP_VERBATIM 48
181207
182208
/*
183209
** The various markup is divided into the following types:
184210
*/
185211
#define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */
@@ -209,71 +235,72 @@
209235
short int iType; /* The MUTYPE_* code */
210236
int allowedAttr; /* Allowed attributes on this markup */
211237
} aMarkup[] = {
212238
{ 0, MARKUP_INVALID, 0, 0 },
213239
{ "a", MARKUP_A, MUTYPE_HYPERLINK,
214
- ATTR_HREF|ATTR_NAME },
240
+ AMSK_HREF|AMSK_NAME },
215241
{ "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 },
216242
{ "b", MARKUP_B, MUTYPE_FONT, 0 },
217243
{ "big", MARKUP_BIG, MUTYPE_FONT, 0 },
218244
{ "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 },
219
- { "br", MARKUP_BR, MUTYPE_SINGLE, ATTR_CLEAR },
245
+ { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR },
220246
{ "center", MARKUP_CENTER, MUTYPE_BLOCK, 0 },
221247
{ "cite", MARKUP_CITE, MUTYPE_FONT, 0 },
222248
{ "code", MARKUP_CODE, MUTYPE_FONT, 0 },
223249
{ "dd", MARKUP_DD, MUTYPE_LI, 0 },
224250
{ "dfn", MARKUP_DFN, MUTYPE_FONT, 0 },
225
- { "dl", MARKUP_DL, MUTYPE_LIST, ATTR_COMPACT },
251
+ { "div", MARKUP_DIV, MUTYPE_BLOCK, AMSK_ID },
252
+ { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_COMPACT },
226253
{ "dt", MARKUP_DT, MUTYPE_LI, 0 },
227254
{ "em", MARKUP_EM, MUTYPE_FONT, 0 },
228255
{ "font", MARKUP_FONT, MUTYPE_FONT,
229
- ATTR_COLOR|ATTR_FACE|ATTR_SIZE },
230
- { "h1", MARKUP_H1, MUTYPE_BLOCK, ATTR_ALIGN },
231
- { "h2", MARKUP_H2, MUTYPE_BLOCK, ATTR_ALIGN },
232
- { "h3", MARKUP_H3, MUTYPE_BLOCK, ATTR_ALIGN },
233
- { "h4", MARKUP_H4, MUTYPE_BLOCK, ATTR_ALIGN },
234
- { "h5", MARKUP_H5, MUTYPE_BLOCK, ATTR_ALIGN },
235
- { "h6", MARKUP_H6, MUTYPE_BLOCK, ATTR_ALIGN },
256
+ AMSK_COLOR|AMSK_FACE|AMSK_SIZE },
257
+ { "h1", MARKUP_H1, MUTYPE_BLOCK, AMSK_ALIGN },
258
+ { "h2", MARKUP_H2, MUTYPE_BLOCK, AMSK_ALIGN },
259
+ { "h3", MARKUP_H3, MUTYPE_BLOCK, AMSK_ALIGN },
260
+ { "h4", MARKUP_H4, MUTYPE_BLOCK, AMSK_ALIGN },
261
+ { "h5", MARKUP_H5, MUTYPE_BLOCK, AMSK_ALIGN },
262
+ { "h6", MARKUP_H6, MUTYPE_BLOCK, AMSK_ALIGN },
236263
{ "hr", MARKUP_HR, MUTYPE_SINGLE,
237
- ATTR_ALIGN|ATTR_COLOR|ATTR_SIZE|ATTR_WIDTH },
264
+ AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH },
238265
{ "i", MARKUP_I, MUTYPE_FONT, 0 },
239266
{ "img", MARKUP_IMG, MUTYPE_SINGLE,
240
- ATTR_ALIGN|ATTR_ALT|ATTR_BORDER|ATTR_HEIGHT|
241
- ATTR_HSPACE|ATTR_SRC|ATTR_VSPACE|ATTR_WIDTH },
267
+ AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT|
268
+ AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH },
242269
{ "kbd", MARKUP_KBD, MUTYPE_FONT, 0 },
243270
{ "li", MARKUP_LI, MUTYPE_LI,
244
- ATTR_TYPE|ATTR_VALUE },
271
+ AMSK_TYPE|AMSK_VALUE },
245272
{ "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 },
246273
{ "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 },
247274
{ "ol", MARKUP_OL, MUTYPE_LIST,
248
- ATTR_START|ATTR_TYPE|ATTR_COMPACT },
249
- { "p", MARKUP_P, MUTYPE_BLOCK, ATTR_ALIGN },
275
+ AMSK_START|AMSK_TYPE|AMSK_COMPACT },
276
+ { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN },
250277
{ "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 },
251278
{ "s", MARKUP_S, MUTYPE_FONT, 0 },
252279
{ "samp", MARKUP_SAMP, MUTYPE_FONT, 0 },
253280
{ "small", MARKUP_SMALL, MUTYPE_FONT, 0 },
254281
{ "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 },
255282
{ "strong", MARKUP_STRONG, MUTYPE_FONT, 0 },
256283
{ "sub", MARKUP_SUB, MUTYPE_FONT, 0 },
257284
{ "sup", MARKUP_SUP, MUTYPE_FONT, 0 },
258285
{ "table", MARKUP_TABLE, MUTYPE_TABLE,
259
- ATTR_ALIGN|ATTR_BGCOLOR|ATTR_BORDER|ATTR_CELLPADDING|
260
- ATTR_CELLSPACING|ATTR_HSPACE|ATTR_VSPACE },
286
+ AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING|
287
+ AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE },
261288
{ "td", MARKUP_TD, MUTYPE_TD,
262
- ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN|
263
- ATTR_ROWSPAN|ATTR_VALIGN },
289
+ AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
290
+ AMSK_ROWSPAN|AMSK_VALIGN },
264291
{ "th", MARKUP_TH, MUTYPE_TD,
265
- ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN|
266
- ATTR_ROWSPAN|ATTR_VALIGN },
292
+ AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
293
+ AMSK_ROWSPAN|AMSK_VALIGN },
267294
{ "tr", MARKUP_TR, MUTYPE_TR,
268
- ATTR_ALIGN|ATTR_BGCOLOR||ATTR_VALIGN },
295
+ AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN },
269296
{ "tt", MARKUP_TT, MUTYPE_FONT, 0 },
270297
{ "u", MARKUP_U, MUTYPE_FONT, 0 },
271298
{ "ul", MARKUP_UL, MUTYPE_LIST,
272
- ATTR_TYPE|ATTR_COMPACT },
299
+ AMSK_TYPE|AMSK_COMPACT },
273300
{ "var", MARKUP_VAR, MUTYPE_FONT, 0 },
274
- { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, ATTR_ID },
301
+ { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID },
275302
};
276303
277304
/*
278305
** Use binary search to locate a tag in the aMarkup[] table.
279306
*/
@@ -332,11 +359,15 @@
332359
int wantAutoParagraph; /* True if a <p> is desired */
333360
int inAutoParagraph; /* True if within an automatic paragraph */
334361
const char *zVerbatimId; /* The id= attribute of <verbatim> */
335362
int nStack; /* Number of elements on the stack */
336363
int nAlloc; /* Space allocated for aStack */
337
- unsigned char *aStack; /* Open markup stack */
364
+ struct sStack {
365
+ short iCode; /* Markup code */
366
+ short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
367
+ const char *zId; /* ID attribute or NULL */
368
+ } *aStack;
338369
};
339370
340371
341372
/*
342373
** z points to a "<" character. Check to see if this is the start of
@@ -585,11 +616,11 @@
585616
unsigned char endTag; /* True if </...> instead of <...> */
586617
unsigned char iCode; /* MARKUP_* */
587618
unsigned char nAttr; /* Number of attributes */
588619
unsigned short iType; /* MUTYPE_* */
589620
struct {
590
- unsigned char iCode; /* ATTR_* */
621
+ unsigned char iACode; /* ATTR_* */
591622
char *zValue; /* Argument to this attribute. Might be NULL */
592623
char cTerm; /* Original argument termination character */
593624
} aAttr[10];
594625
};
595626
@@ -600,11 +631,11 @@
600631
** The content of z[] might be modified by converting characters
601632
** to lowercase and by inserting some "\000" characters.
602633
*/
603634
static void parseMarkup(ParsedMarkup *p, char *z){
604635
int i, j, c;
605
- int iCode;
636
+ int iACode;
606637
char *zValue;
607638
int seen = 0;
608639
char zTag[100];
609640
610641
if( z[1]=='/' ){
@@ -630,12 +661,12 @@
630661
while( isalnum(z[i]) ){
631662
if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]);
632663
i++;
633664
}
634665
zTag[j] = 0;
635
- p->aAttr[p->nAttr].iCode = iCode = findAttr(zTag);
636
- attrOk = iCode!=0 && (seen & aAttribute[iCode].iMask)==0;
666
+ p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag);
667
+ attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0;
637668
while( isspace(z[i]) ){ z++; }
638669
if( z[i]!='=' ){
639670
p->aAttr[p->nAttr].zValue = 0;
640671
p->aAttr[p->nAttr].cTerm = 0;
641672
c = 0;
@@ -656,11 +687,11 @@
656687
z[i] = 0;
657688
}
658689
i++;
659690
}
660691
if( attrOk ){
661
- seen |= aAttribute[iCode].iMask;
692
+ seen |= aAttribute[iACode].iMask;
662693
p->nAttr++;
663694
}
664695
while( isspace(z[i]) ){ i++; }
665696
if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break;
666697
}
@@ -674,11 +705,11 @@
674705
if( p->endTag ){
675706
blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
676707
}else{
677708
blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
678709
for(i=0; i<p->nAttr; i++){
679
- blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iCode].zName);
710
+ blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
680711
if( p->aAttr[i].zValue ){
681712
blob_appendf(pOut, "=\"%s\"", p->aAttr[i].zValue);
682713
}
683714
}
684715
blob_append(pOut, ">", 1);
@@ -697,50 +728,94 @@
697728
if( z==0 ) continue;
698729
n = strlen(z);
699730
z[n] = p->aAttr[i].cTerm;
700731
}
701732
}
733
+
734
+/*
735
+** Return the ID attribute for markup. Return NULL if there is no
736
+** ID attribute.
737
+*/
738
+static const char *markupId(ParsedMarkup *p){
739
+ int i;
740
+ for(i=0; i<p->nAttr; i++){
741
+ if( p->aAttr[i].iACode==ATTR_ID ){
742
+ return p->aAttr[i].zValue;
743
+ }
744
+ }
745
+ return 0;
746
+}
702747
703748
/*
704749
** Pop a single element off of the stack. As the element is popped,
705
-** output its end tag.
750
+** output its end tag if it is not a </div> tag.
706751
*/
707752
static void popStack(Renderer *p){
708753
if( p->nStack ){
754
+ int iCode;
709755
p->nStack--;
710
- blob_appendf(p->pOut, "</%s>", aMarkup[p->aStack[p->nStack]].zName);
756
+ iCode = p->aStack[p->nStack].iCode;
757
+ if( iCode!=MARKUP_DIV ){
758
+ blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
759
+ }
711760
}
712761
}
713762
714763
/*
715764
** Push a new markup value onto the stack. Enlarge the stack
716765
** if necessary.
717766
*/
718
-static void pushStack(Renderer *p, int elem){
767
+static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){
719768
if( p->nStack>=p->nAlloc ){
720769
p->nAlloc = p->nAlloc*2 + 100;
721
- p->aStack = realloc(p->aStack, p->nAlloc);
770
+ p->aStack = realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0]));
722771
if( p->aStack==0 ){
723772
fossil_panic("out of memory");
724773
}
725774
}
726
- p->aStack[p->nStack++] = elem;
775
+ p->aStack[p->nStack].iCode = elem;
776
+ p->aStack[p->nStack].zId = zId;
777
+ p->aStack[p->nStack].allowWiki = w;
778
+ p->nStack++;
779
+}
780
+static void pushStack(Renderer *p, int elem){
781
+ pushStackWithId(p, elem, 0, 0);
727782
}
728783
729784
/*
730785
** Pop the stack until the top-most iTag element is removed.
731786
** If there is no iTag element on the stack, this routine
732787
** is a no-op.
733788
*/
734789
static void popStackToTag(Renderer *p, int iTag){
735790
int i;
736
- for(i=p->nStack-1; i>=0 && p->aStack[i]!=iTag; i--){}
791
+ for(i=p->nStack-1; i>=0; i--){
792
+ if( p->aStack[i].iCode!=iTag ) continue;
793
+ if( p->aStack[i].zId ) continue;
794
+ break;
795
+ }
737796
if( i<0 ) return;
738797
while( p->nStack>i ){
739798
popStack(p);
740799
}
741800
}
801
+
802
+/*
803
+** Attempt to find a find a tag of type iTag with id zId. Return -1
804
+** if not found. If found, return its stack level.
805
+*/
806
+static int findTagWithId(Renderer *p, int iTag, const char *zId){
807
+ int i;
808
+ assert( zId!=0 );
809
+ for(i=p->nStack-1; i>=0; i--){
810
+ if( p->aStack[i].iCode!=iTag ) continue;
811
+ if( p->aStack[i].zId==0 ) continue;
812
+ if( strcmp(zId, p->aStack[i].zId)!=0 ) continue;
813
+ break;
814
+ }
815
+ return i;
816
+}
742817
743818
/*
744819
** Pop the stack until the top-most element of the stack
745820
** is an element that matches the type in iMask. Return
746821
** code of the markup element that is on left on top of the stack.
@@ -748,17 +823,19 @@
748823
** that matches iMask, then leave the stack unchanged and
749824
** return false (MARKUP_INVALID).
750825
*/
751826
static int backupToType(Renderer *p, int iMask){
752827
int i;
753
- for(i=p->nStack-1; i>=0 && (aMarkup[p->aStack[i]].iType&iMask)==0; i--){}
828
+ for(i=p->nStack-1; i>=0; i--){
829
+ if( aMarkup[p->aStack[i].iCode].iType & iMask ) break;
830
+ }
754831
if( i<0 ) return 0;
755832
i++;
756833
while( p->nStack>i ){
757834
popStack(p);
758835
}
759
- return p->aStack[i-1];
836
+ return p->aStack[i-1].iCode;
760837
}
761838
762839
/*
763840
** Begin a new paragraph if that something that is needed.
764841
*/
@@ -849,11 +926,11 @@
849926
/*
850927
** Return the MUTYPE for the top of the stack.
851928
*/
852929
static int stackTopType(Renderer *p){
853930
if( p->nStack<=0 ) return 0;
854
- return aMarkup[p->aStack[p->nStack-1]].iType;
931
+ return aMarkup[p->aStack[p->nStack-1].iCode].iType;
855932
}
856933
857934
/*
858935
** Convert the wiki in z[] into html in the renderer p. The
859936
** renderer has already been initialized.
@@ -987,11 +1064,41 @@
9871064
startAutoParagraph(p);
9881065
blob_append(p->pOut, z, n);
9891066
break;
9901067
}
9911068
case TOKEN_MARKUP: {
1069
+ const char *zId;
1070
+ int iDiv;
9921071
parseMarkup(&markup, z);
1072
+
1073
+ /* Markup of the form </div id=ID> where there is a matching
1074
+ ** ID somewhere on the stack. Exit the verbatim if were are in
1075
+ ** it. Pop the stack up to the matching <div>. Discard the
1076
+ ** </div>
1077
+ */
1078
+ if( markup.iCode==MARKUP_DIV && markup.endTag &&
1079
+ (zId = markupId(&markup))!=0 &&
1080
+ (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0
1081
+ ){
1082
+ if( p->inVerbatim ){
1083
+ p->inVerbatim = 0;
1084
+ p->state = p->preVerbState;
1085
+ blob_append(p->pOut, "</pre>", 6);
1086
+ }
1087
+ while( p->nStack>iDiv+1 ) popStack(p);
1088
+ if( p->aStack[iDiv].allowWiki ){
1089
+ p->state |= ALLOW_WIKI;
1090
+ }else{
1091
+ p->state &= ~ALLOW_WIKI;
1092
+ }
1093
+ assert( p->nStack==iDiv+1 );
1094
+ p->nStack--;
1095
+ }else
1096
+
1097
+ /* If within <verbatim id=ID> ignore everything other than
1098
+ ** </verbatim id=ID> and the </dev id=ID2> above.
1099
+ */
9931100
if( p->inVerbatim ){
9941101
if( endVerbatim(p, &markup) ){
9951102
p->inVerbatim = 0;
9961103
p->state = p->preVerbState;
9971104
blob_append(p->pOut, "</pre>", 6);
@@ -998,29 +1105,60 @@
9981105
}else{
9991106
unparseMarkup(&markup);
10001107
blob_append(p->pOut, "&lt;", 4);
10011108
n = 1;
10021109
}
1003
- }else if( markup.iCode==MARKUP_INVALID ){
1110
+ }else
1111
+
1112
+ /* Render invalid markup literally. The markup appears in the
1113
+ ** final output as plain text.
1114
+ */
1115
+ if( markup.iCode==MARKUP_INVALID ){
10041116
unparseMarkup(&markup);
10051117
startAutoParagraph(p);
10061118
blob_append(p->pOut, "&lt;", 4);
10071119
n = 1;
1008
- }else if( (markup.iType&MUTYPE_FONT)==0
1009
- && (p->state & FONT_MARKUP_ONLY)!=0 ){
1120
+ }else
1121
+
1122
+ /* If the markup is not font-change markup ignore it if the
1123
+ ** font-change-only flag is set.
1124
+ */
1125
+ if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){
10101126
/* Do nothing */
1011
- }else if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
1127
+ }else
1128
+
1129
+ /* Ignore block markup for in-line rendering.
1130
+ */
1131
+ if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
10121132
/* Do nothing */
1013
- }else if( markup.iCode==MARKUP_NOWIKI ){
1133
+ }else
1134
+
1135
+ if( markup.iCode==MARKUP_NOWIKI ){
10141136
if( markup.endTag ){
10151137
p->state |= ALLOW_WIKI;
10161138
}else{
10171139
p->state &= ~ALLOW_WIKI;
10181140
}
1019
- }else if( markup.endTag ){
1141
+ }else
1142
+
1143
+ /* Generate end-tags */
1144
+ if( markup.endTag ){
10201145
popStackToTag(p, markup.iCode);
1021
- }else if( markup.iCode==MARKUP_VERBATIM ){
1146
+ }else
1147
+
1148
+ /* Push <div> markup onto the stack together with the id=ID attribute.
1149
+ */
1150
+ if( markup.iCode==MARKUP_DIV ){
1151
+ pushStackWithId(p, markup.iCode, markupId(&markup),
1152
+ (p->state & ALLOW_WIKI)!=0);
1153
+ }else
1154
+
1155
+ /* Enter <verbatim> processing. With verbatim enabled, all other
1156
+ ** markup other than the corresponding end-tag with the same ID is
1157
+ ** ignored.
1158
+ */
1159
+ if( markup.iCode==MARKUP_VERBATIM ){
10221160
if( markup.nAttr==1 ){
10231161
p->zVerbatimId = markup.aAttr[0].zValue;
10241162
}else{
10251163
p->zVerbatimId = 0;
10261164
}
@@ -1027,37 +1165,42 @@
10271165
p->inVerbatim = 1;
10281166
p->preVerbState = p->state;
10291167
p->state &= ~ALLOW_WIKI;
10301168
blob_append(p->pOut, "<pre class='verbatim'>",-1);
10311169
p->wantAutoParagraph = 0;
1032
- }else if( markup.iType==MUTYPE_LI ){
1170
+ }else
1171
+ if( markup.iType==MUTYPE_LI ){
10331172
if( backupToType(p, MUTYPE_LIST)==0 ){
10341173
pushStack(p, MARKUP_UL);
10351174
blob_append(p->pOut, "<ul>", 4);
10361175
}
10371176
pushStack(p, MARKUP_LI);
10381177
renderMarkup(p->pOut, &markup);
1039
- }else if( markup.iType==MUTYPE_TR ){
1178
+ }else
1179
+ if( markup.iType==MUTYPE_TR ){
10401180
if( backupToType(p, MUTYPE_TABLE) ){
10411181
pushStack(p, MARKUP_TR);
10421182
renderMarkup(p->pOut, &markup);
10431183
}
1044
- }else if( markup.iType==MUTYPE_TD ){
1184
+ }else
1185
+ if( markup.iType==MUTYPE_TD ){
10451186
if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){
10461187
if( stackTopType(p)==MUTYPE_TABLE ){
10471188
pushStack(p, MARKUP_TR);
10481189
blob_append(p->pOut, "<tr>", 4);
10491190
}
10501191
pushStack(p, markup.iCode);
10511192
renderMarkup(p->pOut, &markup);
10521193
}
1053
- }else if( markup.iType==MUTYPE_HYPERLINK ){
1194
+ }else
1195
+ if( markup.iType==MUTYPE_HYPERLINK ){
10541196
popStackToTag(p, markup.iCode);
10551197
startAutoParagraph(p);
10561198
renderMarkup(p->pOut, &markup);
10571199
pushStack(p, markup.iCode);
1058
- }else{
1200
+ }else
1201
+ {
10591202
if( markup.iType==MUTYPE_FONT ){
10601203
startAutoParagraph(p);
10611204
}else if( markup.iType==MUTYPE_BLOCK ){
10621205
p->wantAutoParagraph = 0;
10631206
}
10641207
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -39,66 +39,91 @@
39
40
41 /*
42 ** These are the only markup attributes allowed.
43 */
44 #define ATTR_ALIGN 0x0000001
45 #define ATTR_ALT 0x0000002
46 #define ATTR_BGCOLOR 0x0000004
47 #define ATTR_BORDER 0x0000008
48 #define ATTR_CELLPADDING 0x0000010
49 #define ATTR_CELLSPACING 0x0000020
50 #define ATTR_CLEAR 0x0000040
51 #define ATTR_COLOR 0x0000080
52 #define ATTR_COLSPAN 0x0000100
53 #define ATTR_COMPACT 0x0000200
54 #define ATTR_FACE 0x0000400
55 #define ATTR_HEIGHT 0x0000800
56 #define ATTR_HREF 0x0001000
57 #define ATTR_HSPACE 0x0002000
58 #define ATTR_ID 0x0004000
59 #define ATTR_NAME 0x0008000
60 #define ATTR_ROWSPAN 0x0010000
61 #define ATTR_SIZE 0x0020000
62 #define ATTR_SRC 0x0040000
63 #define ATTR_START 0x0080000
64 #define ATTR_TYPE 0x0100000
65 #define ATTR_VALIGN 0x0200000
66 #define ATTR_VALUE 0x0400000
67 #define ATTR_VSPACE 0x0800000
68 #define ATTR_WIDTH 0x1000000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
70 static const struct AllowedAttribute {
71 const char *zName;
72 unsigned int iMask;
73 } aAttribute[] = {
74 { 0, 0 },
75 { "align", ATTR_ALIGN, },
76 { "alt", ATTR_ALT, },
77 { "bgcolor", ATTR_BGCOLOR, },
78 { "border", ATTR_BORDER, },
79 { "cellpadding", ATTR_CELLPADDING, },
80 { "cellspacing", ATTR_CELLSPACING, },
81 { "clear", ATTR_CLEAR, },
82 { "color", ATTR_COLOR, },
83 { "colspan", ATTR_COLSPAN, },
84 { "compact", ATTR_COMPACT, },
85 { "face", ATTR_FACE, },
86 { "height", ATTR_HEIGHT, },
87 { "href", ATTR_HREF, },
88 { "hspace", ATTR_HSPACE, },
89 { "id", ATTR_ID, },
90 { "name", ATTR_NAME, },
91 { "rowspan", ATTR_ROWSPAN, },
92 { "size", ATTR_SIZE, },
93 { "src", ATTR_SRC, },
94 { "start", ATTR_START, },
95 { "type", ATTR_TYPE, },
96 { "valign", ATTR_VALIGN, },
97 { "value", ATTR_VALUE, },
98 { "vspace", ATTR_VSPACE, },
99 { "width", ATTR_WIDTH, },
100 };
101
102 /*
103 ** Use binary search to locate a tag in the aAttribute[] table.
104 */
@@ -140,46 +165,47 @@
140 #define MARKUP_CENTER 7
141 #define MARKUP_CITE 8
142 #define MARKUP_CODE 9
143 #define MARKUP_DD 10
144 #define MARKUP_DFN 11
145 #define MARKUP_DL 12
146 #define MARKUP_DT 13
147 #define MARKUP_EM 14
148 #define MARKUP_FONT 15
149 #define MARKUP_H1 16
150 #define MARKUP_H2 17
151 #define MARKUP_H3 18
152 #define MARKUP_H4 19
153 #define MARKUP_H5 20
154 #define MARKUP_H6 21
155 #define MARKUP_HR 22
156 #define MARKUP_I 23
157 #define MARKUP_IMG 24
158 #define MARKUP_KBD 25
159 #define MARKUP_LI 26
160 #define MARKUP_NOBR 27
161 #define MARKUP_NOWIKI 28
162 #define MARKUP_OL 29
163 #define MARKUP_P 30
164 #define MARKUP_PRE 31
165 #define MARKUP_S 32
166 #define MARKUP_SAMP 33
167 #define MARKUP_SMALL 34
168 #define MARKUP_STRIKE 35
169 #define MARKUP_STRONG 36
170 #define MARKUP_SUB 37
171 #define MARKUP_SUP 38
172 #define MARKUP_TABLE 39
173 #define MARKUP_TD 40
174 #define MARKUP_TH 41
175 #define MARKUP_TR 42
176 #define MARKUP_TT 43
177 #define MARKUP_U 44
178 #define MARKUP_UL 45
179 #define MARKUP_VAR 46
180 #define MARKUP_VERBATIM 47
 
181
182 /*
183 ** The various markup is divided into the following types:
184 */
185 #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */
@@ -209,71 +235,72 @@
209 short int iType; /* The MUTYPE_* code */
210 int allowedAttr; /* Allowed attributes on this markup */
211 } aMarkup[] = {
212 { 0, MARKUP_INVALID, 0, 0 },
213 { "a", MARKUP_A, MUTYPE_HYPERLINK,
214 ATTR_HREF|ATTR_NAME },
215 { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 },
216 { "b", MARKUP_B, MUTYPE_FONT, 0 },
217 { "big", MARKUP_BIG, MUTYPE_FONT, 0 },
218 { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 },
219 { "br", MARKUP_BR, MUTYPE_SINGLE, ATTR_CLEAR },
220 { "center", MARKUP_CENTER, MUTYPE_BLOCK, 0 },
221 { "cite", MARKUP_CITE, MUTYPE_FONT, 0 },
222 { "code", MARKUP_CODE, MUTYPE_FONT, 0 },
223 { "dd", MARKUP_DD, MUTYPE_LI, 0 },
224 { "dfn", MARKUP_DFN, MUTYPE_FONT, 0 },
225 { "dl", MARKUP_DL, MUTYPE_LIST, ATTR_COMPACT },
 
226 { "dt", MARKUP_DT, MUTYPE_LI, 0 },
227 { "em", MARKUP_EM, MUTYPE_FONT, 0 },
228 { "font", MARKUP_FONT, MUTYPE_FONT,
229 ATTR_COLOR|ATTR_FACE|ATTR_SIZE },
230 { "h1", MARKUP_H1, MUTYPE_BLOCK, ATTR_ALIGN },
231 { "h2", MARKUP_H2, MUTYPE_BLOCK, ATTR_ALIGN },
232 { "h3", MARKUP_H3, MUTYPE_BLOCK, ATTR_ALIGN },
233 { "h4", MARKUP_H4, MUTYPE_BLOCK, ATTR_ALIGN },
234 { "h5", MARKUP_H5, MUTYPE_BLOCK, ATTR_ALIGN },
235 { "h6", MARKUP_H6, MUTYPE_BLOCK, ATTR_ALIGN },
236 { "hr", MARKUP_HR, MUTYPE_SINGLE,
237 ATTR_ALIGN|ATTR_COLOR|ATTR_SIZE|ATTR_WIDTH },
238 { "i", MARKUP_I, MUTYPE_FONT, 0 },
239 { "img", MARKUP_IMG, MUTYPE_SINGLE,
240 ATTR_ALIGN|ATTR_ALT|ATTR_BORDER|ATTR_HEIGHT|
241 ATTR_HSPACE|ATTR_SRC|ATTR_VSPACE|ATTR_WIDTH },
242 { "kbd", MARKUP_KBD, MUTYPE_FONT, 0 },
243 { "li", MARKUP_LI, MUTYPE_LI,
244 ATTR_TYPE|ATTR_VALUE },
245 { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 },
246 { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 },
247 { "ol", MARKUP_OL, MUTYPE_LIST,
248 ATTR_START|ATTR_TYPE|ATTR_COMPACT },
249 { "p", MARKUP_P, MUTYPE_BLOCK, ATTR_ALIGN },
250 { "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 },
251 { "s", MARKUP_S, MUTYPE_FONT, 0 },
252 { "samp", MARKUP_SAMP, MUTYPE_FONT, 0 },
253 { "small", MARKUP_SMALL, MUTYPE_FONT, 0 },
254 { "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 },
255 { "strong", MARKUP_STRONG, MUTYPE_FONT, 0 },
256 { "sub", MARKUP_SUB, MUTYPE_FONT, 0 },
257 { "sup", MARKUP_SUP, MUTYPE_FONT, 0 },
258 { "table", MARKUP_TABLE, MUTYPE_TABLE,
259 ATTR_ALIGN|ATTR_BGCOLOR|ATTR_BORDER|ATTR_CELLPADDING|
260 ATTR_CELLSPACING|ATTR_HSPACE|ATTR_VSPACE },
261 { "td", MARKUP_TD, MUTYPE_TD,
262 ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN|
263 ATTR_ROWSPAN|ATTR_VALIGN },
264 { "th", MARKUP_TH, MUTYPE_TD,
265 ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN|
266 ATTR_ROWSPAN|ATTR_VALIGN },
267 { "tr", MARKUP_TR, MUTYPE_TR,
268 ATTR_ALIGN|ATTR_BGCOLOR||ATTR_VALIGN },
269 { "tt", MARKUP_TT, MUTYPE_FONT, 0 },
270 { "u", MARKUP_U, MUTYPE_FONT, 0 },
271 { "ul", MARKUP_UL, MUTYPE_LIST,
272 ATTR_TYPE|ATTR_COMPACT },
273 { "var", MARKUP_VAR, MUTYPE_FONT, 0 },
274 { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, ATTR_ID },
275 };
276
277 /*
278 ** Use binary search to locate a tag in the aMarkup[] table.
279 */
@@ -332,11 +359,15 @@
332 int wantAutoParagraph; /* True if a <p> is desired */
333 int inAutoParagraph; /* True if within an automatic paragraph */
334 const char *zVerbatimId; /* The id= attribute of <verbatim> */
335 int nStack; /* Number of elements on the stack */
336 int nAlloc; /* Space allocated for aStack */
337 unsigned char *aStack; /* Open markup stack */
 
 
 
 
338 };
339
340
341 /*
342 ** z points to a "<" character. Check to see if this is the start of
@@ -585,11 +616,11 @@
585 unsigned char endTag; /* True if </...> instead of <...> */
586 unsigned char iCode; /* MARKUP_* */
587 unsigned char nAttr; /* Number of attributes */
588 unsigned short iType; /* MUTYPE_* */
589 struct {
590 unsigned char iCode; /* ATTR_* */
591 char *zValue; /* Argument to this attribute. Might be NULL */
592 char cTerm; /* Original argument termination character */
593 } aAttr[10];
594 };
595
@@ -600,11 +631,11 @@
600 ** The content of z[] might be modified by converting characters
601 ** to lowercase and by inserting some "\000" characters.
602 */
603 static void parseMarkup(ParsedMarkup *p, char *z){
604 int i, j, c;
605 int iCode;
606 char *zValue;
607 int seen = 0;
608 char zTag[100];
609
610 if( z[1]=='/' ){
@@ -630,12 +661,12 @@
630 while( isalnum(z[i]) ){
631 if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]);
632 i++;
633 }
634 zTag[j] = 0;
635 p->aAttr[p->nAttr].iCode = iCode = findAttr(zTag);
636 attrOk = iCode!=0 && (seen & aAttribute[iCode].iMask)==0;
637 while( isspace(z[i]) ){ z++; }
638 if( z[i]!='=' ){
639 p->aAttr[p->nAttr].zValue = 0;
640 p->aAttr[p->nAttr].cTerm = 0;
641 c = 0;
@@ -656,11 +687,11 @@
656 z[i] = 0;
657 }
658 i++;
659 }
660 if( attrOk ){
661 seen |= aAttribute[iCode].iMask;
662 p->nAttr++;
663 }
664 while( isspace(z[i]) ){ i++; }
665 if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break;
666 }
@@ -674,11 +705,11 @@
674 if( p->endTag ){
675 blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
676 }else{
677 blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
678 for(i=0; i<p->nAttr; i++){
679 blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iCode].zName);
680 if( p->aAttr[i].zValue ){
681 blob_appendf(pOut, "=\"%s\"", p->aAttr[i].zValue);
682 }
683 }
684 blob_append(pOut, ">", 1);
@@ -697,50 +728,94 @@
697 if( z==0 ) continue;
698 n = strlen(z);
699 z[n] = p->aAttr[i].cTerm;
700 }
701 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
702
703 /*
704 ** Pop a single element off of the stack. As the element is popped,
705 ** output its end tag.
706 */
707 static void popStack(Renderer *p){
708 if( p->nStack ){
 
709 p->nStack--;
710 blob_appendf(p->pOut, "</%s>", aMarkup[p->aStack[p->nStack]].zName);
 
 
 
711 }
712 }
713
714 /*
715 ** Push a new markup value onto the stack. Enlarge the stack
716 ** if necessary.
717 */
718 static void pushStack(Renderer *p, int elem){
719 if( p->nStack>=p->nAlloc ){
720 p->nAlloc = p->nAlloc*2 + 100;
721 p->aStack = realloc(p->aStack, p->nAlloc);
722 if( p->aStack==0 ){
723 fossil_panic("out of memory");
724 }
725 }
726 p->aStack[p->nStack++] = elem;
 
 
 
 
 
 
727 }
728
729 /*
730 ** Pop the stack until the top-most iTag element is removed.
731 ** If there is no iTag element on the stack, this routine
732 ** is a no-op.
733 */
734 static void popStackToTag(Renderer *p, int iTag){
735 int i;
736 for(i=p->nStack-1; i>=0 && p->aStack[i]!=iTag; i--){}
 
 
 
 
737 if( i<0 ) return;
738 while( p->nStack>i ){
739 popStack(p);
740 }
741 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742
743 /*
744 ** Pop the stack until the top-most element of the stack
745 ** is an element that matches the type in iMask. Return
746 ** code of the markup element that is on left on top of the stack.
@@ -748,17 +823,19 @@
748 ** that matches iMask, then leave the stack unchanged and
749 ** return false (MARKUP_INVALID).
750 */
751 static int backupToType(Renderer *p, int iMask){
752 int i;
753 for(i=p->nStack-1; i>=0 && (aMarkup[p->aStack[i]].iType&iMask)==0; i--){}
 
 
754 if( i<0 ) return 0;
755 i++;
756 while( p->nStack>i ){
757 popStack(p);
758 }
759 return p->aStack[i-1];
760 }
761
762 /*
763 ** Begin a new paragraph if that something that is needed.
764 */
@@ -849,11 +926,11 @@
849 /*
850 ** Return the MUTYPE for the top of the stack.
851 */
852 static int stackTopType(Renderer *p){
853 if( p->nStack<=0 ) return 0;
854 return aMarkup[p->aStack[p->nStack-1]].iType;
855 }
856
857 /*
858 ** Convert the wiki in z[] into html in the renderer p. The
859 ** renderer has already been initialized.
@@ -987,11 +1064,41 @@
987 startAutoParagraph(p);
988 blob_append(p->pOut, z, n);
989 break;
990 }
991 case TOKEN_MARKUP: {
 
 
992 parseMarkup(&markup, z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
993 if( p->inVerbatim ){
994 if( endVerbatim(p, &markup) ){
995 p->inVerbatim = 0;
996 p->state = p->preVerbState;
997 blob_append(p->pOut, "</pre>", 6);
@@ -998,29 +1105,60 @@
998 }else{
999 unparseMarkup(&markup);
1000 blob_append(p->pOut, "&lt;", 4);
1001 n = 1;
1002 }
1003 }else if( markup.iCode==MARKUP_INVALID ){
 
 
 
 
 
1004 unparseMarkup(&markup);
1005 startAutoParagraph(p);
1006 blob_append(p->pOut, "&lt;", 4);
1007 n = 1;
1008 }else if( (markup.iType&MUTYPE_FONT)==0
1009 && (p->state & FONT_MARKUP_ONLY)!=0 ){
 
 
 
 
1010 /* Do nothing */
1011 }else if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
 
 
 
 
1012 /* Do nothing */
1013 }else if( markup.iCode==MARKUP_NOWIKI ){
 
 
1014 if( markup.endTag ){
1015 p->state |= ALLOW_WIKI;
1016 }else{
1017 p->state &= ~ALLOW_WIKI;
1018 }
1019 }else if( markup.endTag ){
 
 
 
1020 popStackToTag(p, markup.iCode);
1021 }else if( markup.iCode==MARKUP_VERBATIM ){
 
 
 
 
 
 
 
 
 
 
 
 
 
1022 if( markup.nAttr==1 ){
1023 p->zVerbatimId = markup.aAttr[0].zValue;
1024 }else{
1025 p->zVerbatimId = 0;
1026 }
@@ -1027,37 +1165,42 @@
1027 p->inVerbatim = 1;
1028 p->preVerbState = p->state;
1029 p->state &= ~ALLOW_WIKI;
1030 blob_append(p->pOut, "<pre class='verbatim'>",-1);
1031 p->wantAutoParagraph = 0;
1032 }else if( markup.iType==MUTYPE_LI ){
 
1033 if( backupToType(p, MUTYPE_LIST)==0 ){
1034 pushStack(p, MARKUP_UL);
1035 blob_append(p->pOut, "<ul>", 4);
1036 }
1037 pushStack(p, MARKUP_LI);
1038 renderMarkup(p->pOut, &markup);
1039 }else if( markup.iType==MUTYPE_TR ){
 
1040 if( backupToType(p, MUTYPE_TABLE) ){
1041 pushStack(p, MARKUP_TR);
1042 renderMarkup(p->pOut, &markup);
1043 }
1044 }else if( markup.iType==MUTYPE_TD ){
 
1045 if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){
1046 if( stackTopType(p)==MUTYPE_TABLE ){
1047 pushStack(p, MARKUP_TR);
1048 blob_append(p->pOut, "<tr>", 4);
1049 }
1050 pushStack(p, markup.iCode);
1051 renderMarkup(p->pOut, &markup);
1052 }
1053 }else if( markup.iType==MUTYPE_HYPERLINK ){
 
1054 popStackToTag(p, markup.iCode);
1055 startAutoParagraph(p);
1056 renderMarkup(p->pOut, &markup);
1057 pushStack(p, markup.iCode);
1058 }else{
 
1059 if( markup.iType==MUTYPE_FONT ){
1060 startAutoParagraph(p);
1061 }else if( markup.iType==MUTYPE_BLOCK ){
1062 p->wantAutoParagraph = 0;
1063 }
1064
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -39,66 +39,91 @@
39
40
41 /*
42 ** These are the only markup attributes allowed.
43 */
44 #define ATTR_ALIGN 1
45 #define ATTR_ALT 2
46 #define ATTR_BGCOLOR 3
47 #define ATTR_BORDER 4
48 #define ATTR_CELLPADDING 5
49 #define ATTR_CELLSPACING 6
50 #define ATTR_CLEAR 7
51 #define ATTR_COLOR 8
52 #define ATTR_COLSPAN 9
53 #define ATTR_COMPACT 10
54 #define ATTR_FACE 11
55 #define ATTR_HEIGHT 12
56 #define ATTR_HREF 13
57 #define ATTR_HSPACE 14
58 #define ATTR_ID 15
59 #define ATTR_NAME 16
60 #define ATTR_ROWSPAN 17
61 #define ATTR_SIZE 18
62 #define ATTR_SRC 19
63 #define ATTR_START 20
64 #define ATTR_TYPE 21
65 #define ATTR_VALIGN 22
66 #define ATTR_VALUE 23
67 #define ATTR_VSPACE 24
68 #define ATTR_WIDTH 25
69 #define AMSK_ALIGN 0x0000001
70 #define AMSK_ALT 0x0000002
71 #define AMSK_BGCOLOR 0x0000004
72 #define AMSK_BORDER 0x0000008
73 #define AMSK_CELLPADDING 0x0000010
74 #define AMSK_CELLSPACING 0x0000020
75 #define AMSK_CLEAR 0x0000040
76 #define AMSK_COLOR 0x0000080
77 #define AMSK_COLSPAN 0x0000100
78 #define AMSK_COMPACT 0x0000200
79 #define AMSK_FACE 0x0000400
80 #define AMSK_HEIGHT 0x0000800
81 #define AMSK_HREF 0x0001000
82 #define AMSK_HSPACE 0x0002000
83 #define AMSK_ID 0x0004000
84 #define AMSK_NAME 0x0008000
85 #define AMSK_ROWSPAN 0x0010000
86 #define AMSK_SIZE 0x0020000
87 #define AMSK_SRC 0x0040000
88 #define AMSK_START 0x0080000
89 #define AMSK_TYPE 0x0100000
90 #define AMSK_VALIGN 0x0200000
91 #define AMSK_VALUE 0x0400000
92 #define AMSK_VSPACE 0x0800000
93 #define AMSK_WIDTH 0x1000000
94
95 static const struct AllowedAttribute {
96 const char *zName;
97 unsigned int iMask;
98 } aAttribute[] = {
99 { 0, 0 },
100 { "align", AMSK_ALIGN, },
101 { "alt", AMSK_ALT, },
102 { "bgcolor", AMSK_BGCOLOR, },
103 { "border", AMSK_BORDER, },
104 { "cellpadding", AMSK_CELLPADDING, },
105 { "cellspacing", AMSK_CELLSPACING, },
106 { "clear", AMSK_CLEAR, },
107 { "color", AMSK_COLOR, },
108 { "colspan", AMSK_COLSPAN, },
109 { "compact", AMSK_COMPACT, },
110 { "face", AMSK_FACE, },
111 { "height", AMSK_HEIGHT, },
112 { "href", AMSK_HREF, },
113 { "hspace", AMSK_HSPACE, },
114 { "id", AMSK_ID, },
115 { "name", AMSK_NAME, },
116 { "rowspan", AMSK_ROWSPAN, },
117 { "size", AMSK_SIZE, },
118 { "src", AMSK_SRC, },
119 { "start", AMSK_START, },
120 { "type", AMSK_TYPE, },
121 { "valign", AMSK_VALIGN, },
122 { "value", AMSK_VALUE, },
123 { "vspace", AMSK_VSPACE, },
124 { "width", AMSK_WIDTH, },
125 };
126
127 /*
128 ** Use binary search to locate a tag in the aAttribute[] table.
129 */
@@ -140,46 +165,47 @@
165 #define MARKUP_CENTER 7
166 #define MARKUP_CITE 8
167 #define MARKUP_CODE 9
168 #define MARKUP_DD 10
169 #define MARKUP_DFN 11
170 #define MARKUP_DIV 12
171 #define MARKUP_DL 13
172 #define MARKUP_DT 14
173 #define MARKUP_EM 15
174 #define MARKUP_FONT 16
175 #define MARKUP_H1 17
176 #define MARKUP_H2 18
177 #define MARKUP_H3 19
178 #define MARKUP_H4 20
179 #define MARKUP_H5 21
180 #define MARKUP_H6 22
181 #define MARKUP_HR 23
182 #define MARKUP_I 24
183 #define MARKUP_IMG 25
184 #define MARKUP_KBD 26
185 #define MARKUP_LI 27
186 #define MARKUP_NOBR 28
187 #define MARKUP_NOWIKI 29
188 #define MARKUP_OL 30
189 #define MARKUP_P 31
190 #define MARKUP_PRE 32
191 #define MARKUP_S 33
192 #define MARKUP_SAMP 34
193 #define MARKUP_SMALL 35
194 #define MARKUP_STRIKE 36
195 #define MARKUP_STRONG 37
196 #define MARKUP_SUB 38
197 #define MARKUP_SUP 39
198 #define MARKUP_TABLE 40
199 #define MARKUP_TD 41
200 #define MARKUP_TH 42
201 #define MARKUP_TR 43
202 #define MARKUP_TT 44
203 #define MARKUP_U 45
204 #define MARKUP_UL 46
205 #define MARKUP_VAR 47
206 #define MARKUP_VERBATIM 48
207
208 /*
209 ** The various markup is divided into the following types:
210 */
211 #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */
@@ -209,71 +235,72 @@
235 short int iType; /* The MUTYPE_* code */
236 int allowedAttr; /* Allowed attributes on this markup */
237 } aMarkup[] = {
238 { 0, MARKUP_INVALID, 0, 0 },
239 { "a", MARKUP_A, MUTYPE_HYPERLINK,
240 AMSK_HREF|AMSK_NAME },
241 { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 },
242 { "b", MARKUP_B, MUTYPE_FONT, 0 },
243 { "big", MARKUP_BIG, MUTYPE_FONT, 0 },
244 { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 },
245 { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR },
246 { "center", MARKUP_CENTER, MUTYPE_BLOCK, 0 },
247 { "cite", MARKUP_CITE, MUTYPE_FONT, 0 },
248 { "code", MARKUP_CODE, MUTYPE_FONT, 0 },
249 { "dd", MARKUP_DD, MUTYPE_LI, 0 },
250 { "dfn", MARKUP_DFN, MUTYPE_FONT, 0 },
251 { "div", MARKUP_DIV, MUTYPE_BLOCK, AMSK_ID },
252 { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_COMPACT },
253 { "dt", MARKUP_DT, MUTYPE_LI, 0 },
254 { "em", MARKUP_EM, MUTYPE_FONT, 0 },
255 { "font", MARKUP_FONT, MUTYPE_FONT,
256 AMSK_COLOR|AMSK_FACE|AMSK_SIZE },
257 { "h1", MARKUP_H1, MUTYPE_BLOCK, AMSK_ALIGN },
258 { "h2", MARKUP_H2, MUTYPE_BLOCK, AMSK_ALIGN },
259 { "h3", MARKUP_H3, MUTYPE_BLOCK, AMSK_ALIGN },
260 { "h4", MARKUP_H4, MUTYPE_BLOCK, AMSK_ALIGN },
261 { "h5", MARKUP_H5, MUTYPE_BLOCK, AMSK_ALIGN },
262 { "h6", MARKUP_H6, MUTYPE_BLOCK, AMSK_ALIGN },
263 { "hr", MARKUP_HR, MUTYPE_SINGLE,
264 AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH },
265 { "i", MARKUP_I, MUTYPE_FONT, 0 },
266 { "img", MARKUP_IMG, MUTYPE_SINGLE,
267 AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT|
268 AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH },
269 { "kbd", MARKUP_KBD, MUTYPE_FONT, 0 },
270 { "li", MARKUP_LI, MUTYPE_LI,
271 AMSK_TYPE|AMSK_VALUE },
272 { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 },
273 { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 },
274 { "ol", MARKUP_OL, MUTYPE_LIST,
275 AMSK_START|AMSK_TYPE|AMSK_COMPACT },
276 { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN },
277 { "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 },
278 { "s", MARKUP_S, MUTYPE_FONT, 0 },
279 { "samp", MARKUP_SAMP, MUTYPE_FONT, 0 },
280 { "small", MARKUP_SMALL, MUTYPE_FONT, 0 },
281 { "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 },
282 { "strong", MARKUP_STRONG, MUTYPE_FONT, 0 },
283 { "sub", MARKUP_SUB, MUTYPE_FONT, 0 },
284 { "sup", MARKUP_SUP, MUTYPE_FONT, 0 },
285 { "table", MARKUP_TABLE, MUTYPE_TABLE,
286 AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING|
287 AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE },
288 { "td", MARKUP_TD, MUTYPE_TD,
289 AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
290 AMSK_ROWSPAN|AMSK_VALIGN },
291 { "th", MARKUP_TH, MUTYPE_TD,
292 AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
293 AMSK_ROWSPAN|AMSK_VALIGN },
294 { "tr", MARKUP_TR, MUTYPE_TR,
295 AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN },
296 { "tt", MARKUP_TT, MUTYPE_FONT, 0 },
297 { "u", MARKUP_U, MUTYPE_FONT, 0 },
298 { "ul", MARKUP_UL, MUTYPE_LIST,
299 AMSK_TYPE|AMSK_COMPACT },
300 { "var", MARKUP_VAR, MUTYPE_FONT, 0 },
301 { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID },
302 };
303
304 /*
305 ** Use binary search to locate a tag in the aMarkup[] table.
306 */
@@ -332,11 +359,15 @@
359 int wantAutoParagraph; /* True if a <p> is desired */
360 int inAutoParagraph; /* True if within an automatic paragraph */
361 const char *zVerbatimId; /* The id= attribute of <verbatim> */
362 int nStack; /* Number of elements on the stack */
363 int nAlloc; /* Space allocated for aStack */
364 struct sStack {
365 short iCode; /* Markup code */
366 short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
367 const char *zId; /* ID attribute or NULL */
368 } *aStack;
369 };
370
371
372 /*
373 ** z points to a "<" character. Check to see if this is the start of
@@ -585,11 +616,11 @@
616 unsigned char endTag; /* True if </...> instead of <...> */
617 unsigned char iCode; /* MARKUP_* */
618 unsigned char nAttr; /* Number of attributes */
619 unsigned short iType; /* MUTYPE_* */
620 struct {
621 unsigned char iACode; /* ATTR_* */
622 char *zValue; /* Argument to this attribute. Might be NULL */
623 char cTerm; /* Original argument termination character */
624 } aAttr[10];
625 };
626
@@ -600,11 +631,11 @@
631 ** The content of z[] might be modified by converting characters
632 ** to lowercase and by inserting some "\000" characters.
633 */
634 static void parseMarkup(ParsedMarkup *p, char *z){
635 int i, j, c;
636 int iACode;
637 char *zValue;
638 int seen = 0;
639 char zTag[100];
640
641 if( z[1]=='/' ){
@@ -630,12 +661,12 @@
661 while( isalnum(z[i]) ){
662 if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]);
663 i++;
664 }
665 zTag[j] = 0;
666 p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag);
667 attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0;
668 while( isspace(z[i]) ){ z++; }
669 if( z[i]!='=' ){
670 p->aAttr[p->nAttr].zValue = 0;
671 p->aAttr[p->nAttr].cTerm = 0;
672 c = 0;
@@ -656,11 +687,11 @@
687 z[i] = 0;
688 }
689 i++;
690 }
691 if( attrOk ){
692 seen |= aAttribute[iACode].iMask;
693 p->nAttr++;
694 }
695 while( isspace(z[i]) ){ i++; }
696 if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break;
697 }
@@ -674,11 +705,11 @@
705 if( p->endTag ){
706 blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
707 }else{
708 blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
709 for(i=0; i<p->nAttr; i++){
710 blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
711 if( p->aAttr[i].zValue ){
712 blob_appendf(pOut, "=\"%s\"", p->aAttr[i].zValue);
713 }
714 }
715 blob_append(pOut, ">", 1);
@@ -697,50 +728,94 @@
728 if( z==0 ) continue;
729 n = strlen(z);
730 z[n] = p->aAttr[i].cTerm;
731 }
732 }
733
734 /*
735 ** Return the ID attribute for markup. Return NULL if there is no
736 ** ID attribute.
737 */
738 static const char *markupId(ParsedMarkup *p){
739 int i;
740 for(i=0; i<p->nAttr; i++){
741 if( p->aAttr[i].iACode==ATTR_ID ){
742 return p->aAttr[i].zValue;
743 }
744 }
745 return 0;
746 }
747
748 /*
749 ** Pop a single element off of the stack. As the element is popped,
750 ** output its end tag if it is not a </div> tag.
751 */
752 static void popStack(Renderer *p){
753 if( p->nStack ){
754 int iCode;
755 p->nStack--;
756 iCode = p->aStack[p->nStack].iCode;
757 if( iCode!=MARKUP_DIV ){
758 blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName);
759 }
760 }
761 }
762
763 /*
764 ** Push a new markup value onto the stack. Enlarge the stack
765 ** if necessary.
766 */
767 static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){
768 if( p->nStack>=p->nAlloc ){
769 p->nAlloc = p->nAlloc*2 + 100;
770 p->aStack = realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0]));
771 if( p->aStack==0 ){
772 fossil_panic("out of memory");
773 }
774 }
775 p->aStack[p->nStack].iCode = elem;
776 p->aStack[p->nStack].zId = zId;
777 p->aStack[p->nStack].allowWiki = w;
778 p->nStack++;
779 }
780 static void pushStack(Renderer *p, int elem){
781 pushStackWithId(p, elem, 0, 0);
782 }
783
784 /*
785 ** Pop the stack until the top-most iTag element is removed.
786 ** If there is no iTag element on the stack, this routine
787 ** is a no-op.
788 */
789 static void popStackToTag(Renderer *p, int iTag){
790 int i;
791 for(i=p->nStack-1; i>=0; i--){
792 if( p->aStack[i].iCode!=iTag ) continue;
793 if( p->aStack[i].zId ) continue;
794 break;
795 }
796 if( i<0 ) return;
797 while( p->nStack>i ){
798 popStack(p);
799 }
800 }
801
802 /*
803 ** Attempt to find a find a tag of type iTag with id zId. Return -1
804 ** if not found. If found, return its stack level.
805 */
806 static int findTagWithId(Renderer *p, int iTag, const char *zId){
807 int i;
808 assert( zId!=0 );
809 for(i=p->nStack-1; i>=0; i--){
810 if( p->aStack[i].iCode!=iTag ) continue;
811 if( p->aStack[i].zId==0 ) continue;
812 if( strcmp(zId, p->aStack[i].zId)!=0 ) continue;
813 break;
814 }
815 return i;
816 }
817
818 /*
819 ** Pop the stack until the top-most element of the stack
820 ** is an element that matches the type in iMask. Return
821 ** code of the markup element that is on left on top of the stack.
@@ -748,17 +823,19 @@
823 ** that matches iMask, then leave the stack unchanged and
824 ** return false (MARKUP_INVALID).
825 */
826 static int backupToType(Renderer *p, int iMask){
827 int i;
828 for(i=p->nStack-1; i>=0; i--){
829 if( aMarkup[p->aStack[i].iCode].iType & iMask ) break;
830 }
831 if( i<0 ) return 0;
832 i++;
833 while( p->nStack>i ){
834 popStack(p);
835 }
836 return p->aStack[i-1].iCode;
837 }
838
839 /*
840 ** Begin a new paragraph if that something that is needed.
841 */
@@ -849,11 +926,11 @@
926 /*
927 ** Return the MUTYPE for the top of the stack.
928 */
929 static int stackTopType(Renderer *p){
930 if( p->nStack<=0 ) return 0;
931 return aMarkup[p->aStack[p->nStack-1].iCode].iType;
932 }
933
934 /*
935 ** Convert the wiki in z[] into html in the renderer p. The
936 ** renderer has already been initialized.
@@ -987,11 +1064,41 @@
1064 startAutoParagraph(p);
1065 blob_append(p->pOut, z, n);
1066 break;
1067 }
1068 case TOKEN_MARKUP: {
1069 const char *zId;
1070 int iDiv;
1071 parseMarkup(&markup, z);
1072
1073 /* Markup of the form </div id=ID> where there is a matching
1074 ** ID somewhere on the stack. Exit the verbatim if were are in
1075 ** it. Pop the stack up to the matching <div>. Discard the
1076 ** </div>
1077 */
1078 if( markup.iCode==MARKUP_DIV && markup.endTag &&
1079 (zId = markupId(&markup))!=0 &&
1080 (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0
1081 ){
1082 if( p->inVerbatim ){
1083 p->inVerbatim = 0;
1084 p->state = p->preVerbState;
1085 blob_append(p->pOut, "</pre>", 6);
1086 }
1087 while( p->nStack>iDiv+1 ) popStack(p);
1088 if( p->aStack[iDiv].allowWiki ){
1089 p->state |= ALLOW_WIKI;
1090 }else{
1091 p->state &= ~ALLOW_WIKI;
1092 }
1093 assert( p->nStack==iDiv+1 );
1094 p->nStack--;
1095 }else
1096
1097 /* If within <verbatim id=ID> ignore everything other than
1098 ** </verbatim id=ID> and the </dev id=ID2> above.
1099 */
1100 if( p->inVerbatim ){
1101 if( endVerbatim(p, &markup) ){
1102 p->inVerbatim = 0;
1103 p->state = p->preVerbState;
1104 blob_append(p->pOut, "</pre>", 6);
@@ -998,29 +1105,60 @@
1105 }else{
1106 unparseMarkup(&markup);
1107 blob_append(p->pOut, "&lt;", 4);
1108 n = 1;
1109 }
1110 }else
1111
1112 /* Render invalid markup literally. The markup appears in the
1113 ** final output as plain text.
1114 */
1115 if( markup.iCode==MARKUP_INVALID ){
1116 unparseMarkup(&markup);
1117 startAutoParagraph(p);
1118 blob_append(p->pOut, "&lt;", 4);
1119 n = 1;
1120 }else
1121
1122 /* If the markup is not font-change markup ignore it if the
1123 ** font-change-only flag is set.
1124 */
1125 if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){
1126 /* Do nothing */
1127 }else
1128
1129 /* Ignore block markup for in-line rendering.
1130 */
1131 if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
1132 /* Do nothing */
1133 }else
1134
1135 if( markup.iCode==MARKUP_NOWIKI ){
1136 if( markup.endTag ){
1137 p->state |= ALLOW_WIKI;
1138 }else{
1139 p->state &= ~ALLOW_WIKI;
1140 }
1141 }else
1142
1143 /* Generate end-tags */
1144 if( markup.endTag ){
1145 popStackToTag(p, markup.iCode);
1146 }else
1147
1148 /* Push <div> markup onto the stack together with the id=ID attribute.
1149 */
1150 if( markup.iCode==MARKUP_DIV ){
1151 pushStackWithId(p, markup.iCode, markupId(&markup),
1152 (p->state & ALLOW_WIKI)!=0);
1153 }else
1154
1155 /* Enter <verbatim> processing. With verbatim enabled, all other
1156 ** markup other than the corresponding end-tag with the same ID is
1157 ** ignored.
1158 */
1159 if( markup.iCode==MARKUP_VERBATIM ){
1160 if( markup.nAttr==1 ){
1161 p->zVerbatimId = markup.aAttr[0].zValue;
1162 }else{
1163 p->zVerbatimId = 0;
1164 }
@@ -1027,37 +1165,42 @@
1165 p->inVerbatim = 1;
1166 p->preVerbState = p->state;
1167 p->state &= ~ALLOW_WIKI;
1168 blob_append(p->pOut, "<pre class='verbatim'>",-1);
1169 p->wantAutoParagraph = 0;
1170 }else
1171 if( markup.iType==MUTYPE_LI ){
1172 if( backupToType(p, MUTYPE_LIST)==0 ){
1173 pushStack(p, MARKUP_UL);
1174 blob_append(p->pOut, "<ul>", 4);
1175 }
1176 pushStack(p, MARKUP_LI);
1177 renderMarkup(p->pOut, &markup);
1178 }else
1179 if( markup.iType==MUTYPE_TR ){
1180 if( backupToType(p, MUTYPE_TABLE) ){
1181 pushStack(p, MARKUP_TR);
1182 renderMarkup(p->pOut, &markup);
1183 }
1184 }else
1185 if( markup.iType==MUTYPE_TD ){
1186 if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){
1187 if( stackTopType(p)==MUTYPE_TABLE ){
1188 pushStack(p, MARKUP_TR);
1189 blob_append(p->pOut, "<tr>", 4);
1190 }
1191 pushStack(p, markup.iCode);
1192 renderMarkup(p->pOut, &markup);
1193 }
1194 }else
1195 if( markup.iType==MUTYPE_HYPERLINK ){
1196 popStackToTag(p, markup.iCode);
1197 startAutoParagraph(p);
1198 renderMarkup(p->pOut, &markup);
1199 pushStack(p, markup.iCode);
1200 }else
1201 {
1202 if( markup.iType==MUTYPE_FONT ){
1203 startAutoParagraph(p);
1204 }else if( markup.iType==MUTYPE_BLOCK ){
1205 p->wantAutoParagraph = 0;
1206 }
1207

Keyboard Shortcuts

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