Fossil SCM
Allow the type= attribute on the verbatim wiki tag. This attribute does not pass right through. If found, it is treated as the 'Language Type' and alters the output of the verbatim tag slightly. <verbatim type='cpp'> would render as <pre name='code' class='cpp'>. This allows JavaScript tools such as SyntaxHighlighter to function properly for any language.
Commit
63d31b0448b3d25b559f6c0e840318c26d2c90f4
Parent
71edacd95f3b58a…
1 file changed
+39
-32
+39
-32
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -7,11 +7,11 @@ | ||
| 7 | 7 | ** |
| 8 | 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | 9 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | 10 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 11 | 11 | ** General Public License for more details. |
| 12 | -** | |
| 12 | +** | |
| 13 | 13 | ** You should have received a copy of the GNU General Public |
| 14 | 14 | ** License along with this library; if not, write to the |
| 15 | 15 | ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 16 | 16 | ** Boston, MA 02111-1307, USA. |
| 17 | 17 | ** |
| @@ -150,11 +150,11 @@ | ||
| 150 | 150 | /* |
| 151 | 151 | ** Allowed markup. |
| 152 | 152 | ** |
| 153 | 153 | ** Except for MARKUP_INVALID, this must all be in alphabetical order |
| 154 | 154 | ** and in numerical sequence. The first markup type must be zero. |
| 155 | -** The value for MARKUP_XYZ must correspond to the <xyz> entry | |
| 155 | +** The value for MARKUP_XYZ must correspond to the <xyz> entry | |
| 156 | 156 | ** in aAllowedMarkup[]. |
| 157 | 157 | */ |
| 158 | 158 | #define MARKUP_INVALID 0 |
| 159 | 159 | #define MARKUP_A 1 |
| 160 | 160 | #define MARKUP_ADDRESS 2 |
| @@ -258,22 +258,22 @@ | ||
| 258 | 258 | { "h2", MARKUP_H2, MUTYPE_BLOCK, AMSK_ALIGN }, |
| 259 | 259 | { "h3", MARKUP_H3, MUTYPE_BLOCK, AMSK_ALIGN }, |
| 260 | 260 | { "h4", MARKUP_H4, MUTYPE_BLOCK, AMSK_ALIGN }, |
| 261 | 261 | { "h5", MARKUP_H5, MUTYPE_BLOCK, AMSK_ALIGN }, |
| 262 | 262 | { "h6", MARKUP_H6, MUTYPE_BLOCK, AMSK_ALIGN }, |
| 263 | - { "hr", MARKUP_HR, MUTYPE_SINGLE, | |
| 263 | + { "hr", MARKUP_HR, MUTYPE_SINGLE, | |
| 264 | 264 | AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH }, |
| 265 | 265 | { "i", MARKUP_I, MUTYPE_FONT, 0 }, |
| 266 | - { "img", MARKUP_IMG, MUTYPE_SINGLE, | |
| 266 | + { "img", MARKUP_IMG, MUTYPE_SINGLE, | |
| 267 | 267 | AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT| |
| 268 | 268 | AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH }, |
| 269 | 269 | { "kbd", MARKUP_KBD, MUTYPE_FONT, 0 }, |
| 270 | - { "li", MARKUP_LI, MUTYPE_LI, | |
| 270 | + { "li", MARKUP_LI, MUTYPE_LI, | |
| 271 | 271 | AMSK_TYPE|AMSK_VALUE }, |
| 272 | 272 | { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, |
| 273 | 273 | { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, |
| 274 | - { "ol", MARKUP_OL, MUTYPE_LIST, | |
| 274 | + { "ol", MARKUP_OL, MUTYPE_LIST, | |
| 275 | 275 | AMSK_START|AMSK_TYPE|AMSK_COMPACT }, |
| 276 | 276 | { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN }, |
| 277 | 277 | { "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 }, |
| 278 | 278 | { "s", MARKUP_S, MUTYPE_FONT, 0 }, |
| 279 | 279 | { "samp", MARKUP_SAMP, MUTYPE_FONT, 0 }, |
| @@ -280,27 +280,27 @@ | ||
| 280 | 280 | { "small", MARKUP_SMALL, MUTYPE_FONT, 0 }, |
| 281 | 281 | { "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 }, |
| 282 | 282 | { "strong", MARKUP_STRONG, MUTYPE_FONT, 0 }, |
| 283 | 283 | { "sub", MARKUP_SUB, MUTYPE_FONT, 0 }, |
| 284 | 284 | { "sup", MARKUP_SUP, MUTYPE_FONT, 0 }, |
| 285 | - { "table", MARKUP_TABLE, MUTYPE_TABLE, | |
| 285 | + { "table", MARKUP_TABLE, MUTYPE_TABLE, | |
| 286 | 286 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| |
| 287 | 287 | AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE }, |
| 288 | - { "td", MARKUP_TD, MUTYPE_TD, | |
| 288 | + { "td", MARKUP_TD, MUTYPE_TD, | |
| 289 | 289 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 290 | 290 | AMSK_ROWSPAN|AMSK_VALIGN }, |
| 291 | 291 | { "th", MARKUP_TH, MUTYPE_TD, |
| 292 | 292 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 293 | 293 | AMSK_ROWSPAN|AMSK_VALIGN }, |
| 294 | - { "tr", MARKUP_TR, MUTYPE_TR, | |
| 294 | + { "tr", MARKUP_TR, MUTYPE_TR, | |
| 295 | 295 | AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN }, |
| 296 | 296 | { "tt", MARKUP_TT, MUTYPE_FONT, 0 }, |
| 297 | 297 | { "u", MARKUP_U, MUTYPE_FONT, 0 }, |
| 298 | - { "ul", MARKUP_UL, MUTYPE_LIST, | |
| 298 | + { "ul", MARKUP_UL, MUTYPE_LIST, | |
| 299 | 299 | AMSK_TYPE|AMSK_COMPACT }, |
| 300 | 300 | { "var", MARKUP_VAR, MUTYPE_FONT, 0 }, |
| 301 | - { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID }, | |
| 301 | + { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID|AMSK_TYPE }, | |
| 302 | 302 | }; |
| 303 | 303 | |
| 304 | 304 | /* |
| 305 | 305 | ** Use binary search to locate a tag in the aMarkup[] table. |
| 306 | 306 | */ |
| @@ -436,11 +436,11 @@ | ||
| 436 | 436 | while( (c = z[0])!=0 && c!='<' && c!='&' && |
| 437 | 437 | (useWiki==0 || (c!='[' && c!='\n')) ){ |
| 438 | 438 | n++; |
| 439 | 439 | z++; |
| 440 | 440 | } |
| 441 | - return n; | |
| 441 | + return n; | |
| 442 | 442 | } |
| 443 | 443 | |
| 444 | 444 | /* |
| 445 | 445 | ** Return true if z[] begins with an HTML character element. |
| 446 | 446 | */ |
| @@ -550,11 +550,11 @@ | ||
| 550 | 550 | } |
| 551 | 551 | } |
| 552 | 552 | |
| 553 | 553 | /* |
| 554 | 554 | ** Get the next wiki token. |
| 555 | -** | |
| 555 | +** | |
| 556 | 556 | ** z points to the start of a token. Return the number of |
| 557 | 557 | ** characters in that token. Write the token type into *pTokenType. |
| 558 | 558 | */ |
| 559 | 559 | static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ |
| 560 | 560 | int n; |
| @@ -616,11 +616,11 @@ | ||
| 616 | 616 | return 1 + textLength(z+1, p->state & ALLOW_WIKI); |
| 617 | 617 | } |
| 618 | 618 | |
| 619 | 619 | /* |
| 620 | 620 | ** Parse only Wiki links, return everything else as TOKEN_RAW. |
| 621 | -** | |
| 621 | +** | |
| 622 | 622 | ** z points to the start of a token. Return the number of |
| 623 | 623 | ** characters in that token. Write the token type into *pTokenType. |
| 624 | 624 | */ |
| 625 | 625 | |
| 626 | 626 | static int nextRawToken(const char *z, Renderer *p, int *pTokenType){ |
| @@ -652,11 +652,11 @@ | ||
| 652 | 652 | |
| 653 | 653 | /* |
| 654 | 654 | ** z[] is an HTML markup element - something that begins with '<'. |
| 655 | 655 | ** Parse this element into the p structure. |
| 656 | 656 | ** |
| 657 | -** The content of z[] might be modified by converting characters | |
| 657 | +** The content of z[] might be modified by converting characters | |
| 658 | 658 | ** to lowercase and by inserting some "\000" characters. |
| 659 | 659 | */ |
| 660 | 660 | static void parseMarkup(ParsedMarkup *p, char *z){ |
| 661 | 661 | int i, j, c; |
| 662 | 662 | int iACode; |
| @@ -670,11 +670,11 @@ | ||
| 670 | 670 | }else{ |
| 671 | 671 | p->endTag = 0; |
| 672 | 672 | i = 1; |
| 673 | 673 | } |
| 674 | 674 | j = 0; |
| 675 | - while( isalnum(z[i]) ){ | |
| 675 | + while( isalnum(z[i]) ){ | |
| 676 | 676 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 677 | 677 | i++; |
| 678 | 678 | } |
| 679 | 679 | zTag[j] = 0; |
| 680 | 680 | p->iCode = findTag(zTag); |
| @@ -682,11 +682,11 @@ | ||
| 682 | 682 | p->nAttr = 0; |
| 683 | 683 | while( isspace(z[i]) ){ i++; } |
| 684 | 684 | while( p->nAttr<8 && isalpha(z[i]) ){ |
| 685 | 685 | int attrOk; /* True to preserver attribute. False to ignore it */ |
| 686 | 686 | j = 0; |
| 687 | - while( isalnum(z[i]) ){ | |
| 687 | + while( isalnum(z[i]) ){ | |
| 688 | 688 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 689 | 689 | i++; |
| 690 | 690 | } |
| 691 | 691 | zTag[j] = 0; |
| 692 | 692 | p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); |
| @@ -825,11 +825,11 @@ | ||
| 825 | 825 | } |
| 826 | 826 | } |
| 827 | 827 | |
| 828 | 828 | /* |
| 829 | 829 | ** Attempt to find a find a tag of type iTag with id zId. Return -1 |
| 830 | -** if not found. If found, return its stack level. | |
| 830 | +** if not found. If found, return its stack level. | |
| 831 | 831 | */ |
| 832 | 832 | static int findTagWithId(Renderer *p, int iTag, const char *zId){ |
| 833 | 833 | int i; |
| 834 | 834 | assert( zId!=0 ); |
| 835 | 835 | for(i=p->nStack-1; i>=0; i--){ |
| @@ -915,11 +915,11 @@ | ||
| 915 | 915 | canonical16(zLower, n+1); |
| 916 | 916 | memcpy(zUpper, zLower, n+1); |
| 917 | 917 | zUpper[n-1]++; |
| 918 | 918 | if( once ){ |
| 919 | 919 | const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'"); |
| 920 | - db_static_prepare(&q, | |
| 920 | + db_static_prepare(&q, | |
| 921 | 921 | "SELECT %s FROM ticket " |
| 922 | 922 | " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr", |
| 923 | 923 | zClosedExpr |
| 924 | 924 | ); |
| 925 | 925 | once = 0; |
| @@ -970,13 +970,13 @@ | ||
| 970 | 970 | int nClose /* Bytes available in zClose[] */ |
| 971 | 971 | ){ |
| 972 | 972 | const char *zTerm = "</a>"; |
| 973 | 973 | assert( nClose>=20 ); |
| 974 | 974 | |
| 975 | - if( strncmp(zTarget, "http:", 5)==0 | |
| 975 | + if( strncmp(zTarget, "http:", 5)==0 | |
| 976 | 976 | || strncmp(zTarget, "https:", 6)==0 |
| 977 | - || strncmp(zTarget, "ftp:", 4)==0 | |
| 977 | + || strncmp(zTarget, "ftp:", 4)==0 | |
| 978 | 978 | || strncmp(zTarget, "mailto:", 7)==0 |
| 979 | 979 | ){ |
| 980 | 980 | blob_appendf(p->pOut, "<a href=\"%s\">", zTarget); |
| 981 | 981 | /* zTerm = "⟾</a>"; // doesn't work on windows */ |
| 982 | 982 | }else if( zTarget[0]=='/' ){ |
| @@ -1218,11 +1218,11 @@ | ||
| 1218 | 1218 | int iDiv; |
| 1219 | 1219 | parseMarkup(&markup, z); |
| 1220 | 1220 | |
| 1221 | 1221 | /* Markup of the form </div id=ID> where there is a matching |
| 1222 | 1222 | ** ID somewhere on the stack. Exit the verbatim if were are in |
| 1223 | - ** it. Pop the stack up to the matching <div>. Discard the | |
| 1223 | + ** it. Pop the stack up to the matching <div>. Discard the | |
| 1224 | 1224 | ** </div> |
| 1225 | 1225 | */ |
| 1226 | 1226 | if( markup.iCode==MARKUP_DIV && markup.endTag && |
| 1227 | 1227 | (zId = markupId(&markup))!=0 && |
| 1228 | 1228 | (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0 |
| @@ -1242,11 +1242,11 @@ | ||
| 1242 | 1242 | p->nStack--; |
| 1243 | 1243 | }else |
| 1244 | 1244 | |
| 1245 | 1245 | /* If within <verbatim id=ID> ignore everything other than |
| 1246 | 1246 | ** </verbatim id=ID> and the </dev id=ID2> above. |
| 1247 | - */ | |
| 1247 | + */ | |
| 1248 | 1248 | if( p->inVerbatim ){ |
| 1249 | 1249 | if( endVerbatim(p, &markup) ){ |
| 1250 | 1250 | p->inVerbatim = 0; |
| 1251 | 1251 | p->state = p->preVerbState; |
| 1252 | 1252 | blob_append(p->pOut, "</pre>", 6); |
| @@ -1300,22 +1300,29 @@ | ||
| 1300 | 1300 | (p->state & ALLOW_WIKI)!=0); |
| 1301 | 1301 | }else |
| 1302 | 1302 | |
| 1303 | 1303 | /* Enter <verbatim> processing. With verbatim enabled, all other |
| 1304 | 1304 | ** markup other than the corresponding end-tag with the same ID is |
| 1305 | - ** ignored. | |
| 1305 | + ** ignored. | |
| 1306 | 1306 | */ |
| 1307 | 1307 | if( markup.iCode==MARKUP_VERBATIM ){ |
| 1308 | - if( markup.nAttr==1 ){ | |
| 1309 | - p->zVerbatimId = markup.aAttr[0].zValue; | |
| 1310 | - }else{ | |
| 1311 | - p->zVerbatimId = 0; | |
| 1312 | - } | |
| 1308 | + int vAttrIdx, vAttrDidAppend=0; | |
| 1309 | + p->zVerbatimId = 0; | |
| 1313 | 1310 | p->inVerbatim = 1; |
| 1314 | 1311 | p->preVerbState = p->state; |
| 1315 | 1312 | p->state &= ~ALLOW_WIKI; |
| 1316 | - blob_append(p->pOut, "<pre class='verbatim'>",-1); | |
| 1313 | + for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){ | |
| 1314 | + if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){ | |
| 1315 | + p->zVerbatimId = markup.aAttr[0].zValue; | |
| 1316 | + }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){ | |
| 1317 | + blob_appendf(p->pOut, "<pre name='code' class='%s'>", | |
| 1318 | + markup.aAttr[vAttrIdx].zValue); | |
| 1319 | + vAttrDidAppend=1; | |
| 1320 | + } | |
| 1321 | + } | |
| 1322 | + if( !vAttrDidAppend ) | |
| 1323 | + blob_append(p->pOut, "<pre class='verbatim'>",-1); | |
| 1317 | 1324 | p->wantAutoParagraph = 0; |
| 1318 | 1325 | }else |
| 1319 | 1326 | if( markup.iType==MUTYPE_LI ){ |
| 1320 | 1327 | if( backupToType(p, MUTYPE_LIST)==0 ){ |
| 1321 | 1328 | pushStack(p, MARKUP_UL); |
| @@ -1373,11 +1380,11 @@ | ||
| 1373 | 1380 | ** reply. |
| 1374 | 1381 | */ |
| 1375 | 1382 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1376 | 1383 | char *z; |
| 1377 | 1384 | Renderer renderer; |
| 1378 | - | |
| 1385 | + | |
| 1379 | 1386 | memset(&renderer, 0, sizeof(renderer)); |
| 1380 | 1387 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; |
| 1381 | 1388 | if( flags & WIKI_NOBLOCK ){ |
| 1382 | 1389 | renderer.state |= INLINE_MARKUP_ONLY; |
| 1383 | 1390 | } |
| @@ -1419,11 +1426,11 @@ | ||
| 1419 | 1426 | |
| 1420 | 1427 | /* |
| 1421 | 1428 | ** Search for a <title>...</title> at the beginning of a wiki page. |
| 1422 | 1429 | ** Return true (nonzero) if a title is found. Return zero if there is |
| 1423 | 1430 | ** not title. |
| 1424 | -** | |
| 1431 | +** | |
| 1425 | 1432 | ** If a title is found, initialize the pTitle blob to be the content |
| 1426 | 1433 | ** of the title and initialize pTail to be the text that follows the |
| 1427 | 1434 | ** title. |
| 1428 | 1435 | */ |
| 1429 | 1436 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1430 | 1437 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -7,11 +7,11 @@ | |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 11 | ** General Public License for more details. |
| 12 | ** |
| 13 | ** You should have received a copy of the GNU General Public |
| 14 | ** License along with this library; if not, write to the |
| 15 | ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 16 | ** Boston, MA 02111-1307, USA. |
| 17 | ** |
| @@ -150,11 +150,11 @@ | |
| 150 | /* |
| 151 | ** Allowed markup. |
| 152 | ** |
| 153 | ** Except for MARKUP_INVALID, this must all be in alphabetical order |
| 154 | ** and in numerical sequence. The first markup type must be zero. |
| 155 | ** The value for MARKUP_XYZ must correspond to the <xyz> entry |
| 156 | ** in aAllowedMarkup[]. |
| 157 | */ |
| 158 | #define MARKUP_INVALID 0 |
| 159 | #define MARKUP_A 1 |
| 160 | #define MARKUP_ADDRESS 2 |
| @@ -258,22 +258,22 @@ | |
| 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,27 +280,27 @@ | |
| 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 | */ |
| @@ -436,11 +436,11 @@ | |
| 436 | while( (c = z[0])!=0 && c!='<' && c!='&' && |
| 437 | (useWiki==0 || (c!='[' && c!='\n')) ){ |
| 438 | n++; |
| 439 | z++; |
| 440 | } |
| 441 | return n; |
| 442 | } |
| 443 | |
| 444 | /* |
| 445 | ** Return true if z[] begins with an HTML character element. |
| 446 | */ |
| @@ -550,11 +550,11 @@ | |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | /* |
| 554 | ** Get the next wiki token. |
| 555 | ** |
| 556 | ** z points to the start of a token. Return the number of |
| 557 | ** characters in that token. Write the token type into *pTokenType. |
| 558 | */ |
| 559 | static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ |
| 560 | int n; |
| @@ -616,11 +616,11 @@ | |
| 616 | return 1 + textLength(z+1, p->state & ALLOW_WIKI); |
| 617 | } |
| 618 | |
| 619 | /* |
| 620 | ** Parse only Wiki links, return everything else as TOKEN_RAW. |
| 621 | ** |
| 622 | ** z points to the start of a token. Return the number of |
| 623 | ** characters in that token. Write the token type into *pTokenType. |
| 624 | */ |
| 625 | |
| 626 | static int nextRawToken(const char *z, Renderer *p, int *pTokenType){ |
| @@ -652,11 +652,11 @@ | |
| 652 | |
| 653 | /* |
| 654 | ** z[] is an HTML markup element - something that begins with '<'. |
| 655 | ** Parse this element into the p structure. |
| 656 | ** |
| 657 | ** The content of z[] might be modified by converting characters |
| 658 | ** to lowercase and by inserting some "\000" characters. |
| 659 | */ |
| 660 | static void parseMarkup(ParsedMarkup *p, char *z){ |
| 661 | int i, j, c; |
| 662 | int iACode; |
| @@ -670,11 +670,11 @@ | |
| 670 | }else{ |
| 671 | p->endTag = 0; |
| 672 | i = 1; |
| 673 | } |
| 674 | j = 0; |
| 675 | while( isalnum(z[i]) ){ |
| 676 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 677 | i++; |
| 678 | } |
| 679 | zTag[j] = 0; |
| 680 | p->iCode = findTag(zTag); |
| @@ -682,11 +682,11 @@ | |
| 682 | p->nAttr = 0; |
| 683 | while( isspace(z[i]) ){ i++; } |
| 684 | while( p->nAttr<8 && isalpha(z[i]) ){ |
| 685 | int attrOk; /* True to preserver attribute. False to ignore it */ |
| 686 | j = 0; |
| 687 | while( isalnum(z[i]) ){ |
| 688 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 689 | i++; |
| 690 | } |
| 691 | zTag[j] = 0; |
| 692 | p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); |
| @@ -825,11 +825,11 @@ | |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | /* |
| 829 | ** Attempt to find a find a tag of type iTag with id zId. Return -1 |
| 830 | ** if not found. If found, return its stack level. |
| 831 | */ |
| 832 | static int findTagWithId(Renderer *p, int iTag, const char *zId){ |
| 833 | int i; |
| 834 | assert( zId!=0 ); |
| 835 | for(i=p->nStack-1; i>=0; i--){ |
| @@ -915,11 +915,11 @@ | |
| 915 | canonical16(zLower, n+1); |
| 916 | memcpy(zUpper, zLower, n+1); |
| 917 | zUpper[n-1]++; |
| 918 | if( once ){ |
| 919 | const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'"); |
| 920 | db_static_prepare(&q, |
| 921 | "SELECT %s FROM ticket " |
| 922 | " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr", |
| 923 | zClosedExpr |
| 924 | ); |
| 925 | once = 0; |
| @@ -970,13 +970,13 @@ | |
| 970 | int nClose /* Bytes available in zClose[] */ |
| 971 | ){ |
| 972 | const char *zTerm = "</a>"; |
| 973 | assert( nClose>=20 ); |
| 974 | |
| 975 | if( strncmp(zTarget, "http:", 5)==0 |
| 976 | || strncmp(zTarget, "https:", 6)==0 |
| 977 | || strncmp(zTarget, "ftp:", 4)==0 |
| 978 | || strncmp(zTarget, "mailto:", 7)==0 |
| 979 | ){ |
| 980 | blob_appendf(p->pOut, "<a href=\"%s\">", zTarget); |
| 981 | /* zTerm = "⟾</a>"; // doesn't work on windows */ |
| 982 | }else if( zTarget[0]=='/' ){ |
| @@ -1218,11 +1218,11 @@ | |
| 1218 | int iDiv; |
| 1219 | parseMarkup(&markup, z); |
| 1220 | |
| 1221 | /* Markup of the form </div id=ID> where there is a matching |
| 1222 | ** ID somewhere on the stack. Exit the verbatim if were are in |
| 1223 | ** it. Pop the stack up to the matching <div>. Discard the |
| 1224 | ** </div> |
| 1225 | */ |
| 1226 | if( markup.iCode==MARKUP_DIV && markup.endTag && |
| 1227 | (zId = markupId(&markup))!=0 && |
| 1228 | (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0 |
| @@ -1242,11 +1242,11 @@ | |
| 1242 | p->nStack--; |
| 1243 | }else |
| 1244 | |
| 1245 | /* If within <verbatim id=ID> ignore everything other than |
| 1246 | ** </verbatim id=ID> and the </dev id=ID2> above. |
| 1247 | */ |
| 1248 | if( p->inVerbatim ){ |
| 1249 | if( endVerbatim(p, &markup) ){ |
| 1250 | p->inVerbatim = 0; |
| 1251 | p->state = p->preVerbState; |
| 1252 | blob_append(p->pOut, "</pre>", 6); |
| @@ -1300,22 +1300,29 @@ | |
| 1300 | (p->state & ALLOW_WIKI)!=0); |
| 1301 | }else |
| 1302 | |
| 1303 | /* Enter <verbatim> processing. With verbatim enabled, all other |
| 1304 | ** markup other than the corresponding end-tag with the same ID is |
| 1305 | ** ignored. |
| 1306 | */ |
| 1307 | if( markup.iCode==MARKUP_VERBATIM ){ |
| 1308 | if( markup.nAttr==1 ){ |
| 1309 | p->zVerbatimId = markup.aAttr[0].zValue; |
| 1310 | }else{ |
| 1311 | p->zVerbatimId = 0; |
| 1312 | } |
| 1313 | p->inVerbatim = 1; |
| 1314 | p->preVerbState = p->state; |
| 1315 | p->state &= ~ALLOW_WIKI; |
| 1316 | blob_append(p->pOut, "<pre class='verbatim'>",-1); |
| 1317 | p->wantAutoParagraph = 0; |
| 1318 | }else |
| 1319 | if( markup.iType==MUTYPE_LI ){ |
| 1320 | if( backupToType(p, MUTYPE_LIST)==0 ){ |
| 1321 | pushStack(p, MARKUP_UL); |
| @@ -1373,11 +1380,11 @@ | |
| 1373 | ** reply. |
| 1374 | */ |
| 1375 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1376 | char *z; |
| 1377 | Renderer renderer; |
| 1378 | |
| 1379 | memset(&renderer, 0, sizeof(renderer)); |
| 1380 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; |
| 1381 | if( flags & WIKI_NOBLOCK ){ |
| 1382 | renderer.state |= INLINE_MARKUP_ONLY; |
| 1383 | } |
| @@ -1419,11 +1426,11 @@ | |
| 1419 | |
| 1420 | /* |
| 1421 | ** Search for a <title>...</title> at the beginning of a wiki page. |
| 1422 | ** Return true (nonzero) if a title is found. Return zero if there is |
| 1423 | ** not title. |
| 1424 | ** |
| 1425 | ** If a title is found, initialize the pTitle blob to be the content |
| 1426 | ** of the title and initialize pTail to be the text that follows the |
| 1427 | ** title. |
| 1428 | */ |
| 1429 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1430 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -7,11 +7,11 @@ | |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 11 | ** General Public License for more details. |
| 12 | ** |
| 13 | ** You should have received a copy of the GNU General Public |
| 14 | ** License along with this library; if not, write to the |
| 15 | ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| 16 | ** Boston, MA 02111-1307, USA. |
| 17 | ** |
| @@ -150,11 +150,11 @@ | |
| 150 | /* |
| 151 | ** Allowed markup. |
| 152 | ** |
| 153 | ** Except for MARKUP_INVALID, this must all be in alphabetical order |
| 154 | ** and in numerical sequence. The first markup type must be zero. |
| 155 | ** The value for MARKUP_XYZ must correspond to the <xyz> entry |
| 156 | ** in aAllowedMarkup[]. |
| 157 | */ |
| 158 | #define MARKUP_INVALID 0 |
| 159 | #define MARKUP_A 1 |
| 160 | #define MARKUP_ADDRESS 2 |
| @@ -258,22 +258,22 @@ | |
| 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,27 +280,27 @@ | |
| 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|AMSK_TYPE }, |
| 302 | }; |
| 303 | |
| 304 | /* |
| 305 | ** Use binary search to locate a tag in the aMarkup[] table. |
| 306 | */ |
| @@ -436,11 +436,11 @@ | |
| 436 | while( (c = z[0])!=0 && c!='<' && c!='&' && |
| 437 | (useWiki==0 || (c!='[' && c!='\n')) ){ |
| 438 | n++; |
| 439 | z++; |
| 440 | } |
| 441 | return n; |
| 442 | } |
| 443 | |
| 444 | /* |
| 445 | ** Return true if z[] begins with an HTML character element. |
| 446 | */ |
| @@ -550,11 +550,11 @@ | |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | /* |
| 554 | ** Get the next wiki token. |
| 555 | ** |
| 556 | ** z points to the start of a token. Return the number of |
| 557 | ** characters in that token. Write the token type into *pTokenType. |
| 558 | */ |
| 559 | static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ |
| 560 | int n; |
| @@ -616,11 +616,11 @@ | |
| 616 | return 1 + textLength(z+1, p->state & ALLOW_WIKI); |
| 617 | } |
| 618 | |
| 619 | /* |
| 620 | ** Parse only Wiki links, return everything else as TOKEN_RAW. |
| 621 | ** |
| 622 | ** z points to the start of a token. Return the number of |
| 623 | ** characters in that token. Write the token type into *pTokenType. |
| 624 | */ |
| 625 | |
| 626 | static int nextRawToken(const char *z, Renderer *p, int *pTokenType){ |
| @@ -652,11 +652,11 @@ | |
| 652 | |
| 653 | /* |
| 654 | ** z[] is an HTML markup element - something that begins with '<'. |
| 655 | ** Parse this element into the p structure. |
| 656 | ** |
| 657 | ** The content of z[] might be modified by converting characters |
| 658 | ** to lowercase and by inserting some "\000" characters. |
| 659 | */ |
| 660 | static void parseMarkup(ParsedMarkup *p, char *z){ |
| 661 | int i, j, c; |
| 662 | int iACode; |
| @@ -670,11 +670,11 @@ | |
| 670 | }else{ |
| 671 | p->endTag = 0; |
| 672 | i = 1; |
| 673 | } |
| 674 | j = 0; |
| 675 | while( isalnum(z[i]) ){ |
| 676 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 677 | i++; |
| 678 | } |
| 679 | zTag[j] = 0; |
| 680 | p->iCode = findTag(zTag); |
| @@ -682,11 +682,11 @@ | |
| 682 | p->nAttr = 0; |
| 683 | while( isspace(z[i]) ){ i++; } |
| 684 | while( p->nAttr<8 && isalpha(z[i]) ){ |
| 685 | int attrOk; /* True to preserver attribute. False to ignore it */ |
| 686 | j = 0; |
| 687 | while( isalnum(z[i]) ){ |
| 688 | if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); |
| 689 | i++; |
| 690 | } |
| 691 | zTag[j] = 0; |
| 692 | p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); |
| @@ -825,11 +825,11 @@ | |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | /* |
| 829 | ** Attempt to find a find a tag of type iTag with id zId. Return -1 |
| 830 | ** if not found. If found, return its stack level. |
| 831 | */ |
| 832 | static int findTagWithId(Renderer *p, int iTag, const char *zId){ |
| 833 | int i; |
| 834 | assert( zId!=0 ); |
| 835 | for(i=p->nStack-1; i>=0; i--){ |
| @@ -915,11 +915,11 @@ | |
| 915 | canonical16(zLower, n+1); |
| 916 | memcpy(zUpper, zLower, n+1); |
| 917 | zUpper[n-1]++; |
| 918 | if( once ){ |
| 919 | const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'"); |
| 920 | db_static_prepare(&q, |
| 921 | "SELECT %s FROM ticket " |
| 922 | " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr", |
| 923 | zClosedExpr |
| 924 | ); |
| 925 | once = 0; |
| @@ -970,13 +970,13 @@ | |
| 970 | int nClose /* Bytes available in zClose[] */ |
| 971 | ){ |
| 972 | const char *zTerm = "</a>"; |
| 973 | assert( nClose>=20 ); |
| 974 | |
| 975 | if( strncmp(zTarget, "http:", 5)==0 |
| 976 | || strncmp(zTarget, "https:", 6)==0 |
| 977 | || strncmp(zTarget, "ftp:", 4)==0 |
| 978 | || strncmp(zTarget, "mailto:", 7)==0 |
| 979 | ){ |
| 980 | blob_appendf(p->pOut, "<a href=\"%s\">", zTarget); |
| 981 | /* zTerm = "⟾</a>"; // doesn't work on windows */ |
| 982 | }else if( zTarget[0]=='/' ){ |
| @@ -1218,11 +1218,11 @@ | |
| 1218 | int iDiv; |
| 1219 | parseMarkup(&markup, z); |
| 1220 | |
| 1221 | /* Markup of the form </div id=ID> where there is a matching |
| 1222 | ** ID somewhere on the stack. Exit the verbatim if were are in |
| 1223 | ** it. Pop the stack up to the matching <div>. Discard the |
| 1224 | ** </div> |
| 1225 | */ |
| 1226 | if( markup.iCode==MARKUP_DIV && markup.endTag && |
| 1227 | (zId = markupId(&markup))!=0 && |
| 1228 | (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0 |
| @@ -1242,11 +1242,11 @@ | |
| 1242 | p->nStack--; |
| 1243 | }else |
| 1244 | |
| 1245 | /* If within <verbatim id=ID> ignore everything other than |
| 1246 | ** </verbatim id=ID> and the </dev id=ID2> above. |
| 1247 | */ |
| 1248 | if( p->inVerbatim ){ |
| 1249 | if( endVerbatim(p, &markup) ){ |
| 1250 | p->inVerbatim = 0; |
| 1251 | p->state = p->preVerbState; |
| 1252 | blob_append(p->pOut, "</pre>", 6); |
| @@ -1300,22 +1300,29 @@ | |
| 1300 | (p->state & ALLOW_WIKI)!=0); |
| 1301 | }else |
| 1302 | |
| 1303 | /* Enter <verbatim> processing. With verbatim enabled, all other |
| 1304 | ** markup other than the corresponding end-tag with the same ID is |
| 1305 | ** ignored. |
| 1306 | */ |
| 1307 | if( markup.iCode==MARKUP_VERBATIM ){ |
| 1308 | int vAttrIdx, vAttrDidAppend=0; |
| 1309 | p->zVerbatimId = 0; |
| 1310 | p->inVerbatim = 1; |
| 1311 | p->preVerbState = p->state; |
| 1312 | p->state &= ~ALLOW_WIKI; |
| 1313 | for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){ |
| 1314 | if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){ |
| 1315 | p->zVerbatimId = markup.aAttr[0].zValue; |
| 1316 | }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){ |
| 1317 | blob_appendf(p->pOut, "<pre name='code' class='%s'>", |
| 1318 | markup.aAttr[vAttrIdx].zValue); |
| 1319 | vAttrDidAppend=1; |
| 1320 | } |
| 1321 | } |
| 1322 | if( !vAttrDidAppend ) |
| 1323 | blob_append(p->pOut, "<pre class='verbatim'>",-1); |
| 1324 | p->wantAutoParagraph = 0; |
| 1325 | }else |
| 1326 | if( markup.iType==MUTYPE_LI ){ |
| 1327 | if( backupToType(p, MUTYPE_LIST)==0 ){ |
| 1328 | pushStack(p, MARKUP_UL); |
| @@ -1373,11 +1380,11 @@ | |
| 1380 | ** reply. |
| 1381 | */ |
| 1382 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1383 | char *z; |
| 1384 | Renderer renderer; |
| 1385 | |
| 1386 | memset(&renderer, 0, sizeof(renderer)); |
| 1387 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; |
| 1388 | if( flags & WIKI_NOBLOCK ){ |
| 1389 | renderer.state |= INLINE_MARKUP_ONLY; |
| 1390 | } |
| @@ -1419,11 +1426,11 @@ | |
| 1426 | |
| 1427 | /* |
| 1428 | ** Search for a <title>...</title> at the beginning of a wiki page. |
| 1429 | ** Return true (nonzero) if a title is found. Return zero if there is |
| 1430 | ** not title. |
| 1431 | ** |
| 1432 | ** If a title is found, initialize the pTitle blob to be the content |
| 1433 | ** of the title and initialize pTail to be the text that follows the |
| 1434 | ** title. |
| 1435 | */ |
| 1436 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1437 |