Fossil

Check-in [23c75abd]
Login

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

Overview
Comment:All markup of the form
...</verbatim> with an options
"links" or "links=BOOLEAN" attribute.  Improved TH1 tracing and error
reporting capabilities.  Improved documentation on how reports work.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | ticket-enhancements
Files: files | file ages | folders
SHA1: 23c75abde4e26afd24715f69f69af05702e3f0b0
User & Date: drh 2012-11-23 15:57:08
Context
2012-11-23
16:37
Add the [string trim ...] command to TH1. Accept query parameters in tktview. check-in: 7ba3f653 user: drh tags: ticket-enhancements
15:57
All markup of the form
...</verbatim> with an options
"links" or "links=BOOLEAN" attribute.  Improved TH1 tracing and error
reporting capabilities.  Improved documentation on how reports work.

check-in: 23c75abd user: drh tags: ticket-enhancements
01:50
When db_open_config() is called with the useAttach parameter set to non-zero, it may need to close and reopen the database using ATTACH if that was not done previously. check-in: fe453a48 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

1848
1849
1850
1851
1852
1853
1854




1855
1856
1857
1858
1859
1860
1861
/*
** Note that the following command is used by ssh:// processing.
**
** COMMAND: test-http
** Works like the http command but gives setup permission to all users.
*/
void cmd_test_http(void){




  login_set_capabilities("sx", 0);
  g.useLocalauth = 1;
  cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
  g.httpIn = stdin;
  g.httpOut = stdout;
  find_server_repository(0);
  g.cgiOutput = 1;







>
>
>
>







1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
/*
** Note that the following command is used by ssh:// processing.
**
** COMMAND: test-http
** Works like the http command but gives setup permission to all users.
*/
void cmd_test_http(void){
  g.thTrace = find_option("th-trace", 0, 0)!=0;
  if( g.thTrace ){
    blob_zero(&g.thLog);
  }
  login_set_capabilities("sx", 0);
  g.useLocalauth = 1;
  cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
  g.httpIn = stdin;
  g.httpOut = stdout;
  find_server_repository(0);
  g.cgiOutput = 1;

Changes to src/report.c.

486
487
488
489
490
491
492
493





494
495


496
497
498
499
500
501
502
503
...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
...
726
727
728
729
730
731
732

733
734
735
736
737
738
739
740
  @
  @ <li><p>If a column of the result set is named "#" then that column
  @ is assumed to hold a ticket number.  A hyperlink will be created from
  @ that column to a detailed view of the ticket.</p></li>
  @
  @ <li><p>If a column of the result set is named "bgcolor" then the content
  @ of that column determines the background color of the row.</p></li>
  @





  @ <li><p>The first column whose name begins with underscore ("_") and all
  @ subsequent columns are shown on their own rows in the table.  This might


  @ be useful for displaying the description of tickets.
  @ </p></li>
  @
  @ <li><p>The query can join other tables in the database besides TICKET.
  @ </p></li>
  @ </ul>
  @
  @ <h3>Examples</h3>
................................................................................
  @    owner AS 'By',
  @    subsystem AS 'Subsys',
  @    sdate(changetime) AS 'Changed',
  @    assignedto AS 'Assigned',
  @    severity AS 'Svr',
  @    priority AS 'Pri',
  @    title AS 'Title',
  @    description AS '_Description',   -- When the column name begins with '_'
  @    remarks AS '_Remarks'            -- the data is shown on a separate row.
  @  FROM ticket
  @ </pre></blockquote>
  @
  @ <p>Or, to see part of the description on the same row, use the
  @ <b>wiki()</b> function with some string manipulation. Using the
  @ <b>tkt()</b> function on the ticket number will also generate a linked
  @ field, but without the extra <i>edit</i> column:
................................................................................
    if( pState->iNewRow>=0 && i>=pState->iNewRow ){
      if( zTid && g.perm.Write ){
        @ <td valign="top">%z(href("%R/tktedit/%h",zTid))edit</a></td>
        zTid = 0;
      }
      if( zData[0] ){
        Blob content;

        @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
        blob_init(&content, zData, -1);
        wiki_convert(&content, 0, WIKI_NOBADLINKS);
        blob_reset(&content);
      }
    }else if( azName[i][0]=='#' ){
      zTid = zData;
      @ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>








>
>
>
>
>

|
>
>
|







 







|
|







 







>
|







486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
...
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
...
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
  @
  @ <li><p>If a column of the result set is named "#" then that column
  @ is assumed to hold a ticket number.  A hyperlink will be created from
  @ that column to a detailed view of the ticket.</p></li>
  @
  @ <li><p>If a column of the result set is named "bgcolor" then the content
  @ of that column determines the background color of the row.</p></li>
  @
  @ <li><p>The text of all columns prior to the first column whose name begins
  @ with underscore ("_") is shown character-for-character as it appears in
  @ the database.  In other words, it is assumed to have a mimetype of
  @ text/plain.
  @
  @ <li><p>The first column whose name begins with underscore ("_") and all
  @ subsequent columns are shown on their own rows in the table and with
  @ wiki formatting.  In other words, such rows are shown with a mimetype
  @ of text/x-fossil-wiki.  This is recommended for the "description" field
  @ of tickets.
  @ </p></li>
  @
  @ <li><p>The query can join other tables in the database besides TICKET.
  @ </p></li>
  @ </ul>
  @
  @ <h3>Examples</h3>
................................................................................
  @    owner AS 'By',
  @    subsystem AS 'Subsys',
  @    sdate(changetime) AS 'Changed',
  @    assignedto AS 'Assigned',
  @    severity AS 'Svr',
  @    priority AS 'Pri',
  @    title AS 'Title',
  @    description AS '_Description',  -- When the column name begins with '_'
  @    remarks AS '_Remarks'           -- content is rendered as wiki
  @  FROM ticket
  @ </pre></blockquote>
  @
  @ <p>Or, to see part of the description on the same row, use the
  @ <b>wiki()</b> function with some string manipulation. Using the
  @ <b>tkt()</b> function on the ticket number will also generate a linked
  @ field, but without the extra <i>edit</i> column:
................................................................................
    if( pState->iNewRow>=0 && i>=pState->iNewRow ){
      if( zTid && g.perm.Write ){
        @ <td valign="top">%z(href("%R/tktedit/%h",zTid))edit</a></td>
        zTid = 0;
      }
      if( zData[0] ){
        Blob content;
        @ </tr>
        @ <tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
        blob_init(&content, zData, -1);
        wiki_convert(&content, 0, WIKI_NOBADLINKS);
        blob_reset(&content);
      }
    }else if( azName[i][0]=='#' ){
      zTid = zData;
      @ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>

Changes to src/th_main.c.

70
71
72
73
74
75
76

77
78
79
80




81
82
83
84
85
86
87
...
117
118
119
120
121
122
123


124
125
126
127
128
129

130
131
132
133
134
135
136
...
799
800
801
802
803
804
805



806
807
808
809
810
811
812
static int enableOutputCmd(
  Th_Interp *interp, 
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){

  if( argc!=2 ){
    return Th_WrongNumArgs(interp, "enable_output BOOLEAN");
  }
  return Th_ToInt(interp, argv[1], argl[1], &enableOutput);




}

/*
** Return a name for a TH1 return code.
*/
const char *Th_ReturnCodeName(int rc){
  static char zRc[32];
................................................................................
      fflush(stdout);
    }
    if( encode ) free((char*)z);
  }
}

static void sendError(const char *z, int n, int forceCgi){


  if( forceCgi || g.cgiOutput ){
    sendText("<hr><p class=\"thmainError\">", -1, 0);
  }
  sendText("ERROR: ", -1, 0);
  sendText((char*)z, n, 1);
  sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);

}

/*
** TH command:     puts STRING
** TH command:     html STRING
**
** Output STRING escaped for HTML (html) or unchanged (puts).  
................................................................................
      i = 0;
      zResult = (char*)Th_GetResult(g.interp, &n);
      sendText((char*)zResult, n, encode);
    }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
      sendText(z, i, 0);
      z += i+5;
      for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}



      rc = Th_Eval(g.interp, 0, (const char*)z, i);
      if( rc!=TH_OK ) break;
      z += i;
      if( z[0] ){ z += 6; }
      i = 0;
    }else{
      i++;







>
|
|

|
>
>
>
>







 







>
>






>







 







>
>
>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
static int enableOutputCmd(
  Th_Interp *interp, 
  void *p, 
  int argc, 
  const char **argv, 
  int *argl
){
  int rc;
  if( argc<2 || argc>3 ){
    return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
  }
  rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
  if( g.thTrace ){
    Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput);
  }
  return rc;
}

/*
** Return a name for a TH1 return code.
*/
const char *Th_ReturnCodeName(int rc){
  static char zRc[32];
................................................................................
      fflush(stdout);
    }
    if( encode ) free((char*)z);
  }
}

static void sendError(const char *z, int n, int forceCgi){
  int savedEnable = enableOutput;
  enableOutput = 1;
  if( forceCgi || g.cgiOutput ){
    sendText("<hr><p class=\"thmainError\">", -1, 0);
  }
  sendText("ERROR: ", -1, 0);
  sendText((char*)z, n, 1);
  sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
  enableOutput = savedEnable;
}

/*
** TH command:     puts STRING
** TH command:     html STRING
**
** Output STRING escaped for HTML (html) or unchanged (puts).  
................................................................................
      i = 0;
      zResult = (char*)Th_GetResult(g.interp, &n);
      sendText((char*)zResult, n, encode);
    }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
      sendText(z, i, 0);
      z += i+5;
      for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
      if( g.thTrace ){
        Th_Trace("eval %d{<pre>%.*h</pre>}<br>", i, i, z);
      }
      rc = Th_Eval(g.interp, 0, (const char*)z, i);
      if( rc!=TH_OK ) break;
      z += i;
      if( z[0] ){ z += 6; }
      i = 0;
    }else{
      i++;

Changes to src/wikiformat.c.

49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
..
77
78
79
80
81
82
83

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
111
112
113
114
115
116
117

118
119
120
121
122
123
124
...
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
...
748
749
750
751
752
753
754











755
756
757
758
759
760
761
762
763
....
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
....
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539



1540
1541
1542
1543
1544
1545
1546
#define ATTR_COLSPAN            10
#define ATTR_COMPACT            11
#define ATTR_FACE               12
#define ATTR_HEIGHT             13
#define ATTR_HREF               14
#define ATTR_HSPACE             15
#define ATTR_ID                 16

#define ATTR_NAME               17
#define ATTR_ROWSPAN            18
#define ATTR_SIZE               19
#define ATTR_SRC                20
#define ATTR_START              21
#define ATTR_STYLE              22
#define ATTR_TARGET             23
#define ATTR_TYPE               24
#define ATTR_VALIGN             25
#define ATTR_VALUE              26
#define ATTR_VSPACE             27
#define ATTR_WIDTH              28
#define AMSK_ALIGN              0x00000001
#define AMSK_ALT                0x00000002
#define AMSK_BGCOLOR            0x00000004
#define AMSK_BORDER             0x00000008
#define AMSK_CELLPADDING        0x00000010
#define AMSK_CELLSPACING        0x00000020
#define AMSK_CLASS              0x00000040
................................................................................
#define AMSK_COLSPAN            0x00000200
#define AMSK_COMPACT            0x00000400
#define AMSK_FACE               0x00000800
#define AMSK_HEIGHT             0x00001000
#define AMSK_HREF               0x00002000
#define AMSK_HSPACE             0x00004000
#define AMSK_ID                 0x00008000

#define AMSK_NAME               0x00010000
#define AMSK_ROWSPAN            0x00020000
#define AMSK_SIZE               0x00040000
#define AMSK_SRC                0x00080000
#define AMSK_START              0x00100000
#define AMSK_STYLE              0x00200000
#define AMSK_TARGET             0x00400000
#define AMSK_TYPE               0x00800000
#define AMSK_VALIGN             0x01000000
#define AMSK_VALUE              0x02000000
#define AMSK_VSPACE             0x04000000
#define AMSK_WIDTH              0x08000000

static const struct AllowedAttribute {
  const char *zName;
  unsigned int iMask;
} aAttribute[] = {
  { 0, 0 },
  { "align",         AMSK_ALIGN,          },
................................................................................
  { "colspan",       AMSK_COLSPAN,        },
  { "compact",       AMSK_COMPACT,        },
  { "face",          AMSK_FACE,           },
  { "height",        AMSK_HEIGHT,         },
  { "href",          AMSK_HREF,           },
  { "hspace",        AMSK_HSPACE,         },
  { "id",            AMSK_ID,             },

  { "name",          AMSK_NAME,           },
  { "rowspan",       AMSK_ROWSPAN,        },
  { "size",          AMSK_SIZE,           },
  { "src",           AMSK_SRC,            },
  { "start",         AMSK_START,          },
  { "style",         AMSK_STYLE,          },
  { "target",        AMSK_TARGET,         },
................................................................................
*/
static int markupLength(const char *z){
  int n = 1;
  int inparen = 0;
  int c;
  if( z[n]=='/' ){ n++; }
  if( !fossil_isalpha(z[n]) ) return 0;
  while( fossil_isalnum(z[n]) ){ n++; }
  c = z[n];
  if( c=='/' && z[n+1]=='>' ){ return n+2; }
  if( c!='>' && !fossil_isspace(c) ) return 0;
  while( (c = z[n])!=0 && (c!='>' || inparen) ){
    if( c==inparen ){
      inparen = 0;
    }else if( inparen==0 && (c=='"' || c=='\'') ){
................................................................................
    if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
    i++;
  }
  zTag[j] = 0;
  p->iCode = findTag(zTag);
  p->iType = aMarkup[p->iCode].iType;
  p->nAttr = 0;











  while( fossil_isspace(z[i]) ){ i++; }
  while( p->nAttr<8 && fossil_isalpha(z[i]) ){
    int attrOk;    /* True to preserver attribute.  False to ignore it */
    j = 0;
    while( fossil_isalnum(z[i]) ){
      if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
      i++;
    }
    zTag[j] = 0;
................................................................................
      }
      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
        ** </div>
        */
        if( markup.iCode==MARKUP_DIV && markup.endTag &&
             (zId = markupId(&markup))!=0 &&
             (iDiv = findTagWithId(p, MARKUP_DIV, zId))>=0
        ){
          if( p->inVerbatim ){
            p->inVerbatim = 0;
................................................................................
          p->zVerbatimId = 0;
          p->inVerbatim = 1;
          p->preVerbState = p->state;
          p->state &= ~ALLOW_WIKI;
          for(ii=0; ii<markup.nAttr; ii++){
            if( markup.aAttr[ii].iACode == ATTR_ID ){
              p->zVerbatimId = markup.aAttr[ii].zValue;
            }else if( markup.aAttr[ii].iACode == ATTR_TYPE ){
              if( fossil_stricmp(markup.aAttr[ii].zValue, "allow-links")==0 ){
                p->state |= ALLOW_LINKS;
              }else{
                blob_appendf(p->pOut, "<pre name='code' class='%s'>",
                  markup.aAttr[ii].zValue);
                vAttrDidAppend=1;
              }



            }
          }
          if( !vAttrDidAppend ) {
            endAutoParagraph(p);
            blob_append(p->pOut, "<pre class='verbatim'>",-1);
          }
          p->wantAutoParagraph = 0;







>
|
|
|
|
|
|
|
|
|
|
|
|







 







>
|
|
|
|
|
|
|
|
|
|
|
|







 







>







 







|







 







>
>
>
>
>
>
>
>
>
>
>

|







 







|
|
<







 







|
<
<
<
|
|
|
<
>
>
>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
..
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
...
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
....
1445
1446
1447
1448
1449
1450
1451
1452
1453

1454
1455
1456
1457
1458
1459
1460
....
1538
1539
1540
1541
1542
1543
1544
1545



1546
1547
1548

1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
#define ATTR_COLSPAN            10
#define ATTR_COMPACT            11
#define ATTR_FACE               12
#define ATTR_HEIGHT             13
#define ATTR_HREF               14
#define ATTR_HSPACE             15
#define ATTR_ID                 16
#define ATTR_LINKS              17
#define ATTR_NAME               18
#define ATTR_ROWSPAN            19
#define ATTR_SIZE               20
#define ATTR_SRC                21
#define ATTR_START              22
#define ATTR_STYLE              23
#define ATTR_TARGET             24
#define ATTR_TYPE               25
#define ATTR_VALIGN             26
#define ATTR_VALUE              27
#define ATTR_VSPACE             28
#define ATTR_WIDTH              29
#define AMSK_ALIGN              0x00000001
#define AMSK_ALT                0x00000002
#define AMSK_BGCOLOR            0x00000004
#define AMSK_BORDER             0x00000008
#define AMSK_CELLPADDING        0x00000010
#define AMSK_CELLSPACING        0x00000020
#define AMSK_CLASS              0x00000040
................................................................................
#define AMSK_COLSPAN            0x00000200
#define AMSK_COMPACT            0x00000400
#define AMSK_FACE               0x00000800
#define AMSK_HEIGHT             0x00001000
#define AMSK_HREF               0x00002000
#define AMSK_HSPACE             0x00004000
#define AMSK_ID                 0x00008000
#define AMSK_LINKS              0x00010000
#define AMSK_NAME               0x00020000
#define AMSK_ROWSPAN            0x00040000
#define AMSK_SIZE               0x00080000
#define AMSK_SRC                0x00100000
#define AMSK_START              0x00200000
#define AMSK_STYLE              0x00400000
#define AMSK_TARGET             0x00800000
#define AMSK_TYPE               0x01000000
#define AMSK_VALIGN             0x02000000
#define AMSK_VALUE              0x04000000
#define AMSK_VSPACE             0x08000000
#define AMSK_WIDTH              0x10000000

static const struct AllowedAttribute {
  const char *zName;
  unsigned int iMask;
} aAttribute[] = {
  { 0, 0 },
  { "align",         AMSK_ALIGN,          },
................................................................................
  { "colspan",       AMSK_COLSPAN,        },
  { "compact",       AMSK_COMPACT,        },
  { "face",          AMSK_FACE,           },
  { "height",        AMSK_HEIGHT,         },
  { "href",          AMSK_HREF,           },
  { "hspace",        AMSK_HSPACE,         },
  { "id",            AMSK_ID,             },
  { "links",         AMSK_LINKS,          },
  { "name",          AMSK_NAME,           },
  { "rowspan",       AMSK_ROWSPAN,        },
  { "size",          AMSK_SIZE,           },
  { "src",           AMSK_SRC,            },
  { "start",         AMSK_START,          },
  { "style",         AMSK_STYLE,          },
  { "target",        AMSK_TARGET,         },
................................................................................
*/
static int markupLength(const char *z){
  int n = 1;
  int inparen = 0;
  int c;
  if( z[n]=='/' ){ n++; }
  if( !fossil_isalpha(z[n]) ) return 0;
  while( fossil_isalnum(z[n]) || z[n]=='-' ){ n++; }
  c = z[n];
  if( c=='/' && z[n+1]=='>' ){ return n+2; }
  if( c!='>' && !fossil_isspace(c) ) return 0;
  while( (c = z[n])!=0 && (c!='>' || inparen) ){
    if( c==inparen ){
      inparen = 0;
    }else if( inparen==0 && (c=='"' || c=='\'') ){
................................................................................
    if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
    i++;
  }
  zTag[j] = 0;
  p->iCode = findTag(zTag);
  p->iType = aMarkup[p->iCode].iType;
  p->nAttr = 0;
  c = 0;
  if( z[i]=='-' ){
    p->aAttr[0].iACode = iACode = ATTR_ID;
    i++;
    p->aAttr[0].zValue = &z[i];
    while( fossil_isalnum(z[i]) ){ i++; }
    p->aAttr[0].cTerm = c = z[i];
    z[i++] = 0;
    p->nAttr = 1;
    if( c=='>' ) return;
  }
  while( fossil_isspace(z[i]) ){ i++; }
  while( c!='>' && p->nAttr<8 && fossil_isalpha(z[i]) ){
    int attrOk;    /* True to preserver attribute.  False to ignore it */
    j = 0;
    while( fossil_isalnum(z[i]) ){
      if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
      i++;
    }
    zTag[j] = 0;
................................................................................
      }
      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 any contained verbatim.
        ** 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 ){
            p->inVerbatim = 0;
................................................................................
          p->zVerbatimId = 0;
          p->inVerbatim = 1;
          p->preVerbState = p->state;
          p->state &= ~ALLOW_WIKI;
          for(ii=0; ii<markup.nAttr; ii++){
            if( markup.aAttr[ii].iACode == ATTR_ID ){
              p->zVerbatimId = markup.aAttr[ii].zValue;
            }else if( markup.aAttr[ii].iACode==ATTR_TYPE ){



              blob_appendf(p->pOut, "<pre name='code' class='%s'>",
                markup.aAttr[ii].zValue);
              vAttrDidAppend=1;

            }else if( markup.aAttr[ii].iACode==ATTR_LINKS
                   && !is_false(markup.aAttr[ii].zValue) ){
              p->state |= ALLOW_LINKS;
            }
          }
          if( !vAttrDidAppend ) {
            endAutoParagraph(p);
            blob_append(p->pOut, "<pre class='verbatim'>",-1);
          }
          p->wantAutoParagraph = 0;