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, "<", 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, ">", 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, "&", 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, ¯oId); 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, " ¶ ", -1); */ 1077 1115 blob_append(p->pOut, " ", -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 */