Fossil

Check-in [23c86b50]
Login

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

Overview
Comment:Merge enhancements and fixes from trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | search-enhancements
Files: files | file ages | folders
SHA1:23c86b503f0f0336703270b47a07b19240aa0859
User & Date: drh 2015-02-13 21:21:46
Context
2015-02-13
22:03
When extracting search text from tickets, use the title column for the title and pay attention to mimetypes when translating. check-in: b552f55b user: drh tags: search-enhancements
21:21
Merge enhancements and fixes from trunk. check-in: 23c86b50 user: drh tags: search-enhancements
09:03
A few more places where displayed UUID length should be configurable. check-in: c62e94f8 user: jan.nijtmans tags: trunk
2015-02-12
02:32
Improvements to the way that searchable text is parsed out of documents, wiki, and check-in comments. The first line of the text is the title. Subsequent lines are message body. Still need to do this for tickets. check-in: 837d9b5b user: drh tags: search-enhancements
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/attach.c.

441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
    db_end_transaction(0);
    @ <p>The attachment below has been deleted.</p>
  }

  if( P("del")
   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
  ){
    form_begin(0, "%R/ainfo/%s", zUuid);
    @ <p>Confirm you want to delete the attachment shown below.
    @ <input type="submit" name="confirm" value="Confirm">
    @ </form>
  }

  isModerator = g.perm.Admin ||
                (zTktUuid && g.perm.ModTkt) ||
                (zWikiName && g.perm.ModWiki);
  if( isModerator && (zModAction = P("modaction"))!=0 ){
    if( strcmp(zModAction,"delete")==0 ){
      moderation_disapprove(rid);
      if( zTktUuid ){
        cgi_redirectf("%R/tktview/%s", zTktUuid);
      }else{
        cgi_redirectf("%R/wiki?name=%t", zWikiName);
      }
      return;
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve(rid);







|












|







441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
    db_end_transaction(0);
    @ <p>The attachment below has been deleted.</p>
  }

  if( P("del")
   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
  ){
    form_begin(0, "%R/ainfo/%!S", zUuid);
    @ <p>Confirm you want to delete the attachment shown below.
    @ <input type="submit" name="confirm" value="Confirm">
    @ </form>
  }

  isModerator = g.perm.Admin ||
                (zTktUuid && g.perm.ModTkt) ||
                (zWikiName && g.perm.ModWiki);
  if( isModerator && (zModAction = P("modaction"))!=0 ){
    if( strcmp(zModAction,"delete")==0 ){
      moderation_disapprove(rid);
      if( zTktUuid ){
        cgi_redirectf("%R/tktview/%!S", zTktUuid);
      }else{
        cgi_redirectf("%R/wiki?name=%t", zWikiName);
      }
      return;
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve(rid);

Changes to src/branch.c.

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
  if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
    fossil_fatal("%s\n", g.zErrMsg);
  }
  assert( blob_is_reset(&branch) );
  content_deltify(rootid, brid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
  fossil_print("New branch: %s\n", zUuid);
  if( g.argc==3 ){
    fossil_print(
      "\n"
      "Note: the local check-out has not been updated to the new\n"
      "      branch.  To begin working on the new branch, do this:\n"
      "\n"
      "      %s update %s\n",







|







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
  if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
    fossil_fatal("%s\n", g.zErrMsg);
  }
  assert( blob_is_reset(&branch) );
  content_deltify(rootid, brid, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid);
  fossil_print("New branch: %S\n", zUuid);
  if( g.argc==3 ){
    fossil_print(
      "\n"
      "Note: the local check-out has not been updated to the new\n"
      "      branch.  To begin working on the new branch, do this:\n"
      "\n"
      "      %s update %s\n",

Changes to src/browse.c.

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  if( linkTip ){
    style_submenu_element("Tip", "Tip", "%s",
                          url_render(&sURI, "ci", "tip", 0, 0));
  }
  if( zCI ){
    @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%R/dir?ci=%s&name=%T", zUuid, zPrefix);
    if( nD==0 ){
      style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%s",
                            zUuid);
    }
  }else{
    @ <h2>The union of all files from all check-ins
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
  }







|

|







179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  if( linkTip ){
    style_submenu_element("Tip", "Tip", "%s",
                          url_render(&sURI, "ci", "tip", 0, 0));
  }
  if( zCI ){
    @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%R/dir?name=%T&ci=%!S", zPrefix, zUuid);
    if( nD==0 ){
      style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%!S",
                            zUuid);
    }
  }else{
    @ <h2>The union of all files from all check-ins
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
  }

Changes to src/checkin.c.

1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);

  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=-4");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zIntegrateUuid = db_column_text(&q, 0);
    if( is_a_leaf(db_column_int(&q, 1)) ){
      fossil_print("Closed: %s\n", zIntegrateUuid);
    }else{
      fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid);
    }
  }
  db_finalize(&q);

  fossil_print("New_Version: %s\n", zUuid);
  if( outputManifest ){
    zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
    blob_zero(&muuid);
    blob_appendf(&muuid, "%s\n", zUuid);
    blob_write_to_file(&muuid, zManifestFile);
    free(zManifestFile);
    blob_reset(&muuid);







|

|




|







1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);

  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=-4");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zIntegrateUuid = db_column_text(&q, 0);
    if( is_a_leaf(db_column_int(&q, 1)) ){
      fossil_print("Closed: %S\n", zIntegrateUuid);
    }else{
      fossil_print("Not_Closed: %S (not a leaf any more)\n", zIntegrateUuid);
    }
  }
  db_finalize(&q);

  fossil_print("New_Version: %S\n", zUuid);
  if( outputManifest ){
    zManifestFile = mprintf("%smanifest.uuid", g.zLocalRoot);
    blob_zero(&muuid);
    blob_appendf(&muuid, "%s\n", zUuid);
    blob_write_to_file(&muuid, zManifestFile);
    free(zManifestFile);
    blob_reset(&muuid);

Changes to src/db.c.

2387
2388
2389
2390
2391
2392
2393

2394
2395
2396
2397
2398
2399
2400
....
2563
2564
2565
2566
2567
2568
2569



2570
2571
2572
2573
2574
2575
2576
  { "diff-command",     0,             40, 0, 0, ""                    },
  { "dont-push",        0,              0, 0, 0, "off"                 },
  { "editor",           0,             32, 0, 0, ""                    },
  { "empty-dirs",       0,             40, 1, 0, ""                    },
  { "encoding-glob",    0,             40, 1, 0, ""                    },
  { "gdiff-command",    0,             40, 0, 0, "gdiff"               },
  { "gmerge-command",   0,             40, 0, 0, ""                    },

  { "http-port",        0,             16, 0, 0, "8080"                },
  { "https-login",      0,              0, 0, 0, "off"                 },
  { "ignore-glob",      0,             40, 1, 0, ""                    },
  { "keep-glob",        0,             40, 1, 0, ""                    },
  { "localauth",        0,              0, 0, 0, "off"                 },
  { "main-branch",      0,             40, 0, 0, "trunk"               },
  { "manifest",         0,              0, 1, 0, "off"                 },
................................................................................
**                     diff. If undefined, text diff will be used.
**
**    gmerge-command   A graphical merge conflict resolver command operating
**                     on four files.
**                     Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output"
**                     Ex: xxdiff "%original" "%baseline" "%merge" -M "%output"
**                     Ex: meld "%baseline" "%original" "%merge" "%output"



**
**    http-port        The TCP/IP port number to use by the "server"
**                     and "ui" commands.  Default: 8080
**
**    https-login      Send login credentials using HTTPS instead of HTTP
**                     even if the login page request came via HTTP.
**







>







 







>
>
>







2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
....
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
  { "diff-command",     0,             40, 0, 0, ""                    },
  { "dont-push",        0,              0, 0, 0, "off"                 },
  { "editor",           0,             32, 0, 0, ""                    },
  { "empty-dirs",       0,             40, 1, 0, ""                    },
  { "encoding-glob",    0,             40, 1, 0, ""                    },
  { "gdiff-command",    0,             40, 0, 0, "gdiff"               },
  { "gmerge-command",   0,             40, 0, 0, ""                    },
  { "hash-digits",      0,              5, 0, 0, "10"                  },
  { "http-port",        0,             16, 0, 0, "8080"                },
  { "https-login",      0,              0, 0, 0, "off"                 },
  { "ignore-glob",      0,             40, 1, 0, ""                    },
  { "keep-glob",        0,             40, 1, 0, ""                    },
  { "localauth",        0,              0, 0, 0, "off"                 },
  { "main-branch",      0,             40, 0, 0, "trunk"               },
  { "manifest",         0,              0, 1, 0, "off"                 },
................................................................................
**                     diff. If undefined, text diff will be used.
**
**    gmerge-command   A graphical merge conflict resolver command operating
**                     on four files.
**                     Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output"
**                     Ex: xxdiff "%original" "%baseline" "%merge" -M "%output"
**                     Ex: meld "%baseline" "%original" "%merge" "%output"
**
**    hash-digits      The number of hexadecimal digits of the SHA1 hash to
**                     display.  (Default: 10; Minimum: 6)
**
**    http-port        The TCP/IP port number to use by the "server"
**                     and "ui" commands.  Default: 8080
**
**    https-login      Send login credentials using HTTPS instead of HTTP
**                     even if the login page request came via HTTP.
**

Changes to src/doc.c.

104
105
106
107
108
109
110

111
112
113
114
115
116
117
...
289
290
291
292
293
294
295














296
297
298
299
300
301
302
...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
...
354
355
356
357
358
359
360

361
362
363
364
365
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
  { "class",      5, "application/octet-stream"          },
  { "cod",        3, "application/vnd.rim.cod"           },
  { "com",        3, "application/x-msdos-program"       },
  { "cpio",       4, "application/x-cpio"                },
  { "cpt",        3, "application/mac-compactpro"        },
  { "csh",        3, "application/x-csh"                 },
  { "css",        3, "text/css"                          },

  { "dcr",        3, "application/x-director"            },
  { "deb",        3, "application/x-debian-package"      },
  { "dir",        3, "application/x-director"            },
  { "dl",         2, "video/dl"                          },
  { "dms",        3, "application/octet-stream"          },
  { "doc",        3, "application/msword"                },
  { "docx",       4, "application/vnd.openxmlformats-"
................................................................................
  { "xlw",        3, "application/vnd.ms-excel"          },
  { "xml",        3, "text/xml"                          },
  { "xpm",        3, "image/x-xpixmap"                   },
  { "xwd",        3, "image/x-xwindowdump"               },
  { "xyz",        3, "chemical/x-pdb"                    },
  { "zip",        3, "application/zip"                   },
};















/*
** Guess the mime-type of a document based on its name.
*/
const char *mimetype_from_name(const char *zName){
  const char *z;
  int i;
................................................................................


#ifdef FOSSIL_DEBUG
  /* This is test code to make sure the table above is in the correct
  ** order
  */
  if( fossil_strcmp(zName, "mimetype-test")==0 ){
    for(i=1; i<ArraySize(aMime); i++){
      if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){
        fossil_fatal("mimetypes out of sequence: %s before %s",
                     aMime[i-1].zSuffix, aMime[i].zSuffix);
      }
    }
    return "ok";
  }
#endif

  z = zName;
  for(i=0; zName[i]; i++){
    if( zName[i]=='.' ) z = &zName[i+1];
................................................................................
**
** If Fossil is compiled with -DFOSSIL_DEBUG then the "mimetype-test"
** filename is special and verifies the integrity of the mimetype table.
** It should return "ok".
*/
void mimetype_test_cmd(void){
  int i;

  for(i=2; i<g.argc; i++){
    fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
  }
}

/*
** WEBPAGE: mimetype_list
**
** Show the built-in table used to guess embedded document mimetypes
** from file suffixes.
*/
void mimetype_list_page(void){
  int i;

  style_header("Mimetype List");
  @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
  @ suffixes and the following table to guess at the appropriate mimetype
  @ for each document.</p>
  @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'>
  @ <thead>
  @ <tr><th>Suffix<th>Mimetype







>







 







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







 







|
<
<
<
<
<







 







>













>







104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
...
321
322
323
324
325
326
327
328





329
330
331
332
333
334
335
...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
  { "class",      5, "application/octet-stream"          },
  { "cod",        3, "application/vnd.rim.cod"           },
  { "com",        3, "application/x-msdos-program"       },
  { "cpio",       4, "application/x-cpio"                },
  { "cpt",        3, "application/mac-compactpro"        },
  { "csh",        3, "application/x-csh"                 },
  { "css",        3, "text/css"                          },
  { "csv",        3, "text/csv"                          },
  { "dcr",        3, "application/x-director"            },
  { "deb",        3, "application/x-debian-package"      },
  { "dir",        3, "application/x-director"            },
  { "dl",         2, "video/dl"                          },
  { "dms",        3, "application/octet-stream"          },
  { "doc",        3, "application/msword"                },
  { "docx",       4, "application/vnd.openxmlformats-"
................................................................................
  { "xlw",        3, "application/vnd.ms-excel"          },
  { "xml",        3, "text/xml"                          },
  { "xpm",        3, "image/x-xpixmap"                   },
  { "xwd",        3, "image/x-xwindowdump"               },
  { "xyz",        3, "chemical/x-pdb"                    },
  { "zip",        3, "application/zip"                   },
};

/*
** Verify that all entries in the aMime[] table are in sorted order.
** Abort with a fatal error if any is out-of-order.
*/
static void mimetype_verify(void){
  int i;
  for(i=1; i<ArraySize(aMime); i++){
    if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){
      fossil_fatal("mimetypes out of sequence: %s before %s",
                   aMime[i-1].zSuffix, aMime[i].zSuffix);
    }
  }
}

/*
** Guess the mime-type of a document based on its name.
*/
const char *mimetype_from_name(const char *zName){
  const char *z;
  int i;
................................................................................


#ifdef FOSSIL_DEBUG
  /* This is test code to make sure the table above is in the correct
  ** order
  */
  if( fossil_strcmp(zName, "mimetype-test")==0 ){
    mimetype_verify();





    return "ok";
  }
#endif

  z = zName;
  for(i=0; zName[i]; i++){
    if( zName[i]=='.' ) z = &zName[i+1];
................................................................................
**
** If Fossil is compiled with -DFOSSIL_DEBUG then the "mimetype-test"
** filename is special and verifies the integrity of the mimetype table.
** It should return "ok".
*/
void mimetype_test_cmd(void){
  int i;
  mimetype_verify();
  for(i=2; i<g.argc; i++){
    fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
  }
}

/*
** WEBPAGE: mimetype_list
**
** Show the built-in table used to guess embedded document mimetypes
** from file suffixes.
*/
void mimetype_list_page(void){
  int i;
  mimetype_verify();
  style_header("Mimetype List");
  @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename
  @ suffixes and the following table to guess at the appropriate mimetype
  @ for each document.</p>
  @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'>
  @ <thead>
  @ <tr><th>Suffix<th>Mimetype

Changes to src/finfo.c.

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
        zOut = mprintf(
           "[%S] %s (user: %s, artifact: [%S], branch: %s)",
           zCiUuid, zCom, zUser, zFileUuid, zBr);
        comment_print(zOut, zCom, 11, iWidth, g.comFmtFlags);
        fossil_free(zOut);
      }else{
        blob_reset(&line);
        blob_appendf(&line, "%.10s ", zCiUuid);
        blob_appendf(&line, "%.10s ", zDate);
        blob_appendf(&line, "%8.8s ", zUser);
        blob_appendf(&line, "%8.8s ", zBr);
        blob_appendf(&line,"%-39.39s", zCom );
        comment_print(blob_str(&line), zCom, 0, iWidth, g.comFmtFlags);
      }
    }







|







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
        zOut = mprintf(
           "[%S] %s (user: %s, artifact: [%S], branch: %s)",
           zCiUuid, zCom, zUser, zFileUuid, zBr);
        comment_print(zOut, zCom, 11, iWidth, g.comFmtFlags);
        fossil_free(zOut);
      }else{
        blob_reset(&line);
        blob_appendf(&line, "%S ", zCiUuid);
        blob_appendf(&line, "%.10s ", zDate);
        blob_appendf(&line, "%8.8s ", zUser);
        blob_appendf(&line, "%8.8s ", zBr);
        blob_appendf(&line,"%-39.39s", zCom );
        comment_print(blob_str(&line), zCom, 0, iWidth, g.comFmtFlags);
      }
    }

Changes to src/info.c.

1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
....
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
      if( zType[0]!='e' ){
        hyperlink_to_uuid(zUuid);
      }
      @ - %!w(zCom) by
      hyperlink_to_user(zUser,zDate," on");
      hyperlink_to_date(zDate, ".");
      if( pDownloadName && blob_size(pDownloadName)==0 ){
        blob_appendf(pDownloadName, "%.10s.txt", zUuid);
      }
      tag_private_status(rid);
      cnt++;
    }
    db_finalize(&q);
  }
  db_prepare(&q,
................................................................................
    }
    tag_private_status(rid);
  }
  db_finalize(&q);
  if( cnt==0 ){
    @ Control artifact.
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_appendf(pDownloadName, "%.10s.txt", zUuid);
    }
    tag_private_status(rid);
  }
  return objType;
}









|







 







|







1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
....
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
      if( zType[0]!='e' ){
        hyperlink_to_uuid(zUuid);
      }
      @ - %!w(zCom) by
      hyperlink_to_user(zUser,zDate," on");
      hyperlink_to_date(zDate, ".");
      if( pDownloadName && blob_size(pDownloadName)==0 ){
        blob_appendf(pDownloadName, "%S.txt", zUuid);
      }
      tag_private_status(rid);
      cnt++;
    }
    db_finalize(&q);
  }
  db_prepare(&q,
................................................................................
    }
    tag_private_status(rid);
  }
  db_finalize(&q);
  if( cnt==0 ){
    @ Control artifact.
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_appendf(pDownloadName, "%S.txt", zUuid);
    }
    tag_private_status(rid);
  }
  return objType;
}


Changes to src/printf.c.

27
28
29
30
31
32
33
34





35
36
37
38
39
40
41



















42
43
44
45
46
47
48
...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
#include <time.h>

/* Two custom conversions are used to show a prefix of SHA1 hashes:
**
**      %!S       Prefix of a length appropriate for URLs
**      %S        Prefix of a length appropriate for human display
**
** The following macros determine those lengths.





*/
#ifndef FOSSIL_SHA1_PREFIX_LEN
# define FOSSIL_SHA1_PREFIX_LEN 10      /* For %S (human display) */
#endif
#ifndef FOSSIL_SHA1_URLPREFIX_LEN
# define FOSSIL_SHA1_URLPREFIX_LEN 16   /* For %!S (embedded in URLs) */
#endif




















/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
#define etRADIX       1 /* Integer types.  %d, %x, %o, and so forth */
#define etFLOAT       2 /* Floating point.  %f */
................................................................................
        int limit = flag_alternateform ? va_arg(ap,int) : -1;
        bufpt = va_arg(ap,char*);
        if( bufpt==0 ){
          bufpt = "";
        }else if( xtype==etDYNSTRING ){
          zExtra = bufpt;
        }else if( xtype==etSTRINGID ){
          precision = 	flag_altform2 ? FOSSIL_SHA1_URLPREFIX_LEN :
                                       FOSSIL_SHA1_PREFIX_LEN;
        }
        length = StrNLen32(bufpt, limit);
        if( precision>=0 && precision<length ) length = precision;
        break;
      }
      case etBLOB: {
        int limit = flag_alternateform ? va_arg(ap, int) : -1;







|
>
>
>
>
>

|
|

|
|

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







 







|
<







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
...
656
657
658
659
660
661
662
663

664
665
666
667
668
669
670
#include <time.h>

/* Two custom conversions are used to show a prefix of SHA1 hashes:
**
**      %!S       Prefix of a length appropriate for URLs
**      %S        Prefix of a length appropriate for human display
**
** The following macros help determine those lengths.  FOSSIL_HASH_DIGITS
** is the default number of digits to display to humans.  This value can
** be overridden using the hash-digits setting.  FOSSIL_HASH_DIGITS_URL
** is the minimum number of digits to be used in URLs.  The number used
** will always be at least 6 more than the number used for human output,
** or 40 if the number of digits in human output is 34 or more.
*/
#ifndef FOSSIL_HASH_DIGITS
# define FOSSIL_HASH_DIGITS 10       /* For %S (human display) */
#endif
#ifndef FOSSIL_HASH_DIGITS_URL
# define FOSSIL_HASH_DIGITS_URL 16   /* For %!S (embedded in URLs) */
#endif

/*
** Return the number of SHA1 hash digits to display.  The number is for
** human output if the bForUrl is false and is destined for a URL if
** bForUrl is false.
*/
static int hashDigits(int bForUrl){
  static int nDigitHuman = 0;
  static int nDigitUrl = 0;
  if( nDigitHuman==0 ){
    nDigitHuman = db_get_int("hash-digits", FOSSIL_HASH_DIGITS);
    if( nDigitHuman < 6 ) nDigitHuman = 6;
    if( nDigitHuman > 40 ) nDigitHuman = 40;
    nDigitUrl = nDigitHuman + 6;
    if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL;
    if( nDigitUrl > 40 ) nDigitUrl = 40;
  }
  return bForUrl ? nDigitUrl : nDigitHuman;
}

/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
#define etRADIX       1 /* Integer types.  %d, %x, %o, and so forth */
#define etFLOAT       2 /* Floating point.  %f */
................................................................................
        int limit = flag_alternateform ? va_arg(ap,int) : -1;
        bufpt = va_arg(ap,char*);
        if( bufpt==0 ){
          bufpt = "";
        }else if( xtype==etDYNSTRING ){
          zExtra = bufpt;
        }else if( xtype==etSTRINGID ){
          precision = 	hashDigits(flag_altform2);

        }
        length = StrNLen32(bufpt, limit);
        if( precision>=0 && precision<length ) length = precision;
        break;
      }
      case etBLOB: {
        int limit = flag_alternateform ? va_arg(ap, int) : -1;

Changes to src/search.c.

991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
** is blank.
*/
static void get_stext_by_mimetype(
  Blob *pIn,
  const char *zMimetype,
  Blob *pOut
){
  Blob html, title, tail;
  blob_init(&html, 0, 0);
  blob_init(&title, 0, 0);
  if( zMimetype==0 ) zMimetype = "text/plain";
  if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
    Blob tail;
    blob_init(&tail, 0, 0);
    if( wiki_find_title(pIn, &title, &tail) ){







|







991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
** is blank.
*/
static void get_stext_by_mimetype(
  Blob *pIn,
  const char *zMimetype,
  Blob *pOut
){
  Blob html, title;
  blob_init(&html, 0, 0);
  blob_init(&title, 0, 0);
  if( zMimetype==0 ) zMimetype = "text/plain";
  if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
    Blob tail;
    blob_init(&tail, 0, 0);
    if( wiki_find_title(pIn, &title, &tail) ){

Changes to src/setup.c.

1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
  db_begin_transaction();
  @ <form action="%R/setup_modreq" method="post"><div>
  login_insert_csrf_secret();
  @ <hr />
  onoff_attribute("Moderate ticket changes",
     "modreq-tkt", "modreq-tkt", 0, 0);
  @ <p>When enabled, any change to tickets is subject to the approval
  @ a ticket moderator - a user with the "q" or Mod-Tkt privilege.
  @ Ticket changes enter the system and are shown locally, but are not
  @ synced until they are approved.  The moderator has the option to
  @ delete the change rather than approve it.  Ticket changes made by
  @ a user who has the Mod-Tkt privilege are never subject to
  @ moderation.
  @
  @ <hr />
  onoff_attribute("Moderate wiki changes",
     "modreq-wiki", "modreq-wiki", 0, 0);
  @ <p>When enabled, any change to wiki is subject to the approval
  @ a ticket moderator - a user with the "l" or Mod-Wiki privilege.
  @ Wiki changes enter the system and are shown locally, but are not
  @ synced until they are approved.  The moderator has the option to
  @ delete the change rather than approve it.  Wiki changes made by
  @ a user who has the Mod-Wiki privilege are never subject to
  @ moderation.
  @ </p>








|










|







1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
  db_begin_transaction();
  @ <form action="%R/setup_modreq" method="post"><div>
  login_insert_csrf_secret();
  @ <hr />
  onoff_attribute("Moderate ticket changes",
     "modreq-tkt", "modreq-tkt", 0, 0);
  @ <p>When enabled, any change to tickets is subject to the approval
  @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege.
  @ Ticket changes enter the system and are shown locally, but are not
  @ synced until they are approved.  The moderator has the option to
  @ delete the change rather than approve it.  Ticket changes made by
  @ a user who has the Mod-Tkt privilege are never subject to
  @ moderation.
  @
  @ <hr />
  onoff_attribute("Moderate wiki changes",
     "modreq-wiki", "modreq-wiki", 0, 0);
  @ <p>When enabled, any change to wiki is subject to the approval
  @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege.
  @ Wiki changes enter the system and are shown locally, but are not
  @ synced until they are approved.  The moderator has the option to
  @ delete the change rather than approve it.  Wiki changes made by
  @ a user who has the Mod-Wiki privilege are never subject to
  @ moderation.
  @ </p>

Changes to src/shell.c.

1740
1741
1742
1743
1744
1745
1746

1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
....
2432
2433
2434
2435
2436
2437
2438













































































































2439
2440
2441
2442
2443
2444
2445
....
2574
2575
2576
2577
2578
2579
2580




2581
2582
2583
2584
2585
2586
2587
....
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
....
2943
2944
2945
2946
2947
2948
2949
2950

2951
2952
2953
2954
2955
2956
2957
....
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
** Text of a help message
*/
static char zHelp[] =
  ".backup ?DB? FILE      Backup DB (default \"main\") to FILE\n"
  ".bail on|off           Stop after hitting an error.  Default OFF\n"
  ".clone NEWDB           Clone data into NEWDB from the existing database\n"
  ".databases             List names and files of attached databases\n"

  ".dump ?TABLE? ...      Dump the database in an SQL text format\n"
  "                         If TABLE specified, only dump tables matching\n"
  "                         LIKE pattern TABLE.\n"
  ".echo on|off           Turn command echo on or off\n"
  ".eqp on|off            Enable or disable automatic EXPLAIN QUERY PLAN\n"
  ".exit                  Exit this program\n"
  ".explain ?on|off?      Turn output mode suitable for EXPLAIN on or off.\n"
  "                         With no args, it turns EXPLAIN on.\n"
  ".fullschema            Show schema and the content of sqlite_stat tables\n"
  ".headers on|off        Turn display of headers on or off\n"
  ".help                  Show this message\n"
  ".import FILE TABLE     Import data from FILE into TABLE\n"
  ".indices ?TABLE?       Show names of all indices\n"
  "                         If TABLE specified, only show indices for tables\n"
  "                         matching LIKE pattern TABLE.\n"
#ifdef SQLITE_ENABLE_IOTRACE
  ".iotrace FILE          Enable I/O diagnostic logging to FILE\n"
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
  ".load FILE ?ENTRY?     Load an extension library\n"
#endif
................................................................................
    pclose(p->out);
  }else{
    output_file_close(p->out);
  }
  p->outfile[0] = 0;
  p->out = stdout;
}














































































































/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
................................................................................
    sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg);
    if( zErrMsg ){
      fprintf(stderr,"Error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
      rc = 1;
    }
  }else





  if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
    open_db(p, 0);
    /* When playing back a "dump", the content might appear in an order
    ** which causes immediate foreign key constraints to be violated.
    ** So disable foreign-key constraint enforcement to prevent problems. */
    if( nArg!=1 && nArg!=2 ){
................................................................................
        */
        if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
        sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
        if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
          fprintf(stderr, "%s:%d: expected %d columns but found %d - "
                          "filling the rest with NULL\n",
                          sCtx.zFile, startLine, nCol, i+1);
          i++;
          while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
        }
      }
      if( sCtx.cTerm==sCtx.cColSep ){
        do{
          xRead(&sCtx);
          i++;
................................................................................

    xCloser(sCtx.in);
    sqlite3_free(sCtx.z);
    sqlite3_finalize(pStmt);
    if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
  }else

  if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){

    ShellState data;
    char *zErrMsg = 0;
    open_db(p, 0);
    memcpy(&data, p, sizeof(data));
    data.showHeader = 0;
    data.mode = MODE_List;
    if( nArg==1 ){
................................................................................
        "SELECT name FROM sqlite_temp_master "
        "WHERE type='index' AND tbl_name LIKE shellstatic() "
        "ORDER BY 1",
        callback, &data, &zErrMsg
      );
      zShellStatic = 0;
    }else{
      fprintf(stderr, "Usage: .indices ?LIKE-PATTERN?\n");
      rc = 1;
      goto meta_command_exit;
    }
    if( zErrMsg ){
      fprintf(stderr,"Error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
      rc = 1;







>












|
|







 







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







 







>
>
>
>







 







|







 







|
>







 







|







1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
....
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
....
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
....
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
....
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
....
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
** Text of a help message
*/
static char zHelp[] =
  ".backup ?DB? FILE      Backup DB (default \"main\") to FILE\n"
  ".bail on|off           Stop after hitting an error.  Default OFF\n"
  ".clone NEWDB           Clone data into NEWDB from the existing database\n"
  ".databases             List names and files of attached databases\n"
  ".dbinfo ?DB?           Show status information about the database\n"
  ".dump ?TABLE? ...      Dump the database in an SQL text format\n"
  "                         If TABLE specified, only dump tables matching\n"
  "                         LIKE pattern TABLE.\n"
  ".echo on|off           Turn command echo on or off\n"
  ".eqp on|off            Enable or disable automatic EXPLAIN QUERY PLAN\n"
  ".exit                  Exit this program\n"
  ".explain ?on|off?      Turn output mode suitable for EXPLAIN on or off.\n"
  "                         With no args, it turns EXPLAIN on.\n"
  ".fullschema            Show schema and the content of sqlite_stat tables\n"
  ".headers on|off        Turn display of headers on or off\n"
  ".help                  Show this message\n"
  ".import FILE TABLE     Import data from FILE into TABLE\n"
  ".indexes ?TABLE?       Show names of all indexes\n"
  "                         If TABLE specified, only show indexes for tables\n"
  "                         matching LIKE pattern TABLE.\n"
#ifdef SQLITE_ENABLE_IOTRACE
  ".iotrace FILE          Enable I/O diagnostic logging to FILE\n"
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
  ".load FILE ?ENTRY?     Load an extension library\n"
#endif
................................................................................
    pclose(p->out);
  }else{
    output_file_close(p->out);
  }
  p->outfile[0] = 0;
  p->out = stdout;
}

/*
** Run an SQL command and return the single integer result.
*/
static int db_int(ShellState *p, const char *zSql){
  sqlite3_stmt *pStmt;
  int res = 0;
  sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
  if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
    res = sqlite3_column_int(pStmt,0);
  }
  sqlite3_finalize(pStmt);
  return res;
}

/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
unsigned int get2byteInt(unsigned char *a){
  return (a[0]<<8) + a[1];
}
unsigned int get4byteInt(unsigned char *a){
  return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
}

/*
** Implementation of the ".info" command.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
  static const struct { const char *zName; int ofst; } aField[] = {
     { "file change counter:",  24  },
     { "database page count:",  28  },
     { "freelist page count:",  36  },
     { "schema cookie:",        40  },
     { "schema format:",        44  },
     { "default cache size:",   48  },
     { "autovacuum top root:",  52  },
     { "incremental vacuum:",   64  },
     { "text encoding:",        56  },
     { "user version:",         60  },
     { "application id:",       68  },
     { "software version:",     96  },
  };
  static const struct { const char *zName; const char *zSql; } aQuery[] = {
     { "number of tables:",
       "SELECT count(*) FROM %s WHERE type='table'" },
     { "number of indexes:",
       "SELECT count(*) FROM %s WHERE type='index'" },
     { "number of triggers:",
       "SELECT count(*) FROM %s WHERE type='trigger'" },
     { "number of views:",
       "SELECT count(*) FROM %s WHERE type='view'" },
     { "schema size:",
       "SELECT total(length(sql)) FROM %s" },
  };
  sqlite3_file *pFile;
  int i;
  char *zSchemaTab;
  char *zDb = nArg>=2 ? azArg[1] : "main";
  unsigned char aHdr[100];
  open_db(p, 0);
  if( p->db==0 ) return 1;
  sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFile);
  if( pFile==0 || pFile->pMethods==0 || pFile->pMethods->xRead==0 ){
    return 1;
  }
  i = pFile->pMethods->xRead(pFile, aHdr, 100, 0);
  if( i!=SQLITE_OK ){
    fprintf(stderr, "unable to read database header\n");
    return 1;
  }
  i = get2byteInt(aHdr+16);
  if( i==1 ) i = 65536;
  fprintf(p->out, "%-20s %d\n", "database page size:", i);
  fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]);
  fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]);
  fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]);
  for(i=0; i<sizeof(aField)/sizeof(aField[0]); i++){
    int ofst = aField[i].ofst;
    unsigned int val = get4byteInt(aHdr + ofst);
    fprintf(p->out, "%-20s %u", aField[i].zName, val);
    switch( ofst ){
      case 56: {
        if( val==1 ) fprintf(p->out, " (utf8)"); 
        if( val==2 ) fprintf(p->out, " (utf16le)"); 
        if( val==3 ) fprintf(p->out, " (utf16be)"); 
      }
    }
    fprintf(p->out, "\n");
  }
  if( zDb==0 ){
    zSchemaTab = sqlite3_mprintf("main.sqlite_master");
  }else if( strcmp(zDb,"temp")==0 ){
    zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_master");
  }else{
    zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_master", zDb);
  }
  for(i=0; i<sizeof(aQuery)/sizeof(aQuery[0]); i++){
    char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
    int val = db_int(p, zSql);
    sqlite3_free(zSql);
    fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
  }
  sqlite3_free(zSchemaTab);
  return 0;
}


/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
................................................................................
    sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg);
    if( zErrMsg ){
      fprintf(stderr,"Error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
      rc = 1;
    }
  }else

  if( c=='d' && strncmp(azArg[0], "dbinfo", n)==0 ){
    rc = shell_dbinfo_command(p, nArg, azArg);
  }else

  if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
    open_db(p, 0);
    /* When playing back a "dump", the content might appear in an order
    ** which causes immediate foreign key constraints to be violated.
    ** So disable foreign-key constraint enforcement to prevent problems. */
    if( nArg!=1 && nArg!=2 ){
................................................................................
        */
        if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
        sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
        if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
          fprintf(stderr, "%s:%d: expected %d columns but found %d - "
                          "filling the rest with NULL\n",
                          sCtx.zFile, startLine, nCol, i+1);
          i += 2;
          while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
        }
      }
      if( sCtx.cTerm==sCtx.cColSep ){
        do{
          xRead(&sCtx);
          i++;
................................................................................

    xCloser(sCtx.in);
    sqlite3_free(sCtx.z);
    sqlite3_finalize(pStmt);
    if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
  }else

  if( c=='i' && (strncmp(azArg[0], "indices", n)==0
                 || strncmp(azArg[0], "indexes", n)==0) ){
    ShellState data;
    char *zErrMsg = 0;
    open_db(p, 0);
    memcpy(&data, p, sizeof(data));
    data.showHeader = 0;
    data.mode = MODE_List;
    if( nArg==1 ){
................................................................................
        "SELECT name FROM sqlite_temp_master "
        "WHERE type='index' AND tbl_name LIKE shellstatic() "
        "ORDER BY 1",
        callback, &data, &zErrMsg
      );
      zShellStatic = 0;
    }else{
      fprintf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
      rc = 1;
      goto meta_command_exit;
    }
    if( zErrMsg ){
      fprintf(stderr,"Error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
      rc = 1;

Changes to src/tkt.c.

426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
static void showAllFields(void){
  int i;
  @ <font color="blue">
  @ <p>Database fields:</p><ul>
  for(i=0; i<nField; i++){
    @ <li>aField[%d(i)].zName = "%h(aField[i].zName)";
    @ originally = "%h(aField[i].zValue)";
    @ currently = "%h(PD(aField[i].zName,""))"";
    if( aField[i].zAppend ){
      @ zAppend = "%h(aField[i].zAppend)";
    }
    @ mUsed = %d(aField[i].mUsed);
  }
  @ </ul></font>
}







|







426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
static void showAllFields(void){
  int i;
  @ <font color="blue">
  @ <p>Database fields:</p><ul>
  for(i=0; i<nField; i++){
    @ <li>aField[%d(i)].zName = "%h(aField[i].zName)";
    @ originally = "%h(aField[i].zValue)";
    @ currently = "%h(PD(aField[i].zName,""))";
    if( aField[i].zAppend ){
      @ zAppend = "%h(aField[i].zAppend)";
    }
    @ mUsed = %d(aField[i].mUsed);
  }
  @ </ul></font>
}