Fossil

Diff
Login

Differences From Artifact [7abff38b1d]:

To Artifact [3654a443f2]:


1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19











-
+







/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
148
149
150
151
152
153
154
155

156
157
158
159
160
161
162
148
149
150
151
152
153
154

155
156
157
158
159
160
161
162







-
+









/*
** Allowed markup.
**
** Except for MARKUP_INVALID, this must all be in alphabetical order
** and in numerical sequence.  The first markup type must be zero.
** The value for MARKUP_XYZ must correspond to the <xyz> entry
** The value for MARKUP_XYZ must correspond to the <xyz> entry 
** in aAllowedMarkup[].
*/
#define MARKUP_INVALID           0
#define MARKUP_A                 1
#define MARKUP_ADDRESS           2
#define MARKUP_B                 3
#define MARKUP_BIG               4
256
257
258
259
260
261
262
263

264
265
266

267
268
269
270

271
272
273
274

275
276
277
278
279
280
281
282
283
284
285

286
287
288

289
290
291
292
293
294

295
296
297
298

299
300
301
302
303
304
305
256
257
258
259
260
261
262

263
264
265

266
267
268
269

270
271
272
273

274
275
276
277
278
279
280
281
282
283
284

285
286
287

288
289
290
291
292
293

294
295
296
297

298
299
300
301
302
303
304
305







-
+


-
+



-
+



-
+










-
+


-
+





-
+



-
+







                    AMSK_COLOR|AMSK_FACE|AMSK_SIZE   },
 { "h1",            MARKUP_H1,           MUTYPE_BLOCK,         AMSK_ALIGN  },
 { "h2",            MARKUP_H2,           MUTYPE_BLOCK,         AMSK_ALIGN  },
 { "h3",            MARKUP_H3,           MUTYPE_BLOCK,         AMSK_ALIGN  },
 { "h4",            MARKUP_H4,           MUTYPE_BLOCK,         AMSK_ALIGN  },
 { "h5",            MARKUP_H5,           MUTYPE_BLOCK,         AMSK_ALIGN  },
 { "h6",            MARKUP_H6,           MUTYPE_BLOCK,         AMSK_ALIGN  },
 { "hr",            MARKUP_HR,           MUTYPE_SINGLE,
 { "hr",            MARKUP_HR,           MUTYPE_SINGLE,        
                    AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH  },
 { "i",             MARKUP_I,            MUTYPE_FONT,          0  },
 { "img",           MARKUP_IMG,          MUTYPE_SINGLE,
 { "img",           MARKUP_IMG,          MUTYPE_SINGLE,        
                    AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT|
                    AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH  },
 { "kbd",           MARKUP_KBD,          MUTYPE_FONT,          0  },
 { "li",            MARKUP_LI,           MUTYPE_LI,
 { "li",            MARKUP_LI,           MUTYPE_LI,            
                    AMSK_TYPE|AMSK_VALUE  },
 { "nobr",          MARKUP_NOBR,         MUTYPE_FONT,          0  },
 { "nowiki",        MARKUP_NOWIKI,       MUTYPE_SPECIAL,       0  },
 { "ol",            MARKUP_OL,           MUTYPE_LIST,
 { "ol",            MARKUP_OL,           MUTYPE_LIST,          
                    AMSK_START|AMSK_TYPE|AMSK_COMPACT  },
 { "p",             MARKUP_P,            MUTYPE_BLOCK,         AMSK_ALIGN  },
 { "pre",           MARKUP_PRE,          MUTYPE_BLOCK,         0  },
 { "s",             MARKUP_S,            MUTYPE_FONT,          0  },
 { "samp",          MARKUP_SAMP,         MUTYPE_FONT,          0  },
 { "small",         MARKUP_SMALL,        MUTYPE_FONT,          0  },
 { "strike",        MARKUP_STRIKE,       MUTYPE_FONT,          0  },
 { "strong",        MARKUP_STRONG,       MUTYPE_FONT,          0  },
 { "sub",           MARKUP_SUB,          MUTYPE_FONT,          0  },
 { "sup",           MARKUP_SUP,          MUTYPE_FONT,          0  },
 { "table",         MARKUP_TABLE,        MUTYPE_TABLE,
 { "table",         MARKUP_TABLE,        MUTYPE_TABLE,         
                    AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING|
                    AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE  },
 { "td",            MARKUP_TD,           MUTYPE_TD,
 { "td",            MARKUP_TD,           MUTYPE_TD,            
                    AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
                    AMSK_ROWSPAN|AMSK_VALIGN  },
 { "th",            MARKUP_TH,           MUTYPE_TD,
                    AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN|
                    AMSK_ROWSPAN|AMSK_VALIGN  },
 { "tr",            MARKUP_TR,           MUTYPE_TR,
 { "tr",            MARKUP_TR,           MUTYPE_TR, 
                    AMSK_ALIGN|AMSK_BGCOLOR||AMSK_VALIGN  },
 { "tt",            MARKUP_TT,           MUTYPE_FONT,          0  },
 { "u",             MARKUP_U,            MUTYPE_FONT,          0  },
 { "ul",            MARKUP_UL,           MUTYPE_LIST,
 { "ul",            MARKUP_UL,           MUTYPE_LIST,          
                    AMSK_TYPE|AMSK_COMPACT  },
 { "var",           MARKUP_VAR,          MUTYPE_FONT,          0  },
 { "verbatim",      MARKUP_VERBATIM,     MUTYPE_SPECIAL,       AMSK_ID },
};

/*
** Use binary search to locate a tag in the aMarkup[] table.
432
433
434
435
436
437
438
439

440
441
442
443
444
445
446
432
433
434
435
436
437
438

439
440
441
442
443
444
445
446







-
+







  int n = 0;
  int c;
  while( (c = z[0])!=0 && c!='<' && c!='&' &&
               (useWiki==0 || (c!='[' && c!='\n')) ){
    n++;
    z++;
  }
  return n;
  return n; 
}

/*
** Return true if z[] begins with an HTML character element.
*/
static int isElement(const char *z){
  int i;
625
626
627
628
629
630
631
632

633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650

651
652
653
654
655
656
657
658
659
660
661
662

663
664
665
666
667
668
669
625
626
627
628
629
630
631

632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649

650
651
652
653
654
655
656
657
658
659
660
661

662
663
664
665
666
667
668
669







-
+

















-
+











-
+







  } aAttr[10];
};

/*
** z[] is an HTML markup element - something that begins with '<'.
** Parse this element into the p structure.
**
** The content of z[] might be modified by converting characters
** The content of z[] might be modified by converting characters 
** to lowercase and by inserting some "\000" characters.
*/
static void parseMarkup(ParsedMarkup *p, char *z){
  int i, j, c;
  int iACode;
  char *zValue;
  int seen = 0;
  char zTag[100];

  if( z[1]=='/' ){
    p->endTag = 1;
    i = 2;
  }else{
    p->endTag = 0;
    i = 1;
  }
  j = 0;
  while( isalnum(z[i]) ){
  while( isalnum(z[i]) ){ 
    if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]);
    i++;
  }
  zTag[j] = 0;
  p->iCode = findTag(zTag);
  p->iType = aMarkup[p->iCode].iType;
  p->nAttr = 0;
  while( isspace(z[i]) ){ i++; }
  while( p->nAttr<8 && isalpha(z[i]) ){
    int attrOk;    /* True to preserver attribute.  False to ignore it */
    j = 0;
    while( isalnum(z[i]) ){
    while( isalnum(z[i]) ){ 
      if( j<sizeof(zTag)-1 ) zTag[j++] = tolower(z[i]);
      i++;
    }
    zTag[j] = 0;
    p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag);
    attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0;
    while( isspace(z[i]) ){ z++; }
798
799
800
801
802
803
804
805

806
807
808
809
810
811
812
798
799
800
801
802
803
804

805
806
807
808
809
810
811
812







-
+







  while( p->nStack>i ){
    popStack(p);
  }
}

/*
** Attempt to find a find a tag of type iTag with id zId.  Return -1
** if not found.  If found, return its stack level.
** if not found.  If found, return its stack level.  
*/
static int findTagWithId(Renderer *p, int iTag, const char *zId){
  int i;
  assert( zId!=0 );
  for(i=p->nStack-1; i>=0; i--){
    if( p->aStack[i].iCode!=iTag ) continue;
    if( p->aStack[i].zId==0 ) continue;
888
889
890
891
892
893
894
895

896
897
898
899
900
901
902
888
889
890
891
892
893
894

895
896
897
898
899
900
901
902







-
+







  n = strlen(zTarget);
  memcpy(zLower, zTarget, n+1);
  canonical16(zLower, n+1);
  memcpy(zUpper, zLower, n+1);
  zUpper[n-1]++;
  if( once ){
    const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
    db_static_prepare(&q,
    db_static_prepare(&q, 
      "SELECT %s FROM ticket "
      " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
      zClosedExpr
    );
    once = 0;
  }
  db_bind_text(&q, ":lwr", zLower);
916
917
918
919
920
921
922















923
924
925
926
927
928
929
930
931
932
933

934
935

936
937
938
939
940
941
942
943
944
945

946
947
948
949
950
951
952

953
954
955
956
957
958
959
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947

948
949

950
951
952
953
954
955
956
957
958
959

960
961
962
963
964
965
966

967
968
969
970
971
972
973
974







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
+

-
+









-
+






-
+







** in the wiki.  Append to the output string whatever text is approprate
** for opening the hyperlink.  Write into zClose[0...nClose-1] text that will
** close the markup.
**
** Actually, this routine might or might not append the hyperlink, depending
** on current rendering rules: specifically does the current user have
** "History" permission.
**
**    [http://www.fossil-scm.org/]
**    [https://www.fossil-scm.org/]
**    [ftp://www.fossil-scm.org/]
**    [mailto:fossil-users@lists.fossil-scm.org]
**
**    [/path]
**
**    [./relpath]
**
**    [WikiPageName]
**
**    [0123456789abcdef]
**
**    [#fragment]
*/
static void openHyperlink(
  Renderer *p,            /* Rendering context */
  const char *zTarget,    /* Hyperlink traget; text within [...] */
  char *zClose,           /* Write hyperlink closing text here */
  int nClose              /* Bytes available in zClose[] */
){
  const char *zTerm = "</a>";
  assert( nClose>10 );

  if( strncmp(zTarget, "http:", 5)==0
  if( strncmp(zTarget, "http:", 5)==0 
   || strncmp(zTarget, "https:", 6)==0
   || strncmp(zTarget, "ftp:", 4)==0
   || strncmp(zTarget, "ftp:", 4)==0 
   || strncmp(zTarget, "mailto:", 7)==0
  ){
    blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
  }else if( zTarget[0]=='/' ){
    if( 1 /* g.okHistory */ ){
      blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zBaseURL, zTarget);
    }else{
      zTerm = "";
    }
  }else if( zTarget[0]=='.' ){
  }else if( zTarget[0]=='.' || zTarget[0]=='#' ){
    if( 1 /* g.okHistory */ ){
      blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
    }else{
      zTerm = "";
    }
  }else if( is_valid_uuid(zTarget) ){
    int isClosed;
    int isClosed = 0;
    if( is_ticket(zTarget, &isClosed) ){
      /* Special display processing for tickets.  Display the hyperlink
      ** as crossed out if the ticket is closed.
      */
      if( isClosed ){
        if( g.okHistory ){
          blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>",
1156
1157
1158
1159
1160
1161
1162
1163

1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177

1178
1179
1180
1181
1182
1183
1184
1185







-
+







      case TOKEN_MARKUP: {
        const char *zId;
        int iDiv;
        parseMarkup(&markup, z);

        /* Markup of the form </div id=ID> where there is a matching
        ** ID somewhere on the stack.  Exit the verbatim if were are in
        ** it.  Pop the stack up to the matching <div>.  Discard the
        ** it.  Pop the stack up to the matching <div>.  Discard the 
        ** </div>
        */
        if( markup.iCode==MARKUP_DIV && markup.endTag &&
             (zId = markupId(&markup))!=0 &&
             (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0
        ){
          if( p->inVerbatim ){
1180
1181
1182
1183
1184
1185
1186
1187

1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201

1202
1203
1204
1205
1206
1207
1208
1209







-
+







          }
          assert( p->nStack==iDiv+1 );
          p->nStack--;
        }else

        /* If within <verbatim id=ID> ignore everything other than
        ** </verbatim id=ID> and the </dev id=ID2> above.
        */
        */           
        if( p->inVerbatim ){
          if( endVerbatim(p, &markup) ){
            p->inVerbatim = 0;
            p->state = p->preVerbState;
            blob_append(p->pOut, "</pre>", 6);
          }else{
            unparseMarkup(&markup);
1238
1239
1240
1241
1242
1243
1244
1245

1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259

1260
1261
1262
1263
1264
1265
1266
1267







-
+







        if( markup.iCode==MARKUP_DIV ){
          pushStackWithId(p, markup.iCode, markupId(&markup),
                          (p->state & ALLOW_WIKI)!=0);
        }else

        /* Enter <verbatim> processing.  With verbatim enabled, all other
        ** markup other than the corresponding end-tag with the same ID is
        ** ignored.
        ** ignored. 
        */
        if( markup.iCode==MARKUP_VERBATIM ){
          if( markup.nAttr==1 ){
            p->zVerbatimId = markup.aAttr[0].zValue;
          }else{
            p->zVerbatimId = 0;
          }
1311
1312
1313
1314
1315
1316
1317
1318

1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332

1333
1334
1335
1336
1337
1338
1339
1340







-
+







** initialized.  The output is merely appended to pOut.
** If pOut is NULL, then the output is appended to the CGI
** reply.
*/
void wiki_convert(Blob *pIn, Blob *pOut, int flags){
  char *z;
  Renderer renderer;

  
  memset(&renderer, 0, sizeof(renderer));
  renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
  if( flags & WIKI_NOBLOCK ){
    renderer.state |= INLINE_MARKUP_ONLY;
  }
  if( flags & WIKI_INLINE ){
    renderer.wantAutoParagraph = 0;
1350
1351
1352
1353
1354
1355
1356


























1357
1358
1359
1360
1361
1362
1363
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  Blob in, out;
  if( g.argc!=3 ) usage("FILE");
  blob_zero(&out);
  blob_read_from_file(&in, g.argv[2]);
  wiki_convert(&in, &out, 0);
  blob_write_to_file(&out, "-");
}

/*
** Search for a <title>...</title> at the beginning of a wiki page.
** Return true (nonzero) if a title is found.  Return zero if there is
** not title.
** 
** If a title is found, initialize the pTitle blob to be the content
** of the title and initialize pTail to be the text that follows the
** title.
*/
int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){
  char *z;
  int i;
  int iStart;
  z = blob_str(pIn);
  for(i=0; isspace(z[i]); i++){}
  if( z[i]!='<' ) return 0;
  i++;
  if( strncmp(&z[i],"title>", 6)!=0 ) return 0;
  iStart = i+6;
  for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){}
  if( z[i]!='<' ) return 0;
  blob_init(pTitle, &z[iStart], i-iStart);
  blob_init(pTail, &z[i+8], -1);
  return 1;
}


/*
** Additions to support creole parser
*/

#ifndef HAVE_MACRO_EXTENSIONS