| | @@ -31,40 +31,42 @@ |
| 31 | 31 | /* |
| 32 | 32 | ** Allowed wiki transformation operations |
| 33 | 33 | */ |
| 34 | 34 | #define WIKI_NOFOLLOW 0x001 |
| 35 | 35 | #define WIKI_HTML 0x002 |
| 36 | +#define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */ |
| 36 | 37 | #endif |
| 37 | 38 | |
| 38 | 39 | |
| 39 | 40 | /* |
| 40 | 41 | ** These are the only markup attributes allowed. |
| 41 | 42 | */ |
| 42 | | -#define ATTR_ALIGN 0x000001 |
| 43 | | -#define ATTR_ALT 0x000002 |
| 44 | | -#define ATTR_BGCOLOR 0x000004 |
| 45 | | -#define ATTR_BORDER 0x000008 |
| 46 | | -#define ATTR_CELLPADDING 0x000010 |
| 47 | | -#define ATTR_CELLSPACING 0x000020 |
| 48 | | -#define ATTR_CLEAR 0x000040 |
| 49 | | -#define ATTR_COLOR 0x000080 |
| 50 | | -#define ATTR_COLSPAN 0x000100 |
| 51 | | -#define ATTR_COMPACT 0x000200 |
| 52 | | -#define ATTR_FACE 0x000400 |
| 53 | | -#define ATTR_HEIGHT 0x000800 |
| 54 | | -#define ATTR_HREF 0x001000 |
| 55 | | -#define ATTR_HSPACE 0x002000 |
| 56 | | -#define ATTR_ID 0x004000 |
| 57 | | -#define ATTR_ROWSPAN 0x008000 |
| 58 | | -#define ATTR_SIZE 0x010000 |
| 59 | | -#define ATTR_SRC 0x020000 |
| 60 | | -#define ATTR_START 0x040000 |
| 61 | | -#define ATTR_TYPE 0x080000 |
| 62 | | -#define ATTR_VALIGN 0x100000 |
| 63 | | -#define ATTR_VALUE 0x200000 |
| 64 | | -#define ATTR_VSPACE 0x400000 |
| 65 | | -#define ATTR_WIDTH 0x800000 |
| 43 | +#define ATTR_ALIGN 0x0000001 |
| 44 | +#define ATTR_ALT 0x0000002 |
| 45 | +#define ATTR_BGCOLOR 0x0000004 |
| 46 | +#define ATTR_BORDER 0x0000008 |
| 47 | +#define ATTR_CELLPADDING 0x0000010 |
| 48 | +#define ATTR_CELLSPACING 0x0000020 |
| 49 | +#define ATTR_CLEAR 0x0000040 |
| 50 | +#define ATTR_COLOR 0x0000080 |
| 51 | +#define ATTR_COLSPAN 0x0000100 |
| 52 | +#define ATTR_COMPACT 0x0000200 |
| 53 | +#define ATTR_FACE 0x0000400 |
| 54 | +#define ATTR_HEIGHT 0x0000800 |
| 55 | +#define ATTR_HREF 0x0001000 |
| 56 | +#define ATTR_HSPACE 0x0002000 |
| 57 | +#define ATTR_ID 0x0004000 |
| 58 | +#define ATTR_NAME 0x0008000 |
| 59 | +#define ATTR_ROWSPAN 0x0010000 |
| 60 | +#define ATTR_SIZE 0x0020000 |
| 61 | +#define ATTR_SRC 0x0040000 |
| 62 | +#define ATTR_START 0x0080000 |
| 63 | +#define ATTR_TYPE 0x0100000 |
| 64 | +#define ATTR_VALIGN 0x0200000 |
| 65 | +#define ATTR_VALUE 0x0400000 |
| 66 | +#define ATTR_VSPACE 0x0800000 |
| 67 | +#define ATTR_WIDTH 0x1000000 |
| 66 | 68 | |
| 67 | 69 | static const struct AllowedAttribute { |
| 68 | 70 | const char *zName; |
| 69 | 71 | unsigned int iMask; |
| 70 | 72 | } aAttribute[] = { |
| | @@ -82,10 +84,11 @@ |
| 82 | 84 | { "face", ATTR_FACE, }, |
| 83 | 85 | { "height", ATTR_HEIGHT, }, |
| 84 | 86 | { "href", ATTR_HREF, }, |
| 85 | 87 | { "hspace", ATTR_HSPACE, }, |
| 86 | 88 | { "id", ATTR_ID, }, |
| 89 | + { "name", ATTR_NAME, }, |
| 87 | 90 | { "rowspan", ATTR_ROWSPAN, }, |
| 88 | 91 | { "size", ATTR_SIZE, }, |
| 89 | 92 | { "src", ATTR_SRC, }, |
| 90 | 93 | { "start", ATTR_START, }, |
| 91 | 94 | { "type", ATTR_TYPE, }, |
| | @@ -179,29 +182,32 @@ |
| 179 | 182 | ** The various markup is divided into the following types: |
| 180 | 183 | */ |
| 181 | 184 | #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */ |
| 182 | 185 | #define MUTYPE_BLOCK 0x0002 /* Forms a new paragraph. ex: <p>, <h2> */ |
| 183 | 186 | #define MUTYPE_FONT 0x0004 /* Font changes. ex: <b>, <font>, <sub> */ |
| 184 | | -#define MUTYPE_LINK 0x0008 /* Hyperlink: <a> */ |
| 185 | 187 | #define MUTYPE_LIST 0x0010 /* Lists. <ol>, <ul>, or <dl> */ |
| 186 | 188 | #define MUTYPE_LI 0x0020 /* List items. <li>, <dd>, <dt> */ |
| 187 | 189 | #define MUTYPE_TABLE 0x0040 /* <table> */ |
| 188 | 190 | #define MUTYPE_TR 0x0080 /* <tr> */ |
| 189 | 191 | #define MUTYPE_TD 0x0100 /* <td> or <th> */ |
| 190 | 192 | #define MUTYPE_SPECIAL 0x0200 /* <nowiki> or <verbatim> */ |
| 191 | 193 | #define MUTYPE_HYPERLINK 0x0400 /* <a> */ |
| 192 | 194 | |
| 195 | +/* |
| 196 | +** These markup types must have an end tag. |
| 197 | +*/ |
| 193 | 198 | #define MUTYPE_STACK (MUTYPE_BLOCK | MUTYPE_FONT | MUTYPE_LIST | MUTYPE_TABLE) |
| 194 | 199 | |
| 195 | 200 | static const struct AllowedMarkup { |
| 196 | 201 | const char *zName; /* Name of the markup */ |
| 197 | 202 | char iCode; /* The MARKUP_* code */ |
| 198 | 203 | short int iType; /* The MUTYPE_* code */ |
| 199 | 204 | int allowedAttr; /* Allowed attributes on this markup */ |
| 200 | 205 | } aMarkup[] = { |
| 201 | 206 | { 0, MARKUP_INVALID, 0, 0 }, |
| 202 | | - { "a", MARKUP_A, MUTYPE_HYPERLINK, ATTR_HREF }, |
| 207 | + { "a", MARKUP_A, MUTYPE_HYPERLINK, |
| 208 | + ATTR_HREF|ATTR_NAME }, |
| 203 | 209 | { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, 0 }, |
| 204 | 210 | { "b", MARKUP_B, MUTYPE_FONT, 0 }, |
| 205 | 211 | { "big", MARKUP_BIG, MUTYPE_FONT, 0 }, |
| 206 | 212 | { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, 0 }, |
| 207 | 213 | { "br", MARKUP_BR, MUTYPE_SINGLE, ATTR_CLEAR }, |
| | @@ -314,10 +320,12 @@ |
| 314 | 320 | Blob *pOut; /* Output appended to this blob */ |
| 315 | 321 | int state; /* Flag that govern rendering */ |
| 316 | 322 | int wikiList; /* Current wiki list type */ |
| 317 | 323 | int inVerbatim; /* True in <verbatim> mode */ |
| 318 | 324 | int preVerbState; /* Value of state prior to verbatim */ |
| 325 | + int wantAutoParagraph; /* True if a <p> is desired */ |
| 326 | + int inAutoParagraph; /* True if within an automatic paragraph */ |
| 319 | 327 | const char *zVerbatimId; /* The id= attribute of <verbatim> */ |
| 320 | 328 | int nStack; /* Number of elements on the stack */ |
| 321 | 329 | int nAlloc; /* Space allocated for aStack */ |
| 322 | 330 | unsigned char *aStack; /* Open markup stack */ |
| 323 | 331 | }; |
| | @@ -438,11 +446,11 @@ |
| 438 | 446 | ** |
| 439 | 447 | ** Syntax: |
| 440 | 448 | ** * a tab or two or more spaces |
| 441 | 449 | ** * one or more digits |
| 442 | 450 | ** * optional "." |
| 443 | | -** * another tab or two or more additional spaces |
| 451 | +** * another tab or two ore more spaces. |
| 444 | 452 | ** |
| 445 | 453 | */ |
| 446 | 454 | static int enumLength(const char *z){ |
| 447 | 455 | int i, n; |
| 448 | 456 | n = 0; |
| | @@ -741,17 +749,28 @@ |
| 741 | 749 | } |
| 742 | 750 | return p->aStack[i-1]; |
| 743 | 751 | } |
| 744 | 752 | |
| 745 | 753 | /* |
| 746 | | -** Add missing markup in preparation for writing text. |
| 747 | | -** |
| 748 | | -** "Missing" markup are things like start tags for table rows |
| 749 | | -** or table columns or paragraphs that are omitted from input. |
| 754 | +** Begin a new paragraph if that something that is needed. |
| 755 | +*/ |
| 756 | +static void startAutoParagraph(Renderer *p){ |
| 757 | + if( p->wantAutoParagraph==0 ) return; |
| 758 | + blob_appendf(p->pOut, "<p>", -1); |
| 759 | + pushStack(p, MARKUP_P); |
| 760 | + p->wantAutoParagraph = 0; |
| 761 | + p->inAutoParagraph = 1; |
| 762 | +} |
| 763 | + |
| 764 | +/* |
| 765 | +** End a paragraph if we are in one. |
| 750 | 766 | */ |
| 751 | | -static void addMissingMarkup(Renderer *p){ |
| 752 | | - /* TBD */ |
| 767 | +static void endAutoParagraph(Renderer *p){ |
| 768 | + if( p->inAutoParagraph ){ |
| 769 | + popStackToTag(p, MARKUP_P); |
| 770 | + p->inAutoParagraph = 0; |
| 771 | + } |
| 753 | 772 | } |
| 754 | 773 | |
| 755 | 774 | /* |
| 756 | 775 | ** Resolve a hyperlink. The argument is the content of the [...] |
| 757 | 776 | ** in the wiki. Append the URL to the output of the Renderer. |
| | @@ -813,13 +832,14 @@ |
| 813 | 832 | case TOKEN_PARAGRAPH: { |
| 814 | 833 | if( p->wikiList ){ |
| 815 | 834 | popStackToTag(p, p->wikiList); |
| 816 | 835 | p->wikiList = 0; |
| 817 | 836 | } |
| 818 | | - blob_append(p->pOut, "\n\n<p>", -1); |
| 837 | + endAutoParagraph(p); |
| 838 | + blob_appendf(p->pOut, "\n\n", 1); |
| 839 | + p->wantAutoParagraph = 1; |
| 819 | 840 | p->state |= AT_PARAGRAPH|AT_NEWLINE; |
| 820 | | - popStackToTag(p, MARKUP_P); |
| 821 | 841 | break; |
| 822 | 842 | } |
| 823 | 843 | case TOKEN_NEWLINE: { |
| 824 | 844 | blob_append(p->pOut, "\n", 1); |
| 825 | 845 | p->state |= AT_NEWLINE; |
| | @@ -832,10 +852,12 @@ |
| 832 | 852 | } |
| 833 | 853 | pushStack(p, MARKUP_UL); |
| 834 | 854 | blob_append(p->pOut, "<ul>", 4); |
| 835 | 855 | p->wikiList = MARKUP_UL; |
| 836 | 856 | } |
| 857 | + popStackToTag(p, MARKUP_LI); |
| 858 | + startAutoParagraph(p); |
| 837 | 859 | pushStack(p, MARKUP_LI); |
| 838 | 860 | blob_append(p->pOut, "<li>", 4); |
| 839 | 861 | break; |
| 840 | 862 | } |
| 841 | 863 | case TOKEN_ENUM: { |
| | @@ -845,22 +867,26 @@ |
| 845 | 867 | } |
| 846 | 868 | pushStack(p, MARKUP_OL); |
| 847 | 869 | blob_append(p->pOut, "<ol>", 4); |
| 848 | 870 | p->wikiList = MARKUP_OL; |
| 849 | 871 | } |
| 872 | + popStackToTag(p, MARKUP_LI); |
| 873 | + startAutoParagraph(p); |
| 850 | 874 | pushStack(p, MARKUP_LI); |
| 851 | 875 | blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); |
| 852 | 876 | break; |
| 853 | 877 | } |
| 854 | 878 | case TOKEN_INDENT: { |
| 855 | 879 | assert( p->wikiList==0 ); |
| 856 | 880 | pushStack(p, MARKUP_BLOCKQUOTE); |
| 857 | 881 | blob_append(p->pOut, "<blockquote>", -1); |
| 882 | + p->wantAutoParagraph = 0; |
| 858 | 883 | p->wikiList = MARKUP_BLOCKQUOTE; |
| 859 | 884 | break; |
| 860 | 885 | } |
| 861 | 886 | case TOKEN_CHARACTER: { |
| 887 | + startAutoParagraph(p); |
| 862 | 888 | if( z[0]=='<' ){ |
| 863 | 889 | blob_append(p->pOut, "<", 4); |
| 864 | 890 | }else if( z[0]=='&' ){ |
| 865 | 891 | blob_append(p->pOut, "&", 5); |
| 866 | 892 | } |
| | @@ -869,11 +895,11 @@ |
| 869 | 895 | case TOKEN_LINK: { |
| 870 | 896 | char *zTarget; |
| 871 | 897 | char *zDisplay = 0; |
| 872 | 898 | int i, j; |
| 873 | 899 | int savedState; |
| 874 | | - addMissingMarkup(p); |
| 900 | + startAutoParagraph(p); |
| 875 | 901 | zTarget = &z[1]; |
| 876 | 902 | for(i=1; z[i] && z[i]!=']'; i++){ |
| 877 | 903 | if( z[i]=='|' && zDisplay==0 ){ |
| 878 | 904 | zDisplay = &z[i+1]; |
| 879 | 905 | z[i] = 0; |
| | @@ -896,11 +922,11 @@ |
| 896 | 922 | p->state = savedState; |
| 897 | 923 | blob_append(p->pOut, "</a>", 4); |
| 898 | 924 | break; |
| 899 | 925 | } |
| 900 | 926 | case TOKEN_TEXT: { |
| 901 | | - addMissingMarkup(p); |
| 927 | + startAutoParagraph(p); |
| 902 | 928 | blob_append(p->pOut, z, n); |
| 903 | 929 | break; |
| 904 | 930 | } |
| 905 | 931 | case TOKEN_MARKUP: { |
| 906 | 932 | parseMarkup(&markup, z); |
| | @@ -913,10 +939,12 @@ |
| 913 | 939 | unparseMarkup(&markup); |
| 914 | 940 | blob_append(p->pOut, "<", 4); |
| 915 | 941 | n = 1; |
| 916 | 942 | } |
| 917 | 943 | }else if( markup.iCode==MARKUP_INVALID ){ |
| 944 | + unparseMarkup(&markup); |
| 945 | + startAutoParagraph(p); |
| 918 | 946 | blob_append(p->pOut, "<", 4); |
| 919 | 947 | n = 1; |
| 920 | 948 | }else if( (markup.iType&MUTYPE_FONT)==0 |
| 921 | 949 | && (p->state & FONT_MARKUP_ONLY)!=0 ){ |
| 922 | 950 | /* Do nothing */ |
| | @@ -936,10 +964,11 @@ |
| 936 | 964 | } |
| 937 | 965 | p->inVerbatim = 1; |
| 938 | 966 | p->preVerbState = p->state; |
| 939 | 967 | p->state &= ~ALLOW_WIKI; |
| 940 | 968 | blob_append(p->pOut, "<pre>", 5); |
| 969 | + p->wantAutoParagraph = 0; |
| 941 | 970 | }else if( markup.iType==MUTYPE_LI ){ |
| 942 | 971 | if( backupToType(p, MUTYPE_LIST)==0 ){ |
| 943 | 972 | pushStack(p, MARKUP_UL); |
| 944 | 973 | blob_append(p->pOut, "<ul>", 4); |
| 945 | 974 | } |
| | @@ -957,11 +986,21 @@ |
| 957 | 986 | blob_append(p->pOut, "<tr>", 4); |
| 958 | 987 | } |
| 959 | 988 | pushStack(p, markup.iCode); |
| 960 | 989 | renderMarkup(p->pOut, &markup); |
| 961 | 990 | } |
| 991 | + }else if( markup.iType==MUTYPE_HYPERLINK ){ |
| 992 | + popStackToTag(p, markup.iCode); |
| 993 | + startAutoParagraph(p); |
| 994 | + renderMarkup(p->pOut, &markup); |
| 995 | + pushStack(p, markup.iCode); |
| 962 | 996 | }else{ |
| 997 | + if( markup.iType==MUTYPE_FONT ){ |
| 998 | + startAutoParagraph(p); |
| 999 | + }else if( markup.iType==MUTYPE_BLOCK ){ |
| 1000 | + p->wantAutoParagraph = 0; |
| 1001 | + } |
| 963 | 1002 | if( (markup.iType & MUTYPE_STACK )!=0 ){ |
| 964 | 1003 | pushStack(p, markup.iCode); |
| 965 | 1004 | } |
| 966 | 1005 | renderMarkup(p->pOut, &markup); |
| 967 | 1006 | } |
| | @@ -968,10 +1007,11 @@ |
| 968 | 1007 | break; |
| 969 | 1008 | } |
| 970 | 1009 | } |
| 971 | 1010 | z += n; |
| 972 | 1011 | } |
| 1012 | + endAutoParagraph(p); |
| 973 | 1013 | } |
| 974 | 1014 | |
| 975 | 1015 | |
| 976 | 1016 | /* |
| 977 | 1017 | ** Transform the text in the pIn blob. Write the results |
| | @@ -978,16 +1018,17 @@ |
| 978 | 1018 | ** into the pOut blob. The pOut blob should already be |
| 979 | 1019 | ** initialized. The output is merely appended to pOut. |
| 980 | 1020 | ** If pOut is NULL, then the output is appended to the CGI |
| 981 | 1021 | ** reply. |
| 982 | 1022 | */ |
| 983 | | -void wiki_convert(Blob *pIn, Blob *pOut){ |
| 1023 | +void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 984 | 1024 | char *z; |
| 985 | 1025 | Renderer renderer; |
| 986 | 1026 | |
| 987 | 1027 | memset(&renderer, 0, sizeof(renderer)); |
| 988 | 1028 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; |
| 1029 | + renderer.wantAutoParagraph = (flags & WIKI_INLINE)==0; |
| 989 | 1030 | if( pOut ){ |
| 990 | 1031 | renderer.pOut = pOut; |
| 991 | 1032 | }else{ |
| 992 | 1033 | renderer.pOut = cgi_output_blob(); |
| 993 | 1034 | } |
| | @@ -1007,8 +1048,8 @@ |
| 1007 | 1048 | void test_wiki_render(void){ |
| 1008 | 1049 | Blob in, out; |
| 1009 | 1050 | if( g.argc!=3 ) usage("FILE"); |
| 1010 | 1051 | blob_zero(&out); |
| 1011 | 1052 | blob_read_from_file(&in, g.argv[2]); |
| 1012 | | - wiki_convert(&in, &out); |
| 1053 | + wiki_convert(&in, &out, 0); |
| 1013 | 1054 | blob_write_to_file(&out, "-"); |
| 1014 | 1055 | } |
| 1015 | 1056 | |