dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Copyright (c) 2007 D. Richard Hipp dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is free software; you can redistribute it and/or dbda8d6ce9 2007-07-21 drh: ** modify it under the terms of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License version 2 as published by the Free Software Foundation. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is distributed in the hope that it will be useful, dbda8d6ce9 2007-07-21 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of dbda8d6ce9 2007-07-21 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dbda8d6ce9 2007-07-21 drh: ** General Public License for more details. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** You should have received a copy of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License along with this library; if not, write to the dbda8d6ce9 2007-07-21 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, dbda8d6ce9 2007-07-21 drh: ** Boston, MA 02111-1307, USA. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Author contact information: dbda8d6ce9 2007-07-21 drh: ** drh@hwaci.com dbda8d6ce9 2007-07-21 drh: ** http://www.hwaci.com/drh/ dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ******************************************************************************* dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This file contains code to do formatting of wiki text. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #include <assert.h> dbda8d6ce9 2007-07-21 drh: #include "config.h" dbda8d6ce9 2007-07-21 drh: #include "wikiformat.h" dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: #if INTERFACE dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Allowed wiki transformation operations dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define WIKI_NOFOLLOW 0x001 dbda8d6ce9 2007-07-21 drh: #define WIKI_HTML 0x002 dbda8d6ce9 2007-07-21 drh: #endif dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** These are the only markup attributes allowed. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define ATTR_ALIGN 0x000001 dbda8d6ce9 2007-07-21 drh: #define ATTR_ALT 0x000002 dbda8d6ce9 2007-07-21 drh: #define ATTR_BGCOLOR 0x000004 dbda8d6ce9 2007-07-21 drh: #define ATTR_BORDER 0x000008 dbda8d6ce9 2007-07-21 drh: #define ATTR_CELLPADDING 0x000010 dbda8d6ce9 2007-07-21 drh: #define ATTR_CELLSPACING 0x000020 dbda8d6ce9 2007-07-21 drh: #define ATTR_CLEAR 0x000040 dbda8d6ce9 2007-07-21 drh: #define ATTR_COLOR 0x000080 dbda8d6ce9 2007-07-21 drh: #define ATTR_COLSPAN 0x000100 dbda8d6ce9 2007-07-21 drh: #define ATTR_COMPACT 0x000200 dbda8d6ce9 2007-07-21 drh: #define ATTR_FACE 0x000400 dbda8d6ce9 2007-07-21 drh: #define ATTR_HEIGHT 0x000800 dbda8d6ce9 2007-07-21 drh: #define ATTR_HREF 0x001000 dbda8d6ce9 2007-07-21 drh: #define ATTR_HSPACE 0x002000 dbda8d6ce9 2007-07-21 drh: #define ATTR_ID 0x004000 dbda8d6ce9 2007-07-21 drh: #define ATTR_ROWSPAN 0x008000 dbda8d6ce9 2007-07-21 drh: #define ATTR_SIZE 0x010000 dbda8d6ce9 2007-07-21 drh: #define ATTR_SRC 0x020000 dbda8d6ce9 2007-07-21 drh: #define ATTR_START 0x040000 dbda8d6ce9 2007-07-21 drh: #define ATTR_TYPE 0x080000 dbda8d6ce9 2007-07-21 drh: #define ATTR_VALIGN 0x100000 dbda8d6ce9 2007-07-21 drh: #define ATTR_VALUE 0x200000 dbda8d6ce9 2007-07-21 drh: #define ATTR_VSPACE 0x400000 dbda8d6ce9 2007-07-21 drh: #define ATTR_WIDTH 0x800000 dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: static const struct AllowedAttribute { dbda8d6ce9 2007-07-21 drh: const char *zName; dbda8d6ce9 2007-07-21 drh: unsigned int iMask; dbda8d6ce9 2007-07-21 drh: } aAttribute[] = { dbda8d6ce9 2007-07-21 drh: { "align", ATTR_ALIGN, }, dbda8d6ce9 2007-07-21 drh: { "alt", ATTR_ALT, }, dbda8d6ce9 2007-07-21 drh: { "bgcolor", ATTR_BGCOLOR, }, dbda8d6ce9 2007-07-21 drh: { "border", ATTR_BORDER, }, dbda8d6ce9 2007-07-21 drh: { "cellpadding", ATTR_CELLPADDING, }, dbda8d6ce9 2007-07-21 drh: { "cellspacing", ATTR_CELLSPACING, }, dbda8d6ce9 2007-07-21 drh: { "clear", ATTR_CLEAR, }, dbda8d6ce9 2007-07-21 drh: { "color", ATTR_COLOR, }, dbda8d6ce9 2007-07-21 drh: { "colspan", ATTR_COLSPAN, }, dbda8d6ce9 2007-07-21 drh: { "compact", ATTR_COMPACT, }, dbda8d6ce9 2007-07-21 drh: { "face", ATTR_FACE, }, dbda8d6ce9 2007-07-21 drh: { "height", ATTR_HEIGHT, }, dbda8d6ce9 2007-07-21 drh: { "href", ATTR_HREF, }, dbda8d6ce9 2007-07-21 drh: { "hspace", ATTR_HSPACE, }, dbda8d6ce9 2007-07-21 drh: { "id", ATTR_ID, }, dbda8d6ce9 2007-07-21 drh: { "rowspan", ATTR_ROWSPAN, }, dbda8d6ce9 2007-07-21 drh: { "size", ATTR_SIZE, }, dbda8d6ce9 2007-07-21 drh: { "src", ATTR_SRC, }, dbda8d6ce9 2007-07-21 drh: { "start", ATTR_START, }, dbda8d6ce9 2007-07-21 drh: { "type", ATTR_TYPE, }, dbda8d6ce9 2007-07-21 drh: { "valign", ATTR_VALIGN, }, dbda8d6ce9 2007-07-21 drh: { "value", ATTR_VALUE, }, dbda8d6ce9 2007-07-21 drh: { "vspace", ATTR_VSPACE, }, dbda8d6ce9 2007-07-21 drh: { "width", ATTR_WIDTH, }, dbda8d6ce9 2007-07-21 drh: }; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Use binary search to locate a tag in the aAttribute[] table. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int findAttr(const char *z){ dbda8d6ce9 2007-07-21 drh: int i, c, first, last; dbda8d6ce9 2007-07-21 drh: first = 0; dbda8d6ce9 2007-07-21 drh: last = sizeof(aAttribute)/sizeof(aAttribute[0]) - 1; dbda8d6ce9 2007-07-21 drh: while( first<=last ){ dbda8d6ce9 2007-07-21 drh: i = (first+last)/2; dbda8d6ce9 2007-07-21 drh: c = strcmp(aAttribute[i].zName, z); dbda8d6ce9 2007-07-21 drh: if( c==0 ){ dbda8d6ce9 2007-07-21 drh: return aAttribute[i].iMask; dbda8d6ce9 2007-07-21 drh: }else if( c<0 ){ dbda8d6ce9 2007-07-21 drh: first = i+1; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: last = i-1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Allowed markup. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Except for MARKUP_INVALID, this must all be in alphabetical order dbda8d6ce9 2007-07-21 drh: ** and in numerical sequence. The first markup type must be zero. dbda8d6ce9 2007-07-21 drh: ** The value for MARKUP_XYZ must correspond to the <xyz> entry dbda8d6ce9 2007-07-21 drh: ** in aAllowedMarkup[]. dbda8d6ce9 2007-07-21 drh: */ ab637af752 2007-09-28 drh: #define MARKUP_INVALID 0 ab637af752 2007-09-28 drh: #define MARKUP_A 1 ab637af752 2007-09-28 drh: #define MARKUP_ADDRESS 2 ab637af752 2007-09-28 drh: #define MARKUP_B 3 ab637af752 2007-09-28 drh: #define MARKUP_BIG 4 ab637af752 2007-09-28 drh: #define MARKUP_BLOCKQUOTE 5 ab637af752 2007-09-28 drh: #define MARKUP_BR 6 ab637af752 2007-09-28 drh: #define MARKUP_CENTER 7 ab637af752 2007-09-28 drh: #define MARKUP_CITE 8 ab637af752 2007-09-28 drh: #define MARKUP_CODE 9 ab637af752 2007-09-28 drh: #define MARKUP_DD 10 ab637af752 2007-09-28 drh: #define MARKUP_DFN 11 ab637af752 2007-09-28 drh: #define MARKUP_DL 12 ab637af752 2007-09-28 drh: #define MARKUP_DT 13 ab637af752 2007-09-28 drh: #define MARKUP_EM 14 ab637af752 2007-09-28 drh: #define MARKUP_FONT 15 ab637af752 2007-09-28 drh: #define MARKUP_H1 16 ab637af752 2007-09-28 drh: #define MARKUP_H2 17 ab637af752 2007-09-28 drh: #define MARKUP_H3 18 ab637af752 2007-09-28 drh: #define MARKUP_H4 19 ab637af752 2007-09-28 drh: #define MARKUP_H5 20 ab637af752 2007-09-28 drh: #define MARKUP_H6 21 ab637af752 2007-09-28 drh: #define MARKUP_HR 22 ab637af752 2007-09-28 drh: #define MARKUP_IMG 23 ab637af752 2007-09-28 drh: #define MARKUP_I 24 ab637af752 2007-09-28 drh: #define MARKUP_KBD 25 ab637af752 2007-09-28 drh: #define MARKUP_LI 26 ab637af752 2007-09-28 drh: #define MARKUP_NOBR 27 ab637af752 2007-09-28 drh: #define MARKUP_NOWIKI 28 ab637af752 2007-09-28 drh: #define MARKUP_OL 29 ab637af752 2007-09-28 drh: #define MARKUP_P 30 ab637af752 2007-09-28 drh: #define MARKUP_PRE 31 ab637af752 2007-09-28 drh: #define MARKUP_S 32 ab637af752 2007-09-28 drh: #define MARKUP_SAMP 33 ab637af752 2007-09-28 drh: #define MARKUP_SMALL 34 ab637af752 2007-09-28 drh: #define MARKUP_STRIKE 35 ab637af752 2007-09-28 drh: #define MARKUP_STRONG 36 ab637af752 2007-09-28 drh: #define MARKUP_SUB 37 ab637af752 2007-09-28 drh: #define MARKUP_SUP 38 ab637af752 2007-09-28 drh: #define MARKUP_TABLE 39 ab637af752 2007-09-28 drh: #define MARKUP_TD 40 ab637af752 2007-09-28 drh: #define MARKUP_TH 41 ab637af752 2007-09-28 drh: #define MARKUP_TR 42 ab637af752 2007-09-28 drh: #define MARKUP_TT 43 dbda8d6ce9 2007-07-21 drh: #define MARKUP_U 44 ab637af752 2007-09-28 drh: #define MARKUP_UL 45 ab637af752 2007-09-28 drh: #define MARKUP_VAR 46 ab637af752 2007-09-28 drh: #define MARKUP_VERBATIM 47 dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** The various markup is divided into the following types: dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_BLOCK 0x0002 /* Forms a new paragraph. ex: <p>, <h2> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_FONT 0x0004 /* Font changes. ex: <b>, <font>, <sub> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_LINK 0x0008 /* Hyperlink: <a> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_LIST 0x0010 /* Lists. <ol>, <ul>, or <dl> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_LI 0x0020 /* List items. <li>, <dd>, <dt> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_TABLE 0x0040 /* <table> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_TR 0x0080 /* <tr> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_TD 0x0100 /* <td> or <th> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_SPECIAL 0x0200 /* <nowiki> or <verbatim> */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_HYPERLINK 0x0400 /* <a> */ dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: #define MUTYPE_STACK (MUTYPE_BLOCK | MUTYPE_FONT | MUTYPE_LIST | MUTYPE_TABLE) dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: static const struct AllowedMarkup { dbda8d6ce9 2007-07-21 drh: const char *zName; /* Name of the markup */ dbda8d6ce9 2007-07-21 drh: char iCode; /* The MARKUP_* code */ dbda8d6ce9 2007-07-21 drh: short int iType; /* The MUTYPE_* code */ dbda8d6ce9 2007-07-21 drh: int allowedAttr; /* Allowed attributes on this markup */ dbda8d6ce9 2007-07-21 drh: } aMarkup[] = { ab637af752 2007-09-28 drh: { 0, MARKUP_INVALID, 0, 0 }, dbda8d6ce9 2007-07-21 drh: { "a", MARKUP_A, MUTYPE_HYPERLINK, ATTR_HREF }, dbda8d6ce9 2007-07-21 drh: { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 }, 55dc2abc60 2007-09-24 jnc: { "b", MARKUP_B, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "big", MARKUP_BIG, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 }, dbda8d6ce9 2007-07-21 drh: { "br", MARKUP_BR, MUTYPE_SINGLE, ATTR_CLEAR }, dbda8d6ce9 2007-07-21 drh: { "center", MARKUP_CENTER, MUTYPE_BLOCK, 0 }, dbda8d6ce9 2007-07-21 drh: { "cite", MARKUP_CITE, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "code", MARKUP_CODE, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "dd", MARKUP_DD, MUTYPE_LI, 0 }, dbda8d6ce9 2007-07-21 drh: { "dfn", MARKUP_DFN, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "dl", MARKUP_DL, MUTYPE_LIST, ATTR_COMPACT }, dbda8d6ce9 2007-07-21 drh: { "dt", MARKUP_DT, MUTYPE_LI, 0 }, dbda8d6ce9 2007-07-21 drh: { "em", MARKUP_EM, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "font", MARKUP_FONT, MUTYPE_FONT, dbda8d6ce9 2007-07-21 drh: ATTR_COLOR|ATTR_FACE|ATTR_SIZE }, dbda8d6ce9 2007-07-21 drh: { "h1", MARKUP_H1, MUTYPE_BLOCK, ATTR_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "h2", MARKUP_H2, MUTYPE_BLOCK, ATTR_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "h3", MARKUP_H3, MUTYPE_BLOCK, ATTR_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "h4", MARKUP_H4, MUTYPE_BLOCK, ATTR_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "h5", MARKUP_H5, MUTYPE_BLOCK, ATTR_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "h6", MARKUP_H6, MUTYPE_BLOCK, ATTR_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "hr", MARKUP_HR, MUTYPE_SINGLE, dbda8d6ce9 2007-07-21 drh: ATTR_ALIGN|ATTR_COLOR|ATTR_SIZE|ATTR_WIDTH }, dbda8d6ce9 2007-07-21 drh: { "img", MARKUP_IMG, MUTYPE_SINGLE, dbda8d6ce9 2007-07-21 drh: ATTR_ALIGN|ATTR_ALT|ATTR_BORDER|ATTR_HEIGHT| dbda8d6ce9 2007-07-21 drh: ATTR_HSPACE|ATTR_SRC|ATTR_VSPACE|ATTR_WIDTH }, dbda8d6ce9 2007-07-21 drh: { "i", MARKUP_I, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "kbd", MARKUP_KBD, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "li", MARKUP_LI, MUTYPE_LI, dbda8d6ce9 2007-07-21 drh: ATTR_TYPE|ATTR_VALUE }, dbda8d6ce9 2007-07-21 drh: { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, dbda8d6ce9 2007-07-21 drh: { "ol", MARKUP_OL, MUTYPE_LIST, dbda8d6ce9 2007-07-21 drh: ATTR_START|ATTR_TYPE|ATTR_COMPACT }, dbda8d6ce9 2007-07-21 drh: { "p", MARKUP_P, MUTYPE_BLOCK, ATTR_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "pre", MARKUP_PRE, MUTYPE_BLOCK, 0 }, 55dc2abc60 2007-09-24 jnc: { "s", MARKUP_S, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "samp", MARKUP_SAMP, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "small", MARKUP_SMALL, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "strike", MARKUP_STRIKE, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "strong", MARKUP_STRONG, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "sub", MARKUP_SUB, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "sup", MARKUP_SUP, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "table", MARKUP_TABLE, MUTYPE_TABLE, dbda8d6ce9 2007-07-21 drh: ATTR_ALIGN|ATTR_BGCOLOR|ATTR_BORDER|ATTR_CELLPADDING| dbda8d6ce9 2007-07-21 drh: ATTR_CELLSPACING|ATTR_HSPACE|ATTR_VSPACE }, dbda8d6ce9 2007-07-21 drh: { "td", MARKUP_TD, MUTYPE_TD, dbda8d6ce9 2007-07-21 drh: ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN| dbda8d6ce9 2007-07-21 drh: ATTR_ROWSPAN|ATTR_VALIGN }, dbda8d6ce9 2007-07-21 drh: { "th", MARKUP_TH, MUTYPE_TD, dbda8d6ce9 2007-07-21 drh: ATTR_ALIGN|ATTR_BGCOLOR|ATTR_COLSPAN| dbda8d6ce9 2007-07-21 drh: ATTR_ROWSPAN|ATTR_VALIGN }, dbda8d6ce9 2007-07-21 drh: { "tr", MARKUP_TR, MUTYPE_TR, dbda8d6ce9 2007-07-21 drh: ATTR_ALIGN|ATTR_BGCOLOR||ATTR_VALIGN }, dbda8d6ce9 2007-07-21 drh: { "tt", MARKUP_TT, MUTYPE_FONT, 0 }, 55dc2abc60 2007-09-24 jnc: { "u", MARKUP_U, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "ul", MARKUP_UL, MUTYPE_LIST, dbda8d6ce9 2007-07-21 drh: ATTR_TYPE|ATTR_COMPACT }, dbda8d6ce9 2007-07-21 drh: { "var", MARKUP_VAR, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, ATTR_ID }, dbda8d6ce9 2007-07-21 drh: }; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Use binary search to locate a tag in the aMarkup[] table. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int findTag(const char *z){ dbda8d6ce9 2007-07-21 drh: int i, c, first, last; ab637af752 2007-09-28 drh: first = 1; dbda8d6ce9 2007-07-21 drh: last = sizeof(aMarkup)/sizeof(aMarkup[0]) - 1; dbda8d6ce9 2007-07-21 drh: while( first<=last ){ dbda8d6ce9 2007-07-21 drh: i = (first+last)/2; dbda8d6ce9 2007-07-21 drh: c = strcmp(aMarkup[i].zName, z); dbda8d6ce9 2007-07-21 drh: if( c==0 ){ dbda8d6ce9 2007-07-21 drh: assert( aMarkup[i].iCode==i ); dbda8d6ce9 2007-07-21 drh: return i; dbda8d6ce9 2007-07-21 drh: }else if( c<0 ){ dbda8d6ce9 2007-07-21 drh: first = i+1; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: last = i-1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: return MARKUP_INVALID; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Token types dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_MARKUP 1 /* <...> */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_CHARACTER 2 /* "&" or "<" not part of markup */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_LINK 3 /* [...] */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_PARAGRAPH 4 /* blank lines */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_NEWLINE 5 /* A single "\n" */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_BULLET 6 /* " * " */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_ENUM 7 /* " \(?\d+[.)]? " */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_INDENT 8 /* " " */ dbda8d6ce9 2007-07-21 drh: #define TOKEN_TEXT 9 /* None of the above */ dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** State flags dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define AT_NEWLINE 0x001 /* At start of a line */ dbda8d6ce9 2007-07-21 drh: #define AT_PARAGRAPH 0x002 /* At start of a paragraph */ dbda8d6ce9 2007-07-21 drh: #define ALLOW_WIKI 0x004 /* Allow wiki markup */ dbda8d6ce9 2007-07-21 drh: #define FONT_MARKUP_ONLY 0x008 /* Only allow MUTYPE_FONT markup */ ab637af752 2007-09-28 drh: #define IN_LIST 0x010 /* Within wiki <ul> or <ol> */ ab637af752 2007-09-28 drh: ab637af752 2007-09-28 drh: /* ab637af752 2007-09-28 drh: ** Current state of the rendering engine ab637af752 2007-09-28 drh: */ ab637af752 2007-09-28 drh: typedef struct Renderer Renderer; ab637af752 2007-09-28 drh: struct Renderer { ab637af752 2007-09-28 drh: Blob *pOut; /* Output appended to this blob */ ab637af752 2007-09-28 drh: int state; /* Flag that govern rendering */ ab637af752 2007-09-28 drh: int wikiList; /* Current wiki list type */ ab637af752 2007-09-28 drh: int inVerbatim; /* True in <verbatim> mode */ ab637af752 2007-09-28 drh: int preVerbState; /* Value of state prior to verbatim */ ab637af752 2007-09-28 drh: const char *zVerbatimId; /* The id= attribute of <verbatim> */ ab637af752 2007-09-28 drh: int nStack; /* Number of elements on the stack */ ab637af752 2007-09-28 drh: int nAlloc; /* Space allocated for aStack */ ab637af752 2007-09-28 drh: unsigned char *aStack; /* Open markup stack */ ab637af752 2007-09-28 drh: }; ab637af752 2007-09-28 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** z points to a "<" character. Check to see if this is the start of dbda8d6ce9 2007-07-21 drh: ** a valid markup. If it is, return the total number of characters in dbda8d6ce9 2007-07-21 drh: ** the markup including the initial "<" and the terminating ">". If dbda8d6ce9 2007-07-21 drh: ** it is not well-formed markup, return 0. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int markupLength(const char *z){ dbda8d6ce9 2007-07-21 drh: int n = 1; dbda8d6ce9 2007-07-21 drh: int inparen = 0; dbda8d6ce9 2007-07-21 drh: if( z[n]=='/' ){ n++; } dbda8d6ce9 2007-07-21 drh: if( !isalpha(z[n]) ) return 0; 55dc2abc60 2007-09-24 jnc: while( isalnum(z[n]) ){ n++; } dbda8d6ce9 2007-07-21 drh: if( z[n]!='>' && !isspace(z[n]) ) return 0; dbda8d6ce9 2007-07-21 drh: while( z[n] && (z[n]!='>' || inparen) ){ dbda8d6ce9 2007-07-21 drh: if( z[n]=='"' ){ dbda8d6ce9 2007-07-21 drh: inparen = !inparen; dbda8d6ce9 2007-07-21 drh: } 11976cfdc2 2007-09-02 drh: n++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( z[n]!='>' ) return 0; dbda8d6ce9 2007-07-21 drh: return n+1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** z points to a "\n" character. Check to see if this newline is dbda8d6ce9 2007-07-21 drh: ** followed by one or more blank lines. If it is, return the number dbda8d6ce9 2007-07-21 drh: ** of characters through the closing "\n". If not, return 0. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int paragraphBreakLength(const char *z){ dbda8d6ce9 2007-07-21 drh: int i, n; dbda8d6ce9 2007-07-21 drh: int nNewline = 1; dbda8d6ce9 2007-07-21 drh: for(i=1, n=0; isspace(z[i]); i++){ dbda8d6ce9 2007-07-21 drh: if( z[i]=='\n' ){ dbda8d6ce9 2007-07-21 drh: nNewline++; dbda8d6ce9 2007-07-21 drh: n = i; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( nNewline>=2 ){ dbda8d6ce9 2007-07-21 drh: return n+1; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Return the number of characters until the next "interesting" dbda8d6ce9 2007-07-21 drh: ** characters. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Interesting characters are: dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** < dbda8d6ce9 2007-07-21 drh: ** & dbda8d6ce9 2007-07-21 drh: ** \n dbda8d6ce9 2007-07-21 drh: ** [ dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** The "[" and "\n" are only considered interesting if the "useWiki" dbda8d6ce9 2007-07-21 drh: ** flag is set. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int textLength(const char *z, int useWiki){ dbda8d6ce9 2007-07-21 drh: int n = 0; dbda8d6ce9 2007-07-21 drh: int c; dbda8d6ce9 2007-07-21 drh: while( (c = z[0])!=0 && c!='<' && c!='&' && dbda8d6ce9 2007-07-21 drh: (useWiki==0 || (c!='[' && c!='\n')) ){ dbda8d6ce9 2007-07-21 drh: n++; dbda8d6ce9 2007-07-21 drh: z++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Return true if z[] begins with an HTML character element. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int isElement(const char *z){ dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: assert( z[0]=='&' ); dbda8d6ce9 2007-07-21 drh: if( z[1]=='#' ){ dbda8d6ce9 2007-07-21 drh: for(i=2; isdigit(z[i]); i++){} dbda8d6ce9 2007-07-21 drh: return i>2 && z[i]==';'; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: for(i=1; isalpha(z[i]); i++){} dbda8d6ce9 2007-07-21 drh: return i>1 && z[i]==';'; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Check to see if the z[] string is the beginning of a wiki bullet. dbda8d6ce9 2007-07-21 drh: ** If it is, return the length of the bullet text. Otherwise return 0. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int bulletLength(const char *z){ dbda8d6ce9 2007-07-21 drh: int i, n; dbda8d6ce9 2007-07-21 drh: n = 0; dbda8d6ce9 2007-07-21 drh: i = 0; dbda8d6ce9 2007-07-21 drh: while( z[n]==' ' || z[n]=='\t' ){ dbda8d6ce9 2007-07-21 drh: if( z[n]=='\t' ) i++; dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: n++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( i<2 || z[n]!='*' ) return 0; dbda8d6ce9 2007-07-21 drh: n++; dbda8d6ce9 2007-07-21 drh: i = 0; dbda8d6ce9 2007-07-21 drh: while( z[n]==' ' || z[n]=='\t' ){ dbda8d6ce9 2007-07-21 drh: if( z[n]=='\t' ) i++; dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: n++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( i<2 || isspace(z[n]) ) return 0; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* ab637af752 2007-09-28 drh: ** Check to see if the z[] string is the beginning of a enumeration value. ab637af752 2007-09-28 drh: ** If it is, return the length of the bullet text. Otherwise return 0. ab637af752 2007-09-28 drh: ** ab637af752 2007-09-28 drh: ** Syntax: ab637af752 2007-09-28 drh: ** * a tab or two or more spaces ab637af752 2007-09-28 drh: ** * one or more digits ab637af752 2007-09-28 drh: ** * optional "." ab637af752 2007-09-28 drh: ** * another tab or two or more additional spaces ab637af752 2007-09-28 drh: ** ab637af752 2007-09-28 drh: */ ab637af752 2007-09-28 drh: static int enumLength(const char *z){ ab637af752 2007-09-28 drh: int i, n; ab637af752 2007-09-28 drh: n = 0; ab637af752 2007-09-28 drh: i = 0; ab637af752 2007-09-28 drh: while( z[n]==' ' || z[n]=='\t' ){ ab637af752 2007-09-28 drh: if( z[n]=='\t' ) i++; ab637af752 2007-09-28 drh: i++; ab637af752 2007-09-28 drh: n++; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: if( i<2 ) return 0; ab637af752 2007-09-28 drh: for(i=0; isdigit(z[n]); i++, n++){} ab637af752 2007-09-28 drh: if( i==0 ) return 0; ab637af752 2007-09-28 drh: if( z[n]=='.' ){ ab637af752 2007-09-28 drh: n++; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: i = 0; ab637af752 2007-09-28 drh: while( z[n]==' ' || z[n]=='\t' ){ ab637af752 2007-09-28 drh: if( z[n]=='\t' ) i++; ab637af752 2007-09-28 drh: i++; ab637af752 2007-09-28 drh: n++; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: if( i<2 || isspace(z[n]) ) return 0; ab637af752 2007-09-28 drh: return n; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: ab637af752 2007-09-28 drh: /* dbda8d6ce9 2007-07-21 drh: ** Check to see if the z[] string is the beginning of an indented dbda8d6ce9 2007-07-21 drh: ** paragraph. If it is, return the length of the indent. Otherwise dbda8d6ce9 2007-07-21 drh: ** return 0. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int indentLength(const char *z){ dbda8d6ce9 2007-07-21 drh: int i, n; dbda8d6ce9 2007-07-21 drh: n = 0; dbda8d6ce9 2007-07-21 drh: i = 0; dbda8d6ce9 2007-07-21 drh: while( z[n]==' ' || z[n]=='\t' ){ dbda8d6ce9 2007-07-21 drh: if( z[n]=='\t' ) i++; dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: n++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( i<2 || isspace(z[n]) ) return 0; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Check to see if the z[] string is a wiki hyperlink. If it is, dbda8d6ce9 2007-07-21 drh: ** return the length of the hyperlink. Otherwise return 0. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int linkLength(const char *z){ dbda8d6ce9 2007-07-21 drh: int n; dbda8d6ce9 2007-07-21 drh: assert( z[0]=='[' ); dbda8d6ce9 2007-07-21 drh: for(n=0; z[n] && z[n]!=']'; n++){} dbda8d6ce9 2007-07-21 drh: if( z[n]==']' ){ dbda8d6ce9 2007-07-21 drh: return n+1; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** z points to the start of a token. Return the number of dbda8d6ce9 2007-07-21 drh: ** characters in that token. Write the token type into *pTokenType. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int nextToken(const char *z, int state, int *pTokenType){ dbda8d6ce9 2007-07-21 drh: int n; dbda8d6ce9 2007-07-21 drh: if( z[0]=='<' ){ dbda8d6ce9 2007-07-21 drh: n = markupLength(z); dbda8d6ce9 2007-07-21 drh: if( n>0 ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_MARKUP; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_CHARACTER; dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( z[0]=='&' && !isElement(z) ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_CHARACTER; dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( (state & ALLOW_WIKI)!=0 ){ dbda8d6ce9 2007-07-21 drh: if( z[0]=='\n' ){ dbda8d6ce9 2007-07-21 drh: n = paragraphBreakLength(z); dbda8d6ce9 2007-07-21 drh: if( n>0 ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_PARAGRAPH; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: }else if( isspace(z[1]) ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_NEWLINE; dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( (state & AT_NEWLINE)!=0 /* && (state & (AT_PARAGRAPH|IN_LIST))!=0 */ dbda8d6ce9 2007-07-21 drh: && isspace(z[0]) ){ dbda8d6ce9 2007-07-21 drh: n = bulletLength(z); dbda8d6ce9 2007-07-21 drh: if( n>0 ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_BULLET; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: n = enumLength(z); dbda8d6ce9 2007-07-21 drh: if( n>0 ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_ENUM; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( (state & AT_PARAGRAPH)!=0 && isspace(z[0]) ){ dbda8d6ce9 2007-07-21 drh: n = indentLength(z); dbda8d6ce9 2007-07-21 drh: if( n>0 ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_INDENT; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( z[0]=='[' && (n = linkLength(z))>0 ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_LINK; dbda8d6ce9 2007-07-21 drh: return n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_TEXT; dbda8d6ce9 2007-07-21 drh: return 1 + textLength(z+1, state & ALLOW_WIKI); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** A single markup is parsed into an instance of the following dbda8d6ce9 2007-07-21 drh: ** structure. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: typedef struct ParsedMarkup ParsedMarkup; dbda8d6ce9 2007-07-21 drh: struct ParsedMarkup { dbda8d6ce9 2007-07-21 drh: unsigned char endTag; /* True if </...> instead of <...> */ dbda8d6ce9 2007-07-21 drh: unsigned char iCode; /* MARKUP_* */ dbda8d6ce9 2007-07-21 drh: unsigned char nAttr; /* Number of attributes */ dbda8d6ce9 2007-07-21 drh: unsigned short iType; /* MUTYPE_* */ dbda8d6ce9 2007-07-21 drh: struct { dbda8d6ce9 2007-07-21 drh: unsigned char iCode; /* ATTR_* */ dbda8d6ce9 2007-07-21 drh: char *zValue; /* Argument to this attribute. Might be NULL */ dbda8d6ce9 2007-07-21 drh: char cTerm; /* Original argument termination character */ dbda8d6ce9 2007-07-21 drh: } aAttr[10]; dbda8d6ce9 2007-07-21 drh: }; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* 22c1ac41d4 2007-08-23 drh: ** z[] is an HTML markup element - something that begins with '<'. 22c1ac41d4 2007-08-23 drh: ** Parse this element into the p structure. 22c1ac41d4 2007-08-23 drh: ** 22c1ac41d4 2007-08-23 drh: ** The content of z[] might be modified by converting characters 22c1ac41d4 2007-08-23 drh: ** to lowercase and by inserting some "\000" characters. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void parseMarkup(ParsedMarkup *p, char *z){ dbda8d6ce9 2007-07-21 drh: int i, c; dbda8d6ce9 2007-07-21 drh: char *zTag, *zValue; dbda8d6ce9 2007-07-21 drh: int seen = 0; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( z[1]=='/' ){ dbda8d6ce9 2007-07-21 drh: p->endTag = 1; dbda8d6ce9 2007-07-21 drh: i = 2; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: p->endTag = 0; dbda8d6ce9 2007-07-21 drh: i = 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: zTag = &z[i]; dbda8d6ce9 2007-07-21 drh: while( isalnum(z[i]) ){ dbda8d6ce9 2007-07-21 drh: z[i] = tolower(z[i]); dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: c = z[i]; dbda8d6ce9 2007-07-21 drh: z[i] = 0; dbda8d6ce9 2007-07-21 drh: p->iCode = findTag(zTag); dbda8d6ce9 2007-07-21 drh: p->iType = aMarkup[p->iCode].iType; dbda8d6ce9 2007-07-21 drh: p->nAttr = 0; dbda8d6ce9 2007-07-21 drh: z[i] = c; dbda8d6ce9 2007-07-21 drh: while( isspace(z[i]) ){ i++; } dbda8d6ce9 2007-07-21 drh: while( p->nAttr<8 && isalpha(z[i]) ){ dbda8d6ce9 2007-07-21 drh: zTag = &z[i]; dbda8d6ce9 2007-07-21 drh: while( isalnum(z[i]) ){ dbda8d6ce9 2007-07-21 drh: z[i] = tolower(z[i]); dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: c = z[i]; dbda8d6ce9 2007-07-21 drh: z[i] = 0; dbda8d6ce9 2007-07-21 drh: p->aAttr[p->nAttr].iCode = findAttr(zTag); dbda8d6ce9 2007-07-21 drh: z[i] = c; dbda8d6ce9 2007-07-21 drh: while( isspace(z[i]) ){ z++; } dbda8d6ce9 2007-07-21 drh: if( z[i]!='=' ){ dbda8d6ce9 2007-07-21 drh: p->aAttr[p->nAttr].zValue = 0; dbda8d6ce9 2007-07-21 drh: p->aAttr[p->nAttr].cTerm = 0; dbda8d6ce9 2007-07-21 drh: c = 0; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: while( isspace(z[i]) ){ z++; } dbda8d6ce9 2007-07-21 drh: if( z[i]=='"' ){ dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: zValue = &z[i]; dbda8d6ce9 2007-07-21 drh: while( z[i] && z[i]!='"' ){ i++; } dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: zValue = &z[i]; dbda8d6ce9 2007-07-21 drh: while( !isspace(z[i]) && z[i]!='>' ){ z++; } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: p->aAttr[p->nAttr].zValue = zValue; dbda8d6ce9 2007-07-21 drh: p->aAttr[p->nAttr].cTerm = c = z[i]; dbda8d6ce9 2007-07-21 drh: z[i] = 0; dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( p->aAttr[p->nAttr].iCode!=0 && (seen & p->aAttr[p->nAttr].iCode)==0 ){ dbda8d6ce9 2007-07-21 drh: seen |= p->aAttr[p->nAttr].iCode; dbda8d6ce9 2007-07-21 drh: p->nAttr++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( c=='>' ) break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Render markup on the given blob. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void renderMarkup(Blob *pOut, ParsedMarkup *p){ dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: if( p->endTag ){ dbda8d6ce9 2007-07-21 drh: blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName); dbda8d6ce9 2007-07-21 drh: for(i=0; i<p->nAttr; i++){ dbda8d6ce9 2007-07-21 drh: blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iCode]); dbda8d6ce9 2007-07-21 drh: if( p->aAttr[i].zValue ){ dbda8d6ce9 2007-07-21 drh: blob_appendf(pOut, "=\"%s\"", p->aAttr[i].zValue); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: blob_append(pOut, ">", 1); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** When the markup was parsed, some "\000" may have been inserted. dbda8d6ce9 2007-07-21 drh: ** This routine restores to those "\000" values back to their dbda8d6ce9 2007-07-21 drh: ** original content. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void unparseMarkup(ParsedMarkup *p){ dbda8d6ce9 2007-07-21 drh: int i, n; dbda8d6ce9 2007-07-21 drh: for(i=0; i<p->nAttr; i++){ dbda8d6ce9 2007-07-21 drh: char *z = p->aAttr[i].zValue; dbda8d6ce9 2007-07-21 drh: if( z==0 ) continue; dbda8d6ce9 2007-07-21 drh: n = strlen(z); dbda8d6ce9 2007-07-21 drh: z[n] = p->aAttr[i].cTerm; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Pop a single element off of the stack. As the element is popped, dbda8d6ce9 2007-07-21 drh: ** output its end tag. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void popStack(Renderer *p){ dbda8d6ce9 2007-07-21 drh: if( p->nStack ){ dbda8d6ce9 2007-07-21 drh: p->nStack--; dbda8d6ce9 2007-07-21 drh: blob_appendf(p->pOut, "</%s>", aMarkup[p->aStack[p->nStack]].zName); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Push a new markup value onto the stack. Enlarge the stack dbda8d6ce9 2007-07-21 drh: ** if necessary. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void pushStack(Renderer *p, int elem){ dbda8d6ce9 2007-07-21 drh: if( p->nStack>=p->nAlloc ){ dbda8d6ce9 2007-07-21 drh: p->nAlloc = p->nAlloc*2 + 100; dbda8d6ce9 2007-07-21 drh: p->aStack = realloc(p->aStack, p->nAlloc); dbda8d6ce9 2007-07-21 drh: if( p->aStack==0 ){ dbda8d6ce9 2007-07-21 drh: fossil_panic("out of memory"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: p->aStack[p->nStack++] = elem; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Pop the stack until the top-most iTag element is removed. dbda8d6ce9 2007-07-21 drh: ** If there is no iTag element on the stack, this routine dbda8d6ce9 2007-07-21 drh: ** is a no-op. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void popStackToTag(Renderer *p, int iTag){ dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: for(i=p->nStack-1; i>=0 && p->aStack[i]!=iTag; i--){} dbda8d6ce9 2007-07-21 drh: if( i<0 ) return; dbda8d6ce9 2007-07-21 drh: while( p->nStack>i ){ dbda8d6ce9 2007-07-21 drh: popStack(p); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Pop the stack until the top-most element of the stack dbda8d6ce9 2007-07-21 drh: ** is an element that matches the type in iMask. Return ab637af752 2007-09-28 drh: ** code of the markup element that is on left on top of the stack. ab637af752 2007-09-28 drh: ** If the stack does not have an element dbda8d6ce9 2007-07-21 drh: ** that matches iMask, then leave the stack unchanged and ab637af752 2007-09-28 drh: ** return false (MARKUP_INVALID). dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int backupToType(Renderer *p, int iMask){ dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: for(i=p->nStack-1; i>=0 && (aMarkup[p->aStack[i]].iType&iMask)==0; i--){} dbda8d6ce9 2007-07-21 drh: if( i<0 ) return 0; dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: while( p->nStack>i ){ dbda8d6ce9 2007-07-21 drh: popStack(p); dbda8d6ce9 2007-07-21 drh: } ab637af752 2007-09-28 drh: return p->aStack[i-1]; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Add missing markup in preparation for writing text. 22c1ac41d4 2007-08-23 drh: ** 22c1ac41d4 2007-08-23 drh: ** "Missing" markup are things like start tags for table rows 22c1ac41d4 2007-08-23 drh: ** or table columns or paragraphs that are omitted from input. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void addMissingMarkup(Renderer *p){ dbda8d6ce9 2007-07-21 drh: /* TBD */ dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Resolve a hyperlink. The argument is the content of the [...] b2e55c0d4d 2007-09-01 drh: ** in the wiki. Append the URL to the output of the Renderer. dbda8d6ce9 2007-07-21 drh: */ b2e55c0d4d 2007-09-01 drh: static void resolveHyperlink(const char *zTarget, Renderer *p){ 55dc2abc60 2007-09-24 jnc: if( strncmp(zTarget, "http:", 5)==0 55dc2abc60 2007-09-24 jnc: || strncmp(zTarget, "https:", 6)==0 55dc2abc60 2007-09-24 jnc: || strncmp(zTarget, "ftp:", 4)==0 55dc2abc60 2007-09-24 jnc: || strncmp(zTarget, "mailto:", 7)==0 55dc2abc60 2007-09-24 jnc: || strncmp(zTarget, "gopher:", 7)==0 55dc2abc60 2007-09-24 jnc: ){ 55dc2abc60 2007-09-24 jnc: blob_appendf(p->pOut, zTarget); 55dc2abc60 2007-09-24 jnc: }else{ 55dc2abc60 2007-09-24 jnc: blob_appendf(p->pOut, "%s/wiki/%T", g.zBaseURL, zTarget); 55dc2abc60 2007-09-24 jnc: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Check to see if the given parsed markup is the correct dbda8d6ce9 2007-07-21 drh: ** </verbatim> tag. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int endVerbatim(Renderer *p, ParsedMarkup *pMarkup){ dbda8d6ce9 2007-07-21 drh: char *z; dbda8d6ce9 2007-07-21 drh: assert( p->inVerbatim ); dbda8d6ce9 2007-07-21 drh: if( pMarkup->iCode!=MARKUP_VERBATIM ) return 0; dbda8d6ce9 2007-07-21 drh: if( !pMarkup->endTag ) return 0; dbda8d6ce9 2007-07-21 drh: if( p->zVerbatimId==0 ) return 1; dbda8d6ce9 2007-07-21 drh: if( pMarkup->nAttr!=1 ) return 0; dbda8d6ce9 2007-07-21 drh: z = pMarkup->aAttr[0].zValue; dbda8d6ce9 2007-07-21 drh: return strcmp(z, p->zVerbatimId)==0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Return the MUTYPE for the top of the stack. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int stackTopType(Renderer *p){ dbda8d6ce9 2007-07-21 drh: if( p->nStack<=0 ) return 0; dbda8d6ce9 2007-07-21 drh: return aMarkup[p->aStack[p->nStack-1]].iType; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Convert the wiki in z[] into html in the renderer p. The dbda8d6ce9 2007-07-21 drh: ** renderer has already been initialized. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This routine will probably modify the content of z[]. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void wiki_render(Renderer *p, char *z){ dbda8d6ce9 2007-07-21 drh: int tokenType; dbda8d6ce9 2007-07-21 drh: ParsedMarkup markup; dbda8d6ce9 2007-07-21 drh: int n; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: while( z[0] ){ dbda8d6ce9 2007-07-21 drh: n = nextToken(z, p->state, &tokenType); dbda8d6ce9 2007-07-21 drh: p->state &= ~(AT_NEWLINE|AT_PARAGRAPH); dbda8d6ce9 2007-07-21 drh: switch( tokenType ){ dbda8d6ce9 2007-07-21 drh: case TOKEN_PARAGRAPH: { ab637af752 2007-09-28 drh: if( p->wikiList ){ ab637af752 2007-09-28 drh: popStackToTag(p, p->wikiList); ab637af752 2007-09-28 drh: p->wikiList = 0; ab637af752 2007-09-28 drh: } dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "\n\n<p>", -1); dbda8d6ce9 2007-07-21 drh: p->state |= AT_PARAGRAPH|AT_NEWLINE; dbda8d6ce9 2007-07-21 drh: popStackToTag(p, MARKUP_P); dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_NEWLINE: { dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "\n", 1); dbda8d6ce9 2007-07-21 drh: p->state |= AT_NEWLINE; dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_BULLET: { ab637af752 2007-09-28 drh: if( p->wikiList!=MARKUP_UL ){ ab637af752 2007-09-28 drh: if( p->wikiList ){ ab637af752 2007-09-28 drh: popStackToTag(p, p->wikiList); ab637af752 2007-09-28 drh: } dbda8d6ce9 2007-07-21 drh: pushStack(p, MARKUP_UL); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<ul>", 4); ab637af752 2007-09-28 drh: p->wikiList = MARKUP_UL; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: pushStack(p, MARKUP_LI); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<li>", 4); ab637af752 2007-09-28 drh: break; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: case TOKEN_ENUM: { ab637af752 2007-09-28 drh: if( p->wikiList!=MARKUP_OL ){ ab637af752 2007-09-28 drh: if( p->wikiList ){ ab637af752 2007-09-28 drh: popStackToTag(p, p->wikiList); ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: pushStack(p, MARKUP_OL); ab637af752 2007-09-28 drh: blob_append(p->pOut, "<ol>", 4); ab637af752 2007-09-28 drh: p->wikiList = MARKUP_OL; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: pushStack(p, MARKUP_LI); ab637af752 2007-09-28 drh: blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); ab637af752 2007-09-28 drh: break; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: case TOKEN_INDENT: { ab637af752 2007-09-28 drh: assert( p->wikiList==0 ); ab637af752 2007-09-28 drh: pushStack(p, MARKUP_BLOCKQUOTE); ab637af752 2007-09-28 drh: blob_append(p->pOut, "<blockquote>", -1); ab637af752 2007-09-28 drh: p->wikiList = MARKUP_BLOCKQUOTE; dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_CHARACTER: { dbda8d6ce9 2007-07-21 drh: if( z[0]=='<' ){ dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<", 4); dbda8d6ce9 2007-07-21 drh: }else if( z[0]=='&' ){ dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "&", 5); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_LINK: { dbda8d6ce9 2007-07-21 drh: char *zTarget; dbda8d6ce9 2007-07-21 drh: char *zDisplay = 0; dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: int savedState; dbda8d6ce9 2007-07-21 drh: addMissingMarkup(p); dbda8d6ce9 2007-07-21 drh: zTarget = &z[1]; dbda8d6ce9 2007-07-21 drh: for(i=1; z[i] && z[i]!=']'; i++){ dbda8d6ce9 2007-07-21 drh: if( z[i]=='|' && zDisplay==0 ){ dbda8d6ce9 2007-07-21 drh: zDisplay = &z[i+1]; dbda8d6ce9 2007-07-21 drh: z[i] = 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: z[i] = 0; dbda8d6ce9 2007-07-21 drh: if( zDisplay==0 ){ dbda8d6ce9 2007-07-21 drh: zDisplay = zTarget; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: while( isspace(*zDisplay) ) zDisplay++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<a href=\"", -1); b2e55c0d4d 2007-09-01 drh: resolveHyperlink(zTarget, p); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "\">", -1); dbda8d6ce9 2007-07-21 drh: savedState = p->state; dbda8d6ce9 2007-07-21 drh: p->state &= ~ALLOW_WIKI; dbda8d6ce9 2007-07-21 drh: p->state |= FONT_MARKUP_ONLY; dbda8d6ce9 2007-07-21 drh: wiki_render(p, zDisplay); dbda8d6ce9 2007-07-21 drh: p->state = savedState; dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "</a>", 4); dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_TEXT: { dbda8d6ce9 2007-07-21 drh: addMissingMarkup(p); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, z, n); dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_MARKUP: { dbda8d6ce9 2007-07-21 drh: parseMarkup(&markup, z); dbda8d6ce9 2007-07-21 drh: if( p->inVerbatim ){ dbda8d6ce9 2007-07-21 drh: if( endVerbatim(p, &markup) ){ dbda8d6ce9 2007-07-21 drh: p->inVerbatim = 0; dbda8d6ce9 2007-07-21 drh: p->state = p->preVerbState; dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "</pre>", 6); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: unparseMarkup(&markup); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<", 4); dbda8d6ce9 2007-07-21 drh: n = 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: }else if( markup.iCode==MARKUP_INVALID ){ dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<", 4); dbda8d6ce9 2007-07-21 drh: n = 1; dbda8d6ce9 2007-07-21 drh: }else if( (markup.iType&MUTYPE_FONT)==0 dbda8d6ce9 2007-07-21 drh: && (p->state & FONT_MARKUP_ONLY)!=0 ){ dbda8d6ce9 2007-07-21 drh: /* Do nothing */ dbda8d6ce9 2007-07-21 drh: }else if( markup.iCode==MARKUP_NOWIKI ){ dbda8d6ce9 2007-07-21 drh: if( markup.endTag ){ dbda8d6ce9 2007-07-21 drh: p->state |= ALLOW_WIKI; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: p->state &= ALLOW_WIKI; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: }else if( markup.endTag ){ dbda8d6ce9 2007-07-21 drh: popStackToTag(p, markup.iCode); dbda8d6ce9 2007-07-21 drh: }else if( markup.iCode==MARKUP_VERBATIM ){ dbda8d6ce9 2007-07-21 drh: if( markup.nAttr==1 ){ dbda8d6ce9 2007-07-21 drh: p->zVerbatimId = markup.aAttr[0].zValue; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: p->zVerbatimId = 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: p->inVerbatim = 1; dbda8d6ce9 2007-07-21 drh: p->preVerbState = p->state; dbda8d6ce9 2007-07-21 drh: p->state &= ~ALLOW_WIKI; dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<pre>", 5); dbda8d6ce9 2007-07-21 drh: }else if( markup.iType==MUTYPE_LI ){ dbda8d6ce9 2007-07-21 drh: if( backupToType(p, MUTYPE_LIST)==0 ){ dbda8d6ce9 2007-07-21 drh: pushStack(p, MARKUP_UL); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<ul>", 4); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: pushStack(p, MARKUP_LI); dbda8d6ce9 2007-07-21 drh: renderMarkup(p->pOut, &markup); dbda8d6ce9 2007-07-21 drh: }else if( markup.iType==MUTYPE_TR ){ dbda8d6ce9 2007-07-21 drh: if( backupToType(p, MUTYPE_TABLE) ){ dbda8d6ce9 2007-07-21 drh: pushStack(p, MARKUP_TR); dbda8d6ce9 2007-07-21 drh: renderMarkup(p->pOut, &markup); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: }else if( markup.iType==MUTYPE_TD ){ dbda8d6ce9 2007-07-21 drh: if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){ dbda8d6ce9 2007-07-21 drh: if( stackTopType(p)==MUTYPE_TABLE ){ dbda8d6ce9 2007-07-21 drh: pushStack(p, MARKUP_TR); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<tr>", 4); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: pushStack(p, markup.iCode); dbda8d6ce9 2007-07-21 drh: renderMarkup(p->pOut, &markup); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: if( (markup.iType & MUTYPE_STACK )!=0 ){ dbda8d6ce9 2007-07-21 drh: pushStack(p, markup.iCode); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: renderMarkup(p->pOut, &markup); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: z += n; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Transform the text in the pIn blob. Write the results dbda8d6ce9 2007-07-21 drh: ** into the pOut blob. The pOut blob should already be dbda8d6ce9 2007-07-21 drh: ** initialized. The output is merely appended to pOut. ab637af752 2007-09-28 drh: ** If pOut is NULL, then the output is appended to the CGI ab637af752 2007-09-28 drh: ** reply. dbda8d6ce9 2007-07-21 drh: */ ab637af752 2007-09-28 drh: void wiki_convert(Blob *pIn, Blob *pOut){ 4c82c7773f 2007-08-30 drh: char *z; dbda8d6ce9 2007-07-21 drh: Renderer renderer; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: memset(&renderer, 0, sizeof(renderer)); dbda8d6ce9 2007-07-21 drh: renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; ab637af752 2007-09-28 drh: if( pOut ){ ab637af752 2007-09-28 drh: renderer.pOut = pOut; ab637af752 2007-09-28 drh: }else{ ab637af752 2007-09-28 drh: renderer.pOut = cgi_output_blob(); ab637af752 2007-09-28 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: z = blob_str(pIn); dbda8d6ce9 2007-07-21 drh: wiki_render(&renderer, z); dbda8d6ce9 2007-07-21 drh: while( renderer.nStack ){ dbda8d6ce9 2007-07-21 drh: popStack(&renderer); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: blob_append(pOut, "\n", 1); dbda8d6ce9 2007-07-21 drh: free(renderer.aStack); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: test-wiki-render dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void test_wiki_render(void){ dbda8d6ce9 2007-07-21 drh: Blob in, out; dbda8d6ce9 2007-07-21 drh: if( g.argc!=3 ) usage("FILE"); dbda8d6ce9 2007-07-21 drh: blob_zero(&out); dbda8d6ce9 2007-07-21 drh: blob_read_from_file(&in, g.argv[2]); ab637af752 2007-09-28 drh: wiki_convert(&in, &out); dbda8d6ce9 2007-07-21 drh: blob_write_to_file(&out, "-"); dbda8d6ce9 2007-07-21 drh: }