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 c963a7763d 2007-10-13 drh: #define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */ ebb2765954 2007-12-04 drh: #define WIKI_NOBLOCK 0x008 /* No block markup of any kind */ 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: */ e01aa8cb4b 2008-07-17 drh: #define ATTR_ALIGN 1 e01aa8cb4b 2008-07-17 drh: #define ATTR_ALT 2 e01aa8cb4b 2008-07-17 drh: #define ATTR_BGCOLOR 3 e01aa8cb4b 2008-07-17 drh: #define ATTR_BORDER 4 e01aa8cb4b 2008-07-17 drh: #define ATTR_CELLPADDING 5 e01aa8cb4b 2008-07-17 drh: #define ATTR_CELLSPACING 6 e01aa8cb4b 2008-07-17 drh: #define ATTR_CLEAR 7 e01aa8cb4b 2008-07-17 drh: #define ATTR_COLOR 8 e01aa8cb4b 2008-07-17 drh: #define ATTR_COLSPAN 9 e01aa8cb4b 2008-07-17 drh: #define ATTR_COMPACT 10 e01aa8cb4b 2008-07-17 drh: #define ATTR_FACE 11 e01aa8cb4b 2008-07-17 drh: #define ATTR_HEIGHT 12 e01aa8cb4b 2008-07-17 drh: #define ATTR_HREF 13 e01aa8cb4b 2008-07-17 drh: #define ATTR_HSPACE 14 e01aa8cb4b 2008-07-17 drh: #define ATTR_ID 15 e01aa8cb4b 2008-07-17 drh: #define ATTR_NAME 16 e01aa8cb4b 2008-07-17 drh: #define ATTR_ROWSPAN 17 e01aa8cb4b 2008-07-17 drh: #define ATTR_SIZE 18 e01aa8cb4b 2008-07-17 drh: #define ATTR_SRC 19 e01aa8cb4b 2008-07-17 drh: #define ATTR_START 20 e01aa8cb4b 2008-07-17 drh: #define ATTR_TYPE 21 e01aa8cb4b 2008-07-17 drh: #define ATTR_VALIGN 22 e01aa8cb4b 2008-07-17 drh: #define ATTR_VALUE 23 e01aa8cb4b 2008-07-17 drh: #define ATTR_VSPACE 24 e01aa8cb4b 2008-07-17 drh: #define ATTR_WIDTH 25 e01aa8cb4b 2008-07-17 drh: #define AMSK_ALIGN 0x0000001 e01aa8cb4b 2008-07-17 drh: #define AMSK_ALT 0x0000002 e01aa8cb4b 2008-07-17 drh: #define AMSK_BGCOLOR 0x0000004 e01aa8cb4b 2008-07-17 drh: #define AMSK_BORDER 0x0000008 e01aa8cb4b 2008-07-17 drh: #define AMSK_CELLPADDING 0x0000010 e01aa8cb4b 2008-07-17 drh: #define AMSK_CELLSPACING 0x0000020 e01aa8cb4b 2008-07-17 drh: #define AMSK_CLEAR 0x0000040 e01aa8cb4b 2008-07-17 drh: #define AMSK_COLOR 0x0000080 e01aa8cb4b 2008-07-17 drh: #define AMSK_COLSPAN 0x0000100 e01aa8cb4b 2008-07-17 drh: #define AMSK_COMPACT 0x0000200 e01aa8cb4b 2008-07-17 drh: #define AMSK_FACE 0x0000400 e01aa8cb4b 2008-07-17 drh: #define AMSK_HEIGHT 0x0000800 e01aa8cb4b 2008-07-17 drh: #define AMSK_HREF 0x0001000 e01aa8cb4b 2008-07-17 drh: #define AMSK_HSPACE 0x0002000 e01aa8cb4b 2008-07-17 drh: #define AMSK_ID 0x0004000 e01aa8cb4b 2008-07-17 drh: #define AMSK_NAME 0x0008000 e01aa8cb4b 2008-07-17 drh: #define AMSK_ROWSPAN 0x0010000 e01aa8cb4b 2008-07-17 drh: #define AMSK_SIZE 0x0020000 e01aa8cb4b 2008-07-17 drh: #define AMSK_SRC 0x0040000 e01aa8cb4b 2008-07-17 drh: #define AMSK_START 0x0080000 e01aa8cb4b 2008-07-17 drh: #define AMSK_TYPE 0x0100000 e01aa8cb4b 2008-07-17 drh: #define AMSK_VALIGN 0x0200000 e01aa8cb4b 2008-07-17 drh: #define AMSK_VALUE 0x0400000 e01aa8cb4b 2008-07-17 drh: #define AMSK_VSPACE 0x0800000 e01aa8cb4b 2008-07-17 drh: #define AMSK_WIDTH 0x1000000 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[] = { df646a7f4c 2007-10-12 drh: { 0, 0 }, e01aa8cb4b 2008-07-17 drh: { "align", AMSK_ALIGN, }, e01aa8cb4b 2008-07-17 drh: { "alt", AMSK_ALT, }, e01aa8cb4b 2008-07-17 drh: { "bgcolor", AMSK_BGCOLOR, }, e01aa8cb4b 2008-07-17 drh: { "border", AMSK_BORDER, }, e01aa8cb4b 2008-07-17 drh: { "cellpadding", AMSK_CELLPADDING, }, e01aa8cb4b 2008-07-17 drh: { "cellspacing", AMSK_CELLSPACING, }, e01aa8cb4b 2008-07-17 drh: { "clear", AMSK_CLEAR, }, e01aa8cb4b 2008-07-17 drh: { "color", AMSK_COLOR, }, e01aa8cb4b 2008-07-17 drh: { "colspan", AMSK_COLSPAN, }, e01aa8cb4b 2008-07-17 drh: { "compact", AMSK_COMPACT, }, e01aa8cb4b 2008-07-17 drh: { "face", AMSK_FACE, }, e01aa8cb4b 2008-07-17 drh: { "height", AMSK_HEIGHT, }, e01aa8cb4b 2008-07-17 drh: { "href", AMSK_HREF, }, e01aa8cb4b 2008-07-17 drh: { "hspace", AMSK_HSPACE, }, e01aa8cb4b 2008-07-17 drh: { "id", AMSK_ID, }, e01aa8cb4b 2008-07-17 drh: { "name", AMSK_NAME, }, e01aa8cb4b 2008-07-17 drh: { "rowspan", AMSK_ROWSPAN, }, e01aa8cb4b 2008-07-17 drh: { "size", AMSK_SIZE, }, e01aa8cb4b 2008-07-17 drh: { "src", AMSK_SRC, }, e01aa8cb4b 2008-07-17 drh: { "start", AMSK_START, }, e01aa8cb4b 2008-07-17 drh: { "type", AMSK_TYPE, }, e01aa8cb4b 2008-07-17 drh: { "valign", AMSK_VALIGN, }, e01aa8cb4b 2008-07-17 drh: { "value", AMSK_VALUE, }, e01aa8cb4b 2008-07-17 drh: { "vspace", AMSK_VSPACE, }, e01aa8cb4b 2008-07-17 drh: { "width", AMSK_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; df646a7f4c 2007-10-12 drh: first = 1; 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 ){ df646a7f4c 2007-10-12 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 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 e01aa8cb4b 2008-07-17 drh: #define MARKUP_DIV 12 e01aa8cb4b 2008-07-17 drh: #define MARKUP_DL 13 e01aa8cb4b 2008-07-17 drh: #define MARKUP_DT 14 e01aa8cb4b 2008-07-17 drh: #define MARKUP_EM 15 e01aa8cb4b 2008-07-17 drh: #define MARKUP_FONT 16 e01aa8cb4b 2008-07-17 drh: #define MARKUP_H1 17 e01aa8cb4b 2008-07-17 drh: #define MARKUP_H2 18 e01aa8cb4b 2008-07-17 drh: #define MARKUP_H3 19 e01aa8cb4b 2008-07-17 drh: #define MARKUP_H4 20 e01aa8cb4b 2008-07-17 drh: #define MARKUP_H5 21 e01aa8cb4b 2008-07-17 drh: #define MARKUP_H6 22 e01aa8cb4b 2008-07-17 drh: #define MARKUP_HR 23 ab637af752 2007-09-28 drh: #define MARKUP_I 24 e01aa8cb4b 2008-07-17 drh: #define MARKUP_IMG 25 e01aa8cb4b 2008-07-17 drh: #define MARKUP_KBD 26 e01aa8cb4b 2008-07-17 drh: #define MARKUP_LI 27 e01aa8cb4b 2008-07-17 drh: #define MARKUP_NOBR 28 e01aa8cb4b 2008-07-17 drh: #define MARKUP_NOWIKI 29 e01aa8cb4b 2008-07-17 drh: #define MARKUP_OL 30 e01aa8cb4b 2008-07-17 drh: #define MARKUP_P 31 e01aa8cb4b 2008-07-17 drh: #define MARKUP_PRE 32 dbda8d6ce9 2007-07-21 drh: #define MARKUP_S 33 e01aa8cb4b 2008-07-17 drh: #define MARKUP_SAMP 34 e01aa8cb4b 2008-07-17 drh: #define MARKUP_SMALL 35 e01aa8cb4b 2008-07-17 drh: #define MARKUP_STRIKE 36 e01aa8cb4b 2008-07-17 drh: #define MARKUP_STRONG 37 e01aa8cb4b 2008-07-17 drh: #define MARKUP_SUB 38 e01aa8cb4b 2008-07-17 drh: #define MARKUP_SUP 39 e01aa8cb4b 2008-07-17 drh: #define MARKUP_TABLE 40 e01aa8cb4b 2008-07-17 drh: #define MARKUP_TD 41 e01aa8cb4b 2008-07-17 drh: #define MARKUP_TH 42 e01aa8cb4b 2008-07-17 drh: #define MARKUP_TR 43 e01aa8cb4b 2008-07-17 drh: #define MARKUP_TT 44 e01aa8cb4b 2008-07-17 drh: #define MARKUP_U 45 e01aa8cb4b 2008-07-17 drh: #define MARKUP_UL 46 e01aa8cb4b 2008-07-17 drh: #define MARKUP_VAR 47 e01aa8cb4b 2008-07-17 drh: #define MARKUP_VERBATIM 48 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_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: c963a7763d 2007-10-13 drh: /* c963a7763d 2007-10-13 drh: ** These markup types must have an end tag. c963a7763d 2007-10-13 drh: */ dbda8d6ce9 2007-07-21 drh: #define MUTYPE_STACK (MUTYPE_BLOCK | MUTYPE_FONT | MUTYPE_LIST | MUTYPE_TABLE) c963a7763d 2007-10-13 drh: ebb2765954 2007-12-04 drh: /* ebb2765954 2007-12-04 drh: ** This markup types are allowed for "inline" text. ebb2765954 2007-12-04 drh: */ ebb2765954 2007-12-04 drh: #define MUTYPE_INLINE (MUTYPE_FONT | MUTYPE_HYPERLINK) 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 }, c963a7763d 2007-10-13 drh: { "a", MARKUP_A, MUTYPE_HYPERLINK, e01aa8cb4b 2008-07-17 drh: AMSK_HREF|AMSK_NAME }, 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 }, e01aa8cb4b 2008-07-17 drh: { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_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 }, e01aa8cb4b 2008-07-17 drh: { "div", MARKUP_DIV, MUTYPE_BLOCK, AMSK_ID }, e01aa8cb4b 2008-07-17 drh: { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_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, e01aa8cb4b 2008-07-17 drh: AMSK_COLOR|AMSK_FACE|AMSK_SIZE }, e01aa8cb4b 2008-07-17 drh: { "h1", MARKUP_H1, MUTYPE_BLOCK, AMSK_ALIGN }, e01aa8cb4b 2008-07-17 drh: { "h2", MARKUP_H2, MUTYPE_BLOCK, AMSK_ALIGN }, e01aa8cb4b 2008-07-17 drh: { "h3", MARKUP_H3, MUTYPE_BLOCK, AMSK_ALIGN }, e01aa8cb4b 2008-07-17 drh: { "h4", MARKUP_H4, MUTYPE_BLOCK, AMSK_ALIGN }, e01aa8cb4b 2008-07-17 drh: { "h5", MARKUP_H5, MUTYPE_BLOCK, AMSK_ALIGN }, e01aa8cb4b 2008-07-17 drh: { "h6", MARKUP_H6, MUTYPE_BLOCK, AMSK_ALIGN }, dbda8d6ce9 2007-07-21 drh: { "hr", MARKUP_HR, MUTYPE_SINGLE, e01aa8cb4b 2008-07-17 drh: AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH }, 7351b6346d 2008-05-15 drh: { "i", MARKUP_I, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "img", MARKUP_IMG, MUTYPE_SINGLE, e01aa8cb4b 2008-07-17 drh: AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT| e01aa8cb4b 2008-07-17 drh: AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH }, dbda8d6ce9 2007-07-21 drh: { "kbd", MARKUP_KBD, MUTYPE_FONT, 0 }, dbda8d6ce9 2007-07-21 drh: { "li", MARKUP_LI, MUTYPE_LI, e01aa8cb4b 2008-07-17 drh: AMSK_TYPE|AMSK_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, e01aa8cb4b 2008-07-17 drh: AMSK_START|AMSK_TYPE|AMSK_COMPACT }, e01aa8cb4b 2008-07-17 drh: { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_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, e01aa8cb4b 2008-07-17 drh: AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING| e01aa8cb4b 2008-07-17 drh: AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE }, dbda8d6ce9 2007-07-21 drh: { "td", MARKUP_TD, MUTYPE_TD, e01aa8cb4b 2008-07-17 drh: AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| e01aa8cb4b 2008-07-17 drh: AMSK_ROWSPAN|AMSK_VALIGN }, dbda8d6ce9 2007-07-21 drh: { "th", MARKUP_TH, MUTYPE_TD, e01aa8cb4b 2008-07-17 drh: AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| e01aa8cb4b 2008-07-17 drh: AMSK_ROWSPAN|AMSK_VALIGN }, dbda8d6ce9 2007-07-21 drh: { "tr", MARKUP_TR, MUTYPE_TR, e01aa8cb4b 2008-07-17 drh: AMSK_ALIGN|AMSK_BGCOLOR||AMSK_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, e01aa8cb4b 2008-07-17 drh: AMSK_TYPE|AMSK_COMPACT }, dbda8d6ce9 2007-07-21 drh: { "var", MARKUP_VAR, MUTYPE_FONT, 0 }, e01aa8cb4b 2008-07-17 drh: { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_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: */ ebb2765954 2007-12-04 drh: #define AT_NEWLINE 0x001 /* At start of a line */ ebb2765954 2007-12-04 drh: #define AT_PARAGRAPH 0x002 /* At start of a paragraph */ ebb2765954 2007-12-04 drh: #define ALLOW_WIKI 0x004 /* Allow wiki markup */ ebb2765954 2007-12-04 drh: #define FONT_MARKUP_ONLY 0x008 /* Only allow MUTYPE_FONT markup */ ebb2765954 2007-12-04 drh: #define INLINE_MARKUP_ONLY 0x010 /* Allow only "inline" markup */ ebb2765954 2007-12-04 drh: #define IN_LIST 0x020 /* 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 */ c963a7763d 2007-10-13 drh: int wantAutoParagraph; /* True if a <p> is desired */ c963a7763d 2007-10-13 drh: int inAutoParagraph; /* True if within an automatic paragraph */ 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 */ e01aa8cb4b 2008-07-17 drh: struct sStack { e01aa8cb4b 2008-07-17 drh: short iCode; /* Markup code */ e01aa8cb4b 2008-07-17 drh: short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */ e01aa8cb4b 2008-07-17 drh: const char *zId; /* ID attribute or NULL */ e01aa8cb4b 2008-07-17 drh: } *aStack; 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 "." c963a7763d 2007-10-13 drh: ** * another tab or two ore more 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: */ e75f9a2ab4 2007-10-28 drh: static int nextToken(const char *z, Renderer *p, 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: } e75f9a2ab4 2007-10-28 drh: if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){ dbda8d6ce9 2007-07-21 drh: *pTokenType = TOKEN_CHARACTER; dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: } e75f9a2ab4 2007-10-28 drh: if( (p->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: } e75f9a2ab4 2007-10-28 drh: if( (p->state & AT_NEWLINE)!=0 && 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: } ab637af752 2007-09-28 drh: } e75f9a2ab4 2007-10-28 drh: if( (p->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; e75f9a2ab4 2007-10-28 drh: return 1 + textLength(z+1, p->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 { e01aa8cb4b 2008-07-17 drh: unsigned char iACode; /* 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){ a4d7e9162d 2008-05-16 drh: int i, j, c; e01aa8cb4b 2008-07-17 drh: int iACode; a4d7e9162d 2008-05-16 drh: char *zValue; dbda8d6ce9 2007-07-21 drh: int seen = 0; a4d7e9162d 2008-05-16 drh: char zTag[100]; 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: } a4d7e9162d 2008-05-16 drh: j = 0; dbda8d6ce9 2007-07-21 drh: while( isalnum(z[i]) ){ a4d7e9162d 2008-05-16 drh: if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: } a4d7e9162d 2008-05-16 drh: zTag[j] = 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: while( isspace(z[i]) ){ i++; } dbda8d6ce9 2007-07-21 drh: while( p->nAttr<8 && isalpha(z[i]) ){ 71104b898d 2008-06-08 drh: int attrOk; /* True to preserver attribute. False to ignore it */ a4d7e9162d 2008-05-16 drh: j = 0; dbda8d6ce9 2007-07-21 drh: while( isalnum(z[i]) ){ a4d7e9162d 2008-05-16 drh: if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]); dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: } a4d7e9162d 2008-05-16 drh: zTag[j] = 0; e01aa8cb4b 2008-07-17 drh: p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); e01aa8cb4b 2008-07-17 drh: attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0; 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: } 71104b898d 2008-06-08 drh: if( attrOk ){ 71104b898d 2008-06-08 drh: p->aAttr[p->nAttr].zValue = zValue; 71104b898d 2008-06-08 drh: p->aAttr[p->nAttr].cTerm = c = z[i]; 71104b898d 2008-06-08 drh: z[i] = 0; 71104b898d 2008-06-08 drh: } dbda8d6ce9 2007-07-21 drh: i++; dbda8d6ce9 2007-07-21 drh: } 71104b898d 2008-06-08 drh: if( attrOk ){ e01aa8cb4b 2008-07-17 drh: seen |= aAttribute[iACode].iMask; dbda8d6ce9 2007-07-21 drh: p->nAttr++; dbda8d6ce9 2007-07-21 drh: } 1bbc5b7e6c 2008-05-16 drh: while( isspace(z[i]) ){ i++; } a4d7e9162d 2008-05-16 drh: if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) 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++){ e01aa8cb4b 2008-07-17 drh: blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName); 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: /* e01aa8cb4b 2008-07-17 drh: ** Return the ID attribute for markup. Return NULL if there is no e01aa8cb4b 2008-07-17 drh: ** ID attribute. e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: static const char *markupId(ParsedMarkup *p){ e01aa8cb4b 2008-07-17 drh: int i; e01aa8cb4b 2008-07-17 drh: for(i=0; i<p->nAttr; i++){ e01aa8cb4b 2008-07-17 drh: if( p->aAttr[i].iACode==ATTR_ID ){ e01aa8cb4b 2008-07-17 drh: return p->aAttr[i].zValue; e01aa8cb4b 2008-07-17 drh: } e01aa8cb4b 2008-07-17 drh: } e01aa8cb4b 2008-07-17 drh: return 0; e01aa8cb4b 2008-07-17 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, e01aa8cb4b 2008-07-17 drh: ** output its end tag if it is not a </div> tag. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void popStack(Renderer *p){ dbda8d6ce9 2007-07-21 drh: if( p->nStack ){ e01aa8cb4b 2008-07-17 drh: int iCode; dbda8d6ce9 2007-07-21 drh: p->nStack--; e01aa8cb4b 2008-07-17 drh: iCode = p->aStack[p->nStack].iCode; e01aa8cb4b 2008-07-17 drh: if( iCode!=MARKUP_DIV ){ e01aa8cb4b 2008-07-17 drh: blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName); e01aa8cb4b 2008-07-17 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: ** Push a new markup value onto the stack. Enlarge the stack dbda8d6ce9 2007-07-21 drh: ** if necessary. dbda8d6ce9 2007-07-21 drh: */ e01aa8cb4b 2008-07-17 drh: static void pushStackWithId(Renderer *p, int elem, const char *zId, int w){ dbda8d6ce9 2007-07-21 drh: if( p->nStack>=p->nAlloc ){ dbda8d6ce9 2007-07-21 drh: p->nAlloc = p->nAlloc*2 + 100; e01aa8cb4b 2008-07-17 drh: p->aStack = realloc(p->aStack, p->nAlloc*sizeof(p->aStack[0])); 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: } e01aa8cb4b 2008-07-17 drh: p->aStack[p->nStack].iCode = elem; e01aa8cb4b 2008-07-17 drh: p->aStack[p->nStack].zId = zId; e01aa8cb4b 2008-07-17 drh: p->aStack[p->nStack].allowWiki = w; e01aa8cb4b 2008-07-17 drh: p->nStack++; e01aa8cb4b 2008-07-17 drh: } e01aa8cb4b 2008-07-17 drh: static void pushStack(Renderer *p, int elem){ e01aa8cb4b 2008-07-17 drh: pushStackWithId(p, elem, 0, 0); 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; e01aa8cb4b 2008-07-17 drh: for(i=p->nStack-1; i>=0; i--){ e01aa8cb4b 2008-07-17 drh: if( p->aStack[i].iCode!=iTag ) continue; e01aa8cb4b 2008-07-17 drh: if( p->aStack[i].zId ) continue; e01aa8cb4b 2008-07-17 drh: break; e01aa8cb4b 2008-07-17 drh: } 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: /* e01aa8cb4b 2008-07-17 drh: ** Attempt to find a find a tag of type iTag with id zId. Return -1 e01aa8cb4b 2008-07-17 drh: ** if not found. If found, return its stack level. e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: static int findTagWithId(Renderer *p, int iTag, const char *zId){ e01aa8cb4b 2008-07-17 drh: int i; e01aa8cb4b 2008-07-17 drh: assert( zId!=0 ); e01aa8cb4b 2008-07-17 drh: for(i=p->nStack-1; i>=0; i--){ e01aa8cb4b 2008-07-17 drh: if( p->aStack[i].iCode!=iTag ) continue; e01aa8cb4b 2008-07-17 drh: if( p->aStack[i].zId==0 ) continue; e01aa8cb4b 2008-07-17 drh: if( strcmp(zId, p->aStack[i].zId)!=0 ) continue; e01aa8cb4b 2008-07-17 drh: break; e01aa8cb4b 2008-07-17 drh: } e01aa8cb4b 2008-07-17 drh: return i; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: ab637af752 2007-09-28 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; e01aa8cb4b 2008-07-17 drh: for(i=p->nStack-1; i>=0; i--){ e01aa8cb4b 2008-07-17 drh: if( aMarkup[p->aStack[i].iCode].iType & iMask ) break; e01aa8cb4b 2008-07-17 drh: } 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: } e01aa8cb4b 2008-07-17 drh: return p->aStack[i-1].iCode; c963a7763d 2007-10-13 drh: } c963a7763d 2007-10-13 drh: c963a7763d 2007-10-13 drh: /* c963a7763d 2007-10-13 drh: ** Begin a new paragraph if that something that is needed. c963a7763d 2007-10-13 drh: */ c963a7763d 2007-10-13 drh: static void startAutoParagraph(Renderer *p){ c963a7763d 2007-10-13 drh: if( p->wantAutoParagraph==0 ) return; c963a7763d 2007-10-13 drh: blob_appendf(p->pOut, "<p>", -1); c963a7763d 2007-10-13 drh: pushStack(p, MARKUP_P); c963a7763d 2007-10-13 drh: p->wantAutoParagraph = 0; c963a7763d 2007-10-13 drh: p->inAutoParagraph = 1; c963a7763d 2007-10-13 drh: } c963a7763d 2007-10-13 drh: c963a7763d 2007-10-13 drh: /* c963a7763d 2007-10-13 drh: ** End a paragraph if we are in one. c963a7763d 2007-10-13 drh: */ c963a7763d 2007-10-13 drh: static void endAutoParagraph(Renderer *p){ c963a7763d 2007-10-13 drh: if( p->inAutoParagraph ){ c963a7763d 2007-10-13 drh: popStackToTag(p, MARKUP_P); c963a7763d 2007-10-13 drh: p->inAutoParagraph = 0; c963a7763d 2007-10-13 drh: } c963a7763d 2007-10-13 drh: } c963a7763d 2007-10-13 drh: c963a7763d 2007-10-13 drh: /* f394d84560 2007-11-25 drh: ** If the input string corresponds to an existing baseline, f394d84560 2007-11-25 drh: ** return true. f394d84560 2007-11-25 drh: */ f394d84560 2007-11-25 drh: static int is_valid_uuid(const char *z){ f394d84560 2007-11-25 drh: int n = strlen(z); f394d84560 2007-11-25 drh: if( n<4 || n>UUID_SIZE ) return 0; f394d84560 2007-11-25 drh: if( !validate16(z, n) ) return 0; dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* ac3f1f2ba7 2008-10-18 drh: ** zTarget is guaranteed to be a UUID. It might be the UUID of a ticket. f0c8693845 2008-10-20 drh: ** If it is, store in *pClosed a true or false depending on whether or not f0c8693845 2008-10-20 drh: ** the ticket is closed and return true. If zTarget ac3f1f2ba7 2008-10-18 drh: ** is not the UUID of a ticket, return false. dbda8d6ce9 2007-07-21 drh: */ ac3f1f2ba7 2008-10-18 drh: static int is_ticket( ac3f1f2ba7 2008-10-18 drh: const char *zTarget, /* Ticket UUID */ ac3f1f2ba7 2008-10-18 drh: int *pClosed /* True if the ticket is closed */ ac3f1f2ba7 2008-10-18 drh: ){ ac3f1f2ba7 2008-10-18 drh: static Stmt q; ac3f1f2ba7 2008-10-18 drh: static int once = 1; ac3f1f2ba7 2008-10-18 drh: int n; ac3f1f2ba7 2008-10-18 drh: int rc; ac3f1f2ba7 2008-10-18 drh: char zLower[UUID_SIZE+1]; ac3f1f2ba7 2008-10-18 drh: char zUpper[UUID_SIZE+1]; ac3f1f2ba7 2008-10-18 drh: n = strlen(zTarget); ac3f1f2ba7 2008-10-18 drh: memcpy(zLower, zTarget, n+1); ac3f1f2ba7 2008-10-18 drh: canonical16(zLower, n+1); ac3f1f2ba7 2008-10-18 drh: memcpy(zUpper, zLower, n+1); ac3f1f2ba7 2008-10-18 drh: zUpper[n-1]++; ac3f1f2ba7 2008-10-18 drh: if( once ){ ac3f1f2ba7 2008-10-18 drh: const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'"); ac3f1f2ba7 2008-10-18 drh: db_static_prepare(&q, f0c8693845 2008-10-20 drh: "SELECT %s FROM ticket " ac3f1f2ba7 2008-10-18 drh: " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr", f0c8693845 2008-10-20 drh: zClosedExpr ac3f1f2ba7 2008-10-18 drh: ); f0c8693845 2008-10-20 drh: once = 0; ac3f1f2ba7 2008-10-18 drh: } ac3f1f2ba7 2008-10-18 drh: db_bind_text(&q, ":lwr", zLower); ac3f1f2ba7 2008-10-18 drh: db_bind_text(&q, ":upr", zUpper); ac3f1f2ba7 2008-10-18 drh: if( db_step(&q)==SQLITE_ROW ){ ac3f1f2ba7 2008-10-18 drh: rc = 1; f0c8693845 2008-10-20 drh: *pClosed = db_column_int(&q, 0); ac3f1f2ba7 2008-10-18 drh: }else{ ac3f1f2ba7 2008-10-18 drh: rc = 0; d57de28756 2008-05-05 drh: } ac3f1f2ba7 2008-10-18 drh: db_reset(&q); ac3f1f2ba7 2008-10-18 drh: return rc; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* ac3f1f2ba7 2008-10-18 drh: ** Resolve a hyperlink. The zTarget argument is the content of the [...] f0c8693845 2008-10-20 drh: ** in the wiki. Append to the output string whatever text is approprate f0c8693845 2008-10-20 drh: ** for opening the hyperlink. Write into zClose[0...nClose-1] text that will f0c8693845 2008-10-20 drh: ** close the markup. ac3f1f2ba7 2008-10-18 drh: ** ac3f1f2ba7 2008-10-18 drh: ** Actually, this routine might or might not append the hyperlink, depending ac3f1f2ba7 2008-10-18 drh: ** on current rendering rules: specifically does the current user have f0c8693845 2008-10-20 drh: ** "History" permission. dbda8d6ce9 2007-07-21 drh: */ f0c8693845 2008-10-20 drh: static void openHyperlink( ac3f1f2ba7 2008-10-18 drh: Renderer *p, /* Rendering context */ ac3f1f2ba7 2008-10-18 drh: const char *zTarget, /* Hyperlink traget; text within [...] */ f0c8693845 2008-10-20 drh: char *zClose, /* Write hyperlink closing text here */ f0c8693845 2008-10-20 drh: int nClose /* Bytes available in zClose[] */ ac3f1f2ba7 2008-10-18 drh: ){ f0c8693845 2008-10-20 drh: const char *zTerm = "</a>"; f0c8693845 2008-10-20 drh: assert( nClose>10 ); f0c8693845 2008-10-20 drh: 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: ){ ac3f1f2ba7 2008-10-18 drh: blob_appendf(p->pOut, "<a href=\"%s\">", zTarget); 50a58adb76 2007-10-10 drh: }else if( zTarget[0]=='/' ){ a8c3a7ea92 2008-11-22 drh: if( 1 /* g.okHistory */ ){ ac3f1f2ba7 2008-10-18 drh: blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget); 63886daad8 2008-11-18 drh: }else{ 63886daad8 2008-11-18 drh: zTerm = ""; 63886daad8 2008-11-18 drh: } 63886daad8 2008-11-18 drh: }else if( zTarget[0]=='.' ){ a8c3a7ea92 2008-11-22 drh: if( 1 /* g.okHistory */ ){ 63886daad8 2008-11-18 drh: blob_appendf(p->pOut, "<a href=\"%h\">", zTarget); f0c8693845 2008-10-20 drh: }else{ f0c8693845 2008-10-20 drh: zTerm = ""; ac3f1f2ba7 2008-10-18 drh: } f394d84560 2007-11-25 drh: }else if( is_valid_uuid(zTarget) ){ ac3f1f2ba7 2008-10-18 drh: int isClosed; f0c8693845 2008-10-20 drh: if( is_ticket(zTarget, &isClosed) ){ ac3f1f2ba7 2008-10-18 drh: /* Special display processing for tickets. Display the hyperlink f0c8693845 2008-10-20 drh: ** as crossed out if the ticket is closed. ac3f1f2ba7 2008-10-18 drh: */ ac3f1f2ba7 2008-10-18 drh: if( isClosed ){ ac3f1f2ba7 2008-10-18 drh: if( g.okHistory ){ f0c8693845 2008-10-20 drh: blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>", f0c8693845 2008-10-20 drh: g.zBaseURL, zTarget ac3f1f2ba7 2008-10-18 drh: ); f0c8693845 2008-10-20 drh: zTerm = "</s></a>"; ac3f1f2ba7 2008-10-18 drh: }else{ f0c8693845 2008-10-20 drh: blob_appendf(p->pOut,"<s>"); f0c8693845 2008-10-20 drh: zTerm = "</s>"; ac3f1f2ba7 2008-10-18 drh: } ac3f1f2ba7 2008-10-18 drh: }else{ ac3f1f2ba7 2008-10-18 drh: if( g.okHistory ){ f0c8693845 2008-10-20 drh: blob_appendf(p->pOut,"<a href=\"%s/info/%s\">", f0c8693845 2008-10-20 drh: g.zBaseURL, zTarget ac3f1f2ba7 2008-10-18 drh: ); ac3f1f2ba7 2008-10-18 drh: }else{ f0c8693845 2008-10-20 drh: zTerm = ""; ac3f1f2ba7 2008-10-18 drh: } ac3f1f2ba7 2008-10-18 drh: } ac3f1f2ba7 2008-10-18 drh: }else if( g.okHistory ){ ac3f1f2ba7 2008-10-18 drh: blob_appendf(p->pOut, "<a href=\"%s/info/%s\">", g.zBaseURL, zTarget); ac3f1f2ba7 2008-10-18 drh: } 488afb9746 2007-10-06 drh: }else if( wiki_name_is_wellformed(zTarget) ){ ac3f1f2ba7 2008-10-18 drh: blob_appendf(p->pOut, "<a href=\"%s/wiki?name=%T\">", g.zBaseURL, zTarget); 55dc2abc60 2007-09-24 jnc: }else{ ac3f1f2ba7 2008-10-18 drh: blob_appendf(p->pOut, "[bad-link: %h]", zTarget); f0c8693845 2008-10-20 drh: zTerm = ""; 55dc2abc60 2007-09-24 jnc: } f0c8693845 2008-10-20 drh: assert( strlen(zTerm)<nClose ); f0c8693845 2008-10-20 drh: strcpy(zClose, zTerm); 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; e01aa8cb4b 2008-07-17 drh: return aMarkup[p->aStack[p->nStack-1].iCode].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; ebb2765954 2007-12-04 drh: int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: while( z[0] ){ e75f9a2ab4 2007-10-28 drh: n = nextToken(z, p, &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: { ebb2765954 2007-12-04 drh: if( inlineOnly ){ ebb2765954 2007-12-04 drh: /* blob_append(p->pOut, " ¶ ", -1); */ ebb2765954 2007-12-04 drh: blob_append(p->pOut, " ", -1); ebb2765954 2007-12-04 drh: }else{ ebb2765954 2007-12-04 drh: if( p->wikiList ){ ebb2765954 2007-12-04 drh: popStackToTag(p, p->wikiList); ebb2765954 2007-12-04 drh: p->wikiList = 0; ebb2765954 2007-12-04 drh: } ebb2765954 2007-12-04 drh: endAutoParagraph(p); ebb2765954 2007-12-04 drh: blob_appendf(p->pOut, "\n\n", 1); ebb2765954 2007-12-04 drh: p->wantAutoParagraph = 1; ab637af752 2007-09-28 drh: } c963a7763d 2007-10-13 drh: p->state |= AT_PARAGRAPH|AT_NEWLINE; 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: { ebb2765954 2007-12-04 drh: if( inlineOnly ){ 72e9eb6a0d 2008-11-27 drh: blob_append(p->pOut, " • ", -1); ebb2765954 2007-12-04 drh: }else{ ebb2765954 2007-12-04 drh: if( p->wikiList!=MARKUP_UL ){ ebb2765954 2007-12-04 drh: if( p->wikiList ){ ebb2765954 2007-12-04 drh: popStackToTag(p, p->wikiList); ebb2765954 2007-12-04 drh: } ebb2765954 2007-12-04 drh: pushStack(p, MARKUP_UL); ebb2765954 2007-12-04 drh: blob_append(p->pOut, "<ul>", 4); ebb2765954 2007-12-04 drh: p->wikiList = MARKUP_UL; ebb2765954 2007-12-04 drh: } ebb2765954 2007-12-04 drh: popStackToTag(p, MARKUP_LI); ebb2765954 2007-12-04 drh: startAutoParagraph(p); ebb2765954 2007-12-04 drh: pushStack(p, MARKUP_LI); ebb2765954 2007-12-04 drh: blob_append(p->pOut, "<li>", 4); ebb2765954 2007-12-04 drh: } ab637af752 2007-09-28 drh: break; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: case TOKEN_ENUM: { ebb2765954 2007-12-04 drh: if( inlineOnly ){ ebb2765954 2007-12-04 drh: blob_appendf(p->pOut, " (%d) ", atoi(z)); ebb2765954 2007-12-04 drh: }else{ ebb2765954 2007-12-04 drh: if( p->wikiList!=MARKUP_OL ){ ebb2765954 2007-12-04 drh: if( p->wikiList ){ ebb2765954 2007-12-04 drh: popStackToTag(p, p->wikiList); ebb2765954 2007-12-04 drh: } ebb2765954 2007-12-04 drh: pushStack(p, MARKUP_OL); ebb2765954 2007-12-04 drh: blob_append(p->pOut, "<ol>", 4); ebb2765954 2007-12-04 drh: p->wikiList = MARKUP_OL; ab637af752 2007-09-28 drh: } ebb2765954 2007-12-04 drh: popStackToTag(p, MARKUP_LI); ebb2765954 2007-12-04 drh: startAutoParagraph(p); ebb2765954 2007-12-04 drh: pushStack(p, MARKUP_LI); ebb2765954 2007-12-04 drh: blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); dbda8d6ce9 2007-07-21 drh: } ab637af752 2007-09-28 drh: break; ab637af752 2007-09-28 drh: } ab637af752 2007-09-28 drh: case TOKEN_INDENT: { 3bc6d0b0c9 2008-07-27 eric: if( !inlineOnly ){ ebb2765954 2007-12-04 drh: assert( p->wikiList==0 ); ebb2765954 2007-12-04 drh: pushStack(p, MARKUP_BLOCKQUOTE); ebb2765954 2007-12-04 drh: blob_append(p->pOut, "<blockquote>", -1); ebb2765954 2007-12-04 drh: p->wantAutoParagraph = 0; ebb2765954 2007-12-04 drh: p->wikiList = MARKUP_BLOCKQUOTE; ebb2765954 2007-12-04 drh: } dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_CHARACTER: { c963a7763d 2007-10-13 drh: startAutoParagraph(p); 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; 488afb9746 2007-10-06 drh: int i, j; dbda8d6ce9 2007-07-21 drh: int savedState; f0c8693845 2008-10-20 drh: char zClose[20]; ac3f1f2ba7 2008-10-18 drh: c963a7763d 2007-10-13 drh: startAutoParagraph(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; 488afb9746 2007-10-06 drh: for(j=i-1; j>0 && isspace(z[j]); j--){ z[j] = 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: } f0c8693845 2008-10-20 drh: openHyperlink(p, zTarget, zClose, sizeof(zClose)); 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; f0c8693845 2008-10-20 drh: blob_append(p->pOut, zClose, -1); dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: case TOKEN_TEXT: { c963a7763d 2007-10-13 drh: startAutoParagraph(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: { e01aa8cb4b 2008-07-17 drh: const char *zId; e01aa8cb4b 2008-07-17 drh: int iDiv; dbda8d6ce9 2007-07-21 drh: parseMarkup(&markup, z); e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* Markup of the form </div id=ID> where there is a matching e01aa8cb4b 2008-07-17 drh: ** ID somewhere on the stack. Exit the verbatim if were are in e01aa8cb4b 2008-07-17 drh: ** it. Pop the stack up to the matching <div>. Discard the e01aa8cb4b 2008-07-17 drh: ** </div> e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: if( markup.iCode==MARKUP_DIV && markup.endTag && e01aa8cb4b 2008-07-17 drh: (zId = markupId(&markup))!=0 && e01aa8cb4b 2008-07-17 drh: (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0 e01aa8cb4b 2008-07-17 drh: ){ e01aa8cb4b 2008-07-17 drh: if( p->inVerbatim ){ e01aa8cb4b 2008-07-17 drh: p->inVerbatim = 0; e01aa8cb4b 2008-07-17 drh: p->state = p->preVerbState; e01aa8cb4b 2008-07-17 drh: blob_append(p->pOut, "</pre>", 6); e01aa8cb4b 2008-07-17 drh: } e01aa8cb4b 2008-07-17 drh: while( p->nStack>iDiv+1 ) popStack(p); e01aa8cb4b 2008-07-17 drh: if( p->aStack[iDiv].allowWiki ){ e01aa8cb4b 2008-07-17 drh: p->state |= ALLOW_WIKI; e01aa8cb4b 2008-07-17 drh: }else{ e01aa8cb4b 2008-07-17 drh: p->state &= ~ALLOW_WIKI; e01aa8cb4b 2008-07-17 drh: } e01aa8cb4b 2008-07-17 drh: assert( p->nStack==iDiv+1 ); e01aa8cb4b 2008-07-17 drh: p->nStack--; e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* If within <verbatim id=ID> ignore everything other than e01aa8cb4b 2008-07-17 drh: ** </verbatim id=ID> and the </dev id=ID2> above. e01aa8cb4b 2008-07-17 drh: */ 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: } e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* Render invalid markup literally. The markup appears in the e01aa8cb4b 2008-07-17 drh: ** final output as plain text. e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: if( markup.iCode==MARKUP_INVALID ){ c963a7763d 2007-10-13 drh: unparseMarkup(&markup); c963a7763d 2007-10-13 drh: startAutoParagraph(p); dbda8d6ce9 2007-07-21 drh: blob_append(p->pOut, "<", 4); dbda8d6ce9 2007-07-21 drh: n = 1; e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* If the markup is not font-change markup ignore it if the e01aa8cb4b 2008-07-17 drh: ** font-change-only flag is set. e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){ ebb2765954 2007-12-04 drh: /* Do nothing */ e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* Ignore block markup for in-line rendering. e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){ dbda8d6ce9 2007-07-21 drh: /* Do nothing */ e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: 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{ e75f9a2ab4 2007-10-28 drh: p->state &= ~ALLOW_WIKI; dbda8d6ce9 2007-07-21 drh: } e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* Generate end-tags */ e01aa8cb4b 2008-07-17 drh: if( markup.endTag ){ dbda8d6ce9 2007-07-21 drh: popStackToTag(p, markup.iCode); e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* Push <div> markup onto the stack together with the id=ID attribute. e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: if( markup.iCode==MARKUP_DIV ){ e01aa8cb4b 2008-07-17 drh: pushStackWithId(p, markup.iCode, markupId(&markup), e01aa8cb4b 2008-07-17 drh: (p->state & ALLOW_WIKI)!=0); e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: e01aa8cb4b 2008-07-17 drh: /* Enter <verbatim> processing. With verbatim enabled, all other e01aa8cb4b 2008-07-17 drh: ** markup other than the corresponding end-tag with the same ID is e01aa8cb4b 2008-07-17 drh: ** ignored. e01aa8cb4b 2008-07-17 drh: */ e01aa8cb4b 2008-07-17 drh: 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; bb542b80c7 2008-05-16 stephan: blob_append(p->pOut, "<pre class='verbatim'>",-1); c963a7763d 2007-10-13 drh: p->wantAutoParagraph = 0; e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: 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); e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: 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: } e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: 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: } e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: if( markup.iType==MUTYPE_HYPERLINK ){ c963a7763d 2007-10-13 drh: popStackToTag(p, markup.iCode); c963a7763d 2007-10-13 drh: startAutoParagraph(p); c963a7763d 2007-10-13 drh: renderMarkup(p->pOut, &markup); c963a7763d 2007-10-13 drh: pushStack(p, markup.iCode); e01aa8cb4b 2008-07-17 drh: }else e01aa8cb4b 2008-07-17 drh: { c963a7763d 2007-10-13 drh: if( markup.iType==MUTYPE_FONT ){ c963a7763d 2007-10-13 drh: startAutoParagraph(p); c963a7763d 2007-10-13 drh: }else if( markup.iType==MUTYPE_BLOCK ){ c963a7763d 2007-10-13 drh: p->wantAutoParagraph = 0; c963a7763d 2007-10-13 drh: } 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: */ c963a7763d 2007-10-13 drh: void wiki_convert(Blob *pIn, Blob *pOut, int flags){ 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; ebb2765954 2007-12-04 drh: if( flags & WIKI_NOBLOCK ){ ebb2765954 2007-12-04 drh: renderer.state |= INLINE_MARKUP_ONLY; ebb2765954 2007-12-04 drh: } ebb2765954 2007-12-04 drh: if( flags & WIKI_INLINE ){ ebb2765954 2007-12-04 drh: renderer.wantAutoParagraph = 0; ebb2765954 2007-12-04 drh: }else{ ebb2765954 2007-12-04 drh: renderer.wantAutoParagraph = 1; ebb2765954 2007-12-04 drh: } 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); 8f423ad438 2007-10-21 drh: endAutoParagraph(&renderer); dbda8d6ce9 2007-07-21 drh: while( renderer.nStack ){ dbda8d6ce9 2007-07-21 drh: popStack(&renderer); dbda8d6ce9 2007-07-21 drh: } bf428e6854 2007-10-06 drh: blob_append(renderer.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: ** 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]); c963a7763d 2007-10-13 drh: wiki_convert(&in, &out, 0); dbda8d6ce9 2007-07-21 drh: blob_write_to_file(&out, "-"); f88e2e7a13 2009-06-07 drh: } f88e2e7a13 2009-06-07 drh: f88e2e7a13 2009-06-07 drh: /* f88e2e7a13 2009-06-07 drh: ** Search for a <title>...</title> at the beginning of a wiki page. f88e2e7a13 2009-06-07 drh: ** Return true (nonzero) if a title is found. Return zero if there is f88e2e7a13 2009-06-07 drh: ** not title. f88e2e7a13 2009-06-07 drh: ** f88e2e7a13 2009-06-07 drh: ** If a title is found, initialize the pTitle blob to be the content f88e2e7a13 2009-06-07 drh: ** of the title and initialize pTail to be the text that follows the f88e2e7a13 2009-06-07 drh: ** title. f88e2e7a13 2009-06-07 drh: */ f88e2e7a13 2009-06-07 drh: int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ f88e2e7a13 2009-06-07 drh: char *z; f88e2e7a13 2009-06-07 drh: int i; f88e2e7a13 2009-06-07 drh: int iStart; f88e2e7a13 2009-06-07 drh: z = blob_str(pIn); f88e2e7a13 2009-06-07 drh: for(i=0; isspace(z[i]); i++){} f88e2e7a13 2009-06-07 drh: if( z[i]!='<' ) return 0; f88e2e7a13 2009-06-07 drh: i++; f88e2e7a13 2009-06-07 drh: if( strncmp(&z[i],"title>", 6)!=0 ) return 0; f88e2e7a13 2009-06-07 drh: iStart = i+6; f88e2e7a13 2009-06-07 drh: for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){} f88e2e7a13 2009-06-07 drh: if( z[i]!='<' ) return 0; f88e2e7a13 2009-06-07 drh: blob_init(pTitle, &z[iStart], i-iStart); f88e2e7a13 2009-06-07 drh: blob_init(pTail, &z[i+8], -1); f88e2e7a13 2009-06-07 drh: return 1; dbda8d6ce9 2007-07-21 drh: }