Overview
SHA1 Hash: | e01aa8cb4b23e512b16f6d0bd05646b578e5531e |
---|---|
Date: | 2008-07-17 15:49:24 |
User: | drh |
Comment: | 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. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/wiki.c from [185275fa54] to [26fbd5a93f].
@@ -292,11 +292,11 @@ } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } if( n<20 ) n = 20; - if( n>200 ) n = 200; + if( n>40 ) n = 40; @ <form method="POST" action="%s(g.zBaseURL)/wikiedit"> @ <input type="hidden" name="name" value="%h(zPageName)"> @ <textarea name="w" class="wikiedit" cols="80" @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> @ <br> @@ -315,20 +315,23 @@ */ static void appendRemark(Blob *p){ char *zDate; const char *zUser; const char *zRemark; + char *zId; zDate = db_text(0, "SELECT datetime('now')"); - blob_appendf(p, "\n\n<hr><i>On %s UTC %h", zDate, g.zLogin); + zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); + blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", + zId, zDate, g.zLogin); free(zDate); zUser = PD("u",g.zLogin); if( zUser[0] && strcmp(zUser,g.zLogin) ){ blob_appendf(p, " (claiming to be %h)", zUser); } zRemark = PD("r",""); - blob_appendf(p, " added:</i><br />\n%s", zRemark); + blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId); } /* ** WEBPAGE: wikiappend ** URL: /wikiappend?name=PAGENAME @@ -352,11 +355,11 @@ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " ORDER BY mtime DESC", zTag ); free(zTag); if( !rid ){ - cgi_redirect("index"); + fossil_redirect_home(); return; } } if( !g.okApndWiki ){ login_needed(); @@ -378,11 +381,11 @@ db_set("sandbox", blob_str(&body), 0); }else{ content_get(rid, &content); manifest_parse(&m, &content); if( m.type==CFTYPE_WIKI ){ - blob_appendf(&body, m.zWiki, -1); + blob_append(&body, m.zWiki, -1); } manifest_clear(&m); blob_zero(&wiki); db_begin_transaction(); zDate = db_text(0, "SELECT datetime('now')"); @@ -395,11 +398,10 @@ free(zUuid); } if( g.zLogin ){ blob_appendf(&wiki, "U %F\n", g.zLogin); } - blob_appendf(&body, "\n<hr>\n"); appendRemark(&body); blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body)); md5sum_blob(&wiki, &cksum); blob_appendf(&wiki, "Z %b\n", &cksum); blob_reset(&cksum); @@ -555,10 +557,11 @@ @ <center> @ <cite> @ <code> @ <dd> @ <dfn> + @ <div> @ <dl> @ <dt> @ <em> @ <font> @ <h1>
Modified src/wikiformat.c from [e49b2821f4] to [55aa7e0f7e].
@@ -39,66 +39,91 @@ /* ** These are the only markup attributes allowed. */ -#define ATTR_ALIGN 0x0000001 -#define ATTR_ALT 0x0000002 -#define ATTR_BGCOLOR 0x0000004 -#define ATTR_BORDER 0x0000008 -#define ATTR_CELLPADDING 0x0000010 -#define ATTR_CELLSPACING 0x0000020 -#define ATTR_CLEAR 0x0000040 -#define ATTR_COLOR 0x0000080 -#define ATTR_COLSPAN 0x0000100 -#define ATTR_COMPACT 0x0000200 -#define ATTR_FACE 0x0000400 -#define ATTR_HEIGHT 0x0000800 -#define ATTR_HREF 0x0001000 -#define ATTR_HSPACE 0x0002000 -#define ATTR_ID 0x0004000 -#define ATTR_NAME 0x0008000 -#define ATTR_ROWSPAN 0x0010000 -#define ATTR_SIZE 0x0020000 -#define ATTR_SRC 0x0040000 -#define ATTR_START 0x0080000 -#define ATTR_TYPE 0x0100000 -#define ATTR_VALIGN 0x0200000 -#define ATTR_VALUE 0x0400000 -#define ATTR_VSPACE 0x0800000 -#define ATTR_WIDTH 0x1000000 +#define ATTR_ALIGN 1 +#define ATTR_ALT 2 +#define ATTR_BGCOLOR 3 +#define ATTR_BORDER 4 +#define ATTR_CELLPADDING 5 +#define ATTR_CELLSPACING 6 +#define ATTR_CLEAR 7 +#define ATTR_COLOR 8 +#define ATTR_COLSPAN 9 +#define ATTR_COMPACT 10 +#define ATTR_FACE 11 +#define ATTR_HEIGHT 12 +#define ATTR_HREF 13 +#define ATTR_HSPACE 14 +#define ATTR_ID 15 +#define ATTR_NAME 16 +#define ATTR_ROWSPAN 17 +#define ATTR_SIZE 18 +#define ATTR_SRC 19 +#define ATTR_START 20 +#define ATTR_TYPE 21 +#define ATTR_VALIGN 22 +#define ATTR_VALUE 23 +#define ATTR_VSPACE 24 +#define ATTR_WIDTH 25 +#define AMSK_ALIGN 0x0000001 +#define AMSK_ALT 0x0000002 +#define AMSK_BGCOLOR 0x0000004 +#define AMSK_BORDER 0x0000008 +#define AMSK_CELLPADDING 0x0000010 +#define AMSK_CELLSPACING 0x0000020 +#define AMSK_CLEAR 0x0000040 +#define AMSK_COLOR 0x0000080 +#define AMSK_COLSPAN 0x0000100 +#define AMSK_COMPACT 0x0000200 +#define AMSK_FACE 0x0000400 +#define AMSK_HEIGHT 0x0000800 +#define AMSK_HREF 0x0001000 +#define AMSK_HSPACE 0x0002000 +#define AMSK_ID 0x0004000 +#define AMSK_NAME 0x0008000 +#define AMSK_ROWSPAN 0x0010000 +#define AMSK_SIZE 0x0020000 +#define AMSK_SRC 0x0040000 +#define AMSK_START 0x0080000 +#define AMSK_TYPE 0x0100000 +#define AMSK_VALIGN 0x0200000 +#define AMSK_VALUE 0x0400000 +#define AMSK_VSPACE 0x0800000 +#define AMSK_WIDTH 0x1000000 static const struct AllowedAttribute { const char *zName; unsigned int iMask; } aAttribute[] = { { 0, 0 }, - { "align", ATTR_ALIGN, }, - { "alt", ATTR_ALT, }, - { "bgcolor", ATTR_BGCOLOR, }, - { "border", ATTR_BORDER, }, - { "cellpadding", ATTR_CELLPADDING, }, - { "cellspacing", ATTR_CELLSPACING, }, - { "clear", ATTR_CLEAR, }, - { "color", ATTR_COLOR, }, - { "colspan", ATTR_COLSPAN, }, - { "compact", ATTR_COMPACT, }, - { "face", ATTR_FACE, }, - { "height", ATTR_HEIGHT, }, - { "href", ATTR_HREF, }, - { "hspace", ATTR_HSPACE, }, - { "id", ATTR_ID, }, - { "name", ATTR_NAME, }, - { "rowspan", ATTR_ROWSPAN, }, - { "size", ATTR_SIZE, }, - { "src", ATTR_SRC, }, - { "start", ATTR_START, }, - { "type", ATTR_TYPE, }, - { "valign", ATTR_VALIGN, }, - { "value", ATTR_VALUE, }, - { "vspace", ATTR_VSPACE, }, - { "width", ATTR_WIDTH, }, + { "align", AMSK_ALIGN, }, + { "alt", AMSK_ALT, }, + { "bgcolor", AMSK_BGCOLOR, }, + { "border", AMSK_BORDER, }, + { "cellpadding", AMSK_CELLPADDING, }, + { "cellspacing", AMSK_CELLSPACING, }, + { "clear", AMSK_CLEAR, }, + { "color", AMSK_COLOR, }, + { "colspan", AMSK_COLSPAN, }, + { "compact", AMSK_COMPACT, }, + { "face", AMSK_FACE, }, + { "height", AMSK_HEIGHT, }, + { "href", AMSK_HREF, }, + { "hspace", AMSK_HSPACE, }, + { "id", AMSK_ID, }, + { "name", AMSK_NAME, }, + { "rowspan", AMSK_ROWSPAN, }, + { "size", AMSK_SIZE, }, + { "src", AMSK_SRC, }, + { "start", AMSK_START, }, + { "type", AMSK_TYPE, }, + { "valign", AMSK_VALIGN, }, + { "value", AMSK_VALUE, }, + { "vspace", AMSK_VSPACE, }, + { "width", AMSK_WIDTH, }, }; /* ** Use binary search to locate a tag in the aAttribute[] table. */ @@ -140,46 +165,47 @@ #define MARKUP_CENTER 7 #define MARKUP_CITE 8 #define MARKUP_CODE 9 #define MARKUP_DD 10 #define MARKUP_DFN 11 -#define MARKUP_DL 12 -#define MARKUP_DT 13 -#define MARKUP_EM 14 -#define MARKUP_FONT 15 -#define MARKUP_H1 16 -#define MARKUP_H2 17 -#define MARKUP_H3 18 -#define MARKUP_H4 19 -#define MARKUP_H5 20 -#define MARKUP_H6 21 -#define MARKUP_HR 22 -#define MARKUP_I 23 -#define MARKUP_IMG 24 -#define MARKUP_KBD 25 -#define MARKUP_LI 26 -#define MARKUP_NOBR 27 -#define MARKUP_NOWIKI 28 -#define MARKUP_OL 29 -#define MARKUP_P 30 -#define MARKUP_PRE 31 -#define MARKUP_S 32 -#define MARKUP_SAMP 33 -#define MARKUP_SMALL 34 -#define MARKUP_STRIKE 35 -#define MARKUP_STRONG 36 -#define MARKUP_SUB 37 -#define MARKUP_SUP 38 -#define MARKUP_TABLE 39 -#define MARKUP_TD 40 -#define MARKUP_TH 41 -#define MARKUP_TR 42 -#define MARKUP_TT 43 -#define MARKUP_U 44 -#define MARKUP_UL 45 -#define MARKUP_VAR 46 -#define MARKUP_VERBATIM 47 +#define MARKUP_DIV 12 +#define MARKUP_DL 13 +#define MARKUP_DT 14 +#define MARKUP_EM 15 +#define MARKUP_FONT 16 +#define MARKUP_H1 17 +#define MARKUP_H2 18 +#define MARKUP_H3 19 +#define MARKUP_H4 20 +#define MARKUP_H5 21 +#define MARKUP_H6 22 +#define MARKUP_HR 23 +#define MARKUP_I 24 +#define MARKUP_IMG 25 +#define MARKUP_KBD 26 +#define MARKUP_LI 27 +#define MARKUP_NOBR 28 +#define MARKUP_NOWIKI 29 +#define MARKUP_OL 30 +#define MARKUP_P 31 +#define MARKUP_PRE 32 +#define MARKUP_S 33 +#define MARKUP_SAMP 34 +#define MARKUP_SMALL 35 +#define MARKUP_STRIKE 36 +#define MARKUP_STRONG 37 +#define MARKUP_SUB 38 +#define MARKUP_SUP 39 +#define MARKUP_TABLE 40 +#define MARKUP_TD 41 +#define MARKUP_TH 42 +#define MARKUP_TR 43 +#define MARKUP_TT 44 +#define MARKUP_U 45 +#define MARKUP_UL 46 +#define MARKUP_VAR 47 +#define MARKUP_VERBATIM 48 /* ** The various markup is divided into the following types: */ #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */ @@ -209,71 +235,72 @@ short int iType; /* The MUTYPE_* code */ int allowedAttr; /* Allowed attributes on this markup */ } aMarkup[] = { { 0, MARKUP_INVALID, 0, 0 }, { "a", MARKUP_A, MUTYPE_HYPERLINK, - ATTR_HREF|ATTR_NAME }, + AMSK_HREF|AMSK_NAME }, { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 }, { "b", MARKUP_B, MUTYPE_FONT, 0 }, { "big", MARKUP_BIG, MUTYPE_FONT, 0 }, { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 }, - { "br", MARKUP_BR, MUTYPE_SINGLE, ATTR_CLEAR }, + { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, { "center", MARKUP_CENTER, MUTYPE_BLOCK, 0 }, { "cite", MARKUP_CITE, MUTYPE_FONT, 0 }, { "code", MARKUP_CODE, MUTYPE_FONT, 0 }, { "dd", MARKUP_DD, MUTYPE_LI, 0 }, { "dfn", MARKUP_DFN, MUTYPE_FONT, 0 }, - { "dl", MARKUP_DL, MUTYPE_LIST, ATTR_COMPACT }, + { "div", MARKUP_DIV, MUTYPE_BLOCK, AMSK_ID }, + { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_COMPACT }, { "dt", MARKUP_DT, MUTYPE_LI, 0 }, { "em", MARKUP_EM, MUTYPE_FONT, 0 }, { "font", MARKUP_FONT, MUTYPE_FONT, - ATTR_COLOR|ATTR_FACE|ATTR_SIZE }, - { "h1", MARKUP_H1, MUTYPE_BLOCK, ATTR_ALIGN }, - { "h2", MARKUP_H2, MUTYPE_BLOCK, ATTR_ALIGN }, - { "h3", MARKUP_H3, MUTYPE_BLOCK, ATTR_ALIGN }, - { "h4", MARKUP_H4, MUTYPE_BLOCK, ATTR_ALIGN }, - { "h5", MARKUP_H5, MUTYPE_BLOCK, ATTR_ALIGN }, - { "h6", MARKUP_H6, MUTYPE_BLOCK, ATTR_ALIGN }, + AMSK_COLOR|AMSK_FACE|AMSK_SIZE }, + { "h1", MARKUP_H1, MUTYPE_BLOCK, AMSK_ALIGN }, + { "h2", MARKUP_H2, MUTYPE_BLOCK, AMSK_ALIGN }, + { "h3", MARKUP_H3, MUTYPE_BLOCK, AMSK_ALIGN }, + { "h4", MARKUP_H4, MUTYPE_BLOCK, AMSK_ALIGN }, + { "h5", MARKUP_H5, MUTYPE_BLOCK, AMSK_ALIGN }, + { "h6", MARKUP_H6, MUTYPE_BLOCK, AMSK_ALIGN }, { "hr", MARKUP_HR, MUTYPE_SINGLE, - ATTR_ALIGN|ATTR_COLOR|ATTR_SIZE|ATTR_WIDTH }, + AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH }, { "i", MARKUP_I, MUTYPE_FONT, 0 }, { "img", MARKUP_IMG, MUTYPE_SINGLE, - ATTR_ALIGN|ATTR_ALT|ATTR_BORDER|ATTR_HEIGHT| - ATTR_HSPACE|ATTR_SRC|ATTR_VSPACE|ATTR_WIDTH }, + AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT| + AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH }, { "kbd", MARKUP_KBD, MUTYPE_FONT, 0 }, { "li", MARKUP_LI, MUTYPE_LI, - ATTR_TYPE|ATTR_VALUE }, + AMSK_TYPE|AMSK_VALUE }, { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, { "ol", MARKUP_OL, MUTYPE_LIST, - ATTR_START|ATTR_TYPE|ATTR_COMPACT }, - { "p", MARKUP_P, MUTYPE_BLOCK, ATTR_ALIGN }, + AMSK_START|AMSK_TYPE|AMSK_COMPACT }, + { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN }, { "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 }, { "s", MARKUP_S, MUTYPE_FONT, 0 }, { "samp", MARKUP_SAMP, MUTYPE_FONT, 0 }, { "small", MARKUP_SMALL, MUTYPE_FONT, 0 }, { "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 }, { "strong", MARKUP_STRONG, MUTYPE_FONT, 0 }, { "sub", MARKUP_SUB, MUTYPE_FONT, 0 }, { "sup", MARKUP_SUP, MUTYPE_FONT, 0 }, { "table", MARKUP_TABLE, MUTYPE_TABLE, - ATTR_ALIGN|ATTR_BGCOLOR|ATTR_BORDER|ATTR_CELLPADDING| - ATTR_CELLSPACING|ATTR_HSPACE|ATTR_VSPACE }, + AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| + AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE }, { "td", MARKUP_TD, MUTYPE_TD, - ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN| - ATTR_ROWSPAN|ATTR_VALIGN }, + AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| + AMSK_ROWSPAN|AMSK_VALIGN }, { "th", MARKUP_TH, MUTYPE_TD, - ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN| - ATTR_ROWSPAN|ATTR_VALIGN }, + AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| + AMSK_ROWSPAN|AMSK_VALIGN }, { "tr", MARKUP_TR, MUTYPE_TR, - ATTR_ALIGN|ATTR_BGCOLOR||ATTR_VALIGN }, + AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN }, { "tt", MARKUP_TT, MUTYPE_FONT, 0 }, { "u", MARKUP_U, MUTYPE_FONT, 0 }, { "ul", MARKUP_UL, MUTYPE_LIST, - ATTR_TYPE|ATTR_COMPACT }, + AMSK_TYPE|AMSK_COMPACT }, { "var", MARKUP_VAR, MUTYPE_FONT, 0 }, - { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, ATTR_ID }, + { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID }, }; /* ** Use binary search to locate a tag in the aMarkup[] table. */ @@ -332,11 +359,15 @@ int wantAutoParagraph; /* True if a <p> is desired */ int inAutoParagraph; /* True if within an automatic paragraph */ const char *zVerbatimId; /* The id= attribute of <verbatim> */ int nStack; /* Number of elements on the stack */ int nAlloc; /* Space allocated for aStack */ - unsigned char *aStack; /* Open markup stack */ + struct sStack { + short iCode; /* Markup code */ + short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */ + const char *zId; /* ID attribute or NULL */ + } *aStack; }; /* ** z points to a "<" character. Check to see if this is the start of @@ -585,11 +616,11 @@ unsigned char endTag; /* True if </...> instead of <...> */ unsigned char iCode; /* MARKUP_* */ unsigned char nAttr; /* Number of attributes */ unsigned short iType; /* MUTYPE_* */ struct { - unsigned char iCode; /* ATTR_* */ + unsigned char iACode; /* ATTR_* */ char *zValue; /* Argument to this attribute. Might be NULL */ char cTerm; /* Original argument termination character */ } aAttr[10]; }; @@ -600,11 +631,11 @@ ** The content of z[] might be modified by converting characters ** to lowercase and by inserting some "\000" characters. */ static void parseMarkup(ParsedMarkup *p, char *z){ int i, j, c; - int iCode; + int iACode; char *zValue; int seen = 0; char zTag[100]; if( z[1]=='/' ){ @@ -630,12 +661,12 @@ while( isalnum(z[i]) ){ if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); i++; } zTag[j] = 0; - p->aAttr[p->nAttr].iCode = iCode = findAttr(zTag); - attrOk = iCode!=0 && (seen & aAttribute[iCode].iMask)==0; + p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); + attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0; while( isspace(z[i]) ){ z++; } if( z[i]!='=' ){ p->aAttr[p->nAttr].zValue = 0; p->aAttr[p->nAttr].cTerm = 0; c = 0; @@ -656,11 +687,11 @@ z[i] = 0; } i++; } if( attrOk ){ - seen |= aAttribute[iCode].iMask; + seen |= aAttribute[iACode].iMask; p->nAttr++; } while( isspace(z[i]) ){ i++; } if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; } @@ -674,11 +705,11 @@ if( p->endTag ){ blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName); }else{ blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName); for(i=0; i<p->nAttr; i++){ - blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iCode].zName); + blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName); if( p->aAttr[i].zValue ){ blob_appendf(pOut, "=\"%s\"", p->aAttr[i].zValue); } } blob_append(pOut, ">", 1); @@ -699,47 +730,91 @@ z[n] = p->aAttr[i].cTerm; } } /* +** Return the ID attribute for markup. Return NULL if there is no +** ID attribute. +*/ +static const char *markupId(ParsedMarkup *p){ + int i; + for(i=0; i<p->nAttr; i++){ + if( p->aAttr[i].iACode==ATTR_ID ){ + return p->aAttr[i].zValue; + } + } + return 0; +} + +/* ** Pop a single element off of the stack. As the element is popped, -** output its end tag. +** output its end tag if it is not a </div> tag. */ static void popStack(Renderer *p){ if( p->nStack ){ + int iCode; p->nStack--; - blob_appendf(p->pOut, "</%s>", aMarkup[p->aStack[p->nStack]].zName); + iCode = p->aStack[p->nStack].iCode; + if( iCode!=MARKUP_DIV ){ + blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName); + } } } /* ** Push a new markup value onto the stack. Enlarge the stack ** if necessary. */ -static void pushStack(Renderer *p, int elem){ +static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ if( p->nStack>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + 100; - p->aStack = realloc(p->aStack, p->nAlloc); + p->aStack = realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); if( p->aStack==0 ){ fossil_panic("out of memory"); } } - p->aStack[p->nStack++] = elem; + p->aStack[p->nStack].iCode = elem; + p->aStack[p->nStack].zId = zId; + p->aStack[p->nStack].allowWiki = w; + p->nStack++; +} +static void pushStack(Renderer *p, int elem){ + pushStackWithId(p, elem, 0, 0); } /* ** Pop the stack until the top-most iTag element is removed. ** If there is no iTag element on the stack, this routine ** is a no-op. */ static void popStackToTag(Renderer *p, int iTag){ int i; - for(i=p->nStack-1; i>=0 && p->aStack[i]!=iTag; i--){} + for(i=p->nStack-1; i>=0; i--){ + if( p->aStack[i].iCode!=iTag ) continue; + if( p->aStack[i].zId ) continue; + break; + } if( i<0 ) return; while( p->nStack>i ){ popStack(p); } +} + +/* +** Attempt to find a find a tag of type iTag with id zId. Return -1 +** if not found. If found, return its stack level. +*/ +static int findTagWithId(Renderer *p, int iTag, const char *zId){ + int i; + assert( zId!=0 ); + for(i=p->nStack-1; i>=0; i--){ + if( p->aStack[i].iCode!=iTag ) continue; + if( p->aStack[i].zId==0 ) continue; + if( strcmp(zId, p->aStack[i].zId)!=0 ) continue; + break; + } + return i; } /* ** Pop the stack until the top-most element of the stack ** is an element that matches the type in iMask. Return @@ -748,17 +823,19 @@ ** that matches iMask, then leave the stack unchanged and ** return false (MARKUP_INVALID). */ static int backupToType(Renderer *p, int iMask){ int i; - for(i=p->nStack-1; i>=0 && (aMarkup[p->aStack[i]].iType&iMask)==0; i--){} + for(i=p->nStack-1; i>=0; i--){ + if( aMarkup[p->aStack[i].iCode].iType & iMask ) break; + } if( i<0 ) return 0; i++; while( p->nStack>i ){ popStack(p); } - return p->aStack[i-1]; + return p->aStack[i-1].iCode; } /* ** Begin a new paragraph if that something that is needed. */ @@ -849,11 +926,11 @@ /* ** Return the MUTYPE for the top of the stack. */ static int stackTopType(Renderer *p){ if( p->nStack<=0 ) return 0; - return aMarkup[p->aStack[p->nStack-1]].iType; + return aMarkup[p->aStack[p->nStack-1].iCode].iType; } /* ** Convert the wiki in z[] into html in the renderer p. The ** renderer has already been initialized. @@ -987,11 +1064,41 @@ startAutoParagraph(p); blob_append(p->pOut, z, n); break; } case TOKEN_MARKUP: { + const char *zId; + int iDiv; parseMarkup(&markup, z); + + /* Markup of the form </div id=ID> where there is a matching + ** ID somewhere on the stack. Exit the verbatim if were are in + ** it. Pop the stack up to the matching <div>. Discard the + ** </div> + */ + if( markup.iCode==MARKUP_DIV && markup.endTag && + (zId = markupId(&markup))!=0 && + (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0 + ){ + if( p->inVerbatim ){ + p->inVerbatim = 0; + p->state = p->preVerbState; + blob_append(p->pOut, "</pre>", 6); + } + while( p->nStack>iDiv+1 ) popStack(p); + if( p->aStack[iDiv].allowWiki ){ + p->state |= ALLOW_WIKI; + }else{ + p->state &= ~ALLOW_WIKI; + } + assert( p->nStack==iDiv+1 ); + p->nStack--; + }else + + /* If within <verbatim id=ID> ignore everything other than + ** </verbatim id=ID> and the </dev id=ID2> above. + */ if( p->inVerbatim ){ if( endVerbatim(p, &markup) ){ p->inVerbatim = 0; p->state = p->preVerbState; blob_append(p->pOut, "</pre>", 6); @@ -998,29 +1105,60 @@ }else{ unparseMarkup(&markup); blob_append(p->pOut, "<", 4); n = 1; } - }else if( markup.iCode==MARKUP_INVALID ){ + }else + + /* Render invalid markup literally. The markup appears in the + ** final output as plain text. + */ + if( markup.iCode==MARKUP_INVALID ){ unparseMarkup(&markup); startAutoParagraph(p); blob_append(p->pOut, "<", 4); n = 1; - }else if( (markup.iType&MUTYPE_FONT)==0 - && (p->state & FONT_MARKUP_ONLY)!=0 ){ + }else + + /* If the markup is not font-change markup ignore it if the + ** font-change-only flag is set. + */ + if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){ /* Do nothing */ - }else if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){ + }else + + /* Ignore block markup for in-line rendering. + */ + if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){ /* Do nothing */ - }else if( markup.iCode==MARKUP_NOWIKI ){ + }else + + if( markup.iCode==MARKUP_NOWIKI ){ if( markup.endTag ){ p->state |= ALLOW_WIKI; }else{ p->state &= ~ALLOW_WIKI; } - }else if( markup.endTag ){ + }else + + /* Generate end-tags */ + if( markup.endTag ){ popStackToTag(p, markup.iCode); - }else if( markup.iCode==MARKUP_VERBATIM ){ + }else + + /* Push <div> markup onto the stack together with the id=ID attribute. + */ + if( markup.iCode==MARKUP_DIV ){ + pushStackWithId(p, markup.iCode, markupId(&markup), + (p->state & ALLOW_WIKI)!=0); + }else + + /* Enter <verbatim> processing. With verbatim enabled, all other + ** markup other than the corresponding end-tag with the same ID is + ** ignored. + */ + if( markup.iCode==MARKUP_VERBATIM ){ if( markup.nAttr==1 ){ p->zVerbatimId = markup.aAttr[0].zValue; }else{ p->zVerbatimId = 0; } @@ -1027,37 +1165,42 @@ p->inVerbatim = 1; p->preVerbState = p->state; p->state &= ~ALLOW_WIKI; blob_append(p->pOut, "<pre class='verbatim'>",-1); p->wantAutoParagraph = 0; - }else if( markup.iType==MUTYPE_LI ){ + }else + if( markup.iType==MUTYPE_LI ){ if( backupToType(p, MUTYPE_LIST)==0 ){ pushStack(p, MARKUP_UL); blob_append(p->pOut, "<ul>", 4); } pushStack(p, MARKUP_LI); renderMarkup(p->pOut, &markup); - }else if( markup.iType==MUTYPE_TR ){ + }else + if( markup.iType==MUTYPE_TR ){ if( backupToType(p, MUTYPE_TABLE) ){ pushStack(p, MARKUP_TR); renderMarkup(p->pOut, &markup); } - }else if( markup.iType==MUTYPE_TD ){ + }else + if( markup.iType==MUTYPE_TD ){ if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){ if( stackTopType(p)==MUTYPE_TABLE ){ pushStack(p, MARKUP_TR); blob_append(p->pOut, "<tr>", 4); } pushStack(p, markup.iCode); renderMarkup(p->pOut, &markup); } - }else if( markup.iType==MUTYPE_HYPERLINK ){ + }else + if( markup.iType==MUTYPE_HYPERLINK ){ popStackToTag(p, markup.iCode); startAutoParagraph(p); renderMarkup(p->pOut, &markup); pushStack(p, markup.iCode); - }else{ + }else + { if( markup.iType==MUTYPE_FONT ){ startAutoParagraph(p); }else if( markup.iType==MUTYPE_BLOCK ){ p->wantAutoParagraph = 0; }