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
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){
6f0df6c741 2009-12-07  jeremy_c:   if( p->wantAutoParagraph==0 || p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) 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.
3a4c7f60a2 2009-09-19       drh: **
3a4c7f60a2 2009-09-19       drh: **    [http://www.fossil-scm.org/]
3a4c7f60a2 2009-09-19       drh: **    [https://www.fossil-scm.org/]
3a4c7f60a2 2009-09-19       drh: **    [ftp://www.fossil-scm.org/]
3a4c7f60a2 2009-09-19       drh: **    [mailto:fossil-users@lists.fossil-scm.org]
3a4c7f60a2 2009-09-19       drh: **
3a4c7f60a2 2009-09-19       drh: **    [/path]
3a4c7f60a2 2009-09-19       drh: **
3a4c7f60a2 2009-09-19       drh: **    [./relpath]
3a4c7f60a2 2009-09-19       drh: **
3a4c7f60a2 2009-09-19       drh: **    [WikiPageName]
3a4c7f60a2 2009-09-19       drh: **
3a4c7f60a2 2009-09-19       drh: **    [0123456789abcdef]
ac3f1f2ba7 2008-10-18       drh: **
3a4c7f60a2 2009-09-19       drh: **    [#fragment]
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>";
0039b7813e 2009-11-01       drh:   assert( nClose>=20 );
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);
980b160bd0 2009-11-02       drh:     /* zTerm = "&#x27FE;</a>"; // doesn't work on windows */
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);
f0c8693845 2008-10-20       drh:     }else{
f0c8693845 2008-10-20       drh:       zTerm = "";
f0c8693845 2008-10-20       drh:     }
3a4c7f60a2 2009-09-19       drh:   }else if( zTarget[0]=='.' || zTarget[0]=='#' ){
a8c3a7ea92 2008-11-22       drh:     if( 1 /* g.okHistory */ ){
63886daad8 2008-11-18       drh:       blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
63886daad8 2008-11-18       drh:     }else{
63886daad8 2008-11-18       drh:       zTerm = "";
ac3f1f2ba7 2008-10-18       drh:     }
f394d84560 2007-11-25       drh:   }else if( is_valid_uuid(zTarget) ){
e0131b1a66 2009-09-19       drh:     int isClosed = 0;
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, " &para; ", -1); */
ebb2765954 2007-12-04       drh:           blob_append(p->pOut, " &nbsp;&nbsp; ", -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, " &bull; ", -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, "&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;
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, "&lt;", 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, "&lt;", 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 ){
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:           }
d6bfe8d9a8 2009-09-23       drh:         }else
d6bfe8d9a8 2009-09-23       drh: 
d6bfe8d9a8 2009-09-23       drh:         /* Ignore block markup for in-line rendering.
d6bfe8d9a8 2009-09-23       drh:         */
d6bfe8d9a8 2009-09-23       drh:         if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
d6bfe8d9a8 2009-09-23       drh:           /* Do nothing */
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: }