Changes On Branch creole
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch creole Excluding Merge-Ins

This is equivalent to a diff from 63d5a4fe25 to df90572760

2010-02-26
13:09
Update SQLite to pre-3.6.23. check-in: 1efd09ed4f user: drh tags: trunk
03:02
Merge in latest changes from trunk. Leaf check-in: df90572760 user: linuxfood tags: creole
2010-02-25
14:06
Pull over the latest clear-title changes from trunk. check-in: 16e703be11 user: drh tags: clear-title
12:58
If a file has been deleted from the filesystem, but not deleted by fossil, then make the "update" command restore that file. Ticket [7c3ca0eae8287] check-in: 63d5a4fe25 user: drh tags: trunk
2010-02-24
22:39
Back out the changes of [0e2281fc8a757] since they were causing a segfault while trying to enter a new ticket. The ticket [6b498a792c0] should still be fixed. check-in: 5a6634c453 user: drh tags: trunk
2009-10-05
20:05
add wiki-contents macro to creole parser, plus minor bug fix check-in: b99aa66d1f user: robert tags: creole

Added src/creoleparser.c.

            1  +/*
            2  +** Copyright (c) 2009 Robert Ledger
            3  +**
            4  +** {{{ License
            5  +**
            6  +** This program is free software; you can redistribute it and/or
            7  +** modify it under the terms of the GNU General Public
            8  +** License version 2 as published by the Free Software Foundation.
            9  +**
           10  +** This program is distributed in the hope that it will be useful,
           11  +** but WITHOUT ANY WARRANTY; without even the implied warranty of
           12  +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
           13  +** General Public License for more details.
           14  +**
           15  +** You should have received a copy of the GNU General Public
           16  +** License along with this library; if not, write to the
           17  +** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
           18  +** Boston, MA  02111-1307, USA.
           19  +**
           20  +** Author contact information:
           21  +**   robert@pytrash.co.uk
           22  +**   http://pytrash.co.uk
           23  +**}}}
           24  +*******************************************************************************
           25  +**
           26  +** This file contains code to render creole 1.0 formated text as html.
           27  +*/
           28  +#include <assert.h>
           29  +#include "config.h"
           30  +#include "creoleparser.h"
           31  +
           32  +#if INTERFACE
           33  +#define HAVE_CREOLE_MACRO 1
           34  +#endif
           35  +
           36  +//{{{ LOCAL INTERFACE
           37  +#if LOCAL_INTERFACE
           38  +
           39  +#define POOL_CHUNK_SIZE 100
           40  +
           41  +//{{{ KIND
           42  +#define KIND_ROOT            0x0000001
           43  +#define KIND_HORIZONTAL_RULE 0x0000002
           44  +#define KIND_HEADING         0x0000004
           45  +#define KIND_ORDERED_LIST    0x0000008
           46  +
           47  +#define KIND_UNORDERED_LIST  0x0000010
           48  +#define KIND_PARAGRAPH       0x0000020
           49  +#define KIND_TABLE           0x0000040
           50  +#define KIND_NO_WIKI_BLOCK   0x0000080
           51  +
           52  +#define KIND_PARA_BREAK      0x0000100
           53  +#define KIND_END_WIKI_MARKER 0x0000200
           54  +
           55  +#define KIND_BOLD            0x0000400
           56  +#define KIND_ITALIC          0x0000800
           57  +#define KIND_SUPERSCRIPT     0x0001000
           58  +#define KIND_SUBSCRIPT       0x0002000
           59  +#define KIND_MONOSPACED      0x0004000
           60  +#define KIND_BREAK           0x0008000
           61  +
           62  +#define KIND_TABLE_ROW       0x0010000
           63  +#define KIND_MACRO           0X0020000
           64  +//}}}
           65  +//{{{ MACRO
           66  +#define MACRO_NONE           0X0000000
           67  +#define MACRO_FOSSIL         0x0000001
           68  +#define MACRO_WIKI_CONTENTS  0X0000002
           69  +//}}}
           70  +//{{{ FLAG
           71  +// keep first four bits free (why?:)
           72  +#define FLAG_CENTER      0x0000100
           73  +#define FLAG_MACRO_BLOCK 0X0000200
           74  +//}}}
           75  +struct Node {//{{{
           76  +
           77  +  char *start;
           78  +  char *end;
           79  +
           80  +  int kind;
           81  +  int level;
           82  +  int flags;
           83  +
           84  +  Node *parent;
           85  +  Node *next;
           86  +  Node *children;
           87  +
           88  +};
           89  +//}}}
           90  +struct NodePool {//{{{
           91  +  NodePool *next;
           92  +  Node a[POOL_CHUNK_SIZE];
           93  +}
           94  +//}}}
           95  +struct Parser {//{{{
           96  +
           97  +  Blob *pOut;                 /* Output appended to this blob */
           98  +  Renderer *r;
           99  +
          100  +  NodePool *pool;
          101  +  int nFree;
          102  +
          103  +  Node *this;
          104  +  Node *previous;
          105  +  Node *list;
          106  +
          107  +  char *cursor;
          108  +
          109  +  int lineWasBlank;
          110  +  int charCount;
          111  +
          112  +  Node *item;
          113  +  Node *istack;
          114  +  char *icursor;
          115  +  char *iend;
          116  +
          117  +  int inLink;
          118  +  int inTable;
          119  +  int iesc;
          120  +
          121  +  Blob *iblob;
          122  +
          123  +};
          124  +//}}}
          125  +
          126  +#endif
          127  +
          128  +const int KIND_LIST = (KIND_UNORDERED_LIST | KIND_ORDERED_LIST);
          129  +const int KIND_LIST_OR_PARAGRAPH = (KIND_PARAGRAPH | KIND_UNORDERED_LIST | KIND_ORDERED_LIST);
          130  +//}}}
          131  +
          132  +//{{{ POOL MANAGEMENT
          133  +static Node *pool_new(Parser *p){
          134  +
          135  +  if ( p->pool == NULL || p->nFree == 0){
          136  +
          137  +    NodePool *temp = p->pool;
          138  +
          139  +    p->pool = malloc(sizeof(NodePool));
          140  +    if( p->pool == NULL ) fossil_panic("out of memory");
          141  +
          142  +    p->pool->next = temp;
          143  +    p->nFree = POOL_CHUNK_SIZE;
          144  +  }
          145  +  p->nFree -= 1;
          146  +  Node *node = &(p->pool->a[p->nFree]);
          147  +  memset(node, 0, sizeof(*node));
          148  +
          149  +  return node;
          150  +}
          151  +
          152  +
          153  +static void pool_free(Parser *p){
          154  +
          155  +  NodePool *temp;
          156  +
          157  +  while (p->pool != NULL){
          158  +    temp = p->pool;
          159  +    p->pool = temp->next;
          160  +    free(temp);
          161  +  }
          162  +
          163  +}
          164  +//}}}
          165  +
          166  +//{{{ Utility Methods
          167  +
          168  +static char *cr_skipBlanks(Parser *p, char* z){//{{{
          169  +  char *s = z;
          170  +  while (z[0] == ' ' || z[0] == '\t') z++;
          171  +  p->charCount = z - s;
          172  +  return z;
          173  +}
          174  +//}}}
          175  +static int cr_countBlanks(Parser *p, char* z){//{{{
          176  +  cr_skipBlanks(p, z);
          177  +  return p->charCount;
          178  +}
          179  +//}}}
          180  +static char *cr_skipChars(Parser *p, char *z, char c){//{{{
          181  +  char *s = z;
          182  +  while (z[0] == c) z++;
          183  +  p->charCount = z - s;
          184  +  return z;
          185  +}
          186  +//}}}
          187  +static int cr_countChars(Parser *p, char *z, char c){//{{{
          188  +  cr_skipChars(p, z, c);
          189  +  return p->charCount;
          190  +}
          191  +//}}}
          192  +static char *cr_nextLine(Parser *p, char *z){//{{{
          193  +
          194  +  p->lineWasBlank = 1;
          195  +
          196  +  while (1){
          197  +
          198  +    switch (z[0]){
          199  +
          200  +      case '\r':
          201  +        if (z[1] == '\n') {
          202  +          z[0] = ' ';
          203  +          return z + 2;
          204  +        }
          205  +        z[0] = '\n';
          206  +        return z + 1;
          207  +
          208  +      case '\n':
          209  +        return z + 1;
          210  +
          211  +      case '\t':
          212  +        z[0] = ' ';
          213  +        z++;
          214  +        break;
          215  +
          216  +      case ' ':
          217  +        z++;
          218  +        break;
          219  +
          220  +      case '\0':
          221  +        return z;
          222  +
          223  +      default:
          224  +        p->lineWasBlank = 0;
          225  +        z++;
          226  +    }
          227  +  }
          228  +}
          229  +//}}}
          230  +//}}}
          231  +
          232  +
          233  +//{{{ INLINE PARSER
          234  +
          235  +static int cr_isEsc(Parser *p){//{{{
          236  +  if (p->iesc){
          237  +    blob_append(p->iblob, p->icursor, 1);
          238  +    p->iesc = 0;
          239  +    p->icursor += 1;
          240  +    return 1;
          241  +  }
          242  +  return 0;
          243  +}
          244  +//}}}
          245  +static int cr_iOpen(Parser *p, int kind){//{{{
          246  +
          247  +  switch (kind){
          248  +
          249  +    case KIND_BOLD:
          250  +      blob_append(p->iblob, "<strong>", 8);
          251  +      return 1;
          252  +
          253  +    case KIND_ITALIC:
          254  +      blob_append(p->iblob, "<em>", 4);
          255  +      return 1;
          256  +
          257  +    case KIND_SUPERSCRIPT:
          258  +      blob_append(p->iblob, "<sup>", 5);
          259  +      return 1;
          260  +
          261  +    case KIND_SUBSCRIPT:
          262  +      blob_append(p->iblob, "<sub>", 5);
          263  +      return 1;
          264  +
          265  +    case KIND_MONOSPACED:
          266  +      blob_append(p->iblob, "<tt>", 4);
          267  +      return 1;
          268  +  }
          269  +  return 0;
          270  +}
          271  +//}}}
          272  +static int cr_iClose(Parser *p, int kind){//{{{
          273  +
          274  +  switch (kind){
          275  +
          276  +    case KIND_BOLD:
          277  +      blob_append(p->iblob, "</strong>", 9);
          278  +      return 1;
          279  +
          280  +    case KIND_ITALIC:
          281  +      blob_append(p->iblob, "</em>", 5);
          282  +      return 1;
          283  +
          284  +    case KIND_SUPERSCRIPT:
          285  +      blob_append(p->iblob, "</sup>", 6);
          286  +      return 1;
          287  +
          288  +    case KIND_SUBSCRIPT:
          289  +      blob_append(p->iblob, "</sub>", 6);
          290  +      return 1;
          291  +
          292  +    case KIND_MONOSPACED:
          293  +      blob_append(p->iblob, "</tt>", 5);
          294  +      return 1;
          295  +  }
          296  +  return 0;
          297  +}
          298  +//}}}
          299  +
          300  +
          301  +static void cr_iMarkup(Parser *p, int kind){//{{{
          302  +
          303  +  if (p->iesc) {
          304  +    blob_append(p->iblob, p->icursor, 1);
          305  +    p->icursor +=1;
          306  +    p->iesc =0;
          307  +    return;
          308  +  }
          309  +
          310  +  if (p->icursor[1] != p->icursor[0]) {
          311  +    blob_append(p->iblob, p->icursor, 1);
          312  +    p->icursor +=1;
          313  +    return;
          314  +  }
          315  +
          316  +  p->icursor += 2;
          317  +
          318  +  if (kind & KIND_BREAK) {
          319  +      blob_append(p->iblob, "<br />", 6);
          320  +      return;
          321  +  }
          322  +
          323  +  if (kind & KIND_ITALIC && p->icursor[-3] == ':'){
          324  +        blob_append(p->iblob, "//", 2);
          325  +        return;
          326  +  }
          327  +
          328  +  Node *n = p->istack;
          329  +
          330  +  int found = 0;
          331  +  while (n) {
          332  +    if (n->kind & kind) {
          333  +      found = 1;
          334  +      break;
          335  +    }
          336  +    n = n->next;
          337  +  }
          338  +
          339  +  if (!found) {
          340  +    n = pool_new(p);
          341  +    n->kind = kind;
          342  +    n->next = p->istack;
          343  +    p->istack = n;
          344  +
          345  +    assert(cr_iOpen(p, kind));
          346  +    return;
          347  +  };
          348  +
          349  +  n= p->istack;
          350  +  while (n){
          351  +    p->istack = n->next;
          352  +
          353  +    assert(cr_iClose(p, n->kind));
          354  +
          355  +    if (kind == n->kind) return;
          356  +    n = p->istack;
          357  +  }
          358  +}
          359  +//}}}
          360  +static int cr_iNoWiki(Parser *p){//{{{
          361  +
          362  +  if ((p->iend - p->icursor)<6) return 0;
          363  +
          364  +  if (p->icursor[1]!='{' || p->icursor[2]!='{')
          365  +    return 0;
          366  +
          367  +  char *s = p->icursor + 3;
          368  +
          369  +  int count = p->iend - p->icursor - 3;
          370  +  while (count--){
          371  +    if (s[0]=='}' && s[1]=='}' && s[2]=='}' && s[3]!='}'){
          372  +      blob_appendf(p->iblob, "<tt class='creole-inline-nowiki'>%s</tt>", htmlize(p->icursor + 3, s - p->icursor-3));
          373  +      p->icursor = s + 3;
          374  +      return 1;
          375  +    }
          376  +    s++;
          377  +  }
          378  +  return 0;
          379  +}
          380  +
          381  +//}}}
          382  +static int cr_iImage(Parser *p){//{{{
          383  +
          384  +  if (p->inLink) return 0;
          385  +  if ((p->iend - p->icursor)<3) return 0;
          386  +
          387  +  if (p->icursor[1]!='{') return 0;
          388  +
          389  +  char *s = p->icursor + 2;
          390  +  char *bar = NULL;
          391  +
          392  +  int count = p->iend - p->icursor - 4;
          393  +  while (count--){
          394  +    if (s[0]=='}' && s[1]=='}'){
          395  +      if (!bar) bar = p->icursor + 2;
          396  +      blob_appendf(p->iblob, "<span class='creole-noimage'>%s</span>", htmlize(bar, s - bar ));
          397  +      p->icursor = s + 2;
          398  +      return 1;
          399  +    }
          400  +    if (!bar && s[0]=='|') bar=s+1;
          401  +    s++;
          402  +  }
          403  +  return 0;
          404  +}
          405  +//}}}
          406  +static int cr_iMacro(Parser *p){//{{{
          407  +
          408  +  if (p->inLink) return 0;
          409  +  if ((p->iend - p->icursor)<3) return 0;
          410  +
          411  +  if (p->icursor[1]!='<') return 0;
          412  +
          413  +  char *s = p->icursor + 2;
          414  +
          415  +  int count = p->iend - p->icursor - 3;
          416  +  while (count--){
          417  +   if (s[0]=='>' && s[1]=='>'){
          418  +      blob_appendf(p->iblob, "<span class='creole-nomacro'>%s</span>", htmlize(p->icursor, s - p->icursor + 2));
          419  +      p->icursor = s + 2;
          420  +      return 1;
          421  +    }
          422  +    s++;
          423  +  }
          424  +  return 0;
          425  +
          426  +}
          427  +//}}}
          428  +
          429  +static void cr_renderLink(Parser *p, char *s, char *bar, char *e){//{{{
          430  +
          431  +  int tsize = bar-s;
          432  +  int dsize = e - bar-1;
          433  +
          434  +  if (tsize < 1) return;
          435  +  if (dsize < 1) dsize = 0;
          436  +
          437  +  char zTarget[tsize + 1];
          438  +  memcpy(zTarget, s, tsize);
          439  +  zTarget[tsize] = '\0';
          440  +
          441  +  char zClose[20];
          442  +
          443  +  Blob *pOut = p->r->pOut;
          444  +
          445  +  p->r->pOut = p->iblob;
          446  +  wf_openHyperlink(p->r, zTarget, zClose, sizeof(zClose));
          447  +  p->r->pOut = pOut;
          448  +
          449  +  if (dsize)
          450  +    cr_parseInline(p, bar+1, e) ;
          451  +  else
          452  +    blob_append(p->iblob, htmlize(s, tsize), -1);
          453  +  blob_append(p->iblob, zClose, -1);
          454  +}
          455  +//}}}
          456  +
          457  +static int cr_iLink(Parser *p){//{{{
          458  +
          459  +  if (p->inLink) return 0;
          460  +  if ((p->iend - p->icursor)<3) return 0;
          461  +
          462  +  if (p->icursor[1]!='[') return 0;
          463  +
          464  +  char *s = p->icursor + 2;
          465  +  char *bar = NULL;
          466  +
          467  +  int count = p->iend - p->icursor -3;
          468  +  while (count--){
          469  +    if (s[0]==']' && s[1]==']'){
          470  +      if (!bar) bar = s;
          471  +      p->inLink = 1;
          472  +      cr_renderLink(p, p->icursor+2, bar, s);
          473  +      p->inLink = 0;
          474  +      p->icursor = s + 2;
          475  +      return 1;
          476  +    }
          477  +    if (!bar && s[0]=='|') bar=s;
          478  +    s++;
          479  +  }
          480  +  return 0;
          481  +}
          482  +//}}}
          483  +
          484  +LOCAL char *cr_parseInline(Parser *p, char *s, char *e){//{{{
          485  +
          486  +  int save_iesc = p->iesc;
          487  +  char *save_iend = p->iend;
          488  +  Node *save_istack = p->istack;
          489  +
          490  +  p->iesc = 0;
          491  +  p->iend = e;
          492  +  p->istack = NULL;
          493  +
          494  +  p->icursor = s;
          495  +
          496  +  char *eof = NULL;
          497  +  while (!eof &&  p->icursor < p->iend ){
          498  +
          499  +    switch (*p->icursor) {//{{{
          500  +
          501  +      case '~':
          502  +        if (p->iesc) {
          503  +          blob_append(p->iblob, "~", 1);
          504  +          p->iesc = 0;
          505  +        }
          506  +        p->iesc = !p->iesc;
          507  +        p->icursor+=1;
          508  +        break;
          509  +
          510  +      case '*':
          511  +        cr_iMarkup(p, KIND_BOLD);
          512  +        break;
          513  +
          514  +      case '/':
          515  +        cr_iMarkup(p, KIND_ITALIC);
          516  +        break;
          517  +
          518  +      case '^':
          519  +        cr_iMarkup(p, KIND_SUPERSCRIPT);
          520  +        break;
          521  +
          522  +      case ',':
          523  +        cr_iMarkup(p, KIND_SUBSCRIPT);
          524  +        break;
          525  +
          526  +      case '#':
          527  +        cr_iMarkup(p, KIND_MONOSPACED);
          528  +        break;
          529  +
          530  +      case '\\':
          531  +        cr_iMarkup(p, KIND_BREAK);
          532  +        break;
          533  +
          534  +      case '{':
          535  +        if (cr_isEsc(p)) break;
          536  +        if (cr_iNoWiki(p)) break;
          537  +        if (cr_iImage(p)) break;
          538  +        blob_append(p->iblob, p->icursor, 1);
          539  +        p->icursor += 1;
          540  +        break;
          541  +
          542  +      case '[':
          543  +        if (cr_isEsc(p)) break;
          544  +        if (cr_iLink(p)) break;
          545  +        blob_append(p->iblob, p->icursor, 1);
          546  +        p->icursor += 1;
          547  +        break;
          548  +
          549  +
          550  +      case '<':
          551  +        if (cr_isEsc(p)) break;
          552  +        if (cr_iMacro(p)) break;
          553  +
          554  +        blob_append(p->iblob, "&lt;", 4);
          555  +        p->icursor += 1;
          556  +        break;
          557  +
          558  +      case '>':
          559  +        if (p->iesc) {
          560  +          blob_append(p->iblob, "~", 1);
          561  +          p->iesc = 0;
          562  +        }
          563  +        blob_append(p->iblob, "&gt;", 4);
          564  +        p->icursor += 1;
          565  +        break;
          566  +
          567  +      case '&':
          568  +        if (p->iesc) {
          569  +          blob_append(p->iblob, "~", 1);
          570  +          p->iesc = 0;
          571  +        }
          572  +        blob_append(p->iblob, "&amp;", 5);
          573  +        p->icursor += 1;
          574  +        break;
          575  +
          576  +      case '|':
          577  +        if (p->inTable){
          578  +          if (p->iesc) {
          579  +            blob_append(p->iblob, p->icursor, 1);
          580  +            p->iesc = 0;
          581  +            p->icursor += 1;
          582  +            break;
          583  +          }
          584  +          eof = p->icursor + 1;
          585  +          break;
          586  +        }
          587  +        // fall through to default
          588  +
          589  +      default:
          590  +        if (p->iesc) {
          591  +          blob_append(p->iblob, "~", 1);
          592  +          p->iesc = 0;
          593  +        }
          594  +        blob_append(p->iblob, p->icursor, 1);
          595  +        p->icursor +=1;
          596  +    }//}}}
          597  +
          598  +  }
          599  +
          600  +  while (p->istack){
          601  +    cr_iClose(p, p->istack->kind);
          602  +    p->istack = p->istack->next;
          603  +  }
          604  +
          605  +  p->iesc = save_iesc;
          606  +  p->iend = save_iend;
          607  +  p->istack = save_istack;
          608  +
          609  +  return eof;
          610  +
          611  +}
          612  +//}}}
          613  +//}}}
          614  +
          615  +//{{{ BLOCK PARSER
          616  +
          617  +static void cr_renderListItem(Parser *p, Node *n){//{{{
          618  +
          619  +
          620  +  blob_append(p->iblob, "<li>", 4);
          621  +  cr_parseInline(p, n->start, n->end);
          622  +
          623  +  if (n->children){
          624  +
          625  +    int ord = (n->children->kind & KIND_ORDERED_LIST);
          626  +
          627  +    if (ord)   blob_append(p->iblob, "<ol>", 4);
          628  +    else       blob_append(p->iblob, "<ul>", 4);
          629  +
          630  +    n = n->children;
          631  +    while (n){
          632  +      cr_renderListItem(p, n);
          633  +      n = n->next;
          634  +    }
          635  +
          636  +    if (ord)   blob_append(p->iblob, "</ol>", 5);
          637  +    else       blob_append(p->iblob, "</ul>", 5);
          638  +  }
          639  +  blob_append(p->iblob, "</li>", 5);
          640  +}
          641  +//}}}
          642  +static void cr_renderList(Parser *p){//{{{
          643  +
          644  +  Node *n = p->list;
          645  +
          646  +  while (n->parent !=n)  n = n->parent;
          647  +
          648  +  int ord = (n->kind & KIND_ORDERED_LIST);
          649  +
          650  +  if (ord)   blob_append(p->iblob, "\n\n<ol>", -1);
          651  +  else       blob_append(p->iblob, "\n\n<ul>", -1);
          652  +
          653  +  while (n) {
          654  +    cr_renderListItem(p, n);
          655  +    n = n->next;
          656  +  }
          657  +
          658  +  if (ord)   blob_append(p->iblob, "</ol>", 5);
          659  +  else       blob_append(p->iblob, "</ul>", 5);
          660  +}
          661  +
          662  +//}}}
          663  +
          664  +static void cr_renderTableRow(Parser *p, Node *row){//{{{
          665  +
          666  +  char *s = row->start;
          667  +  int th;
          668  +
          669  +  blob_append(p->iblob, "\n<tr>", -1);
          670  +
          671  +  while (s && s < row->end){
          672  +
          673  +    if ((th = *s == '=')) {
          674  +      s++;
          675  +      blob_append(p->iblob, "<th>", -1);
          676  +    }
          677  +    else {
          678  +      blob_append(p->iblob, "<td>", -1);
          679  +    }
          680  +
          681  +    s = cr_parseInline(p, s, row->end);
          682  +
          683  +    if (th)
          684  +      blob_append(p->iblob, "</th>\n", -1);
          685  +    else
          686  +      blob_append(p->iblob, "</td>\n", -1);
          687  +
          688  +    if (!s) break;
          689  +  }
          690  +  blob_append(p->iblob, "</tr>", 5);
          691  +}
          692  +//}}}
          693  +static void cr_renderTable(Parser *p, Node *n){//{{{
          694  +
          695  +  Node *row = n->children;
          696  +
          697  +  blob_append(p->iblob, "<table class='creoletable'>", -1);
          698  +  p->inTable = 1;
          699  +  while (row){
          700  +
          701  +    cr_renderTableRow(p, row);
          702  +    row = row->next;
          703  +
          704  +  }
          705  +  blob_append(p->iblob, "</table>", -1);
          706  +  p->inTable = 0;
          707  +
          708  +}
          709  +//}}}
          710  +
          711  +static void cr_renderMacro(Parser *p, Node *n){//{{{
          712  +
          713  +  switch (n->level){
          714  +
          715  +    case MACRO_WIKI_CONTENTS:
          716  +      do_macro_wiki_contents(p, n);
          717  +      break;
          718  +
          719  +  }
          720  +
          721  +}
          722  +//}}}
          723  +
          724  +static void cr_render(Parser *p, Node *node){//{{{
          725  +
          726  +  if (node->kind & KIND_PARAGRAPH){
          727  +    blob_append(p->iblob,   "\n<p>", -1);
          728  +    cr_parseInline(p, node->start, node->end );
          729  +    blob_append(p->iblob, "</p>\n", -1  );
          730  +  }
          731  +
          732  +  if (node->kind & KIND_HEADING){
          733  +    blob_appendf(p->iblob,
          734  +        "\n<h%d %s>",
          735  +        node->level,
          736  +        (node->flags & FLAG_CENTER) ? " style='text-align:center;'" : ""
          737  +    );
          738  +    cr_parseInline(p, node->start, node->end);
          739  +    blob_appendf(p->iblob, "</h%d>\n", node->level  );
          740  +    return;
          741  +  }
          742  +
          743  +  if (node->kind & KIND_MACRO){
          744  +    cr_renderMacro(p, node);
          745  +    return;
          746  +  }
          747  +
          748  +  if (node->kind & KIND_HORIZONTAL_RULE){
          749  +    blob_append(p->iblob, "<hr />", -1);
          750  +    return;
          751  +  }
          752  +
          753  +  if (node->kind & KIND_LIST){
          754  +    cr_renderList(p);
          755  +    p->list = NULL;
          756  +    return;
          757  +  }
          758  +
          759  +  if (node->kind & KIND_TABLE){
          760  +    cr_renderTable(p, node);
          761  +    return;
          762  +  }
          763  +
          764  +  if (node->kind & KIND_NO_WIKI_BLOCK){
          765  +    blob_appendf(p->iblob,
          766  +      "\n<pre class='creole-block-nowiki'>%s</pre>\n",
          767  +        htmlize( node->start, node->end - node->start)
          768  +    );
          769  +  }
          770  +}
          771  +//}}}
          772  +
          773  +static char *cr_findEndOfBlock(Parser *p, char *s, char c){//{{{
          774  +
          775  +  char *end;
          776  +  while (s[0]){
          777  +
          778  +    end = s;
          779  +    if (s[0] == c && s[0] == c && s[0] == c) {
          780  +      s = cr_nextLine(p, s + 3);
          781  +      if (p->lineWasBlank) {
          782  +          p->cursor = s;
          783  +          return end;
          784  +      }
          785  +    }
          786  +    else {
          787  +      s = cr_nextLine(p, s);
          788  +    }
          789  +  }
          790  +  return 0;
          791  +}
          792  +//}}}
          793  +static int cr_addListItem(Parser *p, Node *n){//{{{
          794  +
          795  +  n->parent = n;
          796  +  n->next = n->children = NULL;
          797  +
          798  +  if (!p->list) {
          799  +    if (n->level != 1) return 0;
          800  +    p->list = n;
          801  +    return 1;
          802  +  }
          803  +
          804  +  Node *list = p->list;
          805  +
          806  +  while (n->level < list->level){
          807  +    list = list->parent;
          808  +  }
          809  +
          810  +  if (n->level == list->level){
          811  +
          812  +    if (n->kind != list->kind){
          813  +      if (n->level>1) return 0;
          814  +      cr_renderList(p);
          815  +      p->list = n;
          816  +      return 1;
          817  +    }
          818  +    n->parent = list->parent;
          819  +    p->list = list->next = n;
          820  +    return 1;
          821  +  }
          822  +
          823  +  if ( (n->level - list->level) > 1 ) return 0;
          824  +  n->parent = p->list;
          825  +  p->list->children = n;
          826  +  p->list = n;
          827  +  return 1;
          828  +
          829  +}
          830  +//}}}
          831  +
          832  +static int isEndWikiMarker(Parser *p){//{{{
          833  +
          834  +  char *s = p->cursor;
          835  +  if (memcmp(s, "<<fossil>>", 10)) return 0;
          836  +  p->this->start = s;
          837  +  p->this->kind = KIND_END_WIKI_MARKER;
          838  +  p->cursor += 10;
          839  +  return 1;
          840  +}
          841  +//}}}
          842  +static int isNoWikiBlock(Parser *p){//{{{
          843  +
          844  +  char *s = p->cursor;
          845  +
          846  +  if (s[0] != '{') return 0; s++;
          847  +  if (s[0] != '{') return 0; s++;
          848  +  if (s[0] != '{') return 0; s++;
          849  +
          850  +  s = cr_nextLine(p, s);
          851  +  if (!p->lineWasBlank) return 0;
          852  +
          853  +  p->this->start = s;
          854  +
          855  +  s = cr_findEndOfBlock(p, s, '}');
          856  +
          857  +  if (!s) return 0;
          858  +
          859  +  // p->cursor was set by findEndOfBlock
          860  +  p->this->kind = KIND_NO_WIKI_BLOCK;
          861  +  p->this->end = s;
          862  +  return 1;
          863  +}
          864  +
          865  +//}}}
          866  +static int isParaBreak(Parser *p){//{{{
          867  +
          868  +  char *s = cr_nextLine(p, p->cursor);
          869  +  if (!p->lineWasBlank) return 0;
          870  +
          871  +  p->cursor = s;
          872  +  p->this->kind = KIND_PARA_BREAK;
          873  +  return 1;
          874  +}
          875  +//}}}
          876  +static int isMacro(Parser *p){//{{{
          877  +
          878  +  char *s = p->cursor;
          879  +  int macroId;
          880  +  int matchLength;
          881  +
          882  +  /* This is succinct but somewhat hard to follow.
          883  +     Read as: If not '<' then return, otherwise increment s;
          884  +              Repeat above;
          885  +              If '<' then return;
          886  +  */
          887  +  if (s[0]!='<') return 0; s++;
          888  +  if (s[0]!='<') return 0; s++;
          889  +  if (s[0]=='<') return 0;
          890  +
          891  +  matchLength = cr_has_macro(s, &macroId);
          892  +  if (!matchLength) return 0;
          893  +
          894  +  s += matchLength;
          895  +  p->this->start = s;
          896  +
          897  +  if (s[-1]!='>'){
          898  +    while (s[0] && s[1] && s[0]!='\n' && !(s[0]=='>' && s[1]=='>')) s++;
          899  +    if (!(s[0] == '>' && s[1] == '>')) return 0;
          900  +    s +=2;
          901  +  }
          902  +  p->cursor = s;
          903  +  p->this->kind = KIND_MACRO;
          904  +  p->this->level = macroId;
          905  +  p->this->flags &= FLAG_MACRO_BLOCK;
          906  +  p->this->end = s-2;
          907  +  return 1;
          908  +}
          909  +//}}}
          910  +static int isHeading(Parser *p){//{{{
          911  +
          912  +  char *s = cr_skipBlanks(p, p->cursor);
          913  +
          914  +  int flags = 0;
          915  +  int level = cr_countChars(p, s, '=');
          916  +  if (!level) return 0;
          917  +
          918  +  s += level;
          919  +
          920  +  if (s[0] == '<' && s[1] == '>') {
          921  +    flags |= FLAG_CENTER;
          922  +    s += 2;
          923  +  }
          924  +  s = cr_skipBlanks(p, s);
          925  +
          926  +  p->this->start = s;
          927  +
          928  +  s = cr_nextLine(p, s);
          929  +  char *z = s;
          930  +
          931  +  if (s[-1] == '\n') s--;
          932  +  while(s[-1] == ' ' || s[-1]=='\t') s--;
          933  +  while(s[-1] == '=' ) s--;
          934  +  if (p->this->start < s){
          935  +    p->cursor = z;
          936  +    p->this->kind = KIND_HEADING;
          937  +    p->this->end = s;
          938  +    p->this->level = level;
          939  +    p->this->flags |= flags;
          940  +    return 1;
          941  +  }
          942  +  return 0;
          943  +}
          944  +//}}}
          945  +static int isHorizontalRule(Parser *p){//{{{
          946  +
          947  +  char *s = cr_skipBlanks(p, p->cursor);
          948  +
          949  +  int level = cr_countChars(p, s, '-');
          950  +
          951  +  if  (level < 4) return 0;
          952  +  s = cr_nextLine(p, s + level);
          953  +  if (!p->lineWasBlank) return 0;
          954  +
          955  +  p->cursor = s;
          956  +  p->this->kind = KIND_HORIZONTAL_RULE;
          957  +
          958  +  return 1;
          959  +}
          960  +//}}}
          961  +static int isListItem(Parser *p){//{{{
          962  +
          963  +  char *s = cr_skipBlanks(p, p->cursor);
          964  +
          965  +  int level = cr_countChars(p, s, '#');
          966  +  if (!level) level = cr_countChars(p, s, '*');
          967  +
          968  +  if ( !level) return 0;
          969  +
          970  +  p->this->kind = (s[0] == '#') ? KIND_ORDERED_LIST : KIND_UNORDERED_LIST;
          971  +  p->this->level = level;
          972  +
          973  +  s = cr_skipBlanks(p, s + level);
          974  +  p->this->start = s;
          975  +
          976  +  s = cr_nextLine(p, s);
          977  +  if (p->lineWasBlank) return 0;
          978  +
          979  +  if (cr_addListItem(p, p->this)){
          980  +    p->cursor = p->this->end = s;
          981  +    return 1;
          982  +  }
          983  +  p->this->kind = 0;
          984  +  return 0;
          985  +}
          986  +//}}}
          987  +static int isTable(Parser *p){//{{{
          988  +
          989  +  p->this->start = p->cursor;
          990  +  char *s = cr_skipBlanks(p, p->cursor);
          991  +  if (s[0] != '|') return 0;
          992  +  s +=1;
          993  +  p->this->kind = KIND_TABLE;
          994  +
          995  +
          996  +  //p->cursor =   p->this->end = cr_nextLine(p, s);
          997  +  Node *row;
          998  +  Node *tail = NULL;
          999  +
         1000  +  while (1) {
         1001  +
         1002  +    row = pool_new(p);
         1003  +    row->kind = KIND_TABLE_ROW;
         1004  +
         1005  +    if (tail)   tail = tail->next = row;
         1006  +    else p->this->children = tail = row;
         1007  +
         1008  +    row->start = s;
         1009  +    p->cursor = s =   row->end = p->this->end = cr_nextLine(p, s);
         1010  +
         1011  +    if (row->end[-1] == '\n') row->end -= 1;
         1012  +    while(row->end[-1] == ' ' ) row->end -= 1;
         1013  +    if (row->end[-1] == '|') row->end -= 1;
         1014  +
         1015  +    if (!*s) break;
         1016  +
         1017  +    // blanks *not* normalized
         1018  +    s = cr_skipBlanks(p, p->cursor);
         1019  +    if (s[0] != '|') break;
         1020  +    s++;
         1021  +
         1022  +  }
         1023  +  return 1;
         1024  +
         1025  +};
         1026  +//}}}
         1027  +static int isParagraph(Parser *p){//{{{
         1028  +
         1029  +  char *s = p->cursor;
         1030  +  p->this->start = s;
         1031  +
         1032  +  s = cr_nextLine(p, s);
         1033  +  p->cursor = p->this->end = s;
         1034  +  p->this->kind = KIND_PARAGRAPH;
         1035  +  return 1;
         1036  +
         1037  +}
         1038  +//}}}
         1039  +
         1040  +static void cr_parse(Parser *p, char* z){//{{{
         1041  +
         1042  +  p->previous = pool_new(p);
         1043  +  p->previous->kind = KIND_PARA_BREAK;
         1044  +
         1045  +  p->this = pool_new(p);
         1046  +  p->this->kind = KIND_PARA_BREAK;
         1047  +
         1048  +  p->inLink = 0;
         1049  +  p->inTable = 0;
         1050  +
         1051  +  p->cursor = z;
         1052  +  p->list = NULL;
         1053  +  p->istack = NULL;
         1054  +
         1055  +  while (p->cursor[0]) {
         1056  +
         1057  +    while (1){
         1058  +
         1059  +      // must be first
         1060  +      if (isNoWikiBlock(p)) break;
         1061  +      if (isParaBreak(p))   break;
         1062  +
         1063  +      // order not important
         1064  +      if (isMacro(p)) break;
         1065  +      if (isHeading(p)) break;
         1066  +      if (isHorizontalRule(p)) break;
         1067  +      if (isListItem(p)) break;
         1068  +      if (isTable(p)) break;
         1069  +
         1070  +      // here for efficiency?
         1071  +      if (isEndWikiMarker(p)) break;
         1072  +
         1073  +      // must be last
         1074  +      if (isParagraph(p)); break;
         1075  +
         1076  +      // doh!
         1077  +      assert(0);
         1078  +    }
         1079  +
         1080  +    int kind = p->this->kind;
         1081  +    int prev = p->previous->kind;
         1082  +
         1083  +    if (kind & KIND_END_WIKI_MARKER)  return;
         1084  +
         1085  +    if (kind == KIND_PARAGRAPH && prev & KIND_LIST_OR_PARAGRAPH) {
         1086  +        p->previous->end = p->this->end;
         1087  +        p->this = pool_new(p);
         1088  +        continue;
         1089  +    }
         1090  +
         1091  +    if ( !(kind & KIND_LIST && prev & KIND_LIST) )
         1092  +      cr_render(p, p->previous);
         1093  +
         1094  +    p->previous = p->this;
         1095  +    p->this = pool_new(p);
         1096  +
         1097  +  }
         1098  +}
         1099  +//}}}
         1100  +
         1101  +//}}}
         1102  +
         1103  +//{{{ MACROS
         1104  +LOCAL void do_macro_wiki_contents(Parser *p, Node *n){//{{{
         1105  +
         1106  +  Stmt q;
         1107  +
         1108  +  blob_append(p->iblob, "<ul>", 4);
         1109  +
         1110  +  db_prepare(&q,
         1111  +    "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname GLOB 'wiki-*'"
         1112  +    " ORDER BY lower(tagname)"
         1113  +  );
         1114  +  while( db_step(&q)==SQLITE_ROW ){
         1115  +    const char *zName = db_column_text(&q, 0);
         1116  +    blob_appendf(p->iblob, "<li><a href=\"%s/wiki?name=%T\">%h</a></li>", g.zBaseURL, zName, zName);
         1117  +  }
         1118  +  db_finalize(&q);
         1119  +  blob_append(p->iblob, "</ul>", 5);
         1120  +}//}}}
         1121  +
         1122  +
         1123  +static int cr_match(char *z1, char *z2, int *len){
         1124  +  *len = strlen(z2);
         1125  +  return !memcmp(z1, z2 ,*len);
         1126  +}
         1127  +
         1128  +int cr_has_macro(char *z, int *tokenType){
         1129  +
         1130  +  int len;
         1131  +
         1132  +  if (cr_match(z, "wiki-contents>>", &len)) {
         1133  +    *tokenType = MACRO_WIKI_CONTENTS;
         1134  +    return len;
         1135  +  }
         1136  +
         1137  +  tokenType = MACRO_NONE;
         1138  +  return 0;
         1139  +
         1140  +}
         1141  +//}}}
         1142  +
         1143  +char *wiki_render_creole(Renderer *r, char *z){
         1144  +
         1145  +  Parser parser;
         1146  +  Parser *p = &parser;
         1147  +
         1148  +  p->r = r;
         1149  +  p->iblob = r->pOut;
         1150  +
         1151  +  p->nFree = 0;
         1152  +  p->pool = NULL;
         1153  +
         1154  +  cr_parse(p, z);
         1155  +
         1156  +  cr_render(p, p->previous);
         1157  +
         1158  +  pool_free(p);
         1159  +
         1160  +  return p->cursor;
         1161  +
         1162  +}
         1163  +

Changes to src/main.mk.

    25     25     $(SRCDIR)/checkout.c \
    26     26     $(SRCDIR)/clearsign.c \
    27     27     $(SRCDIR)/clone.c \
    28     28     $(SRCDIR)/comformat.c \
    29     29     $(SRCDIR)/configure.c \
    30     30     $(SRCDIR)/construct.c \
    31     31     $(SRCDIR)/content.c \
           32  +  $(SRCDIR)/creoleparser.c \
    32     33     $(SRCDIR)/db.c \
    33     34     $(SRCDIR)/delta.c \
    34     35     $(SRCDIR)/deltacmd.c \
    35     36     $(SRCDIR)/descendants.c \
    36     37     $(SRCDIR)/diff.c \
    37     38     $(SRCDIR)/diffcmd.c \
    38     39     $(SRCDIR)/doc.c \
................................................................................
    98     99     checkout_.c \
    99    100     clearsign_.c \
   100    101     clone_.c \
   101    102     comformat_.c \
   102    103     configure_.c \
   103    104     construct_.c \
   104    105     content_.c \
          106  +  creoleparser_.c \
   105    107     db_.c \
   106    108     delta_.c \
   107    109     deltacmd_.c \
   108    110     descendants_.c \
   109    111     diff_.c \
   110    112     diffcmd_.c \
   111    113     doc_.c \
................................................................................
   171    173    $(OBJDIR)/checkout.o \
   172    174    $(OBJDIR)/clearsign.o \
   173    175    $(OBJDIR)/clone.o \
   174    176    $(OBJDIR)/comformat.o \
   175    177    $(OBJDIR)/configure.o \
   176    178    $(OBJDIR)/construct.o \
   177    179    $(OBJDIR)/content.o \
          180  + $(OBJDIR)/creoleparser.o \
   178    181    $(OBJDIR)/db.o \
   179    182    $(OBJDIR)/delta.o \
   180    183    $(OBJDIR)/deltacmd.o \
   181    184    $(OBJDIR)/descendants.o \
   182    185    $(OBJDIR)/diff.o \
   183    186    $(OBJDIR)/diffcmd.o \
   184    187    $(OBJDIR)/doc.o \
................................................................................
   271    274   #
   272    275   $(SRCDIR)/../manifest:	
   273    276   	# noop
   274    277   
   275    278   clean:	
   276    279   	rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
   277    280   	rm -f translate makeheaders mkindex page_index.h headers
   278         -	rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h construct.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h rstats.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
          281  +	rm -f add.h allrepo.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h construct.h content.h creoleparser.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h pqueue.h printf.h rebuild.h report.h rss.h rstats.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h
   279    282   
   280    283   page_index.h: $(TRANS_SRC) mkindex
   281    284   	./mkindex $(TRANS_SRC) >$@
   282    285   headers:	page_index.h makeheaders VERSION.h
   283         -	./makeheaders  add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h construct_.c:construct.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h rstats_.c:rstats.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
          286  +	./makeheaders  add_.c:add.h allrepo_.c:allrepo.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h construct_.c:construct.h content_.c:content.h creoleparser_.c:creoleparser.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h rstats_.c:rstats.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
   284    287   	touch headers
   285    288   headers: Makefile
   286    289   Makefile:
   287    290   add_.c:	$(SRCDIR)/add.c translate
   288    291   	./translate $(SRCDIR)/add.c >add_.c
   289    292   
   290    293   $(OBJDIR)/add.o:	add_.c add.h  $(SRCDIR)/config.h
................................................................................
   392    395   content_.c:	$(SRCDIR)/content.c translate
   393    396   	./translate $(SRCDIR)/content.c >content_.c
   394    397   
   395    398   $(OBJDIR)/content.o:	content_.c content.h  $(SRCDIR)/config.h
   396    399   	$(XTCC) -o $(OBJDIR)/content.o -c content_.c
   397    400   
   398    401   content.h:	headers
          402  +creoleparser_.c:	$(SRCDIR)/creoleparser.c translate
          403  +	./translate $(SRCDIR)/creoleparser.c >creoleparser_.c
          404  +
          405  +$(OBJDIR)/creoleparser.o:	creoleparser_.c creoleparser.h  $(SRCDIR)/config.h
          406  +	$(XTCC) -o $(OBJDIR)/creoleparser.o -c creoleparser_.c
          407  +
          408  +creoleparser.h:	headers
   399    409   db_.c:	$(SRCDIR)/db.c translate
   400    410   	./translate $(SRCDIR)/db.c >db_.c
   401    411   
   402    412   $(OBJDIR)/db.o:	db_.c db.h  $(SRCDIR)/config.h
   403    413   	$(XTCC) -o $(OBJDIR)/db.o -c db_.c
   404    414   
   405    415   db.h:	headers

Changes to src/makemake.tcl.

    19     19     checkout
    20     20     clearsign
    21     21     clone
    22     22     comformat
    23     23     configure
    24     24     construct
    25     25     content
           26  +  creoleparser
    26     27     db
    27     28     delta
    28     29     deltacmd
    29     30     descendants
    30     31     diff
    31     32     diffcmd
    32     33     doc

Changes to src/style.c.

   381    381   @
   382    382   @ /* The label/value pairs on (for example) the ci page */
   383    383   @ table.label-value th {
   384    384   @   vertical-align: top;
   385    385   @   text-align: right;
   386    386   @   padding: 0.2ex 2ex;
   387    387   @ }
          388  +@
          389  +@ /* For marking important UI elements which shouldn't be
          390  +@    lightly dismissed. I mainly use it to mark "not yet
          391  +@    implemented" parts of a page. Whether or not to have
          392  +@    a 'border' attribute set is arguable. */
          393  +@ .achtung {
          394  +@   color: #ff0000;
          395  +@   background: #ffff00;
          396  +@   border: 1px solid #ff0000;
          397  +@ }
          398  +@
          399  +@ div.miniform {
          400  +@     font-size: smaller;
          401  +@     margin: 8px;
          402  +@ } 
          403  +@ /* additions to support creole parser */
          404  +@
          405  +@ .creoletable {
          406  +@   border: 1px solid #666666;
          407  +@   border-spacing: 0;
          408  +@   margin: 1.5em 2em 1.8em 2em;
          409  +@ }
          410  +@ .creoletable * tr th {
          411  +@   font-size: 100%;
          412  +@   padding: .5em .7em .5em .7em;
          413  +@   border-left: 1px solid #666666;
          414  +@   background-color: #558195;
          415  +@   vertical-align: bottom;
          416  +@   color: white;
          417  +@   empty-cells: show;
          418  +@ }
          419  +@ .creoletable * tr td {
          420  +@   padding: .4em .7em .45em .7em;
          421  +@   border-left: 1px solid #D9D9D9;
          422  +@   border-top: 1px solid #D9D9D9;
          423  +@   vertical-align: center;
          424  +@   empty-cells: show;
          425  +@ }
          426  +@ .creole-block-nowiki {
          427  +@   background: oldlace;
          428  +@   margin: 2em;
          429  +@   overflow: auto;
          430  +@ }
          431  +@ .creole-inline-nowiki {
          432  +@   background: oldlace;
          433  +@ }
          434  +@ .creole-noimage {
          435  +@   color:green;
          436  +@   border:1px solid green;
          437  +@ }
          438  +@ .creole-nomacro {
          439  +@   color:red;
          440  +@   border:1px solid red;
          441  +@ } 
   388    442   ;
   389    443   
   390    444   /*
   391    445   ** WEBPAGE: style.css
   392    446   */
   393    447   void page_style_css(void){
   394    448     char *zCSS = 0;

Changes to src/wiki.c.

   377    377     char *zDate;
   378    378     const char *zUser;
   379    379     const char *zRemark;
   380    380     char *zId;
   381    381   
   382    382     zDate = db_text(0, "SELECT datetime('now')");
   383    383     zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
          384  +  blob_append(p, "\n<<fossil>>\n", -1);
   384    385     blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", 
   385    386       zId, zDate, g.zLogin);
   386    387     free(zDate);
   387    388     zUser = PD("u",g.zLogin);
   388    389     if( zUser[0] && strcmp(zUser,g.zLogin) ){
   389    390       blob_appendf(p, " (claiming to be %h)", zUser);
   390    391     }
   391    392     zRemark = PD("r","");
   392         -  blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId);
          393  +  blob_appendf(p, " added:</i><br />\n%s\n<<fossil>>\n</div id=\"%s\">", zRemark, zId);
   393    394   }
   394    395   
   395    396   /*
   396    397   ** WEBPAGE: wikiappend
   397    398   ** URL: /wikiappend?name=PAGENAME
   398    399   */
   399    400   void wikiappend_page(void){

Changes to src/wikiformat.c.

   333    333   #define TOKEN_NEWLINE       5    /* A single "\n" */
   334    334   #define TOKEN_BUL_LI        6    /*  "  *  " */
   335    335   #define TOKEN_NUM_LI        7    /*  "  #  " */
   336    336   #define TOKEN_ENUM          8    /*  "  \(?\d+[.)]?  " */
   337    337   #define TOKEN_INDENT        9    /*  "   " */
   338    338   #define TOKEN_RAW           10   /* Output exactly (used when wiki-use-html==1) */
   339    339   #define TOKEN_TEXT          11   /* None of the above */
          340  +/* macro tokens - hackyyyy */
          341  +#define TOKEN_FOSSIL        12   /* fossil macro */
          342  +#define TOKEN_CREOLE        13   /* creole macro */
   340    343   
   341    344   /*
   342    345   ** State flags
   343    346   */
   344    347   #define AT_NEWLINE          0x001  /* At start of a line */
   345    348   #define AT_PARAGRAPH        0x002  /* At start of a paragraph */
   346    349   #define ALLOW_WIKI          0x004  /* Allow wiki markup */
................................................................................
   348    351   #define INLINE_MARKUP_ONLY  0x010  /* Allow only "inline" markup */
   349    352   #define IN_LIST             0x020  /* Within wiki <ul> or <ol> */
   350    353   #define WIKI_USE_HTML       0x040  /* wiki-use-html option = on */
   351    354   
   352    355   /*
   353    356   ** Current state of the rendering engine
   354    357   */
          358  +#if INTERFACE
   355    359   typedef struct Renderer Renderer;
   356    360   struct Renderer {
   357    361     Blob *pOut;                 /* Output appended to this blob */
   358    362     int state;                  /* Flag that govern rendering */
   359    363     int wikiList;               /* Current wiki list type */
   360    364     int inVerbatim;             /* True in <verbatim> mode */
   361    365     int preVerbState;           /* Value of state prior to verbatim */
................................................................................
   366    370     int nAlloc;                 /* Space allocated for aStack */
   367    371     struct sStack {
   368    372       short iCode;                 /* Markup code */
   369    373       short allowWiki;             /* ALLOW_WIKI if wiki allowed before tag */
   370    374       const char *zId;             /* ID attribute or NULL */
   371    375     } *aStack;
   372    376   };
   373         -
          377  +#endif
   374    378   
   375    379   /*
   376    380   ** z points to a "<" character.  Check to see if this is the start of
   377    381   ** a valid markup.  If it is, return the total number of characters in
   378    382   ** the markup including the initial "<" and the terminating ">".  If
   379    383   ** it is not well-formed markup, return 0.
   380    384   */
................................................................................
   554    558   ** Get the next wiki token.
   555    559   ** 
   556    560   ** z points to the start of a token.  Return the number of
   557    561   ** characters in that token.  Write the token type into *pTokenType.
   558    562   */
   559    563   static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
   560    564     int n;
   561         -  if( z[0]=='<' ){
          565  +  if( !p->inVerbatim && z[0] == '<' && z[1] == '<' ) {
          566  +    if (!memcmp(z, "<<fossil>>", 10)) {
          567  +      *pTokenType = TOKEN_FOSSIL;
          568  +      return 10;
          569  +    }
          570  +#ifdef HAVE_CREOLE_MACRO
          571  +    if (!memcmp(z, "<<creole>>", 10)) {
          572  +      *pTokenType = TOKEN_CREOLE; 
          573  +      return 10;
          574  +    }
          575  +#endif
          576  +  }
          577  +  else if( z[0]=='<' ){
   562    578       n = markupLength(z);
   563    579       if( n>0 ){
   564    580         *pTokenType = TOKEN_MARKUP;
   565    581         return n;
   566    582       }else{
   567    583         *pTokenType = TOKEN_CHARACTER;
   568    584         return 1;
................................................................................
  1065   1081   
  1066   1082     while( z[0] ){
  1067   1083       if( wikiUseHtml ){
  1068   1084         n = nextRawToken(z, p, &tokenType);
  1069   1085       }else{
  1070   1086         n = nextWikiToken(z, p, &tokenType);
  1071   1087       }
         1088  +
         1089  +    /*
         1090  +    ** Additions to support creole switch
         1091  +    */
         1092  +    if(tokenType == TOKEN_FOSSIL) {
         1093  +      z+=10;
         1094  +      continue;
         1095  +    }
         1096  +#ifdef HAVE_CREOLE_MACRO
         1097  +    else if(tokenType == TOKEN_CREOLE) {
         1098  +      z = wiki_render_creole(p, z+n);
         1099  +    }
         1100  +#endif
         1101  +
         1102  +    /*
         1103  +    if (!p->inVerbatim && z[0]=='<' && z[1] == '<') {
         1104  +      z = wiki_render_macro(p, z, &tokenType);
         1105  +      if (tokenType) continue;
         1106  +    }
         1107  +    */
         1108  +    /* end additions */
         1109  +
  1072   1110       p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
  1073   1111       switch( tokenType ){
  1074   1112         case TOKEN_PARAGRAPH: {
  1075   1113           if( inlineOnly ){
  1076   1114             /* blob_append(p->pOut, " &para; ", -1); */
  1077   1115             blob_append(p->pOut, " &nbsp;&nbsp; ", -1);
  1078   1116           }else{
................................................................................
  1433   1471     iStart = i+6;
  1434   1472     for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){}
  1435   1473     if( z[i]!='<' ) return 0;
  1436   1474     blob_init(pTitle, &z[iStart], i-iStart);
  1437   1475     blob_init(pTail, &z[i+8], -1);
  1438   1476     return 1;
  1439   1477   }
         1478  +
         1479  +/*
         1480  +** Additions to support macro extensions
         1481  +**
         1482  +** This only allows block level macros, not inline macros
         1483  +**
         1484  +** All macros must recognize '<<fossil>>' in the input
         1485  +** stream and return control to fossil.
         1486  +*/
         1487  +
         1488  +char *wiki_render_macro(Renderer *p, char *z, int *tokenType){
         1489  +  if (!memcmp(z, "<<fossil>>", 10)){
         1490  +    *tokenType = TOKEN_MARKUP; /* Close enough for "in-progress".
         1491  +                                  Should have it's own token type. */
         1492  +    return z + 10;
         1493  +  }
         1494  +
         1495  +#ifdef HAVE_CREOLE_MACRO
         1496  +  if (!memcmp(z, "<<creole>>", 10)) {
         1497  +    *tokenType = TOKEN_MARKUP; 
         1498  +    return wiki_render_creole(p, z+10);
         1499  +  }
         1500  +#endif
         1501  +
         1502  +  *tokenType = 0;
         1503  +  return z;
         1504  +}
         1505  +
         1506  +int wf_linkLength(const char *z){
         1507  +  return linkLength(z);
         1508  +}
         1509  +void wf_openHyperlink(
         1510  +  Renderer *p,            /* Rendering context */
         1511  +  const char *zTarget,    /* Hyperlink traget; text within [...] */
         1512  +  char *zClose,           /* Write hyperlink closing text here */
         1513  +  int nClose              /* Bytes available in zClose[] */
         1514  +){
         1515  +  return openHyperlink(p, zTarget, zClose, nClose);
         1516  +}
         1517  +/* end additions */