Fossil

Changes On Branch wiki-keep-mimetype
Login

Changes On Branch wiki-keep-mimetype

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

Changes In Branch wiki-keep-mimetype Excluding Merge-Ins

This is equivalent to a diff from b2a8924e to 918d07bf

2014-05-30
18:12
integrated wiki-keep-mimetype branch. ... (check-in: 52d242a7 user: stephan tags: trunk)
18:04
Renamed --format|-F to --mimetype|-M. Stumbled across a char-is-signed assumption and replaced it with (int). ... (Closed-Leaf check-in: 918d07bf user: stephan tags: wiki-keep-mimetype)
2014-05-24
00:12
Minor correction to embedded document link to include relative path. ... (check-in: c11f57fa user: andybradford tags: trunk)
2014-05-23
11:01
pulled in trunk. ... (check-in: 64802875 user: stephan tags: wiki-keep-mimetype)
05:07
Correct reference to the shun URL. ... (check-in: b2a8924e user: andybradford tags: trunk)
2014-05-22
04:47
Make room for extra argument in array. ... (check-in: bae2e579 user: andybradford tags: trunk)

Changes to src/json_artifact.c.

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
** Internal helper which returns:
**
** If the "format" (CLI: -f) flag is set function returns the same as
** json_wiki_get_content_format_flag(), else it returns true (non-0)
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/ 
static char json_artifact_get_content_format_flag(){
  enum { MagicValue = -9 };
  char contentFormat = json_wiki_get_content_format_flag(MagicValue);
  if(MagicValue == contentFormat){
    contentFormat = json_find_option_bool("includeContent","content","c",0) /* deprecated */ ? -1 : 0;
  }
  return contentFormat;
}

extern char json_wiki_get_content_format_flag( char defaultValue ) /* json_wiki.c */;

cson_value * json_artifact_wiki(cson_object * zParent, int rid){
  if( ! g.perm.RdWiki ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'j' privileges.");
    return NULL;
  }else{
    enum { MagicValue = -9 };
    char const contentFormat = json_artifact_get_content_format_flag();
    return json_get_wiki_page_by_rid(rid, contentFormat);
  }
}

/*
** Internal helper for routines which add a "status" flag to file
** artifact data. isNew and isDel should be the "is this object new?" 







|

|






|








|







243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
** Internal helper which returns:
**
** If the "format" (CLI: -f) flag is set function returns the same as
** json_wiki_get_content_format_flag(), else it returns true (non-0)
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/ 
static int json_artifact_get_content_format_flag(){
  enum { MagicValue = -9 };
  int contentFormat = json_wiki_get_content_format_flag(MagicValue);
  if(MagicValue == contentFormat){
    contentFormat = json_find_option_bool("includeContent","content","c",0) /* deprecated */ ? -1 : 0;
  }
  return contentFormat;
}

extern int json_wiki_get_content_format_flag( int defaultValue ) /* json_wiki.c */;

cson_value * json_artifact_wiki(cson_object * zParent, int rid){
  if( ! g.perm.RdWiki ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'j' privileges.");
    return NULL;
  }else{
    enum { MagicValue = -9 };
    int const contentFormat = json_artifact_get_content_format_flag();
    return json_get_wiki_page_by_rid(rid, contentFormat);
  }
}

/*
** Internal helper for routines which add a "status" flag to file
** artifact data. isNew and isDel should be the "is this object new?" 
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
       : "modified");
}

cson_value * json_artifact_file(cson_object * zParent, int rid){
  cson_object * pay = NULL;
  Stmt q = empty_Stmt;
  cson_array * checkin_arr = NULL;
  char contentFormat;
  i64 contentSize = -1;
  char * parentUuid;
  if( ! g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' privileges.");
    return NULL;
  }







|







287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
       : "modified");
}

cson_value * json_artifact_file(cson_object * zParent, int rid){
  cson_object * pay = NULL;
  Stmt q = empty_Stmt;
  cson_array * checkin_arr = NULL;
  int contentFormat;
  i64 contentSize = -1;
  char * parentUuid;
  if( ! g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' privileges.");
    return NULL;
  }

Changes to src/json_wiki.c.

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
** is not returned in the response. If contentFormat is 0 then the
** contentSize reflects the number of bytes, not characters, stored in
** the page.
**
** The returned value, if not NULL, is-a JSON Object owned by the
** caller. If it returns NULL then it may set g.json's error state.
*/
cson_value * json_get_wiki_page_by_rid(int rid, char contentFormat){
  Manifest * pWiki = NULL;
  if( NULL == (pWiki = manifest_get(rid, CFTYPE_WIKI, 0)) ){
    json_set_err( FSL_JSON_E_UNKNOWN,
                  "Error reading wiki page from manifest (rid=%d).",
                  rid );
    return NULL;
  }else{







|







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
** is not returned in the response. If contentFormat is 0 then the
** contentSize reflects the number of bytes, not characters, stored in
** the page.
**
** The returned value, if not NULL, is-a JSON Object owned by the
** caller. If it returns NULL then it may set g.json's error state.
*/
cson_value * json_get_wiki_page_by_rid(int rid, int contentFormat){
  Manifest * pWiki = NULL;
  if( NULL == (pWiki = manifest_get(rid, CFTYPE_WIKI, 0)) ){
    json_set_err( FSL_JSON_E_UNKNOWN,
                  "Error reading wiki page from manifest (rid=%d).",
                  rid );
    return NULL;
  }else{
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
}

/*
** Searches for the latest version of a wiki page with the given
** name. If found it behaves like json_get_wiki_page_by_rid(theRid,
** contentFormat), else it returns NULL.
*/
cson_value * json_get_wiki_page_by_name(char const * zPageName, char contentFormat){
  int rid;
  rid = db_int(0,
               "SELECT x.rid FROM tag t, tagxref x, blob b"
               " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' "
               " AND b.rid=x.rid"
               " ORDER BY x.mtime DESC LIMIT 1",
               zPageName 







|







143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
}

/*
** Searches for the latest version of a wiki page with the given
** name. If found it behaves like json_get_wiki_page_by_rid(theRid,
** contentFormat), else it returns NULL.
*/
cson_value * json_get_wiki_page_by_name(char const * zPageName, int contentFormat){
  int rid;
  rid = db_int(0,
               "SELECT x.rid FROM tag t, tagxref x, blob b"
               " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' "
               " AND b.rid=x.rid"
               " ORDER BY x.mtime DESC LIMIT 1",
               zPageName 
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
** [h]tml = 1
** [n]one = 0
** [r]aw = -1
**
** The return value is intended for use with
** json_get_wiki_page_by_rid() and friends.
*/
char json_wiki_get_content_format_flag( char defaultValue ){
  char contentFormat = defaultValue;
  char const * zFormat = json_find_option_cstr("format",NULL,"f");
  if( !zFormat || !*zFormat ){
    return contentFormat;
  }
  else if('r'==*zFormat){
    contentFormat = -1;
  }







|
|







173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
** [h]tml = 1
** [n]one = 0
** [r]aw = -1
**
** The return value is intended for use with
** json_get_wiki_page_by_rid() and friends.
*/
int json_wiki_get_content_format_flag( int defaultValue ){
  int contentFormat = defaultValue;
  char const * zFormat = json_find_option_cstr("format",NULL,"f");
  if( !zFormat || !*zFormat ){
    return contentFormat;
  }
  else if('r'==*zFormat){
    contentFormat = -1;
  }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
** non-empty/non-NULL value. zSymname takes precedence.  On success
** the result of one of json_get_wiki_page_by_rid() or
** json_get_wiki_page_by_name() will be returned (owned by the
** caller). On error g.json's error state is set and NULL is returned.
*/
static cson_value * json_wiki_get_by_name_or_symname(char const * zPageName,
                                                     char const * zSymname,
                                                     char contentFormat ){
  if(!zSymname || !*zSymname){
    return json_get_wiki_page_by_name(zPageName, contentFormat);
  }else{
    int rid = symbolic_name_to_rid( zSymname ? zSymname : zPageName, "w" );
    if(rid<0){
      json_set_err(FSL_JSON_E_AMBIGUOUS_UUID,
                   "UUID [%s] is ambiguous.", zSymname);







|







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
** non-empty/non-NULL value. zSymname takes precedence.  On success
** the result of one of json_get_wiki_page_by_rid() or
** json_get_wiki_page_by_name() will be returned (owned by the
** caller). On error g.json's error state is set and NULL is returned.
*/
static cson_value * json_wiki_get_by_name_or_symname(char const * zPageName,
                                                     char const * zSymname,
                                                     int contentFormat ){
  if(!zSymname || !*zSymname){
    return json_get_wiki_page_by_name(zPageName, contentFormat);
  }else{
    int rid = symbolic_name_to_rid( zSymname ? zSymname : zPageName, "w" );
    if(rid<0){
      json_set_err(FSL_JSON_E_AMBIGUOUS_UUID,
                   "UUID [%s] is ambiguous.", zSymname);
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
** Implementation of /json/wiki/get.
**
*/
static cson_value * json_wiki_get(){
  char const * zPageName;
  char const * zSymName = NULL;
  char contentFormat = -1;
  if( !g.perm.RdWiki && !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' or 'j' access.");
    return NULL;
  }
  zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);








|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
** Implementation of /json/wiki/get.
**
*/
static cson_value * json_wiki_get(){
  char const * zPageName;
  char const * zSymName = NULL;
  int contentFormat = -1;
  if( !g.perm.RdWiki && !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' or 'j' access.");
    return NULL;
  }
  zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);

306
307
308
309
310
311
312

313
314
315
316
317
318
319
  Blob content = empty_blob;  /* wiki  page content */
  cson_value * nameV;         /* wiki page name */
  char const * zPageName;     /* cstr form of page name */
  cson_value * contentV;      /* passed-in content */
  cson_value * emptyContent = NULL;  /* placeholder for empty content. */
  cson_value * payV = NULL;   /* payload/return value */
  cson_string const * jstr = NULL;  /* temp for cson_value-to-cson_string conversions. */

  unsigned int contentLen = 0;
  int rid;
  if( (createMode && !g.perm.NewWiki)
      || (!createMode && !g.perm.WrWiki)){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires '%c' permissions.",
                 (createMode ? 'f' : 'k'));







>







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  Blob content = empty_blob;  /* wiki  page content */
  cson_value * nameV;         /* wiki page name */
  char const * zPageName;     /* cstr form of page name */
  cson_value * contentV;      /* passed-in content */
  cson_value * emptyContent = NULL;  /* placeholder for empty content. */
  cson_value * payV = NULL;   /* payload/return value */
  cson_string const * jstr = NULL;  /* temp for cson_value-to-cson_string conversions. */
  char const * zMimeType = 0;
  unsigned int contentLen = 0;
  int rid;
  if( (createMode && !g.perm.NewWiki)
      || (!createMode && !g.perm.WrWiki)){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires '%c' permissions.",
                 (createMode ? 'f' : 'k'));
369
370
371
372
373
374
375



376
377
378
379
380
381
382
383
    goto error;
  }
  jstr = cson_value_get_string(contentV);
  contentLen = (int)cson_string_length_bytes(jstr);
  if(contentLen){
    blob_append(&content, cson_string_cstr(jstr),contentLen);
  }



  wiki_cmd_commit(zPageName, 0==rid, &content);
  blob_reset(&content);
  /*
    Our return value here has a race condition: if this operation
    is called concurrently for the same wiki page via two requests,
    payV could reflect the results of the other save operation.
  */
  payV = json_get_wiki_page_by_name(







>
>
>
|







370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
    goto error;
  }
  jstr = cson_value_get_string(contentV);
  contentLen = (int)cson_string_length_bytes(jstr);
  if(contentLen){
    blob_append(&content, cson_string_cstr(jstr),contentLen);
  }

  zMimeType = json_find_option_cstr("mimetype","mimetype","M");

  wiki_cmd_commit(zPageName, 0==rid, &content, zMimeType);
  blob_reset(&content);
  /*
    Our return value here has a race condition: if this operation
    is called concurrently for the same wiki page via two requests,
    payV could reflect the results of the other save operation.
  */
  payV = json_get_wiki_page_by_name(

Changes to src/wiki.c.

953
954
955
956
957
958
959




960
961

962
963
964
965
966
967
968
969
970
971
972
973
974
/*
** Add a new wiki page to the repository.  The page name is
** given by the zPageName parameter.  isNew must be true to create
** a new page.  If no previous page with the name zPageName exists
** and isNew is false, then this routine throws an error.
**
** The content of the new page is given by the blob pContent.




*/
int wiki_cmd_commit(char const * zPageName, int isNew, Blob *pContent){

  Blob wiki;              /* Wiki page content */
  Blob cksum;             /* wiki checksum */
  int rid;                /* artifact ID of parent page */
  char *zDate;            /* timestamp */
  char *zUuid;            /* uuid for rid */

  rid = db_int(0,
     "SELECT x.rid FROM tag t, tagxref x"
     " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
     " ORDER BY x.mtime DESC LIMIT 1",
     zPageName
  );
  if( rid==0 && !isNew ){







>
>
>
>

|
>





|







953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
/*
** Add a new wiki page to the repository.  The page name is
** given by the zPageName parameter.  isNew must be true to create
** a new page.  If no previous page with the name zPageName exists
** and isNew is false, then this routine throws an error.
**
** The content of the new page is given by the blob pContent.
**
** zMimeType specifies the N-card for the wiki page. If it is 0,
** empty, or "text/x-fossil-wiki" (the default format) then it is
** ignored.
*/
int wiki_cmd_commit(char const * zPageName, int isNew, Blob *pContent,
                    char const * zMimeType){
  Blob wiki;              /* Wiki page content */
  Blob cksum;             /* wiki checksum */
  int rid;                /* artifact ID of parent page */
  char *zDate;            /* timestamp */
  char *zUuid;            /* uuid for rid */
  
  rid = db_int(0,
     "SELECT x.rid FROM tag t, tagxref x"
     " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
     " ORDER BY x.mtime DESC LIMIT 1",
     zPageName
  );
  if( rid==0 && !isNew ){
985
986
987
988
989
990
991



992
993
994
995
996
997
998
  }

  blob_zero(&wiki);
  zDate = date_in_standard_format("now");
  blob_appendf(&wiki, "D %s\n", zDate);
  free(zDate);
  blob_appendf(&wiki, "L %F\n", zPageName );



  if( rid ){
    zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    blob_appendf(&wiki, "P %s\n", zUuid);
    free(zUuid);
  }
  user_select();
  if( !login_is_nobody() ){







>
>
>







990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
  }

  blob_zero(&wiki);
  zDate = date_in_standard_format("now");
  blob_appendf(&wiki, "D %s\n", zDate);
  free(zDate);
  blob_appendf(&wiki, "L %F\n", zPageName );
  if( zMimeType && fossil_strcmp(zMimeType,"text/x-fossil-wiki")!=0 ){
    blob_appendf(&wiki, "N %F\n", zMimeType);
  }
  if( rid ){
    zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    blob_appendf(&wiki, "P %s\n", zUuid);
    free(zUuid);
  }
  user_select();
  if( !login_is_nobody() ){
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027


1028
1029
1030
1031
1032
1033
1034
1035
1036
** Run various subcommands to work with wiki entries.
**
**     %fossil wiki export PAGENAME ?FILE?
**
**        Sends the latest version of the PAGENAME wiki
**        entry to the given file or standard output.
**
**     %fossil wiki commit PAGENAME ?FILE?
**
**        Commit changes to a wiki page from FILE or from standard
**        input.


**
**     %fossil wiki create PAGENAME ?FILE?
**
**        Create a new wiki page with initial content taken from
**        FILE or from standard input.
**
**     %fossil wiki list
**
**        Lists all wiki entries, one per line, ordered







|


|
>
>

|







1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
** Run various subcommands to work with wiki entries.
**
**     %fossil wiki export PAGENAME ?FILE?
**
**        Sends the latest version of the PAGENAME wiki
**        entry to the given file or standard output.
**
**     %fossil wiki commit PAGENAME ?FILE? [-mimetype TEXT-FORMAT]
**
**        Commit changes to a wiki page from FILE or from standard
**        input. The -mimetype (-M) flag specifies the mime type,
**        defaulting to the type used by the previous version of
**        the page or (for new pages) text/x-fossil-wiki.
**
**     %fossil wiki create PAGENAME ?FILE? [-mimetype TEXT-FORMAT]
**
**        Create a new wiki page with initial content taken from
**        FILE or from standard input.
**
**     %fossil wiki list
**
**        Lists all wiki entries, one per line, ordered
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
    char const *zPageName;        /* Name of the wiki page to export */
    char const *zFile;            /* Name of the output file (0=stdout) */
    int rid;                      /* Artifact ID of the wiki page */
    int i;                        /* Loop counter */
    char *zBody = 0;              /* Wiki page content */
    Blob body;                    /* Wiki page content */
    Manifest *pWiki = 0;          /* Parsed wiki page content */

    if( (g.argc!=4) && (g.argc!=5) ){
      usage("export PAGENAME ?FILE?");
    }
    zPageName = g.argv[3];
    rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
      " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
      " ORDER BY x.mtime DESC LIMIT 1",







<







1062
1063
1064
1065
1066
1067
1068

1069
1070
1071
1072
1073
1074
1075
    char const *zPageName;        /* Name of the wiki page to export */
    char const *zFile;            /* Name of the output file (0=stdout) */
    int rid;                      /* Artifact ID of the wiki page */
    int i;                        /* Loop counter */
    char *zBody = 0;              /* Wiki page content */
    Blob body;                    /* Wiki page content */
    Manifest *pWiki = 0;          /* Parsed wiki page content */

    if( (g.argc!=4) && (g.argc!=5) ){
      usage("export PAGENAME ?FILE?");
    }
    zPageName = g.argv[3];
    rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
      " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
      " ORDER BY x.mtime DESC LIMIT 1",
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088



1089
1090
1091
1092
1093
1094
1095
1096
1097












1098
1099
1100
1101
1102
1103
1104

1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
    zFile  = (g.argc==4) ? "-" : g.argv[4];
    blob_init(&body, zBody, -1);
    blob_append(&body, "\n", 1);
    blob_write_to_file(&body, zFile);
    blob_reset(&body);
    manifest_destroy(pWiki);
    return;
  }else
  if( strncmp(g.argv[2],"commit",n)==0
      || strncmp(g.argv[2],"create",n)==0 ){
    char *zPageName;
    Blob content;



    if( g.argc!=4 && g.argc!=5 ){
      usage("commit PAGENAME ?FILE?");
    }
    zPageName = g.argv[3];
    if( g.argc==4 ){
      blob_read_from_channel(&content, stdin, -1);
    }else{
      blob_read_from_file(&content, g.argv[4]);
    }












    if( g.argv[2][1]=='r' ){
      wiki_cmd_commit(zPageName, 1, &content);
      fossil_print("Created new wiki page %s.\n", zPageName);
    }else{
      wiki_cmd_commit(zPageName, 0, &content);
      fossil_print("Updated wiki page %s.\n", zPageName);
    }

    blob_reset(&content);
  }else
  if( strncmp(g.argv[2],"delete",n)==0 ){
    if( g.argc!=5 ){
      usage("delete PAGENAME");
    }
    fossil_fatal("delete not yet implemented.");
  }else
  if( strncmp(g.argv[2],"list",n)==0 ){
    Stmt q;
    db_prepare(&q,
      "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
      " ORDER BY lower(tagname) /*sort*/"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      fossil_print( "%s\n",zName);
    }
    db_finalize(&q);
  }else
  {
    goto wiki_cmd_usage;
  }
  return;

wiki_cmd_usage:
  usage("export|create|commit|list ...");
}







<
|
|
|
|
>
>
>

|







>
>
>
>
>
>
>
>
>
>
>
>

|


|


>

|
|




<
|










|
<







1086
1087
1088
1089
1090
1091
1092

1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135

1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147

1148
1149
1150
1151
1152
1153
1154
    zFile  = (g.argc==4) ? "-" : g.argv[4];
    blob_init(&body, zBody, -1);
    blob_append(&body, "\n", 1);
    blob_write_to_file(&body, zFile);
    blob_reset(&body);
    manifest_destroy(pWiki);
    return;

  }else if( strncmp(g.argv[2],"commit",n)==0
            || strncmp(g.argv[2],"create",n)==0 ){
    char const *zPageName;        /* page name */
    Blob content;                 /* Input content */
    int rid;
    Manifest *pWiki = 0;          /* Parsed wiki page content */
    char const * zMimeType = find_option("mimetype", "M", 1);
    if( g.argc!=4 && g.argc!=5 ){
      usage("commit PAGENAME ?FILE? [-mimetype TEXT-FORMAT]");
    }
    zPageName = g.argv[3];
    if( g.argc==4 ){
      blob_read_from_channel(&content, stdin, -1);
    }else{
      blob_read_from_file(&content, g.argv[4]);
    }
    if(!zMimeType || !*zMimeType){
      /* Try to deduce the mime type based on the prior version. */
      rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x"
                   " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
                   " ORDER BY x.mtime DESC LIMIT 1",
                   zPageName
                   );
      if(rid>0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0
         && (pWiki->zMimetype && *pWiki->zMimetype)){
        zMimeType = pWiki->zMimetype;
      }
    }
    if( g.argv[2][1]=='r' ){
      wiki_cmd_commit(zPageName, 1, &content, zMimeType);
      fossil_print("Created new wiki page %s.\n", zPageName);
    }else{
      wiki_cmd_commit(zPageName, 0, &content, zMimeType);
      fossil_print("Updated wiki page %s.\n", zPageName);
    }
    manifest_destroy(pWiki);
    blob_reset(&content);
    return;
  }else if( strncmp(g.argv[2],"delete",n)==0 ){
    if( g.argc!=5 ){
      usage("delete PAGENAME");
    }
    fossil_fatal("delete not yet implemented.");

  }else if( strncmp(g.argv[2],"list",n)==0 ){
    Stmt q;
    db_prepare(&q,
      "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'"
      " ORDER BY lower(tagname) /*sort*/"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      fossil_print( "%s\n",zName);
    }
    db_finalize(&q);
  }else{

    goto wiki_cmd_usage;
  }
  return;

wiki_cmd_usage:
  usage("export|create|commit|list ...");
}