File Annotation
Not logged in
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: */
dbda8d6ce9 2007-07-21       drh: #define MARKUP_INVALID         255
dbda8d6ce9 2007-07-21       drh: #define MARKUP_A                 0
dbda8d6ce9 2007-07-21       drh: #define MARKUP_ADDRESS           1
dbda8d6ce9 2007-07-21       drh: #define MARKUP_BIG               2
dbda8d6ce9 2007-07-21       drh: #define MARKUP_BLOCKQUOTE        3
dbda8d6ce9 2007-07-21       drh: #define MARKUP_B                 4
dbda8d6ce9 2007-07-21       drh: #define MARKUP_BR                5
dbda8d6ce9 2007-07-21       drh: #define MARKUP_CENTER            6
dbda8d6ce9 2007-07-21       drh: #define MARKUP_CITE              7
dbda8d6ce9 2007-07-21       drh: #define MARKUP_CODE              8
dbda8d6ce9 2007-07-21       drh: #define MARKUP_DD                9
dbda8d6ce9 2007-07-21       drh: #define MARKUP_DFN              10
dbda8d6ce9 2007-07-21       drh: #define MARKUP_DL               11
dbda8d6ce9 2007-07-21       drh: #define MARKUP_DT               12
dbda8d6ce9 2007-07-21       drh: #define MARKUP_EM               13
dbda8d6ce9 2007-07-21       drh: #define MARKUP_FONT             14
dbda8d6ce9 2007-07-21       drh: #define MARKUP_H1               15
dbda8d6ce9 2007-07-21       drh: #define MARKUP_H2               16
dbda8d6ce9 2007-07-21       drh: #define MARKUP_H3               17
dbda8d6ce9 2007-07-21       drh: #define MARKUP_H4               18
dbda8d6ce9 2007-07-21       drh: #define MARKUP_H5               19
dbda8d6ce9 2007-07-21       drh: #define MARKUP_H6               20
dbda8d6ce9 2007-07-21       drh: #define MARKUP_HR               21
dbda8d6ce9 2007-07-21       drh: #define MARKUP_IMG              22
dbda8d6ce9 2007-07-21       drh: #define MARKUP_I                23
dbda8d6ce9 2007-07-21       drh: #define MARKUP_KBD              24
dbda8d6ce9 2007-07-21       drh: #define MARKUP_LI               25
dbda8d6ce9 2007-07-21       drh: #define MARKUP_NOBR             26
dbda8d6ce9 2007-07-21       drh: #define MARKUP_NOWIKI           27
dbda8d6ce9 2007-07-21       drh: #define MARKUP_OL               28
dbda8d6ce9 2007-07-21       drh: #define MARKUP_P                29
dbda8d6ce9 2007-07-21       drh: #define MARKUP_PRE              30
dbda8d6ce9 2007-07-21       drh: #define MARKUP_SAMP             31
dbda8d6ce9 2007-07-21       drh: #define MARKUP_SMALL            32
dbda8d6ce9 2007-07-21       drh: #define MARKUP_S                33
dbda8d6ce9 2007-07-21       drh: #define MARKUP_STRIKE           34
dbda8d6ce9 2007-07-21       drh: #define MARKUP_STRONG           35
dbda8d6ce9 2007-07-21       drh: #define MARKUP_SUB              36
dbda8d6ce9 2007-07-21       drh: #define MARKUP_SUP              37
dbda8d6ce9 2007-07-21       drh: #define MARKUP_TABLE            38
dbda8d6ce9 2007-07-21       drh: #define MARKUP_TD               39
dbda8d6ce9 2007-07-21       drh: #define MARKUP_TH               40
dbda8d6ce9 2007-07-21       drh: #define MARKUP_TR               41
dbda8d6ce9 2007-07-21       drh: #define MARKUP_TT               42
dbda8d6ce9 2007-07-21       drh: #define MARKUP_UL               43
dbda8d6ce9 2007-07-21       drh: #define MARKUP_U                44
dbda8d6ce9 2007-07-21       drh: #define MARKUP_VAR              45
dbda8d6ce9 2007-07-21       drh: #define MARKUP_VERBATIM         46
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[] = {
dbda8d6ce9 2007-07-21       drh:  { "a",             MARKUP_A,            MUTYPE_HYPERLINK,     ATTR_HREF },
dbda8d6ce9 2007-07-21       drh:  { "address",       MARKUP_ADDRESS,      MUTYPE_BLOCK,         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:  { "b",             MARKUP_B,            MUTYPE_FONT,          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  },
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:  { "s",             MARKUP_S,            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  },
dbda8d6ce9 2007-07-21       drh:  { "ul",            MARKUP_UL,           MUTYPE_LIST,
dbda8d6ce9 2007-07-21       drh:                     ATTR_TYPE|ATTR_COMPACT  },
dbda8d6ce9 2007-07-21       drh:  { "u",             MARKUP_U,            MUTYPE_FONT,          0  },
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;
dbda8d6ce9 2007-07-21       drh:   first = 0;
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 */
dbda8d6ce9 2007-07-21       drh: #define IN_LIST           0x010  /* Within <ul> */
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;
dbda8d6ce9 2007-07-21       drh:   while( isalpha(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:     }
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: /*
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: #if 0
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: #endif
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: ** Current state of the rendering engine
dbda8d6ce9 2007-07-21       drh: */
dbda8d6ce9 2007-07-21       drh: typedef struct Renderer Renderer;
dbda8d6ce9 2007-07-21       drh: struct Renderer {
dbda8d6ce9 2007-07-21       drh:   Blob *pOut;                 /* Output appended to this blob */
dbda8d6ce9 2007-07-21       drh:   int state;                  /* Flag that govern rendering */
dbda8d6ce9 2007-07-21       drh:   int inVerbatim;             /* True in <verbatim> mode */
dbda8d6ce9 2007-07-21       drh:   int preVerbState;           /* Value of state prior to verbatim */
dbda8d6ce9 2007-07-21       drh:   const char *zVerbatimId;    /* The id= attribute of <verbatim> */
dbda8d6ce9 2007-07-21       drh:   int nStack;                 /* Number of elements on the stack */
dbda8d6ce9 2007-07-21       drh:   int nAlloc;                 /* Space allocated for aStack */
dbda8d6ce9 2007-07-21       drh:   unsigned char *aStack;      /* Open markup stack */
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
dbda8d6ce9 2007-07-21       drh: ** true on success.  If the stack does not have an element
dbda8d6ce9 2007-07-21       drh: ** that matches iMask, then leave the stack unchanged and
dbda8d6ce9 2007-07-21       drh: ** return false.
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:   }
dbda8d6ce9 2007-07-21       drh:   return 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){
b2e55c0d4d 2007-09-01       drh:   blob_appendf(p->pOut, "http://www.fossil-scm.org/test-%T", zTarget);
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: {
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: {
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:         blob_append(p->pOut, "<li>", 4);
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, "&lt;", 4);
dbda8d6ce9 2007-07-21       drh:         }else if( z[0]=='&' ){
dbda8d6ce9 2007-07-21       drh:           blob_append(p->pOut, "&amp;", 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, "&lt;", 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, "&lt;", 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.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** The transformations carried out depend on the ops flag:
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** WIKI_NOFOLLOW
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: **     * Add the nofollow attribute to external links
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** WIKI_HTML
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: **     * Convert wiki into HTML
dbda8d6ce9 2007-07-21       drh: **     * Remove <nowiki> and <verbatium>
dbda8d6ce9 2007-07-21       drh: **     * Convert & into &amp;
dbda8d6ce9 2007-07-21       drh: **     * Unrecognized markup and markup within <verbatim>
dbda8d6ce9 2007-07-21       drh: **       is converted into &lt;...&gt;
dbda8d6ce9 2007-07-21       drh: **     * Unauthorized attributes on markup are removed
dbda8d6ce9 2007-07-21       drh: */
dbda8d6ce9 2007-07-21       drh: void wiki_convert(Blob *pIn, Blob *pOut, int ops){
dbda8d6ce9 2007-07-21       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;
dbda8d6ce9 2007-07-21       drh:   renderer.pOut = pOut;
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]);
dbda8d6ce9 2007-07-21       drh:   wiki_convert(&in, &out, WIKI_HTML);
dbda8d6ce9 2007-07-21       drh:   blob_write_to_file(&out, "-");
dbda8d6ce9 2007-07-21       drh: }