Fossil

Check-in [1fd9acb97c]
Login

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

Overview
Comment:Merged in UUID-renaming changes from trunk.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | ui-local-diff
Files: files | file ages | folders
SHA3-256: 1fd9acb97c6dc73a56cda356b5bb5e5a89e18a5f7915b242a54252f3905ebe1d
User & Date: graham 2020-05-30 19:32:34.926
Context
2020-05-30
22:22
Distinguish local-changes from changes from a merge. check-in: db758a3454 user: graham tags: ui-local-diff
19:32
Merged in UUID-renaming changes from trunk. check-in: 1fd9acb97c user: graham tags: ui-local-diff
16:51
Added "& server" to the "native" row in the characterization table at the top of fossil-v-git.wiki. check-in: 6c04e42f76 user: wyoung tags: trunk
16:15
Make two passes through list of local changes so all "one-line" entries are listed first. check-in: bc56945ea4 user: graham tags: ui-local-diff
Changes
Unified Diff Ignore Whitespace Patch
Changes to skins/blitz/ticket.txt.
1
2
3
4
5
6
7
8
9
10
<h4>$<title></h4>
<table class="tktDsp">
<tr><td class="tktDspLabel">Ticket&nbsp;UUID</td>
<th1>
if {[info exists tkt_uuid]} {
  if {[hascap s]} {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid "
    html "($tkt_id)</td></tr>\n"
  } else {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n"


|







1
2
3
4
5
6
7
8
9
10
<h4>$<title></h4>
<table class="tktDsp">
<tr><td class="tktDspLabel">Ticket&nbsp;Hash</td>
<th1>
if {[info exists tkt_uuid]} {
  if {[hascap s]} {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid "
    html "($tkt_id)</td></tr>\n"
  } else {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n"
Changes to skins/blitz_no_logo/ticket.txt.
1
2
3
4
5
6
7
8
9
10
<h4>$<title></h4>
<table class="tktDsp">
<tr><td class="tktDspLabel">Ticket&nbsp;UUID</td>
<th1>
if {[info exists tkt_uuid]} {
  if {[hascap s]} {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid "
    html "($tkt_id)</td></tr>\n"
  } else {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n"


|







1
2
3
4
5
6
7
8
9
10
<h4>$<title></h4>
<table class="tktDsp">
<tr><td class="tktDspLabel">Ticket&nbsp;Hash</td>
<th1>
if {[info exists tkt_uuid]} {
  if {[hascap s]} {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid "
    html "($tkt_id)</td></tr>\n"
  } else {
    html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n"
Changes to src/attach.c.
21
22
23
24
25
26
27
28
29

30
31

32
33

34
35
36
37
38
39
40
41
42
43
#include "attach.h"
#include <assert.h>

/*
** WEBPAGE: attachlist
** List attachments.
**
**    tkt=TICKETUUID
**    page=WIKIPAGE

**
** At most one of technote=, tkt= or page= are supplied.

** If none is given, all attachments are listed.  If one is given,
** only attachments for the designated technote, ticket or wiki page

** are shown. TECHNOTEUUID and TICKETUUID may be just a prefix of the
** relevant technical note or ticket, in which case all attachments
** of all technical notes or tickets with the prefix will be listed.
*/
void attachlist_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zTechNote = P("technote");
  Blob sql;
  Stmt q;







|

>

|
>
|
|
>
|
|
|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "attach.h"
#include <assert.h>

/*
** WEBPAGE: attachlist
** List attachments.
**
**    tkt=HASH
**    page=WIKIPAGE
**    technote=HASH
**
** At most one of technote=, tkt= or page= may be supplied.
**
** If none are given, all attachments are listed.  If one is given, only
** attachments for the designated technote, ticket or wiki page are shown.
**
** HASH may be just a prefix of the relevant technical note or ticket
** artifact hash, in which case all attachments of all technical notes or
** tickets with the prefix will be listed.
*/
void attachlist_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zTechNote = P("technote");
  Blob sql;
  Stmt q;
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168

/*
** WEBPAGE: attachdownload
** WEBPAGE: attachimage
** WEBPAGE: attachview
**
** Download or display an attachment.

** Query parameters:
**
**    tkt=TICKETUUID
**    page=WIKIPAGE
**    technote=TECHNOTEUUID
**    file=FILENAME
**    attachid=ID
**
*/
void attachview_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");







>


|

|







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

/*
** WEBPAGE: attachdownload
** WEBPAGE: attachimage
** WEBPAGE: attachview
**
** Download or display an attachment.
**
** Query parameters:
**
**    tkt=HASH
**    page=WIKIPAGE
**    technote=HASH
**    file=FILENAME
**    attachid=ID
**
*/
void attachview_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262


/*
** Commit a new attachment into the repository
*/
void attach_commit(
  const char *zName,                   /* The filename of the attachment */
  const char *zTarget,                 /* The artifact uuid to attach to */
  const char *aContent,                /* The content of the attachment */
  int         szContent,               /* The length of the attachment */
  int         needModerator,           /* Moderate the attachment? */
  const char *zComment                 /* The comment for the attachment */
){
    Blob content;
    Blob manifest;







|







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266


/*
** Commit a new attachment into the repository
*/
void attach_commit(
  const char *zName,                   /* The filename of the attachment */
  const char *zTarget,                 /* The artifact hash to attach to */
  const char *aContent,                /* The content of the attachment */
  int         szContent,               /* The length of the attachment */
  int         needModerator,           /* Moderate the attachment? */
  const char *zComment                 /* The comment for the attachment */
){
    Blob content;
    Blob manifest;
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
    db_end_transaction(0);
}

/*
** WEBPAGE: attachadd
** Add a new attachment.
**
**    tkt=TICKETUUID
**    page=WIKIPAGE
**    technote=TECHNOTEUUID
**    from=URL
**
*/
void attachadd_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zTechNote = P("technote");







|

|







307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    db_end_transaction(0);
}

/*
** WEBPAGE: attachadd
** Add a new attachment.
**
**    tkt=HASH
**    page=WIKIPAGE
**    technote=HASH
**    from=URL
**
*/
void attachadd_page(void){
  const char *zPage = P("page");
  const char *zTkt = P("tkt");
  const char *zTechNote = P("technote");
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
**
** Show the details of an attachment artifact.
*/
void ainfo_page(void){
  int rid;                       /* RID for the control artifact */
  int ridSrc;                    /* RID for the attached file */
  char *zDate;                   /* Date attached */
  const char *zUuid;             /* UUID of the control artifact */
  Manifest *pAttach;             /* Parse of the control artifact */
  const char *zTarget;           /* Wiki, ticket or tech note attached to */
  const char *zSrc;              /* UUID of the attached file */
  const char *zName;             /* Name of the attached file */
  const char *zDesc;             /* Description of the attached file */
  const char *zWikiName = 0;     /* Wiki page name when attached to Wiki */
  const char *zTNUuid = 0;       /* Tech Note ID when attached to tech note */
  const char *zTktUuid = 0;      /* Ticket ID when attached to a ticket */
  int modPending;                /* True if awaiting moderation */
  const char *zModAction;        /* Moderation action or NULL */







|


|







422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
**
** Show the details of an attachment artifact.
*/
void ainfo_page(void){
  int rid;                       /* RID for the control artifact */
  int ridSrc;                    /* RID for the attached file */
  char *zDate;                   /* Date attached */
  const char *zUuid;             /* Hash of the control artifact */
  Manifest *pAttach;             /* Parse of the control artifact */
  const char *zTarget;           /* Wiki, ticket or tech note attached to */
  const char *zSrc;              /* Hash of the attached file */
  const char *zName;             /* Name of the attached file */
  const char *zDesc;             /* Description of the attached file */
  const char *zWikiName = 0;     /* Wiki page name when attached to Wiki */
  const char *zTNUuid = 0;       /* Tech Note ID when attached to tech note */
  const char *zTktUuid = 0;      /* Ticket ID when attached to a ticket */
  int modPending;                /* True if awaiting moderation */
  const char *zModAction;        /* Moderation action or NULL */
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
      );
      zFile = g.argv[3];
    }
    blob_read_from_file(&content, zFile, ExtFILE);
    user_select();
    attach_commit(
      zFile,                   /* The filename of the attachment */
      zTarget,                 /* The artifact uuid to attach to */
      blob_buffer(&content),   /* The content of the attachment */
      blob_size(&content),     /* The length of the attachment */
      0,                       /* No need to moderate the attachment */
      ""                       /* Empty attachment comment */
    );
    if( !zETime ){
      fossil_print("Attached %s to wiki page %s.\n", zFile, zPageName);







|







765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
      );
      zFile = g.argv[3];
    }
    blob_read_from_file(&content, zFile, ExtFILE);
    user_select();
    attach_commit(
      zFile,                   /* The filename of the attachment */
      zTarget,                 /* The artifact hash to attach to */
      blob_buffer(&content),   /* The content of the attachment */
      blob_size(&content),     /* The length of the attachment */
      0,                       /* No need to moderate the attachment */
      ""                       /* Empty attachment comment */
    );
    if( !zETime ){
      fossil_print("Attached %s to wiki page %s.\n", zFile, zPageName);
Changes to src/blob.c.
480
481
482
483
484
485
486





487
488


489
490
491
492
493
494
495
496
  pBlob->aData[newSize] = 0;
}

/*
** Ensures that the given blob has at least the given amount of memory
** allocated to it. Does not modify pBlob->nUsed nor will it reduce
** the currently-allocated amount of memory.





*/
void blob_reserve(Blob *pBlob, unsigned int newSize){


  if(newSize>pBlob->nUsed){
    pBlob->xRealloc(pBlob, newSize);
    pBlob->aData[newSize] = 0;
  }
}

/*
** Make sure a blob is nul-terminated and is not a pointer to unmanaged







>
>
>
>
>


>
>
|







480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
  pBlob->aData[newSize] = 0;
}

/*
** Ensures that the given blob has at least the given amount of memory
** allocated to it. Does not modify pBlob->nUsed nor will it reduce
** the currently-allocated amount of memory.
**
** For semantic compatibility with blob_append_full(), if newSize is
** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If
** it didn't, it would be possible to bypass that hard-coded limit via
** this function.
*/
void blob_reserve(Blob *pBlob, unsigned int newSize){
  if(newSize>=0x7fff0000 ){
    blob_panic();
  }else if(newSize>pBlob->nUsed){
    pBlob->xRealloc(pBlob, newSize);
    pBlob->aData[newSize] = 0;
  }
}

/*
** Make sure a blob is nul-terminated and is not a pointer to unmanaged
Changes to src/bundle.c.
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
    fossil_fatal("incorrect hash for artifact %b", &h1);
  }
  blob_reset(&h1);
  bag_remove(&busy, blobid);
  db_finalize(&q);
}

/* fossil bundle cat BUNDLE UUID...
**
** Write elements of a bundle on standard output
*/
static void bundle_cat_cmd(void){
  int i;
  Blob x;
  verify_all_options();
  if( g.argc<5 ) usage("cat BUNDLE UUID...");
  bundle_attach_file(g.argv[3], "b1", 1);
  blob_zero(&x);
  for(i=4; i<g.argc; i++){
    int blobid = db_int(0,"SELECT blobid FROM bblob WHERE uuid LIKE '%q%%'",
                        g.argv[i]);
    if( blobid==0 ){
      fossil_fatal("no such artifact in bundle: %s", g.argv[i]);







|







|







528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
    fossil_fatal("incorrect hash for artifact %b", &h1);
  }
  blob_reset(&h1);
  bag_remove(&busy, blobid);
  db_finalize(&q);
}

/* fossil bundle cat BUNDLE HASH...
**
** Write elements of a bundle on standard output
*/
static void bundle_cat_cmd(void){
  int i;
  Blob x;
  verify_all_options();
  if( g.argc<5 ) usage("cat BUNDLE HASH...");
  bundle_attach_file(g.argv[3], "b1", 1);
  blob_zero(&x);
  for(i=4; i<g.argc; i++){
    int blobid = db_int(0,"SELECT blobid FROM bblob WHERE uuid LIKE '%q%%'",
                        g.argv[i]);
    if( blobid==0 ){
      fossil_fatal("no such artifact in bundle: %s", g.argv[i]);
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
** Usage: %fossil bundle SUBCOMMAND ARGS...
**
**   fossil bundle append BUNDLE FILE...
**
**      Add files named on the command line to BUNDLE.  This subcommand has
**      little practical use and is mostly intended for testing.
**
**   fossil bundle cat BUNDLE UUID...
**
**      Extract one or more artifacts from the bundle and write them
**      consecutively on standard output.  This subcommand was designed
**      for testing and introspection of bundles and is not something
**      commonly used.
**
**   fossil bundle export BUNDLE ?OPTIONS?







|







723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
** Usage: %fossil bundle SUBCOMMAND ARGS...
**
**   fossil bundle append BUNDLE FILE...
**
**      Add files named on the command line to BUNDLE.  This subcommand has
**      little practical use and is mostly intended for testing.
**
**   fossil bundle cat BUNDLE HASH...
**
**      Extract one or more artifacts from the bundle and write them
**      consecutively on standard output.  This subcommand was designed
**      for testing and introspection of bundles and is not something
**      commonly used.
**
**   fossil bundle export BUNDLE ?OPTIONS?
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
**
**      Remove from the repository all files that are used exclusively
**      by check-ins in BUNDLE.  This has the effect of undoing a
**      "fossil bundle import".
**
** SUMMARY:
**   fossil bundle append BUNDLE FILE...              Add files to BUNDLE
**   fossil bundle cat BUNDLE UUID...                 Extract file from BUNDLE
**   fossil bundle export BUNDLE ?OPTIONS?            Create a new BUNDLE
**          --branch BRANCH --from TAG1 --to TAG2       Check-ins to include
**          --checkin TAG                               Use only check-in TAG
**          --standalone                                Omit dependencies
**   fossil bundle extend BUNDLE                      Update with newer content
**   fossil bundle import BUNDLE ?OPTIONS?            Import a bundle
**          --publish                                   Publish the import







|







768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
**
**      Remove from the repository all files that are used exclusively
**      by check-ins in BUNDLE.  This has the effect of undoing a
**      "fossil bundle import".
**
** SUMMARY:
**   fossil bundle append BUNDLE FILE...              Add files to BUNDLE
**   fossil bundle cat BUNDLE HASH...                 Extract file from BUNDLE
**   fossil bundle export BUNDLE ?OPTIONS?            Create a new BUNDLE
**          --branch BRANCH --from TAG1 --to TAG2       Check-ins to include
**          --checkin TAG                               Use only check-in TAG
**          --standalone                                Omit dependencies
**   fossil bundle extend BUNDLE                      Update with newer content
**   fossil bundle import BUNDLE ?OPTIONS?            Import a bundle
**          --publish                                   Publish the import
Changes to src/checkin.c.
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436

/*
** Make sure the current check-in with timestamp zDate is younger than its
** ancestor identified rid and zUuid.  Throw a fatal error if not.
*/
static void checkin_verify_younger(
  int rid,              /* The record ID of the ancestor */
  const char *zUuid,    /* The artifact ID of the ancestor */
  const char *zDate     /* Date & time of the current check-in */
){
#ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
  if(checkin_is_younger(rid,zDate)==0){
    fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)"
                 " Use --allow-older to override.", zUuid, zDate);
  }







|







1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436

/*
** Make sure the current check-in with timestamp zDate is younger than its
** ancestor identified rid and zUuid.  Throw a fatal error if not.
*/
static void checkin_verify_younger(
  int rid,              /* The record ID of the ancestor */
  const char *zUuid,    /* The artifact hash of the ancestor */
  const char *zDate     /* Date & time of the current check-in */
){
#ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
  if(checkin_is_younger(rid,zDate)==0){
    fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)"
                 " Use --allow-older to override.", zUuid, zDate);
  }
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
#endif /* INTERFACE */

/*
** Create a manifest.
*/
static void create_manifest(
  Blob *pOut,                 /* Write the manifest here */
  const char *zBaselineUuid,  /* UUID of baseline, or zero */
  Manifest *pBaseline,        /* Make it a delta manifest if not zero */
  int vid,                    /* BLOB.id for the parent check-in */
  CheckinInfo *p,             /* Information about the check-in */
  int *pnFBcard               /* OUT: Number of generated B- and F-cards */
){
  char *zDate;                /* Date of the check-in */
  char *zParentUuid = 0;      /* UUID of parent check-in */
  Blob filename;              /* A single filename */
  int nBasename;              /* Size of base filename */
  Stmt q;                     /* Various queries */
  Blob mcksum;                /* Manifest checksum */
  ManifestFile *pFile;        /* File from the baseline */
  int nFBcard = 0;            /* Number of B-cards and F-cards */
  int i;                      /* Loop counter */







|






|







1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
#endif /* INTERFACE */

/*
** Create a manifest.
*/
static void create_manifest(
  Blob *pOut,                 /* Write the manifest here */
  const char *zBaselineUuid,  /* Hash of baseline, or zero */
  Manifest *pBaseline,        /* Make it a delta manifest if not zero */
  int vid,                    /* BLOB.id for the parent check-in */
  CheckinInfo *p,             /* Information about the check-in */
  int *pnFBcard               /* OUT: Number of generated B- and F-cards */
){
  char *zDate;                /* Date of the check-in */
  char *zParentUuid = 0;      /* Hash of parent check-in */
  Blob filename;              /* A single filename */
  int nBasename;              /* Size of base filename */
  Stmt q;                     /* Various queries */
  Blob mcksum;                /* Manifest checksum */
  ManifestFile *pFile;        /* File from the baseline */
  int nFBcard = 0;            /* Number of B-cards and F-cards */
  int i;                      /* Loop counter */
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
  int hasChanges;        /* True if unsaved changes exist */
  int vid;               /* blob-id of parent version */
  int nrid;              /* blob-id of a modified file */
  int nvid;              /* Blob-id of the new check-in */
  Blob comment;          /* Check-in comment */
  const char *zComment;  /* Check-in comment */
  Stmt q;                /* Various queries */
  char *zUuid;           /* UUID of the new check-in */
  int useHash = 0;       /* True to verify file status using hashing */
  int noSign = 0;        /* True to omit signing the manifest using GPG */
  int privateFlag = 0;   /* True if the --private option is present */
  int privateParent = 0; /* True if the parent check-in is private */
  int isAMerge = 0;      /* True if checking in a merge */
  int noWarningFlag = 0; /* True if skipping all warnings */
  int noPrompt = 0;      /* True if skipping all prompts */







|







2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
  int hasChanges;        /* True if unsaved changes exist */
  int vid;               /* blob-id of parent version */
  int nrid;              /* blob-id of a modified file */
  int nvid;              /* Blob-id of the new check-in */
  Blob comment;          /* Check-in comment */
  const char *zComment;  /* Check-in comment */
  Stmt q;                /* Various queries */
  char *zUuid;           /* Hash of the new check-in */
  int useHash = 0;       /* True to verify file status using hashing */
  int noSign = 0;        /* True to omit signing the manifest using GPG */
  int privateFlag = 0;   /* True if the --private option is present */
  int privateParent = 0; /* True if the parent check-in is private */
  int isAMerge = 0;      /* True if checking in a merge */
  int noWarningFlag = 0; /* True if skipping all warnings */
  int noPrompt = 0;      /* True if skipping all prompts */
Changes to src/checkout.c.
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  );
  fossil_free(zPwd);
  db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
}


/*
** Given the abbreviated UUID name of a version, load the content of that
** version in the VFILE table.  Return the VID for the version.
**
** If anything goes wrong, panic.
*/
int load_vfile(const char *zName, int forceMissingFlag){
  Blob uuid;
  int vid;







|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  );
  fossil_free(zPwd);
  db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
}


/*
** Given the abbreviated hash of a version, load the content of that
** version in the VFILE table.  Return the VID for the version.
**
** If anything goes wrong, panic.
*/
int load_vfile(const char *zName, int forceMissingFlag){
  Blob uuid;
  int vid;
Changes to src/codecheck1.c.
341
342
343
344
345
346
347
348
349
350
351
352
353
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
381
382
383
384
385


386
387
388
389
390
391
392
393
394
395
396
397
398
399



400
401
402
403
404
405
406
407
408
409









410
411
412
413
414
415
416
  if( strncmp(z,"cgi_param",9)==0 ) return 1;
  return 0;
}

/*
** Processing flags
*/
#define FMT_SQL   0x00001     /* Generates SQL text */
#define FMT_HTML  0x00002     /* Generates HTML text */
#define FMT_URL   0x00004     /* Generates URLs */
#define FMT_SAFE  0x00008     /* Always safe for %s */

/*
** A list of internal Fossil interfaces that take a printf-style format
** string.
*/
struct {
  const char *zFName;    /* Name of the function */
  int iFmtArg;           /* Index of format argument.  Leftmost is 1. */
  unsigned fmtFlags;     /* Processing flags */
} aFmtFunc[] = {
  { "admin_log",               1, 0 },


  { "blob_append_sql",         2, FMT_SQL },
  { "blob_appendf",            2, 0 },
  { "cgi_debug",               1, FMT_SAFE },
  { "cgi_panic",               1, FMT_SAFE },
  { "cgi_printf",              1, FMT_HTML },

  { "cgi_redirectf",           1, FMT_URL },
  { "chref",                   2, FMT_URL },

  { "db_blob",                 2, FMT_SQL },
  { "db_debug",                1, FMT_SQL },
  { "db_double",               2, FMT_SQL },
  { "db_err",                  1, 0 },
  { "db_exists",               1, FMT_SQL },
  { "db_get_mprintf",          2, 0 },
  { "db_int",                  2, FMT_SQL },
  { "db_int64",                2, FMT_SQL },
  { "db_multi_exec",           1, FMT_SQL },
  { "db_optional_sql",         2, FMT_SQL },
  { "db_prepare",              2, FMT_SQL },
  { "db_prepare_ignore_error", 2, FMT_SQL },
  { "db_set_mprintf",          3, 0 },
  { "db_static_prepare",       2, FMT_SQL },
  { "db_text",                 2, FMT_SQL },
  { "db_unset_mprintf",        2, 0 },


  { "form_begin",              2, FMT_URL },
  { "fossil_error",            2, FMT_SAFE },
  { "fossil_errorlog",         1, FMT_SAFE },
  { "fossil_fatal",            1, FMT_SAFE },
  { "fossil_fatal_recursive",  1, FMT_SAFE },
  { "fossil_panic",            1, FMT_SAFE },
  { "fossil_print",            1, FMT_SAFE },
  { "fossil_trace",            1, FMT_SAFE },
  { "fossil_warning",          1, FMT_SAFE },
  { "href",                    1, FMT_URL },
  { "json_new_string_f",       1, 0 },
  { "json_set_err",            2, 0 },
  { "json_warn",               2, 0 },
  { "mprintf",                 1, 0 },



  { "socket_set_errmsg",       1, 0 },
  { "ssl_set_errmsg",          1, 0 },
  { "style_header",            1, FMT_HTML },
  { "style_js_onload",         1, FMT_HTML },
  { "style_set_current_page",  1, FMT_URL },
  { "style_submenu_element",   2, FMT_URL },
  { "style_submenu_sql",       3, FMT_SQL },
  { "webpage_error",           1, FMT_SAFE },
  { "xhref",                   2, FMT_URL },
};










/*
** Determine if the indentifier zIdent of length nIndent is a Fossil
** internal interface that uses a printf-style argument.  Return zero if not.
** Return the index of the format string if true with the left-most
** argument having an index of 1.
*/







|
|
|
|





|




|
>
>

|



>


>



|

|






|


|
>
>










|
|
|
|
>
>
>
|
|








>
>
>
>
>
>
>
>
>







341
342
343
344
345
346
347
348
349
350
351
352
353
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  if( strncmp(z,"cgi_param",9)==0 ) return 1;
  return 0;
}

/*
** Processing flags
*/
#define FMT_SQL   0x00001     /* Generator for SQL text */
#define FMT_HTML  0x00002     /* Generator for HTML text */
#define FMT_URL   0x00004     /* Generator for URLs */
#define FMT_SAFE  0x00008     /* Generator for human-readable text */

/*
** A list of internal Fossil interfaces that take a printf-style format
** string.
*/
struct FmtFunc {
  const char *zFName;    /* Name of the function */
  int iFmtArg;           /* Index of format argument.  Leftmost is 1. */
  unsigned fmtFlags;     /* Processing flags */
} aFmtFunc[] = {
  { "admin_log",               1, FMT_SAFE },
  { "audit_append",            3, FMT_SAFE },
  { "backofficeTrace",         1, FMT_SAFE },
  { "blob_append_sql",         2, FMT_SQL },
  { "blob_appendf",            2, FMT_SAFE },
  { "cgi_debug",               1, FMT_SAFE },
  { "cgi_panic",               1, FMT_SAFE },
  { "cgi_printf",              1, FMT_HTML },
  { "cgi_printf_header",       1, FMT_HTML },
  { "cgi_redirectf",           1, FMT_URL },
  { "chref",                   2, FMT_URL },
  { "CX",                      1, FMT_HTML },
  { "db_blob",                 2, FMT_SQL },
  { "db_debug",                1, FMT_SQL },
  { "db_double",               2, FMT_SQL },
  { "db_err",                  1, FMT_SAFE },
  { "db_exists",               1, FMT_SQL },
  { "db_get_mprintf",          2, FMT_SAFE },
  { "db_int",                  2, FMT_SQL },
  { "db_int64",                2, FMT_SQL },
  { "db_multi_exec",           1, FMT_SQL },
  { "db_optional_sql",         2, FMT_SQL },
  { "db_prepare",              2, FMT_SQL },
  { "db_prepare_ignore_error", 2, FMT_SQL },
  { "db_set_mprintf",          3, FMT_SAFE },
  { "db_static_prepare",       2, FMT_SQL },
  { "db_text",                 2, FMT_SQL },
  { "db_unset_mprintf",        2, FMT_SAFE },
  { "emailerError",            2, FMT_SAFE },
  { "fileedit_ajax_error",     2, FMT_SAFE },
  { "form_begin",              2, FMT_URL },
  { "fossil_error",            2, FMT_SAFE },
  { "fossil_errorlog",         1, FMT_SAFE },
  { "fossil_fatal",            1, FMT_SAFE },
  { "fossil_fatal_recursive",  1, FMT_SAFE },
  { "fossil_panic",            1, FMT_SAFE },
  { "fossil_print",            1, FMT_SAFE },
  { "fossil_trace",            1, FMT_SAFE },
  { "fossil_warning",          1, FMT_SAFE },
  { "href",                    1, FMT_URL },
  { "json_new_string_f",       1, FMT_SAFE },
  { "json_set_err",            2, FMT_SAFE },
  { "json_warn",               2, FMT_SAFE },
  { "mprintf",                 1, FMT_SAFE },
  { "pop3_print",              2, FMT_SAFE },
  { "smtp_send_line",          2, FMT_SAFE },
  { "smtp_server_send",        2, FMT_SAFE },
  { "socket_set_errmsg",       1, FMT_SAFE },
  { "ssl_set_errmsg",          1, FMT_SAFE },
  { "style_header",            1, FMT_HTML },
  { "style_js_onload",         1, FMT_HTML },
  { "style_set_current_page",  1, FMT_URL },
  { "style_submenu_element",   2, FMT_URL },
  { "style_submenu_sql",       3, FMT_SQL },
  { "webpage_error",           1, FMT_SAFE },
  { "xhref",                   2, FMT_URL },
};

/*
** Comparison function for two FmtFunc entries
*/
static int fmtfunc_cmp(const void *pAA, const void *pBB){
  const struct FmtFunc *pA = (const struct FmtFunc*)pAA;
  const struct FmtFunc *pB = (const struct FmtFunc*)pBB;
  return strcmp(pA->zFName, pB->zFName);
}

/*
** Determine if the indentifier zIdent of length nIndent is a Fossil
** internal interface that uses a printf-style argument.  Return zero if not.
** Return the index of the format string if true with the left-most
** argument having an index of 1.
*/
623
624
625
626
627
628
629


630
631
632
633
634
635
636
637
638
639
640
641
642
** on the command-line.
**
** The eVerbose global variable is incremented with each "-v" argument.
*/
int main(int argc, char **argv){
  int i;
  int nErr = 0;


  for(i=1; i<argc; i++){
    char *zFile;
    if( strcmp(argv[i],"-v")==0 ){
      eVerbose++;
      continue;
    }
    if( eVerbose>0 ) printf("Processing %s...\n", argv[i]);
    zFile = read_file(argv[i]);
    nErr += scan_file(argv[i], zFile);
    free(zFile);
  }
  return nErr;
}







>
>













641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
** on the command-line.
**
** The eVerbose global variable is incremented with each "-v" argument.
*/
int main(int argc, char **argv){
  int i;
  int nErr = 0;
  qsort(aFmtFunc, sizeof(aFmtFunc)/sizeof(aFmtFunc[0]),
        sizeof(aFmtFunc[0]), fmtfunc_cmp);
  for(i=1; i<argc; i++){
    char *zFile;
    if( strcmp(argv[i],"-v")==0 ){
      eVerbose++;
      continue;
    }
    if( eVerbose>0 ) printf("Processing %s...\n", argv[i]);
    zFile = read_file(argv[i]);
    nErr += scan_file(argv[i], zFile);
    free(zFile);
  }
  return nErr;
}
Changes to src/configure.c.
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  { "dotfiles",               CONFIGSET_PROJ },
  { "parent-project-code",    CONFIGSET_PROJ },
  { "parent-project-name",    CONFIGSET_PROJ },
  { "hash-policy",            CONFIGSET_PROJ },
  { "comment-format",         CONFIGSET_PROJ },
  { "mimetypes",              CONFIGSET_PROJ },
  { "forbid-delta-manifests", CONFIGSET_PROJ },
  { "fileedit-glob",          CONFIGSET_PROJ },

#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
  { "mv-rm-files",            CONFIGSET_PROJ },
#endif

  { "ticket-table",           CONFIGSET_TKT  },
  { "ticket-common",          CONFIGSET_TKT  },







<







146
147
148
149
150
151
152

153
154
155
156
157
158
159
  { "dotfiles",               CONFIGSET_PROJ },
  { "parent-project-code",    CONFIGSET_PROJ },
  { "parent-project-name",    CONFIGSET_PROJ },
  { "hash-policy",            CONFIGSET_PROJ },
  { "comment-format",         CONFIGSET_PROJ },
  { "mimetypes",              CONFIGSET_PROJ },
  { "forbid-delta-manifests", CONFIGSET_PROJ },


#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
  { "mv-rm-files",            CONFIGSET_PROJ },
#endif

  { "ticket-table",           CONFIGSET_TKT  },
  { "ticket-common",          CONFIGSET_TKT  },
Changes to src/content.c.
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
      ** have no data with which to dephantomize it.  In either case,
      ** there is nothing for us to do other than return the RID. */
      db_finalize(&s1);
      db_end_transaction(0);
      return rid;
    }
  }else{
    rid = 0;  /* No entry with the same UUID currently exists */
    markAsUnclustered = 1;
  }
  db_finalize(&s1);

  /* Construct a received-from ID if we do not already have one */
  content_rcvid_init(0);








|







562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
      ** have no data with which to dephantomize it.  In either case,
      ** there is nothing for us to do other than return the RID. */
      db_finalize(&s1);
      db_end_transaction(0);
      return rid;
    }
  }else{
    rid = 0;  /* No entry with the same hash currently exists */
    markAsUnclustered = 1;
  }
  db_finalize(&s1);

  /* Construct a received-from ID if we do not already have one */
  content_rcvid_init(0);

657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
*/
int content_put(Blob *pBlob){
  return content_put_ex(pBlob, 0, 0, 0, 0);
}


/*
** Create a new phantom with the given UUID and return its artifact ID.
*/
int content_new(const char *zUuid, int isPrivate){
  int rid;
  static Stmt s1, s2, s3;

  assert( g.repositoryOpen );
  db_begin_transaction();







|







657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
*/
int content_put(Blob *pBlob){
  return content_put_ex(pBlob, 0, 0, 0, 0);
}


/*
** Create a new phantom with the given hash and return its artifact ID.
*/
int content_new(const char *zUuid, int isPrivate){
  int rid;
  static Stmt s1, s2, s3;

  assert( g.repositoryOpen );
  db_begin_transaction();
1108
1109
1110
1111
1112
1113
1114
1115
1116

1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
}

/* Allowed flags for check_exists */
#define MISSING_SHUNNED   0x0001    /* Do not report shunned artifacts */

/* This is a helper routine for test-artifacts.
**
** Check to see that artifact zUuid exists in the repository.  If it does,
** return 0.  If it does not, generate an error message and return 1.

*/
static int check_exists(
  const char *zUuid,     /* The artifact we are checking for */
  unsigned flags,        /* Flags */
  Manifest *p,           /* The control artifact that references zUuid */
  const char *zRole,     /* Role of zUuid in p */
  const char *zDetail    /* Additional information, such as a filename */
){
  static Stmt q;
  int rc = 0;







|
|
>


|







1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
}

/* Allowed flags for check_exists */
#define MISSING_SHUNNED   0x0001    /* Do not report shunned artifacts */

/* This is a helper routine for test-artifacts.
**
** Check to see that the artifact hash referenced by zUuid exists in the
** repository.  If it does, return 0.  If it does not, generate an error
** message and return 1.
*/
static int check_exists(
  const char *zUuid,     /* Hash of the artifact we are checking for */
  unsigned flags,        /* Flags */
  Manifest *p,           /* The control artifact that references zUuid */
  const char *zRole,     /* Role of zUuid in p */
  const char *zDetail    /* Additional information, such as a filename */
){
  static Stmt q;
  int rc = 0;
Changes to src/event.c.
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
**  v=BOOLEAN         Show details if TRUE.  Default is FALSE.  Optional.
**
** Display an existing tech-note identified by its ID, optionally at a
** specific version, and optionally with additional details.
*/
void event_page(void){
  int rid = 0;             /* rid of the event artifact */
  char *zUuid;             /* UUID corresponding to rid */
  const char *zId;         /* Event identifier */
  const char *zVerbose;    /* Value of verbose option */
  char *zETime;            /* Time of the tech-note */
  char *zATime;            /* Time the artifact was created */
  int specRid;             /* rid specified by aid= parameter */
  int prevRid, nextRid;    /* Previous or next edits of this tech-note */
  Manifest *pTNote;        /* Parsed technote artifact */







|







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
**  v=BOOLEAN         Show details if TRUE.  Default is FALSE.  Optional.
**
** Display an existing tech-note identified by its ID, optionally at a
** specific version, and optionally with additional details.
*/
void event_page(void){
  int rid = 0;             /* rid of the event artifact */
  char *zUuid;             /* artifact hash corresponding to rid */
  const char *zId;         /* Event identifier */
  const char *zVerbose;    /* Value of verbose option */
  char *zETime;            /* Time of the tech-note */
  char *zATime;            /* Time the artifact was created */
  int specRid;             /* rid specified by aid= parameter */
  int prevRid, nextRid;    /* Previous or next edits of this tech-note */
  Manifest *pTNote;        /* Parsed technote artifact */
Changes to src/fileedit.c.
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
  CheckinMiniInfo_cleanup(&cimi);
}

/*
** If the fileedit-glob setting has a value, this returns its Glob
** object (in memory owned by this function), else it returns NULL.
*/
static Glob * fileedit_glob(void){
  static Glob * pGlobs = 0;
  static int once = 0;
  if(0==pGlobs && once==0){
    char * zGlobs = db_get("fileedit-glob",0);
    once = 1;
    if(0!=zGlobs && 0!=*zGlobs){
      pGlobs = glob_create(zGlobs);







|







886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
  CheckinMiniInfo_cleanup(&cimi);
}

/*
** If the fileedit-glob setting has a value, this returns its Glob
** object (in memory owned by this function), else it returns NULL.
*/
Glob *fileedit_glob(void){
  static Glob * pGlobs = 0;
  static int once = 0;
  if(0==pGlobs && once==0){
    char * zGlobs = db_get("fileedit-glob",0);
    once = 1;
    if(0!=zGlobs && 0!=*zGlobs){
      pGlobs = glob_create(zGlobs);
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
      const char *zContent = blob_str(pContent);
      if(FE_PREVIEW_LINE_NUMBERS & flags){
        output_text_with_line_numbers(zContent, "on");
      }else if(zExt && zExt[1]){
        CX("<pre><code class='language-%s'>%h</code></pre>",
           zExt+1, zContent);
      }else{
        CX("<pre>%h</pre>", zExt+1, zContent);
      }
      break;
    }
  }
}

/*







|







985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
      const char *zContent = blob_str(pContent);
      if(FE_PREVIEW_LINE_NUMBERS & flags){
        output_text_with_line_numbers(zContent, "on");
      }else if(zExt && zExt[1]){
        CX("<pre><code class='language-%s'>%h</code></pre>",
           zExt+1, zContent);
      }else{
        CX("<pre>%h</pre>", zContent);
      }
      break;
    }
  }
}

/*
1711
1712
1713
1714
1715
1716
1717

1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
  }
  CX("\"dryRun\": %s",
     (CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
  if(blob_size(&manifest)>0){
    CX(",\"manifest\": %!j", blob_str(&manifest));
  }
  CX("}");

  db_end_transaction(0/*noting that dry-run mode will have already
                      ** set this to rollback mode. */);
end_cleanup:
  fossil_free(zNewUuid);
  blob_reset(&err);
  blob_reset(&manifest);
  CheckinMiniInfo_cleanup(&cimi);
}

/*







>


<







1711
1712
1713
1714
1715
1716
1717
1718
1719
1720

1721
1722
1723
1724
1725
1726
1727
  }
  CX("\"dryRun\": %s",
     (CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
  if(blob_size(&manifest)>0){
    CX(",\"manifest\": %!j", blob_str(&manifest));
  }
  CX("}");
end_cleanup:
  db_end_transaction(0/*noting that dry-run mode will have already
                      ** set this to rollback mode. */);

  fossil_free(zNewUuid);
  blob_reset(&err);
  blob_reset(&manifest);
  CheckinMiniInfo_cleanup(&cimi);
}

/*
1763
1764
1765
1766
1767
1768
1769
1770

1771





































1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797

1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
  Blob err = empty_blob;                /* Error report */
  Blob endScript = empty_blob;          /* Script code to run at the
                                           end. This content will be
                                           combined into a single JS
                                           function call, thus each
                                           entry must end with a
                                           semicolon. */
  const char *zAjax = P("name");







































  if(0!=zAjax){
    if(0==strcmp("content",zAjax)){
      fileedit_ajax_content();
    }else if(0==strcmp("preview",zAjax)){
      fileedit_ajax_preview();
    }else if(0==strcmp("filelist",zAjax)){
      fileedit_ajax_filelist();
    }else if(0==strcmp("diff",zAjax)){
      fileedit_ajax_diff();
    }else if(0==strcmp("commit",zAjax)){
      fileedit_ajax_commit();
    }else{
      fileedit_ajax_error(500, "Unhandled ajax route name.");
    }
    return;
  }
  login_check_credentials();
  if( !g.perm.Write ){
    login_needed(g.anon.Write);
    return;
  }
  db_begin_transaction();
  CheckinMiniInfo_init(&cimi);
  style_header("File Editor");
  /* As of this point, don't use return or fossil_fatal(). Write any
  ** error in (&err) and goto end_footer instead so that we can be

  ** sure to do any cleanup and end the transaction cleanly.
  */
  {
    int isMissingArg = 0;
    if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){
      zFilename = cimi.zFilename;
      zRev = cimi.zParentUuid;
      assert(zRev);
      assert(zFilename);
      zFileMime = mimetype_from_name(cimi.zFilename);
    }else if(isMissingArg!=0){
      /* Squelch these startup warnings - they're non-fatal now but
      ** used to be. */
      blob_reset(&err);
    }
  }

  /********************************************************************
  ** All errors which "could" have happened up to this point are of a
  ** degree which keep us from rendering the rest of the page, and







|
>

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















<
<
<
<
|





>
|











|







1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825




1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
  Blob err = empty_blob;                /* Error report */
  Blob endScript = empty_blob;          /* Script code to run at the
                                           end. This content will be
                                           combined into a single JS
                                           function call, thus each
                                           entry must end with a
                                           semicolon. */
  const char *zAjax = P("name");        /* Name of AJAX route for
                                           sub-dispatching. */

  /* Allow no access to this page without check-in privilege */
  login_check_credentials();
  if( !g.perm.Write ){
    if(zAjax!=0){
      fileedit_ajax_error(403, "Write permissions required.");
    }else{
      login_needed(g.anon.Write);
    }
    return;
  }
  /* No access to anything on this page if the fileedit-glob is empty */
  if( fileedit_glob()==0 ){
    if(zAjax!=0){
      fileedit_ajax_error(403, "Online editing is disabled for this "
                          "repository.");
      return;
    }
    style_header("File Editor (disabled)");
    CX("<h1>Online File Editing Is Disabled</h1>\n");
    if( g.perm.Admin ){
      CX("<p>To enable online editing, the "
         "<a href='%R/setup_settings'>"
         "<code>fileedit-glob</code> repository setting</a>\n"
         "must be set to a comma- and/or newine-delimited list of glob\n"
         "values matching files which may be edited online."
         "</p>\n");
    }else{
      CX("<p>Online editing is disabled for this repository.</p>\n");
    }
    style_footer();
    return;
  }

  /* Dispatch AJAX methods based tail of the request URI.
  ** The AJAX parts do their own permissions/CSRF check and
  ** fail with a JSON-format response if needed.
  */
  if( 0!=zAjax ){
    if(0==strcmp("content",zAjax)){
      fileedit_ajax_content();
    }else if(0==strcmp("preview",zAjax)){
      fileedit_ajax_preview();
    }else if(0==strcmp("filelist",zAjax)){
      fileedit_ajax_filelist();
    }else if(0==strcmp("diff",zAjax)){
      fileedit_ajax_diff();
    }else if(0==strcmp("commit",zAjax)){
      fileedit_ajax_commit();
    }else{
      fileedit_ajax_error(500, "Unhandled ajax route name.");
    }
    return;
  }





  db_begin_transaction();
  CheckinMiniInfo_init(&cimi);
  style_header("File Editor");
  /* As of this point, don't use return or fossil_fatal(). Write any
  ** error in (&err) and goto end_footer instead so that we can be
  ** sure to emit the error message, do any cleanup, and end the
  ** transaction cleanly.
  */
  {
    int isMissingArg = 0;
    if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){
      zFilename = cimi.zFilename;
      zRev = cimi.zParentUuid;
      assert(zRev);
      assert(zFilename);
      zFileMime = mimetype_from_name(cimi.zFilename);
    }else if(isMissingArg!=0){
      /* Squelch these startup warnings - they're non-fatal now but
      ** used to be fatal. */
      blob_reset(&err);
    }
  }

  /********************************************************************
  ** All errors which "could" have happened up to this point are of a
  ** degree which keep us from rendering the rest of the page, and
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
    ** selectors, but we do have the BODY, which we can decorate with
    ** whatever CSS we wish...
    */
    style_emit_script_tag(0,0);
    CX("document.body.classList.add('fileedit');\n");
    style_emit_script_tag(1,0);
  }

  if(fileedit_glob()==0){
    CX("<div class='error'>To enable online editing, the "
       "<code>fileedit-glob</code> repository setting must be set to a "
       "comma- or newine-delimited list of glob values matching files "
       "which may be edited online."
       "</div>");
  }
  
  /* Status bar */
  CX("<div id='fossil-status-bar' "
     "title='Status message area. Double-click to clear them.'>"
     "Status messages will go here.</div>\n"
     /* will be moved into the tab container via JS */);








<
<
<
<
<
<
<
<







1863
1864
1865
1866
1867
1868
1869








1870
1871
1872
1873
1874
1875
1876
    ** selectors, but we do have the BODY, which we can decorate with
    ** whatever CSS we wish...
    */
    style_emit_script_tag(0,0);
    CX("document.body.classList.add('fileedit');\n");
    style_emit_script_tag(1,0);
  }








  
  /* Status bar */
  CX("<div id='fossil-status-bar' "
     "title='Status message area. Double-click to clear them.'>"
     "Status messages will go here.</div>\n"
     /* will be moved into the tab container via JS */);

Changes to src/finfo.c.
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
**
**    a=DATETIME Only show changes after DATETIME
**    b=DATETIME Only show changes before DATETIME
**    m=HASH     Mark this particular file version
**    n=NUM      Show the first NUM changes only
**    brbg       Background color by branch name
**    ubg        Background color by user name
**    ci=UUID    Ancestors of a particular check-in
**    orig=UUID  If both ci and orig are supplied, only show those
**                 changes on a direct path from orig to ci.
**    showid     Show RID values for debugging
**
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, and it may also name a
** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
** (eastward). Either no timezone suffix or "Z" means UTC.







|
|







282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
**
**    a=DATETIME Only show changes after DATETIME
**    b=DATETIME Only show changes before DATETIME
**    m=HASH     Mark this particular file version
**    n=NUM      Show the first NUM changes only
**    brbg       Background color by branch name
**    ubg        Background color by user name
**    ci=HASH    Ancestors of a particular check-in
**    orig=HASH  If both ci and orig are supplied, only show those
**                 changes on a direct path from orig to ci.
**    showid     Show RID values for debugging
**
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, and it may also name a
** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
** (eastward). Either no timezone suffix or "Z" means UTC.
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  blob_append_sql(&sql,
    "SELECT"
    " datetime(min(event.mtime),toLocal()),"         /* Date of change */
    " coalesce(event.ecomment, event.comment),"      /* Check-in comment */
    " coalesce(event.euser, event.user),"            /* User who made chng */
    " mlink.pid,"                                    /* Parent file rid */
    " mlink.fid,"                                    /* File rid */
    " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */
    " blob.uuid,"                                    /* Current file uuid */
    " (SELECT uuid FROM blob WHERE rid=mlink.mid),"  /* Check-in uuid */
    " event.bgcolor,"                                /* Background color */
    " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
                                " AND tagxref.rid=mlink.mid)," /* Branchname */
    " mlink.mid,"                                    /* check-in ID */
    " mlink.pfnid,"                                  /* Previous filename */
    " blob.size"                                     /* File size */
    "  FROM mlink, event, blob"







|
|
|







369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  blob_append_sql(&sql,
    "SELECT"
    " datetime(min(event.mtime),toLocal()),"         /* Date of change */
    " coalesce(event.ecomment, event.comment),"      /* Check-in comment */
    " coalesce(event.euser, event.user),"            /* User who made chng */
    " mlink.pid,"                                    /* Parent file rid */
    " mlink.fid,"                                    /* File rid */
    " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file hash */
    " blob.uuid,"                                    /* Current file hash */
    " (SELECT uuid FROM blob WHERE rid=mlink.mid),"  /* Check-in hash */
    " event.bgcolor,"                                /* Background color */
    " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0"
                                " AND tagxref.rid=mlink.mid)," /* Branchname */
    " mlink.mid,"                                    /* check-in ID */
    " mlink.pfnid,"                                  /* Previous filename */
    " blob.size"                                     /* File size */
    "  FROM mlink, event, blob"
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
      @ <td class="timeline%s(zStyle)Cell">
    }
    if( tmFlags & TIMELINE_COMPACT ){
      @ <span class='timelineCompactComment' data-id='%d(frid)'>
    }else{
      @ <span class='timeline%s(zStyle)Comment'>
      if( (tmFlags & TIMELINE_VERBOSE)!=0 && zUuid ){
        hyperlink_to_uuid(zUuid);
        @ part of check-in \
        hyperlink_to_uuid(zCkin);
      }
    }
    @ %W(zCom)</span>
    if( (tmFlags & TIMELINE_COMPACT)!=0 ){
      @ <span class='timelineEllipsis' data-id='%d(frid)' \
      @ id='ellipsis-%d(frid)'>...</span>
      @ <span class='clutter timelineCompactDetail'







|

|







539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
      @ <td class="timeline%s(zStyle)Cell">
    }
    if( tmFlags & TIMELINE_COMPACT ){
      @ <span class='timelineCompactComment' data-id='%d(frid)'>
    }else{
      @ <span class='timeline%s(zStyle)Comment'>
      if( (tmFlags & TIMELINE_VERBOSE)!=0 && zUuid ){
        hyperlink_to_version(zUuid);
        @ part of check-in \
        hyperlink_to_version(zCkin);
      }
    }
    @ %W(zCom)</span>
    if( (tmFlags & TIMELINE_COMPACT)!=0 ){
      @ <span class='timelineEllipsis' data-id='%d(frid)' \
      @ id='ellipsis-%d(frid)'>...</span>
      @ <span class='clutter timelineCompactDetail'
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
          @ id:&nbsp;%d(frid)&larr;%d(srcId)
        }else{
          @ id:&nbsp;%d(frid)
        }
      }
    }
    @ check-in:&nbsp;\
    hyperlink_to_uuid(zCkin);
    if( fShowId ){
      @ (%d(fmid))
    }
    @ user:&nbsp;\
    hyperlink_to_user(zUser, zDate, ",");
    @ branch:&nbsp;%z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
    if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){







|







574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
          @ id:&nbsp;%d(frid)&larr;%d(srcId)
        }else{
          @ id:&nbsp;%d(frid)
        }
      }
    }
    @ check-in:&nbsp;\
    hyperlink_to_version(zCkin);
    if( fShowId ){
      @ (%d(fmid))
    }
    @ user:&nbsp;\
    hyperlink_to_user(zUser, zDate, ",");
    @ branch:&nbsp;%z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
    if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){
Changes to src/fossil.fetch.js.
20
21
22
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
   of these properties:

   - onload: callback(responseData) (default = output response to the
   console). In the context of the callback, the options object is
   "this", noting that this call may have amended the options object
   with state other than what the caller provided.

   - onerror: callback(XHR onload event | exception) (default = event
   or exception to the console). Triggered if the request generates
   any response other than HTTP 200. In the context of the callback,

   the options object is "this".

   - method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE!

   - payload: anything acceptable by XHR2.send(ARG) (DOMString,
   Document, FormData, Blob, File, ArrayBuffer), or a plain object or
   array, either of which gets JSON.stringify()'d. If payload is set
   then the method is automatically set to 'POST'. By default XHR2







|
|
|
>
|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
   of these properties:

   - onload: callback(responseData) (default = output response to the
   console). In the context of the callback, the options object is
   "this", noting that this call may have amended the options object
   with state other than what the caller provided.

   - onerror: callback(Error object) (default = output error message
   to console.error() and fossil.error()). Triggered if the request
   generates any response other than HTTP 200 or suffers a connection
   error or timeout while awaiting a response. In the context of the
   callback, the options object is "this".

   - method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE!

   - payload: anything acceptable by XHR2.send(ARG) (DOMString,
   Document, FormData, Blob, File, ArrayBuffer), or a plain object or
   array, either of which gets JSON.stringify()'d. If payload is set
   then the method is automatically set to 'POST'. By default XHR2
73
74
75
76
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
   immediately after it is received, regardless of success or
   error. In the context of the callback, the options object is the
   "this". These can be used to, e.g., keep track of in-flight
   requests and update the UI accordingly, e.g. disabling/enabling DOM
   elements. Any exceptions triggered by beforesend/aftersend are
   caught and silently ignored.




   When an options object does not provide
   onload/onerror/beforesend/aftersend handlers of its own, this
   function falls to defaults which are member properties of this
   function with the same name, e.g. fossil.fetch.onload(). The
   default onload/onerror implementations route the data through the
   dev console and (for onerror()) through fossil.error(). The default
   beforesend/aftersend are no-ops. Individual pages may overwrite
   those members to provide default implementations suitable for the
   page's use, e.g. keeping track of how many in-flight




   Returns this object, noting that the XHR request is asynchronous,
   and still in transit (or has yet to be sent) when that happens.
*/
window.fossil.fetch = function f(uri,opt){
  const F = fossil;
  if(!f.onload){
    f.onload = (r)=>console.debug('ajax response:',r);
  }
  if(!f.onerror){
    f.onerror = function(e/*event or exception*/){
      console.error("Ajax error:",e);
      if(e instanceof Error){
        F.error('Exception:',e);
      }
      else if(e.originalTarget && e.originalTarget.responseType==='text'){
        const txt = e.originalTarget.responseText;
        try{
          /* The convention from the /filepage_xyz routes is to
             return error responses in JSON form if possible:
             {error: "..."}
          */
          const j = JSON.parse(txt);
          console.error("Error JSON:",j);
          if(j.error){ F.error(j.error) };
        }catch(e){/* Try harder */
          F.error(txt)
        }
      }
    };
  }/*f.onerror()*/
  if(!f.parseResponseHeaders){
    f.parseResponseHeaders = function(h){
      const rc = {};
      if(!h) return rc;
      const ar = h.trim().split(/[\r\n]+/);







>
>
>









>
>
>







|


|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<







74
75
76
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
103
104
105
106
107
108













109


110
111
112
113
114
115
116
   immediately after it is received, regardless of success or
   error. In the context of the callback, the options object is the
   "this". These can be used to, e.g., keep track of in-flight
   requests and update the UI accordingly, e.g. disabling/enabling DOM
   elements. Any exceptions triggered by beforesend/aftersend are
   caught and silently ignored.

   - timeout: integer in milliseconds specifying the XHR timeout
   duration. Default = fossil.fetch.timeout.

   When an options object does not provide
   onload/onerror/beforesend/aftersend handlers of its own, this
   function falls to defaults which are member properties of this
   function with the same name, e.g. fossil.fetch.onload(). The
   default onload/onerror implementations route the data through the
   dev console and (for onerror()) through fossil.error(). The default
   beforesend/aftersend are no-ops. Individual pages may overwrite
   those members to provide default implementations suitable for the
   page's use, e.g. keeping track of how many in-flight

   Note that this routine may add properties to the 2nd argument, so
   that instance should not be kept around for later use.

   Returns this object, noting that the XHR request is asynchronous,
   and still in transit (or has yet to be sent) when that happens.
*/
window.fossil.fetch = function f(uri,opt){
  const F = fossil;
  if(!f.onload){
    f.onload = (r)=>console.debug('fossil.fetch() XHR response:',r);
  }
  if(!f.onerror){
    f.onerror = function(e/*exception*/){
      console.error("fossil.fetch() XHR error:",e);
      if(e instanceof Error) F.error('Exception:',e);













      else F.error("Unknown error in handling of XHR request.");


    };
  }/*f.onerror()*/
  if(!f.parseResponseHeaders){
    f.parseResponseHeaders = function(h){
      const rc = {};
      if(!h) return rc;
      const ar = h.trim().split(/[\r\n]+/);
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171





172





173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197


198
199
200
201
202
203
204

    }
  }
  const url=[F.repoUrl(uri,opt.urlParams)],
        x=new XMLHttpRequest();
  if('POST'===opt.method && 'string'===typeof opt.contentType){
    x.setRequestHeader('Content-Type',opt.contentType);
  }
  x.open(opt.method||'GET', url.join(''), true);
  if('json'===opt.responseType){
    /* 'json' is an extension to the supported XHR.responseType
       list. We use it as a flag to tell us to JSON.parse()
       the response. */
    jsonResponse = true;
    x.responseType = 'text';
  }else{
    x.responseType = opt.responseType||'text';
  }
  x.onload = function(e){
    try{opt.aftersend()}catch(e){/*ignore*/}





    if(200!==this.status){





      opt.onerror(e);
      return;
    }
    const orh = opt.responseHeaders;
    let head;
    if(true===orh){
      head = f.parseResponseHeaders(this.getAllResponseHeaders());
    }else if('string'===typeof orh){
      head = this.getResponseHeader(orh);
    }else if(orh instanceof Array){
      head = {};
      orh.forEach((s)=>{
        if('string' === typeof s) head[s.toLowerCase()] = x.getResponseHeader(s);
      });
    }
    try{
      const args = [(jsonResponse && this.response)
                    ? JSON.parse(this.response) : this.response];
      if(head) args.push(head);
      opt.onload.apply(opt, args);
    }catch(e){
      opt.onerror(e);
    }
  };
  try{opt.beforesend()}catch(e){/*ignore*/}


  if(undefined!==payload) x.send(payload);
  else x.send();
  return this;
};

window.fossil.fetch.beforesend = function(){};
window.fossil.fetch.aftersend = function(){};








<









|

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





|

|







|
|







>
>







>
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    }
  }
  const url=[F.repoUrl(uri,opt.urlParams)],
        x=new XMLHttpRequest();
  if('POST'===opt.method && 'string'===typeof opt.contentType){
    x.setRequestHeader('Content-Type',opt.contentType);
  }

  if('json'===opt.responseType){
    /* 'json' is an extension to the supported XHR.responseType
       list. We use it as a flag to tell us to JSON.parse()
       the response. */
    jsonResponse = true;
    x.responseType = 'text';
  }else{
    x.responseType = opt.responseType||'text';
  }
  x.ontimeout = function(){
    try{opt.aftersend()}catch(e){/*ignore*/}
    opt.onerror(new Error("XHR timeout of "+x.timeout+"ms expired."));
  };
  x.onreadystatechange = function(){
    if(XMLHttpRequest.DONE !== x.readyState) return;
    try{opt.aftersend()}catch(e){/*ignore*/}
    if(200!==x.status){
      let err;
      try{
        const j = JSON.parse(x.response);
        if(j.error) err = new Error(j.error);
      }catch(ex){/*ignore*/}
      opt.onerror(err || new Error("HTTP response status "+x.status+"."));
      return;
    }
    const orh = opt.responseHeaders;
    let head;
    if(true===orh){
      head = f.parseResponseHeaders(x.getAllResponseHeaders());
    }else if('string'===typeof orh){
      head = x.getResponseHeader(orh);
    }else if(orh instanceof Array){
      head = {};
      orh.forEach((s)=>{
        if('string' === typeof s) head[s.toLowerCase()] = x.getResponseHeader(s);
      });
    }
    try{
      const args = [(jsonResponse && x.response)
                    ? JSON.parse(x.response) : x.response];
      if(head) args.push(head);
      opt.onload.apply(opt, args);
    }catch(e){
      opt.onerror(e);
    }
  };
  try{opt.beforesend()}catch(e){/*ignore*/}
  x.open(opt.method||'GET', url.join(''), true);
  x.timeout = +opt.timeout || f.timeout;
  if(undefined!==payload) x.send(payload);
  else x.send();
  return this;
};

window.fossil.fetch.beforesend = function(){};
window.fossil.fetch.aftersend = function(){};
window.fossil.fetch.timeout = 15000/* Default timeout, in ms. */;
Changes to src/import.c.
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

#if INTERFACE
/*
** A single file change record.
*/
struct ImportFile {
  char *zName;           /* Name of a file */
  char *zUuid;           /* UUID of the file */
  char *zPrior;          /* Prior name if the name was changed */
  char isFrom;           /* True if obtained from the parent */
  char isExe;            /* True if executable */
  char isLink;           /* True if symlink */
};
#endif








|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

#if INTERFACE
/*
** A single file change record.
*/
struct ImportFile {
  char *zName;           /* Name of a file */
  char *zUuid;           /* Hash of the file */
  char *zPrior;          /* Prior name if the name was changed */
  char isFrom;           /* True if obtained from the parent */
  char isExe;            /* True if executable */
  char isLink;           /* True if symlink */
};
#endif

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  char *zBranch;              /* Name of a branch for a commit */
  char *zPrevBranch;          /* The branch of the previous check-in */
  char *aData;                /* Data content */
  char *zMark;                /* The current mark */
  char *zDate;                /* Date/time stamp */
  char *zUser;                /* User name */
  char *zComment;             /* Comment of a commit */
  char *zFrom;                /* from value as a UUID */
  char *zPrevCheckin;         /* Name of the previous check-in */
  char *zFromMark;            /* The mark of the "from" field */
  int nMerge;                 /* Number of merge values */
  int nMergeAlloc;            /* Number of slots in azMerge[] */
  char **azMerge;             /* Merge values */
  int nFile;                  /* Number of aFile values */
  int nFileAlloc;             /* Number of slots in aFile[] */







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  char *zBranch;              /* Name of a branch for a commit */
  char *zPrevBranch;          /* The branch of the previous check-in */
  char *aData;                /* Data content */
  char *zMark;                /* The current mark */
  char *zDate;                /* Date/time stamp */
  char *zUser;                /* User name */
  char *zComment;             /* Comment of a commit */
  char *zFrom;                /* from value as a hash */
  char *zPrevCheckin;         /* Name of the previous check-in */
  char *zFromMark;            /* The mark of the "from" field */
  int nMerge;                 /* Number of merge values */
  int nMergeAlloc;            /* Number of slots in azMerge[] */
  char **azMerge;             /* Merge values */
  int nFile;                  /* Number of aFile values */
  int nFileAlloc;             /* Number of slots in aFile[] */
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
}

/*
** Insert an artifact into the BLOB table if it isn't there already.
** If zMark is not zero, create a cross-reference from that mark back
** to the newly inserted artifact.
**
** If saveUuid is true, then pContent is a commit record.  Record its
** UUID in gg.zPrevCheckin.
*/
static int fast_insert_content(
  Blob *pContent,          /* Content to insert */
  const char *zMark,       /* Label using this mark, if not NULL */
  int saveUuid,            /* Save artifact hash in gg.zPrevCheckin */
  int doParse              /* Invoke manifest_crosslink() */
){
  Blob hash;
  Blob cmpr;
  int rid;

  hname_hash(pContent, 0, &hash);







|
|




|







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
}

/*
** Insert an artifact into the BLOB table if it isn't there already.
** If zMark is not zero, create a cross-reference from that mark back
** to the newly inserted artifact.
**
** If saveHash is true, then pContent is a commit record.  Record its
** artifact hash in gg.zPrevCheckin.
*/
static int fast_insert_content(
  Blob *pContent,          /* Content to insert */
  const char *zMark,       /* Label using this mark, if not NULL */
  int saveHash,            /* Save artifact hash in gg.zPrevCheckin */
  int doParse              /* Invoke manifest_crosslink() */
){
  Blob hash;
  Blob cmpr;
  int rid;

  hname_hash(pContent, 0, &hash);
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    );
    db_multi_exec(
        "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)"
        "VALUES(%B,%d,%B)",
        &hash, rid, &hash
    );
  }
  if( saveUuid ){
    fossil_free(gg.zPrevCheckin);
    gg.zPrevCheckin = fossil_strdup(blob_str(&hash));
  }
  blob_reset(&hash);
  return rid;
}








|







185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    );
    db_multi_exec(
        "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)"
        "VALUES(%B,%d,%B)",
        &hash, rid, &hash
    );
  }
  if( saveHash ){
    fossil_free(gg.zPrevCheckin);
    gg.zPrevCheckin = fossil_strdup(blob_str(&hash));
  }
  blob_reset(&hash);
  return rid;
}

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  }else{
    *pzIn = &z[i];
  }
  return z;
}

/*
** Convert a "mark" or "committish" into the UUID.
*/
static char *resolve_committish(const char *zCommittish){
  char *zRes;

  zRes = db_text(0, "SELECT tuuid FROM xmark WHERE tname=%Q", zCommittish);
  return zRes;
}







|







409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  }else{
    *pzIn = &z[i];
  }
  return z;
}

/*
** Convert a "mark" or "committish" into the artifact hash.
*/
static char *resolve_committish(const char *zCommittish){
  char *zRes;

  zRes = db_text(0, "SELECT tuuid FROM xmark WHERE tname=%Q", zCommittish);
  return zRes;
}
1826
1827
1828
1829
1830
1831
1832
1833

1834
1835
1836
1837
1838
1839
1840
1841
1842
    Bag blobs, vers;
    bag_init(&blobs);
    bag_init(&vers);
    /* The following temp-tables are used to hold information needed for
    ** the import.
    **
    ** The XMARK table provides a mapping from fast-import "marks" and symbols
    ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts).

    ** Given any valid fast-import symbol, the corresponding fossil rid and
    ** uuid can found by searching against the xmark.tname field.
    **
    ** The XBRANCH table maps commit marks and symbols into the branch those
    ** commits belong to.  If xbranch.tname is a fast-import symbol for a
    ** check-in then xbranch.brnm is the branch that check-in is part of.
    **
    ** The XTAG table records information about tags that need to be applied
    ** to various branches after the import finishes.  The xtag.tcontent field







|
>

|







1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
    Bag blobs, vers;
    bag_init(&blobs);
    bag_init(&vers);
    /* The following temp-tables are used to hold information needed for
    ** the import.
    **
    ** The XMARK table provides a mapping from fast-import "marks" and symbols
    ** into artifact hashes.
    **
    ** Given any valid fast-import symbol, the corresponding fossil rid and
    ** hash can found by searching against the xmark.tname field.
    **
    ** The XBRANCH table maps commit marks and symbols into the branch those
    ** commits belong to.  If xbranch.tname is a fast-import symbol for a
    ** check-in then xbranch.brnm is the branch that check-in is part of.
    **
    ** The XTAG table records information about tags that need to be applied
    ** to various branches after the import finishes.  The xtag.tcontent field
Changes to src/info.c.
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  return zTags;
}


/*
** Print common information about a particular record.
**
**     *  The UUID
**     *  The record ID
**     *  mtime and ctime
**     *  who signed it
**
*/
void show_common_info(
  int rid,                   /* The rid for the check-in to display info for */
  const char *zUuidName,     /* Name of the UUID */
  int showComment,           /* True to show the check-in comment */
  int showFamily             /* True to show parents and children */
){
  Stmt q;
  char *zComment = 0;
  char *zTags;
  char *zDate;
  char *zUuid;
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( zUuid ){
    zDate = db_text(0,
      "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
      rid
    );
         /* 01234567890123 */
    fossil_print("%-13s %.40s %s\n", zUuidName, zUuid, zDate ? zDate : "");
    free(zDate);
    if( showComment ){
      zComment = db_text(0,
        "SELECT coalesce(ecomment,comment) || "
        "       ' (user: ' || coalesce(euser,user,'?') || ')' "
        "  FROM event WHERE objid=%d",
        rid







|







|















|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  return zTags;
}


/*
** Print common information about a particular record.
**
**     *  The artifact hash
**     *  The record ID
**     *  mtime and ctime
**     *  who signed it
**
*/
void show_common_info(
  int rid,                   /* The rid for the check-in to display info for */
  const char *zRecDesc,      /* Brief record description; e.g. "checkout:" */
  int showComment,           /* True to show the check-in comment */
  int showFamily             /* True to show parents and children */
){
  Stmt q;
  char *zComment = 0;
  char *zTags;
  char *zDate;
  char *zUuid;
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( zUuid ){
    zDate = db_text(0,
      "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d",
      rid
    );
         /* 01234567890123 */
    fossil_print("%-13s %.40s %s\n", zRecDesc, zUuid, zDate ? zDate : "");
    free(zDate);
    if( showComment ){
      zComment = db_text(0,
        "SELECT coalesce(ecomment,comment) || "
        "       ' (user: ' || coalesce(euser,user,'?') || ')' "
        "  FROM event WHERE objid=%d",
        rid
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
    }
  }else{
    int rid;
    rid = name_to_rid(g.argv[2]);
    if( rid==0 ){
      fossil_fatal("no such object: %s", g.argv[2]);
    }
    show_common_info(rid, "uuid:", 1, 1);
  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid.
*/







|







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
    }
  }else{
    int rid;
    rid = name_to_rid(g.argv[2]);
    if( rid==0 ){
      fossil_fatal("no such object: %s", g.argv[2]);
    }
    show_common_info(rid, "hash:", 1, 1);
  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid.
*/
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
    }else {
      @ <span class="infoTag">%h(zTagname)</span>
    }
    if( tagtype==2 ){
      if( zOrigUuid && zOrigUuid[0] ){
        @ inherited from
        hyperlink_to_uuid(zOrigUuid);
      }else{
        @ propagates to descendants
      }
    }
    if( zSrcUuid && zSrcUuid[0] ){
      if( tagtype==0 ){
        @ by
      }else{
        @ added by
      }
      hyperlink_to_uuid(zSrcUuid);
      @ on
      hyperlink_to_date(zDate,0);
    }
    @ </li>
  }
  db_finalize(&q);
  if( cnt ){







|










|







745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
    }else {
      @ <span class="infoTag">%h(zTagname)</span>
    }
    if( tagtype==2 ){
      if( zOrigUuid && zOrigUuid[0] ){
        @ inherited from
        hyperlink_to_version(zOrigUuid);
      }else{
        @ propagates to descendants
      }
    }
    if( zSrcUuid && zSrcUuid[0] ){
      if( tagtype==0 ){
        @ by
      }else{
        @ added by
      }
      hyperlink_to_version(zSrcUuid);
      @ on
      hyperlink_to_date(zDate,0);
    }
    @ </li>
  }
  db_finalize(&q);
  if( cnt ){
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
void ci_page(void){
  Stmt q1, q2, q3;
  int rid;
  int isLeaf;
  int diffType;        /* 0: no diff,  1: unified,  2: side-by-side */
  u64 diffFlags;       /* Flag parameter for text_diff() */
  const char *zName;   /* Name of the check-in to be displayed */
  const char *zUuid;   /* UUID of zName */
  const char *zParent; /* UUID of the parent check-in (if any) */
  const char *zRe;     /* regex parameter */
  ReCompiled *pRe = 0; /* regex */
  const char *zW;      /* URL param for ignoring whitespace */
  const char *zPage = "vinfo";  /* Page that shows diffs */
  const char *zPageHide = "ci"; /* Page that hides diffs */
  const char *zBrName; /* Branch name */
  int bLocalMode;      /* TRUE for /local; FALSE otherwise */







|
|







827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
void ci_page(void){
  Stmt q1, q2, q3;
  int rid;
  int isLeaf;
  int diffType;        /* 0: no diff,  1: unified,  2: side-by-side */
  u64 diffFlags;       /* Flag parameter for text_diff() */
  const char *zName;   /* Name of the check-in to be displayed */
  const char *zUuid;   /* Hash of zName, found via blob.uuid */
  const char *zParent; /* Hash of the parent check-in (if any) */
  const char *zRe;     /* regex parameter */
  ReCompiled *pRe = 0; /* regex */
  const char *zW;      /* URL param for ignoring whitespace */
  const char *zPage = "vinfo";  /* Page that shows diffs */
  const char *zPageHide = "ci"; /* Page that hides diffs */
  const char *zBrName; /* Branch name */
  int bLocalMode;      /* TRUE for /local; FALSE otherwise */
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
  }
  db_finalize(&q3);
  db_end_transaction(1);  /* ROLLBACK */
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=UUID
**
** Display information about a wiki page.
*/
void winfo_page(void){
  int rid;
  Manifest *pWiki;
  char *zUuid;







|







1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
  }
  db_finalize(&q3);
  db_end_transaction(1);  /* ROLLBACK */
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=HASH
**
** Display information about a wiki page.
*/
void winfo_page(void){
  int rid;
  Manifest *pWiki;
  char *zUuid;
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
    const char *zUuid = db_column_text(&q, 3);
    const char *zTagList = db_column_text(&q, 4);
    Blob comment;
    int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
    if( db_get_boolean("timeline-block-markup", 0)==0 ){
      wikiFlags |= WIKI_NOBLOCK;
    }
    hyperlink_to_uuid(zUuid);
    blob_zero(&comment);
    db_column_blob(&q, 2, &comment);
    wiki_convert(&comment, 0, wikiFlags);
    blob_reset(&comment);
    @ (user:
    hyperlink_to_user(zUser,zDate,",");
    if( zTagList && zTagList[0] && g.perm.Hyperlink ){







|







1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
    const char *zUuid = db_column_text(&q, 3);
    const char *zTagList = db_column_text(&q, 4);
    Blob comment;
    int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
    if( db_get_boolean("timeline-block-markup", 0)==0 ){
      wikiFlags |= WIKI_NOBLOCK;
    }
    hyperlink_to_version(zUuid);
    blob_zero(&comment);
    db_column_blob(&q, 2, &comment);
    wiki_convert(&comment, 0, wikiFlags);
    blob_reset(&comment);
    @ (user:
    hyperlink_to_user(zUser,zDate,",");
    if( zTagList && zTagList[0] && g.perm.Hyperlink ){
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
      }
      prevName = fossil_strdup(zName);
    }
    if( showDetail ){
      @ <li>
      hyperlink_to_date(zDate,"");
      @ &mdash; part of check-in
      hyperlink_to_uuid(zVers);
    }else{
      @ &mdash; part of check-in
      hyperlink_to_uuid(zVers);
      @ at
      hyperlink_to_date(zDate,"");
    }
    if( zBr && zBr[0] ){
      @ on branch %z(href("%R/timeline?r=%T",zBr))%h(zBr)</a>
    }
    @ &mdash; %!W(zCom) (user:







|


|







1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
      }
      prevName = fossil_strdup(zName);
    }
    if( showDetail ){
      @ <li>
      hyperlink_to_date(zDate,"");
      @ &mdash; part of check-in
      hyperlink_to_version(zVers);
    }else{
      @ &mdash; part of check-in
      hyperlink_to_version(zVers);
      @ at
      hyperlink_to_date(zDate,"");
    }
    if( zBr && zBr[0] ){
      @ on branch %z(href("%R/timeline?r=%T",zBr))%h(zBr)</a>
    }
    @ &mdash; %!W(zCom) (user:
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
      }else if( zType[0]=='f' ){
        objType |= OBJTYPE_FORUM;
        @ Forum post
      }else{
        @ Tag referencing
      }
      if( zType[0]!='e' || eventTagId == 0){
        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);
      }







|







1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
      }else if( zType[0]=='f' ){
        objType |= OBJTYPE_FORUM;
        @ Forum post
      }else{
        @ Tag referencing
      }
      if( zType[0]!='e' || eventTagId == 0){
        hyperlink_to_version(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);
      }
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
    /* const char *zSrc = db_column_text(&q, 4); */
    if( cnt>0 ){
      @ Also attachment "%h(zFilename)" to
    }else{
      @ Attachment "%h(zFilename)" to
    }
    objType |= OBJTYPE_ATTACHMENT;
    if( fossil_is_uuid(zTarget) ){
      if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
            zTarget)
      ){
        if( g.perm.Hyperlink && g.anon.RdTkt ){
          @ ticket [%z(href("%R/tktview?name=%!S",zTarget))%S(zTarget)</a>]
        }else{
          @ ticket [%S(zTarget)]







|







1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
    /* const char *zSrc = db_column_text(&q, 4); */
    if( cnt>0 ){
      @ Also attachment "%h(zFilename)" to
    }else{
      @ Attachment "%h(zFilename)" to
    }
    objType |= OBJTYPE_ATTACHMENT;
    if( fossil_is_artifact_hash(zTarget) ){
      if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
            zTarget)
      ){
        if( g.perm.Hyperlink && g.anon.RdTkt ){
          @ ticket [%z(href("%R/tktview?name=%!S",zTarget))%S(zTarget)</a>]
        }else{
          @ ticket [%S(zTarget)]
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
  return objType;
}


/*
** WEBPAGE: fdiff
** WEBPAGE: localdiff
** URL: fdiff?v1=UUID&v2=UUID
** URL: localdiff?name=filename
**
** Two arguments, v1 and v2, identify the artifacts to be diffed.
** Show diff side by side unless sbs is 0.  Generate plain text if
** "patch" is present, otherwise generate "pretty" HTML.
**
** Alternative URL:  fdiff?from=filename1&to=filename2&ci=checkin







|







2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
  return objType;
}


/*
** WEBPAGE: fdiff
** WEBPAGE: localdiff
** URL: fdiff?v1=HASH&v2=HASH
** URL: localdiff?name=filename
**
** Two arguments, v1 and v2, identify the artifacts to be diffed.
** Show diff side by side unless sbs is 0.  Generate plain text if
** "patch" is present, otherwise generate "pretty" HTML.
**
** Alternative URL:  fdiff?from=filename1&to=filename2&ci=checkin
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
** Return the uninterpreted content of an artifact.  This is similar
** to /raw except in this case the only way to specify the artifact
** is by the full-length SHA1 or SHA3 hash.  Abbreviations are not
** accepted.
*/
void secure_rawartifact_page(void){
  int rid = 0;
  const char *zUuid = PD("name", "");

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zUuid);
  if( rid==0 ){
    cgi_set_status(404, "Not Found");
    @ Unknown artifact: "%h(zUuid)"
    return;
  }
  g.isConst = 1;
  deliver_artifact(rid, P("m"));
}









|



|


|







2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
** Return the uninterpreted content of an artifact.  This is similar
** to /raw except in this case the only way to specify the artifact
** is by the full-length SHA1 or SHA3 hash.  Abbreviations are not
** accepted.
*/
void secure_rawartifact_page(void){
  int rid = 0;
  const char *zName = PD("name", "");

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
  if( rid==0 ){
    cgi_set_status(404, "Not Found");
    @ Unknown artifact: "%h(zName)"
    return;
  }
  g.isConst = 1;
  deliver_artifact(rid, P("m"));
}


3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034


3035
3036
3037
3038
3039
3040
3041
3042
  manifest_destroy(pTktChng);
  style_footer();
}


/*
** WEBPAGE: info
** URL: info/ARTIFACTID
**
** The argument is a artifact ID which might be a check-in or a file or
** a ticket changes or a wiki edit or something else.
**


** Figure out what the artifact ID is and display it appropriately.
*/
void info_page(void){
  const char *zName;
  Blob uuid;
  int rid;
  int rc;
  int nLen;







|

|
|

>
>
|







3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
  manifest_destroy(pTktChng);
  style_footer();
}


/*
** WEBPAGE: info
** URL: info/NAME
**
** The NAME argument is any valid artifact name: an artifact hash,
** a timestamp, a tag name, etc.
**
** Because NAME can match so many different things (commit artifacts,
** wiki pages, ticket comments, forum posts...) the format of the output
** page depends on the type of artifact that NAME matches.
*/
void info_page(void){
  const char *zName;
  Blob uuid;
  int rid;
  int rc;
  int nLen;
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
    blob_append(&prompt, zUuid, -1);
  }
  blob_append(&prompt, ".\n# Lines beginning with a # are ignored.\n", -1);
  prompt_for_user_comment(pComment, &prompt);
  blob_reset(&prompt);
}

#define AMEND_USAGE_STMT "UUID OPTION ?OPTION ...?"
/*
** COMMAND: amend
**
** Usage: %fossil amend UUID OPTION ?OPTION ...?
**
** Amend the tags on check-in UUID to change how it displays in the timeline.
**
** Options:
**
**    --author USER           Make USER the author for check-in
**    -m|--comment COMMENT    Make COMMENT the check-in comment
**    -M|--message-file FILE  Read the amended comment from FILE
**    -e|--edit-comment       Launch editor to revise comment







|



|

|







3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
    blob_append(&prompt, zUuid, -1);
  }
  blob_append(&prompt, ".\n# Lines beginning with a # are ignored.\n", -1);
  prompt_for_user_comment(pComment, &prompt);
  blob_reset(&prompt);
}

#define AMEND_USAGE_STMT "HASH OPTION ?OPTION ...?"
/*
** COMMAND: amend
**
** Usage: %fossil amend HASH OPTION ?OPTION ...?
**
** Amend the tags on check-in HASH to change how it displays in the timeline.
**
** Options:
**
**    --author USER           Make USER the author for check-in
**    -m|--comment COMMENT    Make COMMENT the check-in comment
**    -M|--message-file FILE  Read the amended comment from FILE
**    -e|--edit-comment       Launch editor to revise comment
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
  db_find_and_open_repository(0,0);
  user_select();
  verify_all_options();
  if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
  rid = name_to_typed_rid(g.argv[2], "ci");
  if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( zUuid==0 ) fossil_fatal("Unable to find UUID");
  zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
                        "  FROM event WHERE objid=%d", rid);
  zUser = db_text(0, "SELECT coalesce(euser,user)"
                     "  FROM event WHERE objid=%d", rid);
  zDate = db_text(0, "SELECT datetime(mtime)"
                     "  FROM event WHERE objid=%d", rid);
  zColor = db_text("", "SELECT bgcolor"







|







3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
  db_find_and_open_repository(0,0);
  user_select();
  verify_all_options();
  if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
  rid = name_to_typed_rid(g.argv[2], "ci");
  if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( zUuid==0 ) fossil_fatal("Unable to find artifact hash");
  zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
                        "  FROM event WHERE objid=%d", rid);
  zUser = db_text(0, "SELECT coalesce(euser,user)"
                     "  FROM event WHERE objid=%d", rid);
  zDate = db_text(0, "SELECT datetime(mtime)"
                     "  FROM event WHERE objid=%d", rid);
  zColor = db_text("", "SELECT bgcolor"
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
    fossil_free((void *)pzCancelTags);
  }
  if( fHide && !fHasHidden ) hide_branch();
  if( fClose && !fHasClosed ) close_leaf(rid);
  if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
  apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun);
  if( fDryRun==0 ){
    show_common_info(rid, "uuid:", 1, 0);
  }
  if( g.localOpen ){
    manifest_to_disk(rid);
  }
}







|





3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
    fossil_free((void *)pzCancelTags);
  }
  if( fHide && !fHasHidden ) hide_branch();
  if( fClose && !fHasClosed ) close_leaf(rid);
  if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
  apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun);
  if( fDryRun==0 ){
    show_common_info(rid, "hash:", 1, 0);
  }
  if( g.localOpen ){
    manifest_to_disk(rid);
  }
}
Changes to src/json_finfo.c.
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

  if( zCheckin && *zCheckin ){
    char * zU = NULL;
    int rc = name_to_uuid2( zCheckin, "ci", &zU );
    /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
    if(rc<=0){
      json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID : FSL_JSON_E_RESOURCE_NOT_FOUND,
                   "Check-in UUID %s.", (rc<0) ? "is ambiguous" : "not found");
      blob_reset(&sql);
      return NULL;
    }
    blob_append_sql(&sql, " AND ci.uuid='%q'", zU);
    free(zU);
  }else{
    if( zAfter && *zAfter ){







|







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

  if( zCheckin && *zCheckin ){
    char * zU = NULL;
    int rc = name_to_uuid2( zCheckin, "ci", &zU );
    /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
    if(rc<=0){
      json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID : FSL_JSON_E_RESOURCE_NOT_FOUND,
                   "Check-in hash %s.", (rc<0) ? "is ambiguous" : "not found");
      blob_reset(&sql);
      return NULL;
    }
    blob_append_sql(&sql, " AND ci.uuid='%q'", zU);
    free(zU);
  }else{
    if( zAfter && *zAfter ){
Changes to src/json_tag.c.
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
  {
    Blob uu = empty_blob;
    int rc;
    blob_append(&uu, zName, -1);
    rc = name_to_uuid(&uu, 9, "*");
    if(0!=rc){
      json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert name back to UUID!");
      blob_reset(&uu);
      goto error;
    }
    cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
    blob_reset(&uu);
  }








|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
  {
    Blob uu = empty_blob;
    int rc;
    blob_append(&uu, zName, -1);
    rc = name_to_uuid(&uu, 9, "*");
    if(0!=rc){
      json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert name back to artifact hash!");
      blob_reset(&uu);
      goto error;
    }
    cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
    blob_reset(&uu);
  }

Changes to src/main.c.
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  char *zRepositoryOption; /* Most recent cached repository option value */
  char *zRepositoryName;  /* Name of the repository database file */
  char *zLocalDbName;     /* Name of the local database file */
  char *zOpenRevision;    /* Check-in version to use during database open */
  const char *zCmdName;   /* Name of the Fossil command currently running */
  int localOpen;          /* True if the local database is open */
  char *zLocalRoot;       /* The directory holding the  local database */
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int eHashPolicy;        /* Current hash policy.  One of HPOLICY_* */
  int fSqlTrace;          /* True if --sqltrace flag is present */
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  int fSqlPrint;          /* True if --sqlprint flag is present */
  int fCgiTrace;          /* True if --cgitrace is enabled */
  int fQuiet;             /* True if -quiet flag is present */
  int fJail;              /* True if running with a chroot jail */







|







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  char *zRepositoryOption; /* Most recent cached repository option value */
  char *zRepositoryName;  /* Name of the repository database file */
  char *zLocalDbName;     /* Name of the local database file */
  char *zOpenRevision;    /* Check-in version to use during database open */
  const char *zCmdName;   /* Name of the Fossil command currently running */
  int localOpen;          /* True if the local database is open */
  char *zLocalRoot;       /* The directory holding the  local database */
  int minPrefix;          /* Number of digits needed for a distinct hash */
  int eHashPolicy;        /* Current hash policy.  One of HPOLICY_* */
  int fSqlTrace;          /* True if --sqltrace flag is present */
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  int fSqlPrint;          /* True if --sqlprint flag is present */
  int fCgiTrace;          /* True if --cgitrace is enabled */
  int fQuiet;             /* True if -quiet flag is present */
  int fJail;              /* True if running with a chroot jail */
Changes to src/main.mk.
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
                 -DSQLITE_DQS=0 \
                 -DSQLITE_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_MEMSTATUS=0 \
                 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 -DSQLITE_OMIT_DECLTYPE \
                 -DSQLITE_OMIT_DEPRECATED \
                 -DSQLITE_OMIT_GET_TABLE \
                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_USE_ALLOCA \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \







<







606
607
608
609
610
611
612

613
614
615
616
617
618
619
                 -DSQLITE_DQS=0 \
                 -DSQLITE_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_MEMSTATUS=0 \
                 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 -DSQLITE_OMIT_DECLTYPE \
                 -DSQLITE_OMIT_DEPRECATED \

                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_USE_ALLOCA \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
                -DSQLITE_DQS=0 \
                -DSQLITE_THREADSAFE=0 \
                -DSQLITE_DEFAULT_MEMSTATUS=0 \
                -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                -DSQLITE_OMIT_DECLTYPE \
                -DSQLITE_OMIT_DEPRECATED \
                -DSQLITE_OMIT_GET_TABLE \
                -DSQLITE_OMIT_PROGRESS_CALLBACK \
                -DSQLITE_OMIT_SHARED_CACHE \
                -DSQLITE_OMIT_LOAD_EXTENSION \
                -DSQLITE_MAX_EXPR_DEPTH=0 \
                -DSQLITE_USE_ALLOCA \
                -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                -DSQLITE_DEFAULT_FILE_FORMAT=4 \







<







633
634
635
636
637
638
639

640
641
642
643
644
645
646
                -DSQLITE_DQS=0 \
                -DSQLITE_THREADSAFE=0 \
                -DSQLITE_DEFAULT_MEMSTATUS=0 \
                -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                -DSQLITE_OMIT_DECLTYPE \
                -DSQLITE_OMIT_DEPRECATED \

                -DSQLITE_OMIT_PROGRESS_CALLBACK \
                -DSQLITE_OMIT_SHARED_CACHE \
                -DSQLITE_OMIT_LOAD_EXTENSION \
                -DSQLITE_MAX_EXPR_DEPTH=0 \
                -DSQLITE_USE_ALLOCA \
                -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                -DSQLITE_DEFAULT_FILE_FORMAT=4 \
Changes to src/makemake.tcl.
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  -DSQLITE_DQS=0
  -DSQLITE_THREADSAFE=0
  -DSQLITE_DEFAULT_MEMSTATUS=0
  -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
  -DSQLITE_LIKE_DOESNT_MATCH_BLOBS
  -DSQLITE_OMIT_DECLTYPE
  -DSQLITE_OMIT_DEPRECATED
  -DSQLITE_OMIT_GET_TABLE
  -DSQLITE_OMIT_PROGRESS_CALLBACK
  -DSQLITE_OMIT_SHARED_CACHE
  -DSQLITE_OMIT_LOAD_EXTENSION
  -DSQLITE_MAX_EXPR_DEPTH=0
  -DSQLITE_USE_ALLOCA
  -DSQLITE_ENABLE_LOCKING_STYLE=0
  -DSQLITE_DEFAULT_FILE_FORMAT=4







<







190
191
192
193
194
195
196

197
198
199
200
201
202
203
  -DSQLITE_DQS=0
  -DSQLITE_THREADSAFE=0
  -DSQLITE_DEFAULT_MEMSTATUS=0
  -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
  -DSQLITE_LIKE_DOESNT_MATCH_BLOBS
  -DSQLITE_OMIT_DECLTYPE
  -DSQLITE_OMIT_DEPRECATED

  -DSQLITE_OMIT_PROGRESS_CALLBACK
  -DSQLITE_OMIT_SHARED_CACHE
  -DSQLITE_OMIT_LOAD_EXTENSION
  -DSQLITE_MAX_EXPR_DEPTH=0
  -DSQLITE_USE_ALLOCA
  -DSQLITE_ENABLE_LOCKING_STYLE=0
  -DSQLITE_DEFAULT_FILE_FORMAT=4
Changes to src/manifest.c.
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
    char *zComment = 0;
    const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
    /* We assume that we're attaching to a wiki page until we
    ** prove otherwise (which could on a later artifact if we
    ** process the attachment artifact before the artifact to
    ** which it is attached!) */
    char attachToType = 'w';
    if( fossil_is_uuid(p->zAttachTarget) ){
      if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
            p->zAttachTarget)
        ){
        attachToType = 't';          /* Attaching to known ticket */
      }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
                  p->zAttachTarget)
            ){







|







2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
    char *zComment = 0;
    const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0;
    /* We assume that we're attaching to a wiki page until we
    ** prove otherwise (which could on a later artifact if we
    ** process the attachment artifact before the artifact to
    ** which it is attached!) */
    char attachToType = 'w';
    if( fossil_is_artifact_hash(p->zAttachTarget) ){
      if( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'",
            p->zAttachTarget)
        ){
        attachToType = 't';          /* Attaching to known ticket */
      }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'",
                  p->zAttachTarget)
            ){
Changes to src/name.c.
444
445
446
447
448
449
450
451



452

453
454
455
456
457



458
459
460
461
462
463
464
      }
    }
  }
  return rid;
}

/*
** This routine takes a user-entered UUID which might be in mixed



** case and might only be a prefix of the full UUID and converts it

** into the full-length UUID in canonical form.
**
** If the input is not a UUID or a UUID prefix, then try to resolve
** the name as a tag.  If multiple tags match, pick the latest.
** If the input name matches "tag:*" then always resolve as a tag.



**
** If the input is not a tag, then try to match it as an ISO-8601 date
** string YYYY-MM-DD HH:MM:SS and pick the nearest check-in to that date.
** If the input is of the form "date:*" then always resolve the name as
** a date. The forms "utc:*" and "local:" are deprecated.
**
** Return 0 on success.  Return 1 if the name cannot be resolved.







|
>
>
>
|
>
|

|

<
>
>
>







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
469
470
      }
    }
  }
  return rid;
}

/*
** This routine takes a user-entered string and tries to convert it to
** an artifact hash.
**
** We first try to treat the string as an artifact hash, or at least a
** unique prefix of an artifact hash. The input may be in mixed case.
** If we are passed such a string, this routine has the effect of
** converting the hash [prefix] to canonical form.
**
** If the input is not a hash or a hash prefix, then try to resolve
** the name as a tag.  If multiple tags match, pick the latest.

** A caller can force this routine to skip the hash case above by
** prefixing the string with "tag:", a useful property when the tag
** may be misinterpreted as a hex ASCII string. (e.g. "decade" or "facade")
**
** If the input is not a tag, then try to match it as an ISO-8601 date
** string YYYY-MM-DD HH:MM:SS and pick the nearest check-in to that date.
** If the input is of the form "date:*" then always resolve the name as
** a date. The forms "utc:*" and "local:" are deprecated.
**
** Return 0 on success.  Return 1 if the name cannot be resolved.
479
480
481
482
483
484
485
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
511
512
    return 0;
  }
}

/*
** This routine is similar to name_to_uuid() except in the form it
** takes its parameters and returns its value, and in that it does not
** treat errors as fatal. zName must be a UUID, as described for
** name_to_uuid(). zType is also as described for that function. If
** zName does not resolve, 0 is returned. If it is ambiguous, a
** negative value is returned. On success the rid is returned and
** pUuid (if it is not NULL) is set to a newly-allocated string,
** the full UUID, which must eventually be free()d by the caller.
*/
int name_to_uuid2(const char *zName, const char *zType, char **pUuid){
  int rid = symbolic_name_to_rid(zName, zType);
  if((rid>0) && pUuid){
    *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid);
  }
  return rid;
}


/*
** name_collisions searches through events, blobs, and tickets for
** collisions of a given UUID based on its length on UUIDs no shorter

** than 4 characters in length.
*/
int name_collisions(const char *zName){
  int c = 0;         /* count of collisions for zName */
  int nLen;          /* length of zName */
  nLen = strlen(zName);
  if( nLen>=4 && nLen<=HNAME_MAX && validate16(zName, nLen) ){
    c = db_int(0,







|
|



|












|
>
|







485
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
511
512
513
514
515
516
517
518
519
    return 0;
  }
}

/*
** This routine is similar to name_to_uuid() except in the form it
** takes its parameters and returns its value, and in that it does not
** treat errors as fatal. zName must be an artifact hash or prefix of
** a hash. zType is also as described for name_to_uuid(). If
** zName does not resolve, 0 is returned. If it is ambiguous, a
** negative value is returned. On success the rid is returned and
** pUuid (if it is not NULL) is set to a newly-allocated string,
** the full hash, which must eventually be free()d by the caller.
*/
int name_to_uuid2(const char *zName, const char *zType, char **pUuid){
  int rid = symbolic_name_to_rid(zName, zType);
  if((rid>0) && pUuid){
    *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid);
  }
  return rid;
}


/*
** name_collisions searches through events, blobs, and tickets for
** collisions of a given hash based on its length, counting only
** hashes greater than or equal to 4 hex ASCII characters (16 bits)
** in length.
*/
int name_collisions(const char *zName){
  int c = 0;         /* count of collisions for zName */
  int nLen;          /* length of zName */
  nLen = strlen(zName);
  if( nLen>=4 && nLen<=HNAME_MAX && validate16(zName, nLen) ){
    c = db_int(0,
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
    int rid = db_column_int(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    const char *zTitle = db_column_text(&q, 2);
    @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
    @ %s(zUuid)</a> -
    @ <ul></ul>
    @ Ticket
    hyperlink_to_uuid(zUuid);
    @ - %h(zTitle).
    @ <ul><li>
    object_description(rid, 0, 0, 0);
    @ </li></ul>
    @ </p></li>
  }
  db_finalize(&q);







|







638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
    int rid = db_column_int(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    const char *zTitle = db_column_text(&q, 2);
    @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
    @ %s(zUuid)</a> -
    @ <ul></ul>
    @ Ticket
    hyperlink_to_version(zUuid);
    @ - %h(zTitle).
    @ <ul><li>
    object_description(rid, 0, 0, 0);
    @ </li></ul>
    @ </p></li>
  }
  db_finalize(&q);
Changes to src/printf.c.
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#define etPOINTER    16 /* The %p conversion */
#define etHTMLIZE    17 /* Make text safe for HTML */
#define etHTTPIZE    18 /* Make text safe for HTTP.  "/" encoded as %2f */
#define etURLIZE     19 /* Make text safe for HTTP.  "/" not encoded */
#define etFOSSILIZE  20 /* The fossil header encoding format. */
#define etPATH       21 /* Path type */
#define etWIKISTR    22 /* Timeline comment text rendered from a char*: %W */
#define etSTRINGID   23 /* String with length limit for a UUID prefix: %S */
#define etROOT       24 /* String value of g.zTop: %R */
#define etJSONSTR    25 /* String encoded as a JSON string literal: %j
                           Use %!j to include double-quotes around it. */


/*
** An "etByte" is an 8-bit unsigned value.







|







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#define etPOINTER    16 /* The %p conversion */
#define etHTMLIZE    17 /* Make text safe for HTML */
#define etHTTPIZE    18 /* Make text safe for HTTP.  "/" encoded as %2f */
#define etURLIZE     19 /* Make text safe for HTTP.  "/" not encoded */
#define etFOSSILIZE  20 /* The fossil header encoding format. */
#define etPATH       21 /* Path type */
#define etWIKISTR    22 /* Timeline comment text rendered from a char*: %W */
#define etSTRINGID   23 /* String with length limit for a hash prefix: %S */
#define etROOT       24 /* String value of g.zTop: %R */
#define etJSONSTR    25 /* String encoded as a JSON string literal: %j
                           Use %!j to include double-quotes around it. */


/*
** An "etByte" is an 8-bit unsigned value.
Changes to src/purge.c.
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
** ==== WARNING: This command can potentially destroy historical data and ====
** ==== leave your repository in a goofy state. Know what you are doing!  ====
** ==== Make a backup of your repository before using this command!       ====
**
** ==== FURTHER WARNING: This command is a work-in-progress and may yet   ====
** ==== contain bugs.                                                     ====
**
**   fossil purge artifacts UUID... ?OPTIONS?
**
**      Move arbitrary artifacts identified by the UUID list into the
**      graveyard.
**
**   fossil purge cat UUID...
**
**      Write the content of one or more artifacts in the graveyard onto
**      standard output.
**
**   fossil purge checkins TAGS... ?OPTIONS?
**
**      Move the check-ins or branches identified by TAGS and all of







|

|


|







454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
** ==== WARNING: This command can potentially destroy historical data and ====
** ==== leave your repository in a goofy state. Know what you are doing!  ====
** ==== Make a backup of your repository before using this command!       ====
**
** ==== FURTHER WARNING: This command is a work-in-progress and may yet   ====
** ==== contain bugs.                                                     ====
**
**   fossil purge artifacts HASH... ?OPTIONS?
**
**      Move arbitrary artifacts identified by the HASH list into the
**      graveyard.
**
**   fossil purge cat HASH...
**
**      Write the content of one or more artifacts in the graveyard onto
**      standard output.
**
**   fossil purge checkins TAGS... ?OPTIONS?
**
**      Move the check-ins or branches identified by TAGS and all of
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
**
** COMMON OPTIONS:
**
**   --explain         Make no changes, but show what would happen.
**   --dry-run         An alias for --explain
**
** SUMMARY:
**   fossil purge artifacts UUID.. [OPTIONS]
**   fossil purge cat UUID...
**   fossil purge checkins TAGS... [OPTIONS]
**   fossil purge files FILENAME... [OPTIONS]
**   fossil purge list
**   fossil purge obliterate ID...
**   fossil purge tickets NAME... [OPTIONS]
**   fossil purge undo ID
**   fossil purge wiki NAME... [OPTIONS]







|
|







507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
**
** COMMON OPTIONS:
**
**   --explain         Make no changes, but show what would happen.
**   --dry-run         An alias for --explain
**
** SUMMARY:
**   fossil purge artifacts HASH.. [OPTIONS]
**   fossil purge cat HASH...
**   fossil purge checkins TAGS... [OPTIONS]
**   fossil purge files FILENAME... [OPTIONS]
**   fossil purge list
**   fossil purge obliterate ID...
**   fossil purge tickets NAME... [OPTIONS]
**   fossil purge undo ID
**   fossil purge wiki NAME... [OPTIONS]
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
    }
    describe_artifacts_to_stdout("IN ok", 0);
    purge_artifact_list("ok", "", purgeFlags);
    db_end_transaction(0);
  }else if( strncmp(zSubcmd, "cat", n)==0 ){
    int i, piid;
    Blob content;
    if( g.argc<4 ) usage("cat UUID...");
    for(i=3; i<g.argc; i++){
      piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
                       g.argv[i]);
      if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
      purge_extract_item(piid, &content);
      blob_write_to_file(&content, "-");
      blob_reset(&content);







|







545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
    }
    describe_artifacts_to_stdout("IN ok", 0);
    purge_artifact_list("ok", "", purgeFlags);
    db_end_transaction(0);
  }else if( strncmp(zSubcmd, "cat", n)==0 ){
    int i, piid;
    Blob content;
    if( g.argc<4 ) usage("cat HASH...");
    for(i=3; i<g.argc; i++){
      piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'",
                       g.argv[i]);
      if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]);
      purge_extract_item(piid, &content);
      blob_write_to_file(&content, "-");
      blob_reset(&content);
Changes to src/rebuild.c.
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
** Helper functions used by the `deconstruct' and `reconstruct' commands to
** save and restore the contents of the PRIVATE table.
*/
void private_export(char *zFileName)
{
  Stmt q;
  Blob fctx = empty_blob;
  blob_append(&fctx, "# The UUIDs of private artifacts\n", -1);
  db_prepare(&q,
    "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    blob_append(&fctx, zUuid, -1);
    blob_append(&fctx, "\n", -1);
  }







|







1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
** Helper functions used by the `deconstruct' and `reconstruct' commands to
** save and restore the contents of the PRIVATE table.
*/
void private_export(char *zFileName)
{
  Stmt q;
  Blob fctx = empty_blob;
  blob_append(&fctx, "# The hashes of private artifacts\n", -1);
  db_prepare(&q,
    "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    blob_append(&fctx, zUuid, -1);
    blob_append(&fctx, "\n", -1);
  }
Changes to src/report.c.
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
  void *pUser,     /* Pointer to output state */
  int nArg,        /* Number of columns in this result row */
  const char **azArg, /* Text of data in all columns */
  const char **azName /* Names of the columns */
){
  struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
  int i;
  const char *zTid;  /* Ticket UUID.  (value of column named '#') */
  const char *zBg = 0; /* Use this background color */

  /* Do initialization
  */
  if( pState->nCount==0 ){
    /* Turn off the authorizer.  It is no longer doing anything since the
    ** query has already been prepared.







|







668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
  void *pUser,     /* Pointer to output state */
  int nArg,        /* Number of columns in this result row */
  const char **azArg, /* Text of data in all columns */
  const char **azName /* Names of the columns */
){
  struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
  int i;
  const char *zTid;  /* Ticket hash.  (value of column named '#') */
  const char *zBg = 0; /* Use this background color */

  /* Do initialization
  */
  if( pState->nCount==0 ){
    /* Turn off the authorizer.  It is no longer doing anything since the
    ** query has already been prepared.
Changes to src/rss.c.
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "config.h"
#include <time.h>
#include "rss.h"
#include <assert.h>

/*
** WEBPAGE: timeline.rss
** URL:  /timeline.rss?y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME
**
** Produce an RSS feed of the timeline.
**
** TYPE may be: all, ci (show check-ins only), t (show ticket changes only),
** w (show wiki only), e (show tech notes only), f (show forum posts only),
** g (show tag/branch changes only).
**
** LIMIT is the number of items to show.
**
** tkt=UUID filters for only those events for the specified ticket. tag=TAG
** filters for a tag, and wiki=NAME for a wiki page. Only one may be used.
**
** In addition, name=FILENAME filters for a specific file. This may be
** combined with one of the other filters (useful for looking at a specific
** branch).
*/








|









|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "config.h"
#include <time.h>
#include "rss.h"
#include <assert.h>

/*
** WEBPAGE: timeline.rss
** URL:  /timeline.rss?y=TYPE&n=LIMIT&tkt=HASH&tag=TAG&wiki=NAME&name=FILENAME
**
** Produce an RSS feed of the timeline.
**
** TYPE may be: all, ci (show check-ins only), t (show ticket changes only),
** w (show wiki only), e (show tech notes only), f (show forum posts only),
** g (show tag/branch changes only).
**
** LIMIT is the number of items to show.
**
** tkt=HASH filters for only those events for the specified ticket. tag=TAG
** filters for a tag, and wiki=NAME for a wiki page. Only one may be used.
**
** In addition, name=FILENAME filters for a specific file. This may be
** combined with one of the other filters (useful for looking at a specific
** branch).
*/

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
** -type|y FLAG
**    may be: all (default), ci (show check-ins only), t (show tickets only),
**    w (show wiki only).
**
** -limit|n LIMIT
**   The maximum number of items to show.
**
** -tkt UUID
**    Filters for only those events for the specified ticket.
**
** -tag TAG
**    filters for a tag
**
** -wiki NAME
**   Filters on a specific wiki page.







|







229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
** -type|y FLAG
**    may be: all (default), ci (show check-ins only), t (show tickets only),
**    w (show wiki only).
**
** -limit|n LIMIT
**   The maximum number of items to show.
**
** -tkt HASH
**    Filters for only those events for the specified ticket.
**
** -tag TAG
**    filters for a tag
**
** -wiki NAME
**   Filters on a specific wiki page.
Changes to src/schema.c.
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
@ --
@ CREATE TABLE mlink(
@   mid INTEGER,                       -- Check-in that contains fid
@   fid INTEGER,                       -- New file content. 0 if deleted
@   pmid INTEGER,                      -- Check-in that contains pid
@   pid INTEGER,                       -- Prev file content. 0 if new. -1 merge
@   fnid INTEGER REFERENCES filename,  -- Name of the file
@   pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged
@   mperm INTEGER,                     -- File permissions.  1==exec
@   isaux BOOLEAN DEFAULT 0            -- TRUE if pmid is the primary
@ );
@ CREATE INDEX mlink_i1 ON mlink(mid);
@ CREATE INDEX mlink_i2 ON mlink(fnid);
@ CREATE INDEX mlink_i3 ON mlink(fid);
@ CREATE INDEX mlink_i4 ON mlink(pid);







|







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
@ --
@ CREATE TABLE mlink(
@   mid INTEGER,                       -- Check-in that contains fid
@   fid INTEGER,                       -- New file content. 0 if deleted
@   pmid INTEGER,                      -- Check-in that contains pid
@   pid INTEGER,                       -- Prev file content. 0 if new. -1 merge
@   fnid INTEGER REFERENCES filename,  -- Name of the file
@   pfnid INTEGER,                     -- Previous name. 0 if unchanged
@   mperm INTEGER,                     -- File permissions.  1==exec
@   isaux BOOLEAN DEFAULT 0            -- TRUE if pmid is the primary
@ );
@ CREATE INDEX mlink_i1 ON mlink(mid);
@ CREATE INDEX mlink_i2 ON mlink(fnid);
@ CREATE INDEX mlink_i3 ON mlink(fid);
@ CREATE INDEX mlink_i4 ON mlink(pid);
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
@   comment TEXT,                   -- Comment describing the event
@   brief TEXT,                     -- Short comment when tagid already seen
@   omtime DATETIME                 -- Original unchanged date+time, or NULL
@ );
@ CREATE INDEX event_i1 ON event(mtime);
@
@ -- A record of phantoms.  A phantom is a record for which we know the
@ -- UUID but we do not (yet) know the file content.
@ --
@ CREATE TABLE phantom(
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
@
@ -- A record of orphaned delta-manifests.  An orphan is a delta-manifest
@ -- for which we have content, but its baseline-manifest is a phantom.







|







319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
@   comment TEXT,                   -- Comment describing the event
@   brief TEXT,                     -- Short comment when tagid already seen
@   omtime DATETIME                 -- Original unchanged date+time, or NULL
@ );
@ CREATE INDEX event_i1 ON event(mtime);
@
@ -- A record of phantoms.  A phantom is a record for which we know the
@ -- file hash but we do not (yet) know the file content.
@ --
@ CREATE TABLE phantom(
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
@
@ -- A record of orphaned delta-manifests.  An orphan is a delta-manifest
@ -- for which we have content, but its baseline-manifest is a phantom.
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
@
@ -- Each artifact can have one or more tags.  A tag
@ -- is defined by a row in the next table.
@ --
@ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of
@ -- the wiki page.  Tickets changes are tagged with "ticket-UUID" where
@ -- UUID is the indentifier of the ticket.  Tags used to assign symbolic
@ -- names to baselines are branches are of the form "sym-NAME" where
@ -- NAME is the symbolic name.
@ --
@ CREATE TABLE tag(
@   tagid INTEGER PRIMARY KEY,       -- Numeric tag ID
@   tagname TEXT UNIQUE              -- Tag name.
@ );







|
|







360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
@   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
@ );
@
@ -- Each artifact can have one or more tags.  A tag
@ -- is defined by a row in the next table.
@ --
@ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of
@ -- the wiki page.  Tickets changes are tagged with "ticket-HASH" where
@ -- HASH is the indentifier of the ticket.  Tags used to assign symbolic
@ -- names to baselines are branches are of the form "sym-NAME" where
@ -- NAME is the symbolic name.
@ --
@ CREATE TABLE tag(
@   tagid INTEGER PRIMARY KEY,       -- Numeric tag ID
@   tagname TEXT UNIQUE              -- Tag name.
@ );
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
@ -- Each attachment is an entry in the following table.  Only
@ -- the most recent attachment (identified by the D card) is saved.
@ --
@ CREATE TABLE attachment(
@   attachid INTEGER PRIMARY KEY,   -- Local id for this attachment
@   isLatest BOOLEAN DEFAULT 0,     -- True if this is the one to use
@   mtime TIMESTAMP,                -- Last changed.  Julian day.
@   src TEXT,                       -- UUID of the attachment.  NULL to delete
@   target TEXT,                    -- Object attached to. Wikiname or Tkt UUID
@   filename TEXT,                  -- Filename for the attachment
@   comment TEXT,                   -- Comment associated with this attachment
@   user TEXT                       -- Name of user adding attachment
@ );
@ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
@ CREATE INDEX attachment_idx2 ON attachment(src);
@







|
|







419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
@ -- Each attachment is an entry in the following table.  Only
@ -- the most recent attachment (identified by the D card) is saved.
@ --
@ CREATE TABLE attachment(
@   attachid INTEGER PRIMARY KEY,   -- Local id for this attachment
@   isLatest BOOLEAN DEFAULT 0,     -- True if this is the one to use
@   mtime TIMESTAMP,                -- Last changed.  Julian day.
@   src TEXT,                       -- Hash of the attachment.  NULL to delete
@   target TEXT,                    -- Object attached to. Wikiname or Tkt hash
@   filename TEXT,                  -- Filename for the attachment
@   comment TEXT,                   -- Comment associated with this attachment
@   user TEXT                       -- Name of user adding attachment
@ );
@ CREATE INDEX attachment_idx1 ON attachment(target, filename, mtime);
@ CREATE INDEX attachment_idx2 ON attachment(src);
@
Changes to src/security_audit.c.
519
520
521
522
523
524
525







526
527
528
529
530
531
532
    ext_files();
    nFile = db_int(0, "SELECT count(*) FROM sfile");
    nCgi = nFile==0 ? 0 : db_int(0,"SELECT count(*) FROM sfile WHERE isexe");
    @ <li><p> CGI Extensions are enabled with a document root
    @ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
    @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
  }








  @ <li><p> User capability summary:
  capability_summary();


  azCSP = parse_content_security_policy();
  if( azCSP==0 ){







>
>
>
>
>
>
>







519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
    ext_files();
    nFile = db_int(0, "SELECT count(*) FROM sfile");
    nCgi = nFile==0 ? 0 : db_int(0,"SELECT count(*) FROM sfile WHERE isexe");
    @ <li><p> CGI Extensions are enabled with a document root
    @ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
    @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
  }

  if( fileedit_glob()!=0 ){
    @ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled
    @ for this repository.  Clear the 
    @ <a href='%R/setup_settings'>"fileedit-glob" setting</a> to
    @ disable online editing.</p>
  }

  @ <li><p> User capability summary:
  capability_summary();


  azCSP = parse_content_security_policy();
  if( azCSP==0 ){
Changes to src/shell.c.
9698
9699
9700
9701
9702
9703
9704
9705
9706
9707
9708
9709
9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722
9723
  char *zName;             /* Symbolic name for this session */
  int nFilter;             /* Number of xFilter rejection GLOB patterns */
  char **azFilter;         /* Array of xFilter rejection GLOB patterns */
  sqlite3_session *p;      /* The open session */
};
#endif

/*
** Shell output mode information from before ".explain on",
** saved so that it can be restored by ".explain off"
*/
typedef struct SavedModeInfo SavedModeInfo;
struct SavedModeInfo {
  int valid;          /* Is there legit data in here? */
  int mode;           /* Mode prior to ".explain on" */
  int showHeader;     /* The ".header" setting prior to ".explain on" */
  int colWidth[100];  /* Column widths prior to ".explain on" */
};

typedef struct ExpertInfo ExpertInfo;
struct ExpertInfo {
  sqlite3expert *pExpert;
  int bVerbose;
};

/* A single line in the EQP output */







<
<
<
<
<
<
<
<
<
<
<
<







9698
9699
9700
9701
9702
9703
9704












9705
9706
9707
9708
9709
9710
9711
  char *zName;             /* Symbolic name for this session */
  int nFilter;             /* Number of xFilter rejection GLOB patterns */
  char **azFilter;         /* Array of xFilter rejection GLOB patterns */
  sqlite3_session *p;      /* The open session */
};
#endif













typedef struct ExpertInfo ExpertInfo;
struct ExpertInfo {
  sqlite3expert *pExpert;
  int bVerbose;
};

/* A single line in the EQP output */
9779
9780
9781
9782
9783
9784
9785
9786
9787

9788
9789
9790
9791
9792
9793
9794
  char *zDestTable;      /* Name of destination table when MODE_Insert */
  char *zTempFile;       /* Temporary file that might need deleting */
  char zTestcase[30];    /* Name of current test case */
  char colSeparator[20]; /* Column separator character for several modes */
  char rowSeparator[20]; /* Row separator character for MODE_Ascii */
  char colSepPrior[20];  /* Saved column separator */
  char rowSepPrior[20];  /* Saved row separator */
  int colWidth[100];     /* Requested width of each column when in column mode*/
  int actualWidth[100];  /* Actual width of each column */

  char nullValue[20];    /* The text to print when a NULL comes back from
                         ** the database */
  char outfile[FILENAME_MAX]; /* Filename for *out */
  const char *zDbFilename;    /* name of the database file */
  char *zFreeOnClose;         /* Filename to free when closing */
  const char *zVfs;           /* Name of VFS to use */
  sqlite3_stmt *pStmt;   /* Current statement if any. */







|
|
>







9767
9768
9769
9770
9771
9772
9773
9774
9775
9776
9777
9778
9779
9780
9781
9782
9783
  char *zDestTable;      /* Name of destination table when MODE_Insert */
  char *zTempFile;       /* Temporary file that might need deleting */
  char zTestcase[30];    /* Name of current test case */
  char colSeparator[20]; /* Column separator character for several modes */
  char rowSeparator[20]; /* Row separator character for MODE_Ascii */
  char colSepPrior[20];  /* Saved column separator */
  char rowSepPrior[20];  /* Saved row separator */
  int *colWidth;         /* Requested width of each column in columnar modes */
  int *actualWidth;      /* Actual width of each column */
  int nWidth;            /* Number of slots in colWidth[] and actualWidth[] */
  char nullValue[20];    /* The text to print when a NULL comes back from
                         ** the database */
  char outfile[FILENAME_MAX]; /* Filename for *out */
  const char *zDbFilename;    /* name of the database file */
  char *zFreeOnClose;         /* Filename to free when closing */
  const char *zVfs;           /* Name of VFS to use */
  sqlite3_stmt *pStmt;   /* Current statement if any. */
9865
9866
9867
9868
9869
9870
9871



9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886



9887
9888
9889
9890
9891
9892
9893
#define MODE_Quote    6  /* Quote values as for SQL */
#define MODE_Tcl      7  /* Generate ANSI-C or TCL quoted elements */
#define MODE_Csv      8  /* Quote strings, numbers are plain */
#define MODE_Explain  9  /* Like MODE_Column, but do not truncate data */
#define MODE_Ascii   10  /* Use ASCII unit and record separators (0x1F/0x1E) */
#define MODE_Pretty  11  /* Pretty-print schemas */
#define MODE_EQP     12  /* Converts EXPLAIN QUERY PLAN output into a graph */




static const char *modeDescr[] = {
  "line",
  "column",
  "list",
  "semi",
  "html",
  "insert",
  "quote",
  "tcl",
  "csv",
  "explain",
  "ascii",
  "prettyprint",
  "eqp"



};

/*
** These are the column/row/line separators used by the various
** import/export modes.
*/
#define SEP_Column    "|"







>
>
>














|
>
>
>







9854
9855
9856
9857
9858
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
#define MODE_Quote    6  /* Quote values as for SQL */
#define MODE_Tcl      7  /* Generate ANSI-C or TCL quoted elements */
#define MODE_Csv      8  /* Quote strings, numbers are plain */
#define MODE_Explain  9  /* Like MODE_Column, but do not truncate data */
#define MODE_Ascii   10  /* Use ASCII unit and record separators (0x1F/0x1E) */
#define MODE_Pretty  11  /* Pretty-print schemas */
#define MODE_EQP     12  /* Converts EXPLAIN QUERY PLAN output into a graph */
#define MODE_Json    13  /* Output JSON */
#define MODE_Markdown 14 /* Markdown formatting */
#define MODE_Table   15  /* MySQL-style table formatting */

static const char *modeDescr[] = {
  "line",
  "column",
  "list",
  "semi",
  "html",
  "insert",
  "quote",
  "tcl",
  "csv",
  "explain",
  "ascii",
  "prettyprint",
  "eqp",
  "json",
  "markdown",
  "table"
};

/*
** These are the column/row/line separators used by the various
** import/export modes.
*/
#define SEP_Column    "|"
10246
10247
10248
10249
10250
10251
10252


































10253
10254
10255
10256
10257
10258
10259
      raw_printf(out, "\\%03o", c&0xff);
    }else{
      fputc(c, out);
    }
  }
  fputc('"', out);
}



































/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
  int i;







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







10241
10242
10243
10244
10245
10246
10247
10248
10249
10250
10251
10252
10253
10254
10255
10256
10257
10258
10259
10260
10261
10262
10263
10264
10265
10266
10267
10268
10269
10270
10271
10272
10273
10274
10275
10276
10277
10278
10279
10280
10281
10282
10283
10284
10285
10286
10287
10288
      raw_printf(out, "\\%03o", c&0xff);
    }else{
      fputc(c, out);
    }
  }
  fputc('"', out);
}

/*
** Output the given string as a quoted according to JSON quoting rules.
*/
static void output_json_string(FILE *out, const char *z, int n){
  unsigned int c;
  if( n<0 ) n = (int)strlen(z);
  fputc('"', out);
  while( n-- ){
    c = *(z++);
    if( c=='\\' || c=='"' ){
      fputc('\\', out);
      fputc(c, out);
    }else if( c<=0x1f ){
      fputc('\\', out);
      if( c=='\b' ){
        fputc('b', out);
      }else if( c=='\f' ){
        fputc('f', out);
      }else if( c=='\n' ){
        fputc('n', out);
      }else if( c=='\r' ){
        fputc('r', out);
      }else if( c=='\t' ){
        fputc('t', out);
      }else{
         raw_printf(out, "u%04x",c);
      }
    }else{
      fputc(c, out);
    }
  }
  fputc('"', out);
}

/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
  int i;
10555
10556
10557
10558
10559
10560
10561






























10562
10563
10564
10565
10566
10567
10568
10569
10570
10571
10572
10573
10574
10575
10576
10577
10578
10579
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599
10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620
10621
10622
10623
10624
10625
10626
10627
10628
10629
10630
10631
10632
10633
10634
10635
10636
10637
10638
10639
10640
10641
10642
10643
10644
10645
10646
10647
10648
10649
10650
10651
10652
10653
10654
10655
10656
10657
10658
10659
10660
10661
10662
10663
10664
10665
10666
10667
10668
10669
10670
10671
10672
10673
10674
  }
  if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){
    raw_printf(p->out, "Progress %u\n", p->nProgress);
  }
  return 0;
}
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */































/*
** This is the callback routine that the shell
** invokes for each row of a query result.
*/
static int shell_callback(
  void *pArg,
  int nArg,        /* Number of result columns */
  char **azArg,    /* Text of each result column */
  char **azCol,    /* Column names */
  int *aiType      /* Column types */
){
  int i;
  ShellState *p = (ShellState*)pArg;

  if( azArg==0 ) return 0;
  switch( p->cMode ){
    case MODE_Line: {
      int w = 5;
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        int len = strlen30(azCol[i] ? azCol[i] : "");
        if( len>w ) w = len;
      }
      if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator);
      for(i=0; i<nArg; i++){
        utf8_printf(p->out,"%*s = %s%s", w, azCol[i],
                azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator);
      }
      break;
    }
    case MODE_Explain:
    case MODE_Column: {
      static const int aExplainWidths[] = {4, 13, 4, 4, 4, 13, 2, 13};
      const int *colWidth;
      int showHdr;
      char *rowSep;
      int nWidth;
      if( p->cMode==MODE_Column ){
        colWidth = p->colWidth;
        nWidth = ArraySize(p->colWidth);
        showHdr = p->showHeader;
        rowSep = p->rowSeparator;
      }else{
        colWidth = aExplainWidths;
        nWidth = ArraySize(aExplainWidths);
        showHdr = 1;
        rowSep = SEP_Row;
      }
      if( p->cnt++==0 ){
        for(i=0; i<nArg; i++){
          int w, n;
          if( i<nWidth ){
            w = colWidth[i];
          }else{
            w = 0;
          }
          if( w==0 ){
            w = strlenChar(azCol[i] ? azCol[i] : "");
            if( w<10 ) w = 10;
            n = strlenChar(azArg && azArg[i] ? azArg[i] : p->nullValue);
            if( w<n ) w = n;
          }
          if( i<ArraySize(p->actualWidth) ){
            p->actualWidth[i] = w;
          }
          if( showHdr ){
            utf8_width_print(p->out, w, azCol[i]);
            utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : "  ");
          }
        }
        if( showHdr ){
          for(i=0; i<nArg; i++){
            int w;
            if( i<ArraySize(p->actualWidth) ){
               w = p->actualWidth[i];
               if( w<0 ) w = -w;
            }else{
               w = 10;
            }
            utf8_printf(p->out,"%-*.*s%s",w,w,
                   "----------------------------------------------------------"
                   "----------------------------------------------------------",
                    i==nArg-1 ? rowSep : "  ");
          }
        }
      }
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        int w;
        if( i<ArraySize(p->actualWidth) ){
           w = p->actualWidth[i];
        }else{
           w = 10;
        }
        if( p->cMode==MODE_Explain && azArg[i] && strlenChar(azArg[i])>w ){
          w = strlenChar(azArg[i]);
        }
        if( i==1 && p->aiIndent && p->pStmt ){
          if( p->iIndent<p->nIndent ){
            utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
          }
          p->iIndent++;
        }
        utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue);
        utf8_printf(p->out, "%s", i==nArg-1 ? rowSep : "  ");
      }
      break;
    }
    case MODE_Semi: {   /* .schema and .fullschema output */
      printSchemaLine(p->out, azArg[0], ";\n");
      break;
    }







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










|




















|
<
|
<
<
<
<
<
<
<
<
<
<
|
|
<
<



<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
<


<
<
|
<
<
<
|









|







10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599
10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620
10621
10622
10623
10624
10625
10626
10627
10628
10629
10630
10631
10632
10633
10634
10635
10636
10637
10638
10639
10640
10641
10642
10643
10644
10645
10646
10647
10648
10649
10650
10651
10652

10653










10654
10655


10656
10657
10658


10659













10660















10661
10662
10663

10664
10665


10666



10667
10668
10669
10670
10671
10672
10673
10674
10675
10676
10677
10678
10679
10680
10681
10682
10683
10684
  }
  if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){
    raw_printf(p->out, "Progress %u\n", p->nProgress);
  }
  return 0;
}
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */

/*
** Print N dashes
*/
static void print_dashes(FILE *out, int N){
  const char zDash[] = "--------------------------------------------------";
  const int nDash = sizeof(zDash) - 1;
  while( N>nDash ){
    fputs(zDash, out);
    N -= nDash;
  }
  raw_printf(out, "%.*s", N, zDash);
}

/*
** Print a markdown or table-style row separator
*/
static void print_row_separator(
  ShellState *p,
  int nArg,
  const char *zSep
){
  int i;
  for(i=0; i<nArg; i++){
    fputs(zSep, p->out);
    print_dashes(p->out, p->actualWidth[i]+2);
  }
  fputs(zSep, p->out);
  fputs("\n", p->out);
}

/*
** This is the callback routine that the shell
** invokes for each row of a query result.
*/
static int shell_callback(
  void *pArg,
  int nArg,        /* Number of result columns */
  char **azArg,    /* Text of each result column */
  char **azCol,    /* Column names */
  int *aiType      /* Column types.  Might be NULL */
){
  int i;
  ShellState *p = (ShellState*)pArg;

  if( azArg==0 ) return 0;
  switch( p->cMode ){
    case MODE_Line: {
      int w = 5;
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        int len = strlen30(azCol[i] ? azCol[i] : "");
        if( len>w ) w = len;
      }
      if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator);
      for(i=0; i<nArg; i++){
        utf8_printf(p->out,"%*s = %s%s", w, azCol[i],
                azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator);
      }
      break;
    }
    case MODE_Explain: {

      static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13};










      if( nArg>ArraySize(aExplainWidth) ){
        nArg = ArraySize(aExplainWidth);


      }
      if( p->cnt++==0 ){
        for(i=0; i<nArg; i++){


          int w = aExplainWidth[i];













          utf8_width_print(p->out, w, azCol[i]);















          fputs(i==nArg-1 ? "\n" : "  ", p->out);
        }
      }

      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){


        int w = aExplainWidth[i];



        if( azArg[i] && strlenChar(azArg[i])>w ){
          w = strlenChar(azArg[i]);
        }
        if( i==1 && p->aiIndent && p->pStmt ){
          if( p->iIndent<p->nIndent ){
            utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
          }
          p->iIndent++;
        }
        utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue);
        fputs(i==nArg-1 ? "\n" : "  ", p->out);
      }
      break;
    }
    case MODE_Semi: {   /* .schema and .fullschema output */
      printSchemaLine(p->out, azArg[0], ";\n");
      break;
    }
10863
10864
10865
10866
10867
10868
10869
10870
10871
10872
10873





10874










10875



























10876
10877
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
          output_quoted_string(p->out, azArg[i]);
        }else{
          output_quoted_escaped_string(p->out, azArg[i]);
        }
      }
      raw_printf(p->out,");\n");
      break;
    }
    case MODE_Quote: {
      if( azArg==0 ) break;
      if( p->cnt==0 && p->showHeader ){





        for(i=0; i<nArg; i++){










          if( i>0 ) raw_printf(p->out, ",");



























          output_quoted_string(p->out, azCol[i]);
        }
        raw_printf(p->out,"\n");
      }
      p->cnt++;
      for(i=0; i<nArg; i++){
        if( i>0 ) raw_printf(p->out, ",");
        if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
          utf8_printf(p->out,"NULL");
        }else if( aiType && aiType[i]==SQLITE_TEXT ){
          output_quoted_string(p->out, azArg[i]);
        }else if( aiType && aiType[i]==SQLITE_INTEGER ){
          utf8_printf(p->out,"%s", azArg[i]);
        }else if( aiType && aiType[i]==SQLITE_FLOAT ){








|

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


|



|







10873
10874
10875
10876
10877
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
10890
10891
10892
10893
10894
10895
10896
10897
10898
10899
10900
10901
10902
10903
10904
10905
10906
10907
10908
10909
10910
10911
10912
10913
10914
10915
10916
10917
10918
10919
10920
10921
10922
10923
10924
10925
10926
10927
10928
10929
10930
10931
10932
10933
10934
10935
10936
10937
10938
10939
10940
10941
          output_quoted_string(p->out, azArg[i]);
        }else{
          output_quoted_escaped_string(p->out, azArg[i]);
        }
      }
      raw_printf(p->out,");\n");
      break;
    }
    case MODE_Json: {
      if( azArg==0 ) break;
      if( p->cnt==0 ){
        fputs("[{", p->out);
      }else{
        fputs(",\n{", p->out);
      }
      p->cnt++;
      for(i=0; i<nArg; i++){
        output_json_string(p->out, azCol[i], -1);
        putc(':', p->out);
        if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
          fputs("null",p->out);
        }else if( aiType && aiType[i]==SQLITE_FLOAT ){
          char z[50];
          double r = sqlite3_column_double(p->pStmt, i);
          sqlite3_uint64 ur;
          memcpy(&ur,&r,sizeof(r));
          if( ur==0x7ff0000000000000LL ){
            raw_printf(p->out, "1e999");
          }else if( ur==0xfff0000000000000LL ){
            raw_printf(p->out, "-1e999");
          }else{
            sqlite3_snprintf(50,z,"%!.20g", r);
            raw_printf(p->out, "%s", z);
          }
        }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
          const void *pBlob = sqlite3_column_blob(p->pStmt, i);
          int nBlob = sqlite3_column_bytes(p->pStmt, i);
          output_json_string(p->out, pBlob, nBlob);
        }else if( aiType && aiType[i]==SQLITE_TEXT ){
          output_json_string(p->out, azArg[i], -1);
        }else{
          utf8_printf(p->out,"%s", azArg[i]);
        }
        if( i<nArg-1 ){
          putc(',', p->out);
        }
      }
      putc('}', p->out);
      break;
    }
    case MODE_Quote: {
      if( azArg==0 ) break;
      if( p->cnt==0 && p->showHeader ){
        for(i=0; i<nArg; i++){
          if( i>0 ) fputs(p->colSeparator, p->out);
          output_quoted_string(p->out, azCol[i]);
        }
        fputs(p->rowSeparator, p->out);
      }
      p->cnt++;
      for(i=0; i<nArg; i++){
        if( i>0 ) fputs(p->colSeparator, p->out);
        if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
          utf8_printf(p->out,"NULL");
        }else if( aiType && aiType[i]==SQLITE_TEXT ){
          output_quoted_string(p->out, azArg[i]);
        }else if( aiType && aiType[i]==SQLITE_INTEGER ){
          utf8_printf(p->out,"%s", azArg[i]);
        }else if( aiType && aiType[i]==SQLITE_FLOAT ){
10897
10898
10899
10900
10901
10902
10903
10904
10905
10906
10907
10908
10909
10910
10911
          output_hex_blob(p->out, pBlob, nBlob);
        }else if( isNumber(azArg[i], 0) ){
          utf8_printf(p->out,"%s", azArg[i]);
        }else{
          output_quoted_string(p->out, azArg[i]);
        }
      }
      raw_printf(p->out,"\n");
      break;
    }
    case MODE_Ascii: {
      if( p->cnt++==0 && p->showHeader ){
        for(i=0; i<nArg; i++){
          if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator);
          utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : "");







|







10949
10950
10951
10952
10953
10954
10955
10956
10957
10958
10959
10960
10961
10962
10963
          output_hex_blob(p->out, pBlob, nBlob);
        }else if( isNumber(azArg[i], 0) ){
          utf8_printf(p->out,"%s", azArg[i]);
        }else{
          output_quoted_string(p->out, azArg[i]);
        }
      }
      fputs(p->rowSeparator, p->out);
      break;
    }
    case MODE_Ascii: {
      if( p->cnt++==0 && p->showHeader ){
        for(i=0; i<nArg; i++){
          if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator);
          utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : "");
11552
11553
11554
11555
11556
11557
11558
11559
11560




































































































11561
11562
11563
11564
11565
11566
11567








11568
11569
11570
11571
11572
11573
11574
    }else{
      sqlite3_bind_null(pStmt, i);
    }
    sqlite3_reset(pQ);
  }
  sqlite3_finalize(pQ);
}

/*




































































































** Run a prepared statement
*/
static void exec_prepared_stmt(
  ShellState *pArg,                                /* Pointer to ShellState */
  sqlite3_stmt *pStmt                              /* Statment to run */
){
  int rc;









  /* perform the first step.  this will tell us if we
  ** have a result set or not and how wide it is.
  */
  rc = sqlite3_step(pStmt);
  /* if we have a result set... */
  if( SQLITE_ROW == rc ){









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







>
>
>
>
>
>
>
>







11604
11605
11606
11607
11608
11609
11610
11611
11612
11613
11614
11615
11616
11617
11618
11619
11620
11621
11622
11623
11624
11625
11626
11627
11628
11629
11630
11631
11632
11633
11634
11635
11636
11637
11638
11639
11640
11641
11642
11643
11644
11645
11646
11647
11648
11649
11650
11651
11652
11653
11654
11655
11656
11657
11658
11659
11660
11661
11662
11663
11664
11665
11666
11667
11668
11669
11670
11671
11672
11673
11674
11675
11676
11677
11678
11679
11680
11681
11682
11683
11684
11685
11686
11687
11688
11689
11690
11691
11692
11693
11694
11695
11696
11697
11698
11699
11700
11701
11702
11703
11704
11705
11706
11707
11708
11709
11710
11711
11712
11713
11714
11715
11716
11717
11718
11719
11720
11721
11722
11723
11724
11725
11726
11727
11728
11729
11730
11731
11732
11733
11734
    }else{
      sqlite3_bind_null(pStmt, i);
    }
    sqlite3_reset(pQ);
  }
  sqlite3_finalize(pQ);
}

/*
** Run a prepared statement and output the result in one of the
** table-oriented formats: MODE_Column, MODE_Markdown, or MODE_Table.
**
** This is different from ordinary exec_prepared_stmt() in that
** it has to run the entire query and gather the results into memory
** first, in order to determine column widths, before providing
** any output.
*/
static void exec_prepared_stmt_columnar(
  ShellState *p,                        /* Pointer to ShellState */
  sqlite3_stmt *pStmt                   /* Statment to run */
){
  int nRow = 0;
  int nColumn = 0;
  char **azData = 0;
  char *zMsg = 0;
  const char *z;
  int rc;
  int i, j, nTotal, w, n;
  const char *colSep;
  const char *rowSep;

  rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt),
                         &azData, &nRow, &nColumn, &zMsg);
  if( rc ){
    utf8_printf(p->out, "ERROR: %s\n", zMsg);
    sqlite3_free(zMsg);
    sqlite3_free_table(azData);
    return;
  }
  if( nColumn>p->nWidth ){
    p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int));
    if( p->colWidth==0 ) shell_out_of_memory();
    for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0;
    p->nWidth = nColumn;
    p->actualWidth = &p->colWidth[nColumn];
  }
  memset(p->actualWidth, 0, nColumn*sizeof(int));
  for(i=0; i<nColumn; i++){
    w = p->colWidth[i];
    if( w<0 ) w = -w;
    p->actualWidth[i] = w;
  }
  nTotal = nColumn*(nRow+1);
  for(i=0; i<nTotal; i++){
    z = azData[i];
    if( z==0 ) z = p->nullValue;
    n = strlenChar(z);
    j = i%nColumn;
    if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
  }
  if( p->cMode==MODE_Column ){
    colSep = "  ";
    rowSep = "\n";
    if( p->showHeader ){
      for(i=0; i<nColumn; i++){
        w = p->actualWidth[i];
        if( p->colWidth[i]<0 ) w = -w;
        utf8_width_print(p->out, w, azData[i]);
        fputs(i==nColumn-1?"\n":"  ", p->out);
      }
      for(i=0; i<nColumn; i++){
        print_dashes(p->out, p->actualWidth[i]);
        fputs(i==nColumn-1?"\n":"  ", p->out);
      }
    }
  }else{
    colSep = " | ";
    rowSep = " |\n";
    if( p->cMode==MODE_Table ) print_row_separator(p, nColumn, "+");
    fputs("| ", p->out);
    for(i=0; i<nColumn; i++){
      w = p->actualWidth[i];
      n = strlenChar(azData[i]);
      utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
      fputs(i==nColumn-1?" |\n":" | ", p->out);
    }
    print_row_separator(p, nColumn, p->cMode==MODE_Table ? "+" : "|");
  }
  for(i=nColumn, j=0; i<nTotal; i++, j++){
    if( j==0 && p->cMode!=MODE_Column ) fputs("| ", p->out);
    z = azData[i];
    if( z==0 ) z = p->nullValue;
    w = p->actualWidth[j];
    if( p->colWidth[j]<0 ) w = -w;
    utf8_width_print(p->out, w, z);
    if( j==nColumn-1 ){
      fputs(rowSep, p->out);
      j = -1;
    }else{
      fputs(colSep, p->out);
    }
  }
  if( p->cMode==MODE_Table ){
    print_row_separator(p, nColumn, "+");
  }
  sqlite3_free_table(azData);
}

/*
** Run a prepared statement
*/
static void exec_prepared_stmt(
  ShellState *pArg,                                /* Pointer to ShellState */
  sqlite3_stmt *pStmt                              /* Statment to run */
){
  int rc;

  if( pArg->cMode==MODE_Column
   || pArg->cMode==MODE_Table
   || pArg->cMode==MODE_Markdown
  ){
    exec_prepared_stmt_columnar(pArg, pStmt);
    return;
  }

  /* perform the first step.  this will tell us if we
  ** have a result set or not and how wide it is.
  */
  rc = sqlite3_step(pStmt);
  /* if we have a result set... */
  if( SQLITE_ROW == rc ){
11609
11610
11611
11612
11613
11614
11615





11616
11617
11618
11619
11620
11621
11622
            rc = SQLITE_ABORT;
          }else{
            rc = sqlite3_step(pStmt);
          }
        }
      } while( SQLITE_ROW == rc );
      sqlite3_free(pData);





    }
  }
}

#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** This function is called to process SQL if the previous shell command







>
>
>
>
>







11769
11770
11771
11772
11773
11774
11775
11776
11777
11778
11779
11780
11781
11782
11783
11784
11785
11786
11787
            rc = SQLITE_ABORT;
          }else{
            rc = sqlite3_step(pStmt);
          }
        }
      } while( SQLITE_ROW == rc );
      sqlite3_free(pData);
      if( pArg->cMode==MODE_Table ){
        print_row_separator(pArg, nCol, "+");
      }else if( pArg->cMode==MODE_Json ){
        fputs("]\n", pArg->out);
      }
    }
  }
}

#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** This function is called to process SQL if the previous shell command
16881
16882
16883
16884
16885
16886
16887


16888
16889
16890
16891






16892
16893
16894
16895
16896

16897
16898
16899
16900
16901
16902
16903
      p->mode = MODE_List;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
    }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
      p->mode = MODE_Insert;
      set_table_name(p, nArg>=3 ? azArg[2] : "table");
    }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){
      p->mode = MODE_Quote;


    }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){
      p->mode = MODE_Ascii;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);






    }else if( nArg==1 ){
      raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
    }else{
      raw_printf(stderr, "Error: mode should be one of: "
         "ascii column csv html insert line list quote tabs tcl\n");

      rc = 1;
    }
    p->cMode = p->mode;
  }else

  if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
    if( nArg==2 ){







>
>




>
>
>
>
>
>




|
>







17046
17047
17048
17049
17050
17051
17052
17053
17054
17055
17056
17057
17058
17059
17060
17061
17062
17063
17064
17065
17066
17067
17068
17069
17070
17071
17072
17073
17074
17075
17076
17077
      p->mode = MODE_List;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
    }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
      p->mode = MODE_Insert;
      set_table_name(p, nArg>=3 ? azArg[2] : "table");
    }else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){
      p->mode = MODE_Quote;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
    }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){
      p->mode = MODE_Ascii;
      sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
      sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
    }else if( c2=='m' && strncmp(azArg[1],"markdown",n2)==0 ){
      p->mode = MODE_Markdown;
    }else if( c2=='t' && strncmp(azArg[1],"table",n2)==0 ){
      p->mode = MODE_Table;
    }else if( c2=='j' && strncmp(azArg[1],"json",n2)==0 ){
      p->mode = MODE_Json;
    }else if( nArg==1 ){
      raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
    }else{
      raw_printf(stderr, "Error: mode should be one of: "
         "ascii column csv html insert json line list markdown "
         "quote table tabs tcl\n");
      rc = 1;
    }
    p->cMode = p->mode;
  }else

  if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
    if( nArg==2 ){
17990
17991
17992
17993
17994
17995
17996
17997
17998
17999
18000
18001
18002
18003
18004
      output_c_string(p->out, p->colSeparator);
      raw_printf(p->out, "\n");
    utf8_printf(p->out,"%12.12s: ", "rowseparator");
      output_c_string(p->out, p->rowSeparator);
      raw_printf(p->out, "\n");
    utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]);
    utf8_printf(p->out, "%12.12s: ", "width");
    for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
      raw_printf(p->out, "%d ", p->colWidth[i]);
    }
    raw_printf(p->out, "\n");
    utf8_printf(p->out, "%12.12s: %s\n", "filename",
                p->zDbFilename ? p->zDbFilename : "");
  }else








|







18164
18165
18166
18167
18168
18169
18170
18171
18172
18173
18174
18175
18176
18177
18178
      output_c_string(p->out, p->colSeparator);
      raw_printf(p->out, "\n");
    utf8_printf(p->out,"%12.12s: ", "rowseparator");
      output_c_string(p->out, p->rowSeparator);
      raw_printf(p->out, "\n");
    utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]);
    utf8_printf(p->out, "%12.12s: ", "width");
    for (i=0;i<p->nWidth;i++) {
      raw_printf(p->out, "%d ", p->colWidth[i]);
    }
    raw_printf(p->out, "\n");
    utf8_printf(p->out, "%12.12s: %s\n", "filename",
                p->zDbFilename ? p->zDbFilename : "");
  }else

18539
18540
18541
18542
18543
18544
18545




18546
18547
18548
18549
18550
18551
18552
18553
    sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;
  }else
#endif

  if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
    int j;
    assert( nArg<=ArraySize(azArg) );




    for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
      p->colWidth[j-1] = (int)integerValue(azArg[j]);
    }
  }else

  {
    utf8_printf(stderr, "Error: unknown command or invalid arguments: "
      " \"%s\". Enter \".help\" for help\n", azArg[0]);







>
>
>
>
|







18713
18714
18715
18716
18717
18718
18719
18720
18721
18722
18723
18724
18725
18726
18727
18728
18729
18730
18731
    sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;
  }else
#endif

  if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
    int j;
    assert( nArg<=ArraySize(azArg) );
    p->nWidth = nArg-1;
    p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2);
    if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
    if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
    for(j=1; j<nArg; j++){
      p->colWidth[j-1] = (int)integerValue(azArg[j]);
    }
  }else

  {
    utf8_printf(stderr, "Error: unknown command or invalid arguments: "
      " \"%s\". Enter \".help\" for help\n", azArg[0]);
18899
18900
18901
18902
18903
18904
18905

18906
18907
18908

18909
18910
18911
18912
18913
18914
18915
18916
18917
18918
18919
18920
18921
18922
18923
18924
18925
18926
18927

18928
18929
18930
18931
18932
18933
18934
  "   -[no]header          turn headers on or off\n"
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
  "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
#endif
  "   -help                show this message\n"
  "   -html                set output mode to HTML\n"
  "   -interactive         force interactive I/O\n"

  "   -line                set output mode to 'line'\n"
  "   -list                set output mode to 'list'\n"
  "   -lookaside SIZE N    use N entries of SZ bytes for lookaside memory\n"

#if defined(SQLITE_ENABLE_DESERIALIZE)
  "   -maxsize N           maximum size for a --deserialize database\n"
#endif
  "   -memtrace            trace all memory allocations and deallocations\n"
  "   -mmap N              default mmap size set to N\n"
#ifdef SQLITE_ENABLE_MULTIPLEX
  "   -multiplex           enable the multiplexor VFS\n"
#endif
  "   -newline SEP         set output row separator. Default: '\\n'\n"
  "   -nofollow            refuse to open symbolic links to database files\n"
  "   -nullvalue TEXT      set text string for NULL values. Default ''\n"
  "   -pagecache SIZE N    use N slots of SZ bytes each for page cache memory\n"
  "   -quote               set output mode to 'quote'\n"
  "   -readonly            open the database read-only\n"
  "   -separator SEP       set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
  "   -sorterref SIZE      sorter references threshold size\n"
#endif
  "   -stats               print memory stats before each finalize\n"

  "   -version             show SQLite version\n"
  "   -vfs NAME            use NAME as the default VFS\n"
#ifdef SQLITE_ENABLE_VFSTRACE
  "   -vfstrace            enable tracing of all VFS calls\n"
#endif
#ifdef SQLITE_HAVE_ZLIB
  "   -zip                 open the file as a ZIP Archive\n"







>



>



















>







19077
19078
19079
19080
19081
19082
19083
19084
19085
19086
19087
19088
19089
19090
19091
19092
19093
19094
19095
19096
19097
19098
19099
19100
19101
19102
19103
19104
19105
19106
19107
19108
19109
19110
19111
19112
19113
19114
19115
  "   -[no]header          turn headers on or off\n"
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
  "   -heap SIZE           Size of heap for memsys3 or memsys5\n"
#endif
  "   -help                show this message\n"
  "   -html                set output mode to HTML\n"
  "   -interactive         force interactive I/O\n"
  "   -json                set output mode to 'json'\n"
  "   -line                set output mode to 'line'\n"
  "   -list                set output mode to 'list'\n"
  "   -lookaside SIZE N    use N entries of SZ bytes for lookaside memory\n"
  "   -markdown            set output mode to 'markdown'\n"
#if defined(SQLITE_ENABLE_DESERIALIZE)
  "   -maxsize N           maximum size for a --deserialize database\n"
#endif
  "   -memtrace            trace all memory allocations and deallocations\n"
  "   -mmap N              default mmap size set to N\n"
#ifdef SQLITE_ENABLE_MULTIPLEX
  "   -multiplex           enable the multiplexor VFS\n"
#endif
  "   -newline SEP         set output row separator. Default: '\\n'\n"
  "   -nofollow            refuse to open symbolic links to database files\n"
  "   -nullvalue TEXT      set text string for NULL values. Default ''\n"
  "   -pagecache SIZE N    use N slots of SZ bytes each for page cache memory\n"
  "   -quote               set output mode to 'quote'\n"
  "   -readonly            open the database read-only\n"
  "   -separator SEP       set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
  "   -sorterref SIZE      sorter references threshold size\n"
#endif
  "   -stats               print memory stats before each finalize\n"
  "   -table               set output mode to 'table'\n"
  "   -version             show SQLite version\n"
  "   -vfs NAME            use NAME as the default VFS\n"
#ifdef SQLITE_ENABLE_VFSTRACE
  "   -vfstrace            enable tracing of all VFS calls\n"
#endif
#ifdef SQLITE_HAVE_ZLIB
  "   -zip                 open the file as a ZIP Archive\n"
19322
19323
19324
19325
19326
19327
19328






19329
19330
19331
19332
19333
19334
19335
      data.mode = MODE_List;
    }else if( strcmp(z,"-quote")==0 ){
      data.mode = MODE_Quote;
    }else if( strcmp(z,"-line")==0 ){
      data.mode = MODE_Line;
    }else if( strcmp(z,"-column")==0 ){
      data.mode = MODE_Column;






    }else if( strcmp(z,"-csv")==0 ){
      data.mode = MODE_Csv;
      memcpy(data.colSeparator,",",2);
#ifdef SQLITE_HAVE_ZLIB
    }else if( strcmp(z,"-zip")==0 ){
      data.openMode = SHELL_OPEN_ZIPFILE;
#endif







>
>
>
>
>
>







19503
19504
19505
19506
19507
19508
19509
19510
19511
19512
19513
19514
19515
19516
19517
19518
19519
19520
19521
19522
      data.mode = MODE_List;
    }else if( strcmp(z,"-quote")==0 ){
      data.mode = MODE_Quote;
    }else if( strcmp(z,"-line")==0 ){
      data.mode = MODE_Line;
    }else if( strcmp(z,"-column")==0 ){
      data.mode = MODE_Column;
    }else if( strcmp(z,"-json")==0 ){
      data.mode = MODE_Json;
    }else if( strcmp(z,"-markdown")==0 ){
      data.mode = MODE_Markdown;
    }else if( strcmp(z,"-table")==0 ){
      data.mode = MODE_Table;
    }else if( strcmp(z,"-csv")==0 ){
      data.mode = MODE_Csv;
      memcpy(data.colSeparator,",",2);
#ifdef SQLITE_HAVE_ZLIB
    }else if( strcmp(z,"-zip")==0 ){
      data.openMode = SHELL_OPEN_ZIPFILE;
#endif
19539
19540
19541
19542
19543
19544
19545

19546
19547
19548
19549
19550
  output_reset(&data);
  data.doXdgOpen = 0;
  clearTempFile(&data);
#if !SQLITE_SHELL_IS_UTF8
  for(i=0; i<argcToFree; i++) free(argvToFree[i]);
  free(argvToFree);
#endif

  /* Clear the global data structure so that valgrind will detect memory
  ** leaks */
  memset(&data, 0, sizeof(data));
  return rc;
}







>





19726
19727
19728
19729
19730
19731
19732
19733
19734
19735
19736
19737
19738
  output_reset(&data);
  data.doXdgOpen = 0;
  clearTempFile(&data);
#if !SQLITE_SHELL_IS_UTF8
  for(i=0; i<argcToFree; i++) free(argvToFree[i]);
  free(argvToFree);
#endif
  free(data.colWidth);
  /* Clear the global data structure so that valgrind will detect memory
  ** leaks */
  memset(&data, 0, sizeof(data));
  return rc;
}
Changes to src/smtp.c.
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
    if( strcmp(zCmd,"capa")==0 ){
      static const char *const azCap[] = {
          "TOP", "USER", "UIDL",
      };
      int i;
      pop3_print(pLog, "+OK");
      for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){
        pop3_print(pLog, azCap[i]);
      }
      pop3_print(pLog, ".");
      continue;
    }
    if( inAuth ){
      if( strcmp(zCmd,"user")==0 ){
        if( zA1==0 || zA2!=0 ) goto cmd_error;







|







1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
    if( strcmp(zCmd,"capa")==0 ){
      static const char *const azCap[] = {
          "TOP", "USER", "UIDL",
      };
      int i;
      pop3_print(pLog, "+OK");
      for(i=0; i<sizeof(azCap)/sizeof(azCap[0]); i++){
        pop3_print(pLog, "%s", azCap[i]);
      }
      pop3_print(pLog, ".");
      continue;
    }
    if( inAuth ){
      if( strcmp(zCmd,"user")==0 ){
        if( zA1==0 || zA2!=0 ) goto cmd_error;
Changes to src/sqlite3.c.
1
2
3
4
5
6
7
8
9
10
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.32.1.  By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit.  This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately.  Performance improvements
** of 5% or more are commonly seen when SQLite is compiled as a single
** translation unit.
**
** This file is all you need to compile SQLite.  To use SQLite in other


|







1
2
3
4
5
6
7
8
9
10
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.33.0.  By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit.  This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately.  Performance improvements
** of 5% or more are commonly seen when SQLite is compiled as a single
** translation unit.
**
** This file is all you need to compile SQLite.  To use SQLite in other
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.32.1"
#define SQLITE_VERSION_NUMBER 3032001
#define SQLITE_SOURCE_ID      "2020-05-25 16:19:56 0c1fcf4711a2e66c813aed38cf41cd3e2123ee8eb6db98118086764c4ba83350"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros







|
|
|







1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.33.0"
#define SQLITE_VERSION_NUMBER 3033000
#define SQLITE_SOURCE_ID      "2020-05-29 16:15:58 4e1db8e9a9ee370a398f13fd8546a520111b8cfb84460389535b5bc5bd9f4f82"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
38283
38284
38285
38286
38287
38288
38289
38290



38291
38292
38293
38294
38295
38296
38297
  **
  **   1. Checkpointer lock (ofst==1).
  **   2. Write lock (ofst==0).
  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.  */



#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
         (ofst!=2)                                   /* not RECOVER */
      && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
      && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
      && (ofst<3  || (p->exclMask|p->sharedMask)<(1<<ofst))
  ));







|
>
>
>







38283
38284
38285
38286
38287
38288
38289
38290
38291
38292
38293
38294
38295
38296
38297
38298
38299
38300
  **
  **   1. Checkpointer lock (ofst==1).
  **   2. Write lock (ofst==0).
  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.  
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
         (ofst!=2)                                   /* not RECOVER */
      && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
      && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
      && (ofst<3  || (p->exclMask|p->sharedMask)<(1<<ofst))
  ));
80833
80834
80835
80836
80837
80838
80839

80840



80841
80842
80843
80844
80845
80846
80847
80848
80849
80850
80851
80852
80853
80854
80855
80856
80857
80858
80859

80860
80861

80862
80863
80864
80865
80866
80867
80868
  /* If the VDBE has been run even partially, then transfer the error code
  ** and error message from the VDBE into the main database structure.  But
  ** if the VDBE has just been set to run but has not actually executed any
  ** instructions yet, leave the main database error information unchanged.
  */
  if( p->pc>=0 ){
    vdbeInvokeSqllog(p);

    sqlite3VdbeTransferError(p);



    if( p->runOnlyOnce ) p->expired = 1;
  }else if( p->rc && p->expired ){
    /* The expired flag was set on the VDBE before the first call
    ** to sqlite3_step(). For consistency (since sqlite3_step() was
    ** called), set the database error in this case as well.
    */
    sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
  }

  /* Reset register contents and reclaim error message memory.
  */
#ifdef SQLITE_DEBUG
  /* Execute assert() statements to ensure that the Vdbe.apCsr[] and 
  ** Vdbe.aMem[] arrays have already been cleaned up.  */
  if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
  if( p->aMem ){
    for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
  }
#endif

  sqlite3DbFree(db, p->zErrMsg);
  p->zErrMsg = 0;

  p->pResultSet = 0;
#ifdef SQLITE_DEBUG
  p->nWrite = 0;
#endif

  /* Save profiling information from this VDBE run.
  */







>
|
>
>
>



















>
|
|
>







80836
80837
80838
80839
80840
80841
80842
80843
80844
80845
80846
80847
80848
80849
80850
80851
80852
80853
80854
80855
80856
80857
80858
80859
80860
80861
80862
80863
80864
80865
80866
80867
80868
80869
80870
80871
80872
80873
80874
80875
80876
80877
  /* If the VDBE has been run even partially, then transfer the error code
  ** and error message from the VDBE into the main database structure.  But
  ** if the VDBE has just been set to run but has not actually executed any
  ** instructions yet, leave the main database error information unchanged.
  */
  if( p->pc>=0 ){
    vdbeInvokeSqllog(p);
    if( db->pErr || p->zErrMsg ){
      sqlite3VdbeTransferError(p);
    }else{
      db->errCode = p->rc;
    }
    if( p->runOnlyOnce ) p->expired = 1;
  }else if( p->rc && p->expired ){
    /* The expired flag was set on the VDBE before the first call
    ** to sqlite3_step(). For consistency (since sqlite3_step() was
    ** called), set the database error in this case as well.
    */
    sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
  }

  /* Reset register contents and reclaim error message memory.
  */
#ifdef SQLITE_DEBUG
  /* Execute assert() statements to ensure that the Vdbe.apCsr[] and 
  ** Vdbe.aMem[] arrays have already been cleaned up.  */
  if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
  if( p->aMem ){
    for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
  }
#endif
  if( p->zErrMsg ){
    sqlite3DbFree(db, p->zErrMsg);
    p->zErrMsg = 0;
  }
  p->pResultSet = 0;
#ifdef SQLITE_DEBUG
  p->nWrite = 0;
#endif

  /* Save profiling information from this VDBE run.
  */
102030
102031
102032
102033
102034
102035
102036
102037
102038
102039
102040
102041
102042
102043
102044
102045
102046
102047

102048
102049
102050
102051
102052
102053
102054

  /* Check to see if an existing table or index can be used to
  ** satisfy the query.  This is preferable to generating a new 
  ** ephemeral table.  */
  if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
    sqlite3 *db = pParse->db;              /* Database connection */
    Table *pTab;                           /* Table <table>. */
    i16 iDb;                               /* Database idx for pTab */
    ExprList *pEList = p->pEList;
    int nExpr = pEList->nExpr;

    assert( p->pEList!=0 );             /* Because of isCandidateForInOpt(p) */
    assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */
    assert( p->pSrc!=0 );               /* Because of isCandidateForInOpt(p) */
    pTab = p->pSrc->a[0].pTab;

    /* Code an OP_Transaction and OP_TableLock for <table>. */
    iDb = sqlite3SchemaToIndex(db, pTab->pSchema);

    sqlite3CodeVerifySchema(pParse, iDb);
    sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);

    assert(v);  /* sqlite3GetVdbe() has always been previously called */
    if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
      /* The "x IN (SELECT rowid FROM table)" case */
      int iAddr = sqlite3VdbeAddOp0(v, OP_Once);







|










>







102039
102040
102041
102042
102043
102044
102045
102046
102047
102048
102049
102050
102051
102052
102053
102054
102055
102056
102057
102058
102059
102060
102061
102062
102063
102064

  /* Check to see if an existing table or index can be used to
  ** satisfy the query.  This is preferable to generating a new 
  ** ephemeral table.  */
  if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
    sqlite3 *db = pParse->db;              /* Database connection */
    Table *pTab;                           /* Table <table>. */
    int iDb;                               /* Database idx for pTab */
    ExprList *pEList = p->pEList;
    int nExpr = pEList->nExpr;

    assert( p->pEList!=0 );             /* Because of isCandidateForInOpt(p) */
    assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */
    assert( p->pSrc!=0 );               /* Because of isCandidateForInOpt(p) */
    pTab = p->pSrc->a[0].pTab;

    /* Code an OP_Transaction and OP_TableLock for <table>. */
    iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
    assert( iDb>=0 && iDb<SQLITE_MAX_ATTACHED );
    sqlite3CodeVerifySchema(pParse, iDb);
    sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);

    assert(v);  /* sqlite3GetVdbe() has always been previously called */
    if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
      /* The "x IN (SELECT rowid FROM table)" case */
      int iAddr = sqlite3VdbeAddOp0(v, OP_Once);
103274
103275
103276
103277
103278
103279
103280
103281
103282
103283
103284
103285
103286
103287
103288
103289
103290
103291
  int regFree1 = 0;         /* If non-zero free this temporary register */
  int regFree2 = 0;         /* If non-zero free this temporary register */
  int r1, r2;               /* Various register numbers */
  Expr tempX;               /* Temporary expression node */
  int p5 = 0;

  assert( target>0 && target<=pParse->nMem );
  if( v==0 ){
    assert( pParse->db->mallocFailed );
    return 0;
  }

expr_code_doover:
  if( pExpr==0 ){
    op = TK_NULL;
  }else{
    assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
    op = pExpr->op;







<
|
<
<







103284
103285
103286
103287
103288
103289
103290

103291


103292
103293
103294
103295
103296
103297
103298
  int regFree1 = 0;         /* If non-zero free this temporary register */
  int regFree2 = 0;         /* If non-zero free this temporary register */
  int r1, r2;               /* Various register numbers */
  Expr tempX;               /* Temporary expression node */
  int p5 = 0;

  assert( target>0 && target<=pParse->nMem );

  assert( v!=0 );



expr_code_doover:
  if( pExpr==0 ){
    op = TK_NULL;
  }else{
    assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
    op = pExpr->op;
104107
104108
104109
104110
104111
104112
104113
104114
104115


104116
104117
104118
104119
104120
104121
104122
104123
** in register target.
*/
SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
  int inReg;

  assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
  assert( target>0 && target<=pParse->nMem );
  inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
  assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );


  if( inReg!=target && pParse->pVdbe ){
    u8 op;
    if( ExprHasProperty(pExpr,EP_Subquery) ){
      op = OP_Copy;
    }else{
      op = OP_SCopy;
    }
    sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);







<

>
>
|







104114
104115
104116
104117
104118
104119
104120

104121
104122
104123
104124
104125
104126
104127
104128
104129
104130
104131
** in register target.
*/
SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
  int inReg;

  assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
  assert( target>0 && target<=pParse->nMem );

  assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
  if( pParse->pVdbe==0 ) return;
  inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
  if( inReg!=target ){
    u8 op;
    if( ExprHasProperty(pExpr,EP_Subquery) ){
      op = OP_Copy;
    }else{
      op = OP_SCopy;
    }
    sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);
114089
114090
114091
114092
114093
114094
114095
114096
114097
114098

114099
114100
114101
114102
114103
114104
114105
114106
114107







114108




114109
114110
114111
114112
114113
114114
114115
114116
114117
**           aiRowEst[N]>=1
**
** Apart from that, we have little to go on besides intuition as to
** how aiRowEst[] should be initialized.  The numbers generated here
** are based on typical values found in actual indices.
*/
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){
  /*                10,  9,  8,  7,  6 */
  LogEst aVal[] = { 33, 32, 30, 28, 26 };
  LogEst *a = pIdx->aiRowLogEst;

  int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
  int i;

  /* Indexes with default row estimates should not have stat1 data */
  assert( !pIdx->hasStat1 );

  /* Set the first entry (number of rows in the index) to the estimated 
  ** number of rows in the table, or half the number of rows in the table
  ** for a partial index.   But do not let the estimate drop below 10. */







  a[0] = pIdx->pTable->nRowLogEst;




  if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10;  assert( 10==sqlite3LogEst(2) );
  if( a[0]<33 ) a[0] = 33;                  assert( 33==sqlite3LogEst(10) );

  /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
  ** 6 and each subsequent value (if any) is 5.  */
  memcpy(&a[1], aVal, nCopy*sizeof(LogEst));
  for(i=nCopy+1; i<=pIdx->nKeyCol; i++){
    a[i] = 23;                    assert( 23==sqlite3LogEst(5) );
  }







|
|

>








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







114097
114098
114099
114100
114101
114102
114103
114104
114105
114106
114107
114108
114109
114110
114111
114112
114113
114114
114115
114116
114117
114118
114119
114120
114121
114122
114123
114124
114125
114126
114127
114128
114129
114130
114131
114132
114133
114134
114135
114136
114137
**           aiRowEst[N]>=1
**
** Apart from that, we have little to go on besides intuition as to
** how aiRowEst[] should be initialized.  The numbers generated here
** are based on typical values found in actual indices.
*/
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){
               /*                10,  9,  8,  7,  6 */
  static const LogEst aVal[] = { 33, 32, 30, 28, 26 };
  LogEst *a = pIdx->aiRowLogEst;
  LogEst x;
  int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
  int i;

  /* Indexes with default row estimates should not have stat1 data */
  assert( !pIdx->hasStat1 );

  /* Set the first entry (number of rows in the index) to the estimated 
  ** number of rows in the table, or half the number of rows in the table
  ** for a partial index.
  **
  ** 2020-05-27:  If some of the stat data is coming from the sqlite_stat1
  ** table but other parts we are having to guess at, then do not let the
  ** estimated number of rows in the table be less than 1000 (LogEst 99).
  ** Failure to do this can cause the indexes for which we do not have
  ** stat1 data to be ignored by the query planner.  tag-20200527-1
  */
  x = pIdx->pTable->nRowLogEst;
  assert( 99==sqlite3LogEst(1000) );
  if( x<99 ){
    pIdx->pTable->nRowLogEst = x = 99;
  }
  if( pIdx->pPartIdxWhere!=0 ) x -= 10;  assert( 10==sqlite3LogEst(2) );
  a[0] = x;

  /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
  ** 6 and each subsequent value (if any) is 5.  */
  memcpy(&a[1], aVal, nCopy*sizeof(LogEst));
  for(i=nCopy+1; i<=pIdx->nKeyCol; i++){
    a[i] = 23;                    assert( 23==sqlite3LogEst(5) );
  }
128403
128404
128405
128406
128407
128408
128409
128410
128411
128412
128413
128414
128415
128416
128417
128418
128419
128420

128421
128422
128423
128424
128425
128426
128427
** Convert a schema pointer into the iDb index that indicates
** which database file in db->aDb[] the schema refers to.
**
** If the same database is attached more than once, the first
** attached database is returned.
*/
SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
  int i = -1000000;

  /* If pSchema is NULL, then return -1000000. This happens when code in 
  ** expr.c is trying to resolve a reference to a transient table (i.e. one
  ** created by a sub-select). In this case the return value of this 
  ** function should never be used.
  **
  ** We return -1000000 instead of the more usual -1 simply because using
  ** -1000000 as the incorrect index into db->aDb[] is much 
  ** more likely to cause a segfault than -1 (of course there are assert()
  ** statements too, but it never hurts to play the odds).

  */
  assert( sqlite3_mutex_held(db->mutex) );
  if( pSchema ){
    for(i=0; 1; i++){
      assert( i<db->nDb );
      if( db->aDb[i].pSchema==pSchema ){
        break;







|

|




|
|

|
>







128423
128424
128425
128426
128427
128428
128429
128430
128431
128432
128433
128434
128435
128436
128437
128438
128439
128440
128441
128442
128443
128444
128445
128446
128447
128448
** Convert a schema pointer into the iDb index that indicates
** which database file in db->aDb[] the schema refers to.
**
** If the same database is attached more than once, the first
** attached database is returned.
*/
SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
  int i = -32768;

  /* If pSchema is NULL, then return -32768. This happens when code in 
  ** expr.c is trying to resolve a reference to a transient table (i.e. one
  ** created by a sub-select). In this case the return value of this 
  ** function should never be used.
  **
  ** We return -32768 instead of the more usual -1 simply because using
  ** -32768 as the incorrect index into db->aDb[] is much 
  ** more likely to cause a segfault than -1 (of course there are assert()
  ** statements too, but it never hurts to play the odds) and
  ** -32768 will still fit into a 16-bit signed integer.
  */
  assert( sqlite3_mutex_held(db->mutex) );
  if( pSchema ){
    for(i=0; 1; i++){
      assert( i<db->nDb );
      if( db->aDb[i].pSchema==pSchema ){
        break;
128968
128969
128970
128971
128972
128973
128974
128975
128976
128977
128978
128979
128980
128981
128982
128983
128984
  Expr *pWhere,         /* the WHERE clause */
  ExprList *pGroupBy,   /* the GROUP BY clause */
  Expr *pHaving,        /* the HAVING clause */
  ExprList *pOrderBy,   /* the ORDER BY clause */
  u32 selFlags,         /* Flag parameters, such as SF_Distinct */
  Expr *pLimit          /* LIMIT value.  NULL means not used */
){
  Select *pNew;
  Select standin;
  pNew = sqlite3DbMallocRawNN(pParse->db, sizeof(*pNew) );
  if( pNew==0 ){
    assert( pParse->db->mallocFailed );
    pNew = &standin;
  }
  if( pEList==0 ){
    pEList = sqlite3ExprListAppend(pParse, 0,
                                   sqlite3Expr(pParse->db,TK_ASTERISK,0));







|

|







128989
128990
128991
128992
128993
128994
128995
128996
128997
128998
128999
129000
129001
129002
129003
129004
129005
  Expr *pWhere,         /* the WHERE clause */
  ExprList *pGroupBy,   /* the GROUP BY clause */
  Expr *pHaving,        /* the HAVING clause */
  ExprList *pOrderBy,   /* the ORDER BY clause */
  u32 selFlags,         /* Flag parameters, such as SF_Distinct */
  Expr *pLimit          /* LIMIT value.  NULL means not used */
){
  Select *pNew, *pAllocated;
  Select standin;
  pAllocated = pNew = sqlite3DbMallocRawNN(pParse->db, sizeof(*pNew) );
  if( pNew==0 ){
    assert( pParse->db->mallocFailed );
    pNew = &standin;
  }
  if( pEList==0 ){
    pEList = sqlite3ExprListAppend(pParse, 0,
                                   sqlite3Expr(pParse->db,TK_ASTERISK,0));
129004
129005
129006
129007
129008
129009
129010
129011
129012
129013
129014
129015
129016
129017
129018
129019
129020
129021
129022
129023
  pNew->pWith = 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
  pNew->pWin = 0;
  pNew->pWinDefn = 0;
#endif
  if( pParse->db->mallocFailed ) {
    clearSelect(pParse->db, pNew, pNew!=&standin);
    pNew = 0;
  }else{
    assert( pNew->pSrc!=0 || pParse->nErr>0 );
  }
  assert( pNew!=&standin );
  return pNew;
}


/*
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){







|



<
|







129025
129026
129027
129028
129029
129030
129031
129032
129033
129034
129035

129036
129037
129038
129039
129040
129041
129042
129043
  pNew->pWith = 0;
#ifndef SQLITE_OMIT_WINDOWFUNC
  pNew->pWin = 0;
  pNew->pWinDefn = 0;
#endif
  if( pParse->db->mallocFailed ) {
    clearSelect(pParse->db, pNew, pNew!=&standin);
    pAllocated = 0;
  }else{
    assert( pNew->pSrc!=0 || pParse->nErr>0 );
  }

  return pAllocated;
}


/*
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
224818
224819
224820
224821
224822
224823
224824
224825
224826
224827
224828
224829
224830
224831
224832
static void fts5SourceIdFunc(
  sqlite3_context *pCtx,          /* Function call context */
  int nArg,                       /* Number of args */
  sqlite3_value **apUnused        /* Function arguments */
){
  assert( nArg==0 );
  UNUSED_PARAM2(nArg, apUnused);
  sqlite3_result_text(pCtx, "fts5: 2020-05-25 16:19:56 0c1fcf4711a2e66c813aed38cf41cd3e2123ee8eb6db98118086764c4ba83350", -1, SQLITE_TRANSIENT);
}

/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){







|







224838
224839
224840
224841
224842
224843
224844
224845
224846
224847
224848
224849
224850
224851
224852
static void fts5SourceIdFunc(
  sqlite3_context *pCtx,          /* Function call context */
  int nArg,                       /* Number of args */
  sqlite3_value **apUnused        /* Function arguments */
){
  assert( nArg==0 );
  UNUSED_PARAM2(nArg, apUnused);
  sqlite3_result_text(pCtx, "fts5: 2020-05-29 16:15:58 4e1db8e9a9ee370a398f13fd8546a520111b8cfb84460389535b5bc5bd9f4f82", -1, SQLITE_TRANSIENT);
}

/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){
229601
229602
229603
229604
229605
229606
229607
229608
229609
229610
229611
229612
229613
229614
#endif
  return rc;
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */

/************** End of stmt.c ************************************************/
#if __LINE__!=229608
#undef SQLITE_SOURCE_ID
#define SQLITE_SOURCE_ID      "2020-05-25 16:19:56 0c1fcf4711a2e66c813aed38cf41cd3e2123ee8eb6db98118086764c4ba8alt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/







|

|




229621
229622
229623
229624
229625
229626
229627
229628
229629
229630
229631
229632
229633
229634
#endif
  return rc;
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */

/************** End of stmt.c ************************************************/
#if __LINE__!=229628
#undef SQLITE_SOURCE_ID
#define SQLITE_SOURCE_ID      "2020-05-29 16:15:58 4e1db8e9a9ee370a398f13fd8546a520111b8cfb84460389535b5bc5bd9falt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/
Changes to src/sqlite3.h.
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.32.1"
#define SQLITE_VERSION_NUMBER 3032001
#define SQLITE_SOURCE_ID      "2020-05-25 16:19:56 0c1fcf4711a2e66c813aed38cf41cd3e2123ee8eb6db98118086764c4ba83350"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros







|
|
|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.33.0"
#define SQLITE_VERSION_NUMBER 3033000
#define SQLITE_SOURCE_ID      "2020-05-29 16:15:58 4e1db8e9a9ee370a398f13fd8546a520111b8cfb84460389535b5bc5bd9f4f82"

/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version sqlite3_sourceid
**
** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
Changes to src/tar.c.
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
** If the RID object does not exist in the repository, then
** pTar is zeroed.
**
** zDir is a "synthetic" subdirectory which all files get
** added to as part of the tarball. It may be 0 or an empty string, in
** which case it is ignored. The intention is to create a tarball which
** politely expands into a subdir instead of filling your current dir
** with source files. For example, pass a UUID or "ProjectName".
**
*/
void tarball_of_checkin(
  int rid,             /* The RID of the checkin from which to form a tarball */
  Blob *pTar,          /* Write the tarball into this blob */
  const char *zDir,    /* Directory prefix for all file added to tarball */
  Glob *pInclude,      /* Only add files matching this pattern */







|







464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
** If the RID object does not exist in the repository, then
** pTar is zeroed.
**
** zDir is a "synthetic" subdirectory which all files get
** added to as part of the tarball. It may be 0 or an empty string, in
** which case it is ignored. The intention is to create a tarball which
** politely expands into a subdir instead of filling your current dir
** with source files. For example, pass an artifact hash or "ProjectName".
**
*/
void tarball_of_checkin(
  int rid,             /* The RID of the checkin from which to form a tarball */
  Blob *pTar,          /* Write the tarball into this blob */
  const char *zDir,    /* Directory prefix for all file added to tarball */
  Glob *pInclude,      /* Only add files matching this pattern */
Changes to src/timeline.c.
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    cgi_printf(" %s", UNPUB_TAG);
  }
}

/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  if( g.perm.Hyperlink ){
    @ %z(chref("timelineHistLink","%R/info/%!S",zUuid))[%S(zUuid)]</a>
  }else{
    @ <span class="timelineHistDsp">[%S(zUuid)]</span>
  }
}

/*
** Generate a hyperlink to a date & time.
*/
void hyperlink_to_date(const char *zDate, const char *zSuffix){







|

|

|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    cgi_printf(" %s", UNPUB_TAG);
  }
}

/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_version(const char *zVerHash){
  if( g.perm.Hyperlink ){
    @ %z(chref("timelineHistLink","%R/info/%!S",zVerHash))[%S(zVerHash)]</a>
  }else{
    @ <span class="timelineHistDsp">[%S(zVerHash)]</span>
  }
}

/*
** Generate a hyperlink to a date & time.
*/
void hyperlink_to_date(const char *zDate, const char *zSuffix){
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#define TIMELINE_GRAPH    0x0000008 /* Compute a graph */
#define TIMELINE_DISJOINT 0x0000010 /* Elements are not contiguous */
#define TIMELINE_FCHANGES 0x0000020 /* Detail file changes */
#define TIMELINE_BRCOLOR  0x0000040 /* Background color by branch name */
#define TIMELINE_UCOLOR   0x0000080 /* Background color by user */
#define TIMELINE_FRENAMES 0x0000100 /* Detail only file name changes */
#define TIMELINE_UNHIDE   0x0000200 /* Unhide check-ins with "hidden" tag */
#define TIMELINE_SHOWRID  0x0000400 /* Show RID values in addition to UUIDs */
#define TIMELINE_BISECT   0x0000800 /* Show supplimental bisect information */
#define TIMELINE_COMPACT  0x0001000 /* Use the "compact" view style */
#define TIMELINE_VERBOSE  0x0002000 /* Use the "detailed" view style */
#define TIMELINE_MODERN   0x0004000 /* Use the "modern" view style */
#define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */
#define TIMELINE_CLASSIC  0x0010000 /* Use the "classic" view style */
#define TIMELINE_VIEWS    0x001f000 /* Mask for all of the view styles */







|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#define TIMELINE_GRAPH    0x0000008 /* Compute a graph */
#define TIMELINE_DISJOINT 0x0000010 /* Elements are not contiguous */
#define TIMELINE_FCHANGES 0x0000020 /* Detail file changes */
#define TIMELINE_BRCOLOR  0x0000040 /* Background color by branch name */
#define TIMELINE_UCOLOR   0x0000080 /* Background color by user */
#define TIMELINE_FRENAMES 0x0000100 /* Detail only file name changes */
#define TIMELINE_UNHIDE   0x0000200 /* Unhide check-ins with "hidden" tag */
#define TIMELINE_SHOWRID  0x0000400 /* Show RID values in addition to hashes */
#define TIMELINE_BISECT   0x0000800 /* Show supplimental bisect information */
#define TIMELINE_COMPACT  0x0001000 /* Use the "compact" view style */
#define TIMELINE_VERBOSE  0x0002000 /* Use the "detailed" view style */
#define TIMELINE_MODERN   0x0004000 /* Use the "modern" view style */
#define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */
#define TIMELINE_CLASSIC  0x0010000 /* Use the "classic" view style */
#define TIMELINE_VIEWS    0x001f000 /* Mask for all of the view styles */
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
}

/*
** Output a timeline in the web format given a query.  The query
** should return these columns:
**
**    0.  rid
**    1.  UUID
**    2.  Date/Time
**    3.  Comment string
**    4.  User
**    5.  True if is a leaf
**    6.  background color
**    7.  type ("ci", "w", "t", "e", "g", "f", "div")
**    8.  list of symbolic tags.







|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
}

/*
** Output a timeline in the web format given a query.  The query
** should return these columns:
**
**    0.  rid
**    1.  artifact hash
**    2.  Date/Time
**    3.  Comment string
**    4.  User
**    5.  True if is a leaf
**    6.  background color
**    7.  type ("ci", "w", "t", "e", "g", "f", "div")
**    8.  list of symbolic tags.
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
    if( tmFlags & TIMELINE_COMPACT ){
      @ <span class='timelineCompactComment' data-id='%d(rid)'>
    }else{
      @ <span class='timeline%s(zStyle)Comment'>
    }
    if( (tmFlags & TIMELINE_CLASSIC)!=0 ){
      if( zType[0]=='c' ){
        hyperlink_to_uuid(zUuid);
        if( isLeaf ){
          if( db_exists("SELECT 1 FROM tagxref"
                        " WHERE rid=%d AND tagid=%d AND tagtype>0",
                        rid, TAG_CLOSED) ){
            @ <span class="timelineLeaf">Closed-Leaf:</span>
          }else{
            @ <span class="timelineLeaf">Leaf:</span>
          }
        }
      }else if( zType[0]=='e' && tagid ){
        hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
      }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
        hyperlink_to_uuid(zUuid);
      }
      if( tmFlags & TIMELINE_SHOWRID ){
        int srcId = delta_source_rid(rid);
        if( srcId ){
          @ (%d(rid)&larr;%d(srcId))
        }else{
          @ (%d(rid))







|












|







531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
    if( tmFlags & TIMELINE_COMPACT ){
      @ <span class='timelineCompactComment' data-id='%d(rid)'>
    }else{
      @ <span class='timeline%s(zStyle)Comment'>
    }
    if( (tmFlags & TIMELINE_CLASSIC)!=0 ){
      if( zType[0]=='c' ){
        hyperlink_to_version(zUuid);
        if( isLeaf ){
          if( db_exists("SELECT 1 FROM tagxref"
                        " WHERE rid=%d AND tagid=%d AND tagtype>0",
                        rid, TAG_CLOSED) ){
            @ <span class="timelineLeaf">Closed-Leaf:</span>
          }else{
            @ <span class="timelineLeaf">Leaf:</span>
          }
        }
      }else if( zType[0]=='e' && tagid ){
        hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
      }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
        hyperlink_to_version(zUuid);
      }
      if( tmFlags & TIMELINE_SHOWRID ){
        int srcId = delta_source_rid(rid);
        if( srcId ){
          @ (%d(rid)&larr;%d(srcId))
        }else{
          @ (%d(rid))
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
  }
  @ <h1>This Day In History For %h(zToday)</h1>
  z = db_text(0, "SELECT date(%Q,'-1 day')", zToday);
  style_submenu_element("Yesterday", "%R/thisdayinhistory?today=%t", z);
  z = db_text(0, "SELECT date(%Q,'+1 day')", zToday);
  style_submenu_element("Tomorrow", "%R/thisdayinhistory?today=%t", z);
  zStartOfProject = db_text(0,
      "SELECT datetime(min(mtime),toLocal()) FROM event;"
  );
  timeline_temp_table();
  db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
  for(i=0; i<sizeof(aYearsAgo)/sizeof(aYearsAgo[0]); i++){
    int iAgo = aYearsAgo[i];
    char *zThis = db_text(0, "SELECT date(%Q,'-%d years')", zToday, iAgo);
    Blob sql;







|







3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
  }
  @ <h1>This Day In History For %h(zToday)</h1>
  z = db_text(0, "SELECT date(%Q,'-1 day')", zToday);
  style_submenu_element("Yesterday", "%R/thisdayinhistory?today=%t", z);
  z = db_text(0, "SELECT date(%Q,'+1 day')", zToday);
  style_submenu_element("Tomorrow", "%R/thisdayinhistory?today=%t", z);
  zStartOfProject = db_text(0,
      "SELECT datetime(min(mtime),toLocal(),'startofday') FROM event;"
  );
  timeline_temp_table();
  db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
  for(i=0; i<sizeof(aYearsAgo)/sizeof(aYearsAgo[0]); i++){
    int iAgo = aYearsAgo[i];
    char *zThis = db_text(0, "SELECT date(%Q,'-%d years')", zToday, iAgo);
    Blob sql;
Changes to src/tktsetup.c.
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
    0,
    40
  );
}

static const char zDefaultView[] =
@ <table cellpadding="5">
@ <tr><td class="tktDspLabel">Ticket&nbsp;UUID:</td>
@ <th1>
@ if {[info exists tkt_uuid]} {
@   html "<td class='tktDspValue' colspan='3'>"
@   copybtn hash-tk 0 $tkt_uuid 2
@   if {[hascap s]} {
@     html " ($tkt_id)"
@   }







|







446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
    0,
    40
  );
}

static const char zDefaultView[] =
@ <table cellpadding="5">
@ <tr><td class="tktDspLabel">Ticket&nbsp;Hash:</td>
@ <th1>
@ if {[info exists tkt_uuid]} {
@   html "<td class='tktDspValue' colspan='3'>"
@   copybtn hash-tk 0 $tkt_uuid 2
@   if {[hascap s]} {
@     html " ($tkt_id)"
@   }
Changes to src/util.c.
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
#endif
}

/*
** Returns TRUE if zSym is exactly HNAME_LEN_SHA1 or HNAME_LEN_K256
** bytes long and contains only lower-case ASCII hexadecimal values.
*/
int fossil_is_uuid(const char *zSym){
  int sz = zSym ? (int)strlen(zSym) : 0;
  return (HNAME_LEN_SHA1==sz || HNAME_LEN_K256==sz) && validate16(zSym, sz);
}

/*
** Return true if the input string is NULL or all whitespace.
** Return false if the input string contains text.







|







414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
#endif
}

/*
** Returns TRUE if zSym is exactly HNAME_LEN_SHA1 or HNAME_LEN_K256
** bytes long and contains only lower-case ASCII hexadecimal values.
*/
int fossil_is_artifact_hash(const char *zSym){
  int sz = zSym ? (int)strlen(zSym) : 0;
  return (HNAME_LEN_SHA1==sz || HNAME_LEN_K256==sz) && validate16(zSym, sz);
}

/*
** Return true if the input string is NULL or all whitespace.
** Return false if the input string contains text.
Changes to src/zip.c.
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
** If the RID object does not exist in the repository, then
** pZip is zeroed.
**
** zDir is a "synthetic" subdirectory which all zipped files get
** added to as part of the zip file. It may be 0 or an empty string,
** in which case it is ignored. The intention is to create a zip which
** politely expands into a subdir instead of filling your current dir
** with source files. For example, pass a UUID or "ProjectName".
**
*/
static void zip_of_checkin(
  int eType,          /* Type of archive (ZIP or SQLAR) */
  int rid,            /* The RID of the checkin to build the archive from */
  Blob *pZip,         /* Write the archive content into this blob */
  const char *zDir,   /* Top-level directory of the archive */







|







612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
** If the RID object does not exist in the repository, then
** pZip is zeroed.
**
** zDir is a "synthetic" subdirectory which all zipped files get
** added to as part of the zip file. It may be 0 or an empty string,
** in which case it is ignored. The intention is to create a zip which
** politely expands into a subdir instead of filling your current dir
** with source files. For example, pass a commit hash or "ProjectName".
**
*/
static void zip_of_checkin(
  int eType,          /* Type of archive (ZIP or SQLAR) */
  int rid,            /* The RID of the checkin to build the archive from */
  Blob *pZip,         /* Write the archive content into this blob */
  const char *zDir,   /* Top-level directory of the archive */
Changes to test/amend.test.
24
25
26
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
}

proc manifest_comment {comment} {
  string map [list { } {\\s} \n {\\n} \r {\\r}] $comment
}

proc uuid_from_commit {res var} {
  upvar $var UUID
  regexp {^New_Version: ([0-9a-f]{40})[0-9a-f]*$} $res m UUID
}

proc uuid_from_branch {res var} {
  upvar $var UUID
  regexp {^New branch: ([0-9a-f]{40})[0-9a-f]*$} $res m UUID
}

proc uuid_from_checkout {var} {
  global RESULT
  upvar $var UUID
  fossil status
  regexp {checkout:\s+([0-9a-f]{40})} $RESULT m UUID
}

# Make sure we are not in an open repository and initialize new repository
test_setup

########################################
# Setup: Add file and commit           #
########################################

if {![uuid_from_checkout UUIDINIT]} {
  test amend-checkout-failure false
  test_cleanup_then_return
}
write_file datafile "data"
fossil add datafile
fossil commit -m "c1"
if {![uuid_from_commit $RESULT UUID]} {
  test amend-setup-failure false
  test_cleanup_then_return
}

########################################
# Test: -branch                        #
########################################
set UUIDB UUIDB
write_file datafile "data.file"
fossil commit -m "c2"
if {![uuid_from_commit $RESULT UUIDB]} {
  test amend-branch.setup false
}
fossil amend $UUIDB -branch amended-branch
test amend-branch-1.1 {[regexp {tags:\s+amended-branch} $RESULT]}
fossil branch ls
test amend-branch-1.2 {[string first "* amended-branch" $RESULT] != -1}
fossil tag list
test amend-branch-1.3 {[string first amended-branch $RESULT] != -1}
fossil tag list --raw $UUIDB
test amend-branch-1.4 {[string first "branch=amended-branch" $RESULT] != -1}
test amend-branch-1.5 {[string first "sym-amended-branch" $RESULT] != -1}
fossil timeline -n 1
test amend-branch-1.6 {[string match {*Move*to*branch*amended-branch*} $RESULT]}

########################################
# Test: -bgcolor                       #







|
|



|
|




|

|









|






|







|


|


|





|







24
25
26
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
}

proc manifest_comment {comment} {
  string map [list { } {\\s} \n {\\n} \r {\\r}] $comment
}

proc uuid_from_commit {res var} {
  upvar $var HASH
  regexp {^New_Version: ([0-9a-f]{40})[0-9a-f]*$} $res m HASH
}

proc uuid_from_branch {res var} {
  upvar $var HASH
  regexp {^New branch: ([0-9a-f]{40})[0-9a-f]*$} $res m HASH
}

proc uuid_from_checkout {var} {
  global RESULT
  upvar $var HASH
  fossil status
  regexp {checkout:\s+([0-9a-f]{40})[0-9a-f]*} $RESULT m HASH
}

# Make sure we are not in an open repository and initialize new repository
test_setup

########################################
# Setup: Add file and commit           #
########################################

if {![uuid_from_checkout HASHINIT]} {
  test amend-checkout-failure false
  test_cleanup_then_return
}
write_file datafile "data"
fossil add datafile
fossil commit -m "c1"
if {![uuid_from_commit $RESULT HASH]} {
  test amend-setup-failure false
  test_cleanup_then_return
}

########################################
# Test: -branch                        #
########################################
set HASHB HASHB
write_file datafile "data.file"
fossil commit -m "c2"
if {![uuid_from_commit $RESULT HASHB]} {
  test amend-branch.setup false
}
fossil amend $HASHB -branch amended-branch
test amend-branch-1.1 {[regexp {tags:\s+amended-branch} $RESULT]}
fossil branch ls
test amend-branch-1.2 {[string first "* amended-branch" $RESULT] != -1}
fossil tag list
test amend-branch-1.3 {[string first amended-branch $RESULT] != -1}
fossil tag list --raw $HASHB
test amend-branch-1.4 {[string first "branch=amended-branch" $RESULT] != -1}
test amend-branch-1.5 {[string first "sym-amended-branch" $RESULT] != -1}
fossil timeline -n 1
test amend-branch-1.6 {[string match {*Move*to*branch*amended-branch*} $RESULT]}

########################################
# Test: -bgcolor                       #
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
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
276
277
278
279
280
281
282
283
284
285
  acf #acf
  123 #123
  #1234 #1234
  1234 1234
  123456 #123456
} {
  incr tc
  fossil amend $UUID -bgcolor $color
  test amend-bgcolor-1.$tc.a {[string match "*uuid:*$UUID*" $RESULT]}
  fossil tag list --raw $UUID
  test amend-bgcolor-1.$tc.b {[string first "bgcolor=$result" $RESULT] != -1}
  fossil timeline -n 1
  test amend-bgcolor-1.$tc.c {
    [string match "*Change*background*color*to*\"$result\"*" $RESULT]
  }
  if {[artifact_from_timeline $RESULT artid]} {
    fossil artifact $artid
    test amend-bgcolor-1.$tc.d {
      [string match "*T +bgcolor $UUID* $result*" $RESULT]
    }
  } else {
    if {$VERBOSE} { protOut "No artifact found in timeline output" }
    test amend-bgcolor-1.$tc.d false
  }
}
fossil amend $UUID -bgcolor {}
test amend-bgcolor-2.1 {[string match "*uuid:*$UUID*" $RESULT]}
fossil tag list --raw $UUID
test amend-bgcolor-2.2 {
  [string first "bgcolor=" $RESULT] == -1 &&
  [string first "bgcolor" $RESULT] != -1
}
fossil timeline -n 1
test amend-bgcolor-2.3 {[string match "*Cancel*background*color.*" $RESULT]}
if {[artifact_from_timeline $RESULT artid]} {
  fossil artifact $artid
  test amend-bgcolor-2.4 {[string match "*T -bgcolor $UUID*" $RESULT]}
} else {
  if {$VERBOSE} { protOut "No artifact found in timeline output" }
  test amend-bgcolor-2.4 false
}

########################################
# Test: -branchcolor                   #
########################################
set UUID2 UUID2
fossil branch new brclr $UUID
if {![uuid_from_branch $RESULT UUID2]} {
  test amend-branchcolor.setup false
}
fossil update $UUID2
fossil amend $UUID2 -branchcolor yellow
test amend-branchcolor-1.1 {[string match "*uuid:*$UUID2*" $RESULT]}
fossil tag ls --raw $UUID2
test amend-branchcolor-1.2 {[string first "bgcolor=yellow" $RESULT] != -1}
fossil timeline -n 1
test amend-branchcolor-1.3 {
  [string match {*Change*branch*background*color*to*"yellow".*} $RESULT]
}
if {[regexp {(?x)[0-9]{2}(?::[0-9]{2}){2}\s+\[([0-9a-f]+)]} $RESULT m artid]} {
  fossil artifact $artid
  test amend-branchcolor-1.4 {
    [string match "*T \*bgcolor $UUID2* yellow*" $RESULT]
  }
} else {
  if {$VERBOSE} { protOut "No artifact found in timeline output" }
  test amend-branchcolor-1.4 false
}

set UUIDN UUIDN
write_file datafile "brclr"
fossil commit -m "brclr"
if {![uuid_from_commit $RESULT UUIDN]} {
  test amend-branchcolor-propagating.setup false
}
write_file datafile "bc1"
fossil commit -m "mc1"
write_file datafile "bc2"
fossil commit -m "mc2"
fossil amend $UUIDN -branchcolor deadbe
test amend-branchcolor-2.1 {[string match "*uuid:*$UUIDN*" $RESULT]}
fossil tag ls --raw current
test amend-branchcolor-2.2 {[string first "bgcolor=#deadbe" $RESULT] != -1}
fossil timeline -n 1
test amend-branchcolor-2.3 {
  [string match {*Change*branch*background*color*to*"#deadbe".*} $RESULT]
}

########################################
# Test: -author                        #
########################################
fossil amend $UUID -author author-test
test amend-author-1.1 {[string match {*comment:*(user:*author-test)*} $RESULT]}
fossil tag ls --raw $UUID
test amend-author-1.2 {[string first "user=author-test" $RESULT] != -1}
fossil timeline -n 1
test amend-author-1.3 {[string match {*Change*user*to*"author-test".*} $RESULT]}

########################################
# Test: -date                          #
########################################
set timestamp [clock scan yesterday]
set date [clock format $timestamp -format "%Y-%m-%d" -gmt 1]
set time [clock format $timestamp -format "%H:%M:%S" -gmt 1]
set datetime "$date $time"
fossil amend $UUIDINIT -date $datetime
test amend-date-1.1 {[string match "*uuid:*$UUIDINIT*$datetime*" $RESULT]}
fossil tag ls --raw $UUIDINIT
test amend-date-1.2 {[string first "date=$datetime" $RESULT] != -1}
fossil timeline -n 1
test amend-date-1.3 {[string match "*Timestamp*$date*$time*" $RESULT]}
set badformats {
  "%+"
  "%Y-%m-%d %H:%M%:%S %Z"
  "%d/%m/%Y %H:%M%:%S %Z"
  "%d/%m/%Y %H:%M%:%S"
  "%d/%m/%Y"
}
set sc 0
foreach badformat $badformats {
  incr sc
  set datetime [clock format $timestamp -format $badformat -gmt 1]
  fossil amend $UUIDINIT -date $datetime -expectError
  test amend-date-2.$sc {[string first "YYYY-MM-DD HH:MM:SS" $RESULT] != -1}
}

########################################
# Test: -hide                          #
########################################
set UUIDH UUIDH
fossil revert
fossil update trunk
fossil branch new tohide current
if {![uuid_from_branch $RESULT UUIDH]} {
  test amend-hide-setup false
}
fossil amend $UUIDH -hide
test amend-hide-1.1 {[string match "*uuid:*$UUIDH*" $RESULT]}
fossil tag ls --raw $UUIDH
test amend-hide-1.2 {[string first "hidden" $RESULT] != -1}
fossil timeline -n 1
test amend-hide-1.3 {[string match {*Add*propagating*"hidden".*} $RESULT]}

########################################
# Test: -close                          #
########################################
set UUIDC UUIDC
fossil branch new cllf $UUID
if {![uuid_from_branch $RESULT UUIDC]} {
  test amend-close.setup false
}
fossil update $UUIDC
fossil amend $UUIDC -close
test amend-close-1.1.a {[string match "*uuid:*$UUIDC*" $RESULT]}
test amend-close-1.1.b {
  [string match "*comment:*Create*new*branch*named*\"cllf\"*" $RESULT]
}
fossil tag ls --raw $UUIDC
test amend-close-1.2 {[string first "closed" $RESULT] != -1}
fossil timeline -n 1
test amend-close-1.3 {[string match {*Mark*"Closed".*} $RESULT]}
write_file datafile "cllf"
fossil commit -m "should fail" -expectError
test amend-close-2 {[string first "closed leaf" $RESULT] != -1}

set UUID3 UUID3
fossil revert
fossil update trunk
write_file datafile "cb"
fossil commit -m "closed-branch" --branch "closebranch"
if {![uuid_from_commit $RESULT UUID3]} {
  test amend-close-3.setup false
}
write_file datafile "b1"
fossil commit -m "m1"
write_file datafile "b2"
fossil commit -m "m2"
fossil amend $UUID3 --close
test amend-close-3.1 {[string match "*uuid:*$UUID3*" $RESULT]}
fossil tag ls --raw current
test amend-close-3.2 {[string first "closed" $RESULT] != -1}
fossil timeline -n 1
test amend-close-3.3 {
  [string match "*Add*propagating*\"closed\".*" $RESULT]
}
write_file datafile "changed"







|
|
|








|






|
|
|








|








|
|
|


|
|
|
|








|






|


|






|
|










|

|











|
|
|














|






|



|


|
|
|







|
|
|


|
|
|



|







|




|






|
|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
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
276
277
278
279
280
281
282
283
284
285
  acf #acf
  123 #123
  #1234 #1234
  1234 1234
  123456 #123456
} {
  incr tc
  fossil amend $HASH -bgcolor $color
  test amend-bgcolor-1.$tc.a {[string match "*uuid:*$HASH*" $RESULT]}
  fossil tag list --raw $HASH
  test amend-bgcolor-1.$tc.b {[string first "bgcolor=$result" $RESULT] != -1}
  fossil timeline -n 1
  test amend-bgcolor-1.$tc.c {
    [string match "*Change*background*color*to*\"$result\"*" $RESULT]
  }
  if {[artifact_from_timeline $RESULT artid]} {
    fossil artifact $artid
    test amend-bgcolor-1.$tc.d {
      [string match "*T +bgcolor $HASH* $result*" $RESULT]
    }
  } else {
    if {$VERBOSE} { protOut "No artifact found in timeline output" }
    test amend-bgcolor-1.$tc.d false
  }
}
fossil amend $HASH -bgcolor {}
test amend-bgcolor-2.1 {[string match "*uuid:*$HASH*" $RESULT]}
fossil tag list --raw $HASH
test amend-bgcolor-2.2 {
  [string first "bgcolor=" $RESULT] == -1 &&
  [string first "bgcolor" $RESULT] != -1
}
fossil timeline -n 1
test amend-bgcolor-2.3 {[string match "*Cancel*background*color.*" $RESULT]}
if {[artifact_from_timeline $RESULT artid]} {
  fossil artifact $artid
  test amend-bgcolor-2.4 {[string match "*T -bgcolor $HASH*" $RESULT]}
} else {
  if {$VERBOSE} { protOut "No artifact found in timeline output" }
  test amend-bgcolor-2.4 false
}

########################################
# Test: -branchcolor                   #
########################################
set HASH2 HASH2
fossil branch new brclr $HASH
if {![uuid_from_branch $RESULT HASH2]} {
  test amend-branchcolor.setup false
}
fossil update $HASH2
fossil amend $HASH2 -branchcolor yellow
test amend-branchcolor-1.1 {[string match "*uuid:*$HASH2*" $RESULT]}
fossil tag ls --raw $HASH2
test amend-branchcolor-1.2 {[string first "bgcolor=yellow" $RESULT] != -1}
fossil timeline -n 1
test amend-branchcolor-1.3 {
  [string match {*Change*branch*background*color*to*"yellow".*} $RESULT]
}
if {[regexp {(?x)[0-9]{2}(?::[0-9]{2}){2}\s+\[([0-9a-f]+)]} $RESULT m artid]} {
  fossil artifact $artid
  test amend-branchcolor-1.4 {
    [string match "*T \*bgcolor $HASH2* yellow*" $RESULT]
  }
} else {
  if {$VERBOSE} { protOut "No artifact found in timeline output" }
  test amend-branchcolor-1.4 false
}

set HASHN HASHN
write_file datafile "brclr"
fossil commit -m "brclr"
if {![uuid_from_commit $RESULT HASHN]} {
  test amend-branchcolor-propagating.setup false
}
write_file datafile "bc1"
fossil commit -m "mc1"
write_file datafile "bc2"
fossil commit -m "mc2"
fossil amend $HASHN -branchcolor deadbe
test amend-branchcolor-2.1 {[string match "*uuid:*$HASHN*" $RESULT]}
fossil tag ls --raw current
test amend-branchcolor-2.2 {[string first "bgcolor=#deadbe" $RESULT] != -1}
fossil timeline -n 1
test amend-branchcolor-2.3 {
  [string match {*Change*branch*background*color*to*"#deadbe".*} $RESULT]
}

########################################
# Test: -author                        #
########################################
fossil amend $HASH -author author-test
test amend-author-1.1 {[string match {*comment:*(user:*author-test)*} $RESULT]}
fossil tag ls --raw $HASH
test amend-author-1.2 {[string first "user=author-test" $RESULT] != -1}
fossil timeline -n 1
test amend-author-1.3 {[string match {*Change*user*to*"author-test".*} $RESULT]}

########################################
# Test: -date                          #
########################################
set timestamp [clock scan yesterday]
set date [clock format $timestamp -format "%Y-%m-%d" -gmt 1]
set time [clock format $timestamp -format "%H:%M:%S" -gmt 1]
set datetime "$date $time"
fossil amend $HASHINIT -date $datetime
test amend-date-1.1 {[string match "*uuid:*$HASHINIT*$datetime*" $RESULT]}
fossil tag ls --raw $HASHINIT
test amend-date-1.2 {[string first "date=$datetime" $RESULT] != -1}
fossil timeline -n 1
test amend-date-1.3 {[string match "*Timestamp*$date*$time*" $RESULT]}
set badformats {
  "%+"
  "%Y-%m-%d %H:%M%:%S %Z"
  "%d/%m/%Y %H:%M%:%S %Z"
  "%d/%m/%Y %H:%M%:%S"
  "%d/%m/%Y"
}
set sc 0
foreach badformat $badformats {
  incr sc
  set datetime [clock format $timestamp -format $badformat -gmt 1]
  fossil amend $HASHINIT -date $datetime -expectError
  test amend-date-2.$sc {[string first "YYYY-MM-DD HH:MM:SS" $RESULT] != -1}
}

########################################
# Test: -hide                          #
########################################
set HASHH HASHH
fossil revert
fossil update trunk
fossil branch new tohide current
if {![uuid_from_branch $RESULT HASHH]} {
  test amend-hide-setup false
}
fossil amend $HASHH -hide
test amend-hide-1.1 {[string match "*uuid:*$HASHH*" $RESULT]}
fossil tag ls --raw $HASHH
test amend-hide-1.2 {[string first "hidden" $RESULT] != -1}
fossil timeline -n 1
test amend-hide-1.3 {[string match {*Add*propagating*"hidden".*} $RESULT]}

########################################
# Test: -close                          #
########################################
set HASHC HASHC
fossil branch new cllf $HASH
if {![uuid_from_branch $RESULT HASHC]} {
  test amend-close.setup false
}
fossil update $HASHC
fossil amend $HASHC -close
test amend-close-1.1.a {[string match "*uuid:*$HASHC*" $RESULT]}
test amend-close-1.1.b {
  [string match "*comment:*Create*new*branch*named*\"cllf\"*" $RESULT]
}
fossil tag ls --raw $HASHC
test amend-close-1.2 {[string first "closed" $RESULT] != -1}
fossil timeline -n 1
test amend-close-1.3 {[string match {*Mark*"Closed".*} $RESULT]}
write_file datafile "cllf"
fossil commit -m "should fail" -expectError
test amend-close-2 {[string first "closed leaf" $RESULT] != -1}

set HASH3 HASH3
fossil revert
fossil update trunk
write_file datafile "cb"
fossil commit -m "closed-branch" --branch "closebranch"
if {![uuid_from_commit $RESULT HASH3]} {
  test amend-close-3.setup false
}
write_file datafile "b1"
fossil commit -m "m1"
write_file datafile "b2"
fossil commit -m "m2"
fossil amend $HASH3 --close
test amend-close-3.1 {[string match "*uuid:*$HASH3*" $RESULT]}
fossil tag ls --raw current
test amend-close-3.2 {[string first "closed" $RESULT] != -1}
fossil timeline -n 1
test amend-close-3.3 {
  [string match "*Add*propagating*\"closed\".*" $RESULT]
}
write_file datafile "changed"
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  }
  foreach res $result {
    append t1exp ", $res"
    append t2exp "sym-$res*"
    append t3exp "Add*tag*\"$res\".*"
    append t5exp "Cancel*tag*\"$res\".*"
  }
  eval fossil amend $UUID $tags
  test amend-tag-$tc.1 {[string match "*uuid:*$UUID*tags:*$t1exp*" $RESULT]}
  fossil tag ls --raw $UUID
  test amend-tag-$tc.2 {[string match $t2exp $RESULT]}
  fossil timeline -n 1
  test amend-tag-$tc.3 {[string match $t3exp $RESULT]}
  eval fossil amend $UUID $cancels
  test amend-tag-$tc.4 {![string match "*tags:*$t1exp*" $RESULT]}
  fossil timeline -n 1
  test amend-tag-$tc.5 {[string match $t5exp $RESULT]}
}

########################################
# Test: -comment                       #
########################################
proc prep-test {comment content} {
  global UUID RESULT

  fossil revert
  fossil update trunk
  write_file datafile $comment
  fossil commit -m $content
  if {![uuid_from_commit $RESULT UUID]} {
    set UUID ""
  }
}

proc test-comment {name UUID comment} {
  global VERBOSE RESULT

  test amend-comment-$name.1 {
    [string match "*uuid:*$UUID*comment:*$comment*" $RESULT]
  }
  fossil timeline -n 1
  if {[artifact_from_timeline $RESULT artid]} {
    fossil artifact $artid
    test amend-comment-$name.2 {
      [string match "*T +comment $UUID* *[manifest_comment $comment]*" $RESULT]
    }
  } else {
    if {$VERBOSE} { protOut "No artifact found in timeline output: $RESULT" }
    test amend-comment-$name.2 false
  }
  fossil timeline -n 1
  test amend-comment-$name.3 {
    [string match "*[short_uuid $UUID]*Edit*check-in*comment.*" $RESULT]
  }
  fossil info $UUID
  test amend-comment-$name.4 {
    [string match "*uuid:*$UUID*comment:*$comment*" $RESULT]
  }
}

prep-test "revision 1" "revision 1"
fossil amend $UUID -comment "revised revision 1"
test-comment 1 $UUID "revised revision 1"

prep-test "revision 2" "revision 2"
fossil amend $UUID -m "revised revision 2 with -m"
test-comment 2 $UUID "revised revision 2 with -m"

prep-test "revision 3" "revision 3"
write_file commitmsg "revision 3 revised"
fossil amend $UUID -message-file commitmsg
test-comment 3 $UUID "revision 3 revised"

prep-test "revision 4" "revision 4"
write_file commitmsg "revision 4 revised with -M"
fossil amend $UUID -M commitmsg
test-comment 4 $UUID "revision 4 revised with -M"

prep-test "final comment" "final content"
if {[catch {exec which ed} result]} {
  if {$VERBOSE} { protOut "Install ed for interactive comment test: $result" }
  test-comment 5 $UUID "ed required for interactive edit"
} else {
  fossil settings editor "ed -s"
  set comment "interactive edited comment"
  fossil_maybe_answer "a\n$comment\n.\nw\nq\n" amend $UUID --edit-comment
  test-comment 5 $UUID $comment
}

########################################
# Test: NULL UUID                      #
########################################
fossil amend {} -close -expectError
test amend-null-uuid {$CODE && [string first "no such check-in" $RESULT] != -1}

###############################################################################

test_cleanup







|
|
|



|









|





|
|



|



|





|







|

|

|




|
|


|
|



|
|



|
|




|



|
|



|







308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  }
  foreach res $result {
    append t1exp ", $res"
    append t2exp "sym-$res*"
    append t3exp "Add*tag*\"$res\".*"
    append t5exp "Cancel*tag*\"$res\".*"
  }
  eval fossil amend $HASH $tags
  test amend-tag-$tc.1 {[string match "*uuid:*$HASH*tags:*$t1exp*" $RESULT]}
  fossil tag ls --raw $HASH
  test amend-tag-$tc.2 {[string match $t2exp $RESULT]}
  fossil timeline -n 1
  test amend-tag-$tc.3 {[string match $t3exp $RESULT]}
  eval fossil amend $HASH $cancels
  test amend-tag-$tc.4 {![string match "*tags:*$t1exp*" $RESULT]}
  fossil timeline -n 1
  test amend-tag-$tc.5 {[string match $t5exp $RESULT]}
}

########################################
# Test: -comment                       #
########################################
proc prep-test {comment content} {
  global HASH RESULT

  fossil revert
  fossil update trunk
  write_file datafile $comment
  fossil commit -m $content
  if {![uuid_from_commit $RESULT HASH]} {
    set HASH ""
  }
}

proc test-comment {name HASH comment} {
  global VERBOSE RESULT

  test amend-comment-$name.1 {
    [string match "*uuid:*$HASH*comment:*$comment*" $RESULT]
  }
  fossil timeline -n 1
  if {[artifact_from_timeline $RESULT artid]} {
    fossil artifact $artid
    test amend-comment-$name.2 {
      [string match "*T +comment $HASH* *[manifest_comment $comment]*" $RESULT]
    }
  } else {
    if {$VERBOSE} { protOut "No artifact found in timeline output: $RESULT" }
    test amend-comment-$name.2 false
  }
  fossil timeline -n 1
  test amend-comment-$name.3 {
    [string match "*[short_uuid $HASH]*Edit*check-in*comment.*" $RESULT]
  }
  fossil info $HASH
  test amend-comment-$name.4 {
    [string match "*uuid:*$HASH*comment:*$comment*" $RESULT]
  }
}

prep-test "revision 1" "revision 1"
fossil amend $HASH -comment "revised revision 1"
test-comment 1 $HASH "revised revision 1"

prep-test "revision 2" "revision 2"
fossil amend $HASH -m "revised revision 2 with -m"
test-comment 2 $HASH "revised revision 2 with -m"

prep-test "revision 3" "revision 3"
write_file commitmsg "revision 3 revised"
fossil amend $HASH -message-file commitmsg
test-comment 3 $HASH "revision 3 revised"

prep-test "revision 4" "revision 4"
write_file commitmsg "revision 4 revised with -M"
fossil amend $HASH -M commitmsg
test-comment 4 $HASH "revision 4 revised with -M"

prep-test "final comment" "final content"
if {[catch {exec which ed} result]} {
  if {$VERBOSE} { protOut "Install ed for interactive comment test: $result" }
  test-comment 5 $HASH "ed required for interactive edit"
} else {
  fossil settings editor "ed -s"
  set comment "interactive edited comment"
  fossil_maybe_answer "a\n$comment\n.\nw\nq\n" amend $HASH --edit-comment
  test-comment 5 $HASH $comment
}

########################################
# Test: NULL hash                      #
########################################
fossil amend {} -close -expectError
test amend-null-uuid {$CODE && [string first "no such check-in" $RESULT] != -1}

###############################################################################

test_cleanup
Changes to tools/email-sender.tcl.
19
20
21
22
23
24
25




26
27
28
29
30
31
32
33
    msg TXT
  );
}
while {1} {
  db transaction immediate {
    set n 0
    db eval {SELECT msg FROM email} {




      set out [open |$PIPE w]
      puts -nonewline $out $msg
      flush $out
      close $out
      incr n
    }
    if {$n>0} {
      db eval {DELETE FROM email}







>
>
>
>
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    msg TXT
  );
}
while {1} {
  db transaction immediate {
    set n 0
    db eval {SELECT msg FROM email} {
      set pipe $PIPE
      if {[regexp {\nFrom:[^\n]*<([^>]+)>} $msg all addr]} {
        append pipe " -f $addr"
      }
      set out [open |$pipe w]
      puts -nonewline $out $msg
      flush $out
      close $out
      incr n
    }
    if {$n>0} {
      db eval {DELETE FROM email}
Changes to win/Makefile.PellesCGMake.
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
UTILS_OBJ=$(UTILS:.exe=.obj)
UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c))

# define the SQLite files, which need special flags on compile
SQLITESRC=sqlite3.c
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DSQLITE_WIN32_NO_ANSI

# define the SQLite shell files, which need special flags on compile
SQLITESHELLSRC=shell.c
ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
SQLITESHELLDEFINES=-DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen

# define the th scripting files, which need special flags on compile
THSRC=th.c th_lang.c
ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))

# define the zlib files, needed by this compile







|





|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
UTILS_OBJ=$(UTILS:.exe=.obj)
UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c))

# define the SQLite files, which need special flags on compile
SQLITESRC=sqlite3.c
ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf))
SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj))
SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DSQLITE_WIN32_NO_ANSI

# define the SQLite shell files, which need special flags on compile
SQLITESHELLSRC=shell.c
ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf))
SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj))
SQLITESHELLDEFINES=-DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen

# define the th scripting files, which need special flags on compile
THSRC=th.c th_lang.c
ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf))
THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj))

# define the zlib files, needed by this compile
Changes to win/Makefile.dmc.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
SSL    =

CFLAGS = -o
BCC    = $(DMDIR)\bin\dmc $(CFLAGS)
TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi

SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0

SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen

SRC   = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c

OBJ   = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O


RC=$(DMDIR)\bin\rcc







|

|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
SSL    =

CFLAGS = -o
BCC    = $(DMDIR)\bin\dmc $(CFLAGS)
TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi

SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0

SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen

SRC   = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c

OBJ   = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O


RC=$(DMDIR)\bin\rcc
Changes to win/Makefile.mingw.
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
                 -DSQLITE_DQS=0 \
                 -DSQLITE_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_MEMSTATUS=0 \
                 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 -DSQLITE_OMIT_DECLTYPE \
                 -DSQLITE_OMIT_DEPRECATED \
                 -DSQLITE_OMIT_GET_TABLE \
                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_USE_ALLOCA \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \







<







2483
2484
2485
2486
2487
2488
2489

2490
2491
2492
2493
2494
2495
2496
                 -DSQLITE_DQS=0 \
                 -DSQLITE_THREADSAFE=0 \
                 -DSQLITE_DEFAULT_MEMSTATUS=0 \
                 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 -DSQLITE_OMIT_DECLTYPE \
                 -DSQLITE_OMIT_DEPRECATED \

                 -DSQLITE_OMIT_PROGRESS_CALLBACK \
                 -DSQLITE_OMIT_SHARED_CACHE \
                 -DSQLITE_OMIT_LOAD_EXTENSION \
                 -DSQLITE_MAX_EXPR_DEPTH=0 \
                 -DSQLITE_USE_ALLOCA \
                 -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 -DSQLITE_DEFAULT_FILE_FORMAT=4 \
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
                -DSQLITE_DQS=0 \
                -DSQLITE_THREADSAFE=0 \
                -DSQLITE_DEFAULT_MEMSTATUS=0 \
                -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                -DSQLITE_OMIT_DECLTYPE \
                -DSQLITE_OMIT_DEPRECATED \
                -DSQLITE_OMIT_GET_TABLE \
                -DSQLITE_OMIT_PROGRESS_CALLBACK \
                -DSQLITE_OMIT_SHARED_CACHE \
                -DSQLITE_OMIT_LOAD_EXTENSION \
                -DSQLITE_MAX_EXPR_DEPTH=0 \
                -DSQLITE_USE_ALLOCA \
                -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                -DSQLITE_DEFAULT_FILE_FORMAT=4 \







<







2513
2514
2515
2516
2517
2518
2519

2520
2521
2522
2523
2524
2525
2526
                -DSQLITE_DQS=0 \
                -DSQLITE_THREADSAFE=0 \
                -DSQLITE_DEFAULT_MEMSTATUS=0 \
                -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                -DSQLITE_OMIT_DECLTYPE \
                -DSQLITE_OMIT_DEPRECATED \

                -DSQLITE_OMIT_PROGRESS_CALLBACK \
                -DSQLITE_OMIT_SHARED_CACHE \
                -DSQLITE_OMIT_LOAD_EXTENSION \
                -DSQLITE_MAX_EXPR_DEPTH=0 \
                -DSQLITE_USE_ALLOCA \
                -DSQLITE_ENABLE_LOCKING_STYLE=0 \
                -DSQLITE_DEFAULT_FILE_FORMAT=4 \
Changes to win/Makefile.msc.
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
                 /DSQLITE_DQS=0 \
                 /DSQLITE_THREADSAFE=0 \
                 /DSQLITE_DEFAULT_MEMSTATUS=0 \
                 /DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 /DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 /DSQLITE_OMIT_DECLTYPE \
                 /DSQLITE_OMIT_DEPRECATED \
                 /DSQLITE_OMIT_GET_TABLE \
                 /DSQLITE_OMIT_PROGRESS_CALLBACK \
                 /DSQLITE_OMIT_SHARED_CACHE \
                 /DSQLITE_OMIT_LOAD_EXTENSION \
                 /DSQLITE_MAX_EXPR_DEPTH=0 \
                 /DSQLITE_USE_ALLOCA \
                 /DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 /DSQLITE_DEFAULT_FILE_FORMAT=4 \







<







281
282
283
284
285
286
287

288
289
290
291
292
293
294
                 /DSQLITE_DQS=0 \
                 /DSQLITE_THREADSAFE=0 \
                 /DSQLITE_DEFAULT_MEMSTATUS=0 \
                 /DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                 /DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                 /DSQLITE_OMIT_DECLTYPE \
                 /DSQLITE_OMIT_DEPRECATED \

                 /DSQLITE_OMIT_PROGRESS_CALLBACK \
                 /DSQLITE_OMIT_SHARED_CACHE \
                 /DSQLITE_OMIT_LOAD_EXTENSION \
                 /DSQLITE_MAX_EXPR_DEPTH=0 \
                 /DSQLITE_USE_ALLOCA \
                 /DSQLITE_ENABLE_LOCKING_STYLE=0 \
                 /DSQLITE_DEFAULT_FILE_FORMAT=4 \
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
                /DSQLITE_DQS=0 \
                /DSQLITE_THREADSAFE=0 \
                /DSQLITE_DEFAULT_MEMSTATUS=0 \
                /DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                /DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                /DSQLITE_OMIT_DECLTYPE \
                /DSQLITE_OMIT_DEPRECATED \
                /DSQLITE_OMIT_GET_TABLE \
                /DSQLITE_OMIT_PROGRESS_CALLBACK \
                /DSQLITE_OMIT_SHARED_CACHE \
                /DSQLITE_OMIT_LOAD_EXTENSION \
                /DSQLITE_MAX_EXPR_DEPTH=0 \
                /DSQLITE_USE_ALLOCA \
                /DSQLITE_ENABLE_LOCKING_STYLE=0 \
                /DSQLITE_DEFAULT_FILE_FORMAT=4 \







<







308
309
310
311
312
313
314

315
316
317
318
319
320
321
                /DSQLITE_DQS=0 \
                /DSQLITE_THREADSAFE=0 \
                /DSQLITE_DEFAULT_MEMSTATUS=0 \
                /DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
                /DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
                /DSQLITE_OMIT_DECLTYPE \
                /DSQLITE_OMIT_DEPRECATED \

                /DSQLITE_OMIT_PROGRESS_CALLBACK \
                /DSQLITE_OMIT_SHARED_CACHE \
                /DSQLITE_OMIT_LOAD_EXTENSION \
                /DSQLITE_MAX_EXPR_DEPTH=0 \
                /DSQLITE_USE_ALLOCA \
                /DSQLITE_ENABLE_LOCKING_STYLE=0 \
                /DSQLITE_DEFAULT_FILE_FORMAT=4 \
Changes to www/concepts.wiki.
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
is a duplicate of a remote repository.

Communication between repositories is via HTTP.  Remote
repositories are identified by URL.  You can also point a web browser
at a repository and get human-readable status, history, and tracking
information about the project.

<h3>2.1 Identification Of Artifacts</h3>

A particular version of a particular file is called an "artifact".  Each
artifact has a universally unique name which is the <a
href="http://en.wikipedia.org/wiki/SHA1">SHA1</a> or <a
href="http://en.wikipedia.org/wiki/SHA3">SHA3-256</a> hash of the
content of that file expressed as either 40 or 64 characters of
lower-case hexadecimal. (See the [./hashpolicy.wiki|hash policy







|







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
is a duplicate of a remote repository.

Communication between repositories is via HTTP.  Remote
repositories are identified by URL.  You can also point a web browser
at a repository and get human-readable status, history, and tracking
information about the project.

<h3><a id="artifacts"></a>2.1 Identification Of Artifacts</h3>

A particular version of a particular file is called an "artifact".  Each
artifact has a universally unique name which is the <a
href="http://en.wikipedia.org/wiki/SHA1">SHA1</a> or <a
href="http://en.wikipedia.org/wiki/SHA3">SHA3-256</a> hash of the
content of that file expressed as either 40 or 64 characters of
lower-case hexadecimal. (See the [./hashpolicy.wiki|hash policy
Changes to www/fiveminutes.wiki.
56
57
58
59
60
61
62
63
64
65
66
67
68
69
otherwise just the listed file(s) will be checked in.

<h2>Compare two revisions of a file</h2>
<p>If you wish to compare the last revision of a file and its checked out version
in your work directory:</p>
<p>fossil gdiff myfile.c</p>
<p>If you wish to compare two different revisions of a file in the repository:</p>
<p>fossil finfo myfile: Note the first hash, which is the UUID of the commit
when the file was committed</p>
<p>fossil gdiff --from UUID#1 --to UUID#2 myfile.c</p>
<h2>Cancel changes and go back to previous revision</h2>
<p>fossil revert myfile.c</p>
<p>Fossil does not prompt when reverting a file. It simply reminds the user about the
"undo" command, just in case the revert was a mistake.</p>







|

|




56
57
58
59
60
61
62
63
64
65
66
67
68
69
otherwise just the listed file(s) will be checked in.

<h2>Compare two revisions of a file</h2>
<p>If you wish to compare the last revision of a file and its checked out version
in your work directory:</p>
<p>fossil gdiff myfile.c</p>
<p>If you wish to compare two different revisions of a file in the repository:</p>
<p>fossil finfo myfile: Note the first hash, which is the hash of the commit
when the file was committed</p>
<p>fossil gdiff --from HASH#1 --to HASH#2 myfile.c</p>
<h2>Cancel changes and go back to previous revision</h2>
<p>fossil revert myfile.c</p>
<p>Fossil does not prompt when reverting a file. It simply reminds the user about the
"undo" command, just in case the revert was a mistake.</p>
Changes to www/fossil-v-git.wiki.
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
<tr>
    <td>File versioning only</td>
    <td>VCS, tickets, wiki, docs, notes, forum, UI,
    [https://en.wikipedia.org/wiki/Role-based_access_control|RBAC]</td>
    <td><a href="#features">2.1&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Sprawling, incoherent, and inefficient</td>
    <td>Self-contained and efficient</td>
    <td><a href="#efficient">2.2&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Ad-hoc pile-of-files key/value database</td>
    <td>[https://sqlite.org/famous.html|The most popular database in the world]</td>
    <td><a href="#durable">2.3&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Portable to POSIX systems only</td>
    <td>Runs just about anywhere</td>
    <td><a href="#portable">2.4&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Bazaar-style development</td>
    <td>Cathedral-style development</td>
    <td><a href="#devorg">2.5.1&nbsp;&darr;</a></td>
</tr>







|




|




|
|







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
<tr>
    <td>File versioning only</td>
    <td>VCS, tickets, wiki, docs, notes, forum, UI,
    [https://en.wikipedia.org/wiki/Role-based_access_control|RBAC]</td>
    <td><a href="#features">2.1&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Sprawling and inefficient</td>
    <td>Self-contained and efficient</td>
    <td><a href="#efficient">2.2&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>One-off custom pile-of-files data store</td>
    <td>[https://sqlite.org/famous.html|The most popular database in the world]</td>
    <td><a href="#durable">2.3&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Runs natively on POSIX systems only</td>
    <td>Native on common desktop & server platforms</td>
    <td><a href="#portable">2.4&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>Bazaar-style development</td>
    <td>Cathedral-style development</td>
    <td><a href="#devorg">2.5.1&nbsp;&darr;</a></td>
</tr>
Changes to www/hashes.md.
1
2
3
4
5
6
7

8
9
10
11

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
73
74

75
76
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
103
104
105
106
107

108
109

110
111
112
113

114
115


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

134
135
136
137
138
139

140
141
142


143
144
145
# Hashes: Fossil Artifact Identification

All artifacts in Fossil are identified by a unique hash, currently using
[the SHA3 algorithm by default][hpol], but historically using the SHA1
algorithm. Therefore, there are two full-length hash formats used by
Fossil:


| Algorithm | Raw Bits | Hex ASCII Bytes |
|-----------|----------|-----------------|
| SHA3-256  | 256      | 64              |
| SHA1      | 160      | 40              |


There are many types of artifacts in Fossil: commits (a.k.a. check-ins),
tickets, ticket comments, wiki articles, forum postings, file data
belonging to check-ins, etc. ([More info...](./concepts.wiki#artifacts)).

There is a loose hierarchy of terms used instead of “hash” in various
parts of the Fossil UI, terms we try to use consistently, though we have
not always succeeded. We cover each of those terms in the sections
below.


## Names

Several Fossil interfaces accept [a wide variety of check-in
names][cin]: commit artifact hashes, ISO8601 date strings, branch names,
etc.



Artifact hashes are names, but not all names are artifact hashes. We use
the broader term to refer to the whole class of options, and we use the
specific terms when we mean one particular type of name.


## Versions

When an artifact hash refers to a specific commit, Fossil sometimes
calls it a “VERSION,” a “commit ID,” or a “check-in ID.” This is a
specific type of artifact hash, distinct from, let us say, a wiki
article artifact hash.

We may eventually settle on one of these terms, but all three are
currently in common use within Fossil’s docs, UI, and programming
interfaces.




A unique prefix of a VERSION hash is itself a VERSION. That is, if your
repository has exactly one commit artifact with a hash prefix of
“abc123”, then that is a valid version string as long as it remains
unambiguous.



## <a id="uvh"></a>UUIDs: An Unfortunate Historical Artifact

Historically, Fossil incorrectly used the term “[UUID][uuid]where it
should use the term “artifact hash” instead. There are two primary
problems with miscalling Fossil artifact hashes UUIDs:


1. UUIDs are always 128 bits in length — 32 hex ASCII bytes — making
   them shorter than any actual Fossil artifact hash.



2. Artifact hashes are necessarily highly pseudorandom blobs, but only
   [version 4 UUIDs][v4] are pseudorandom in the same way. Other UUID
   types have non-random meanings for certain subgroups of the bits,

   restrictions that Fossil artifact hashes do not meet.





Therefore, no Fossil hash can ever be a proper UUID.






Nevertheless, there are several places in Fossil where we still use the




term UUID, primarily for backwards compatibility:




### Repository DB Schema

Almost all of these uses flow from the `blob.uuid` table column. This is

a key lookup column in the most important persistent Fossil DB table, so
it influences broad swaths of the Fossil internals.

Someday we may rename this column and those it has influenced (e.g.
`purgeitem.uuid`, `shun.uuid`, and `ticket.tkt_uuid`) by making Fossil
detect the outdated schema and silently upgrade it, coincident with
updating all of the SQL in Fossil that refers to these columns. Until
then, Fossil will continue to have “UUID” all through its internals.

In order to avoid needless terminology conflicts, Fossil code that
refers to these misnamed columns also uses some variant of “UUID.” For
example, C code that refers to SQL result data on `blob.uuid` usually
calls the variable `zUuid`. Another example is the internal function

`uuid_to_rid()`. Until and unless we decide to rename these DB columns,
we will keep these associated internal identifiers unchanged.




You may have local SQL code that digs into the repository DB using these
column names. If so, be warned: we are not inclined to consider
existence of such code sufficient reason to avoid renaming the columns.
The Fossil repository DB schema is not considered an external user
interface, and internal interfaces are subject to change at any time. We
suggest switching to a more stable API: the JSON API, `/timeline.rss`,
TH1, etc.

There are also some temporary tables that misuse “UUID” in this way.
(`description.uuid`, `timeline.uuid`, `xmark.uuid`, etc.) There’s a good
chance we’ll fix these before we fix the on-disk DB schema since no
other code can depend on them.


### TH1 Scripting Interfaces

Some [TH1](./th1.md) interfaces use “UUID” where they actually mean some

kind of hash. For example, the `$tkt_uuid` variable, available via TH1
when [customizing Fossil’s ticket system][ctkt].


Because this is considered a public programming interface, we are
unwilling to unilaterally rename such TH1 variables, even though they
are “wrong.” For now, we are simply documenting the misuse. Later, we

may provide a parallel interface — e.g. `$tkt_hash` in this case — and
drop mention of the old interface from the documentation, but still


support it.


### JSON API Parameters and Outputs

The JSON API frequently misuses the term “UUID” in the same sort of way,
most commonly in [artifact][jart] and [timeline][jtim] APIs. As with the
prior case, we can’t fix these without breaking code that uses the JSON
API as originally designed, so our solutions are the same: document the
misuse here for now, then possibly provide a backwards-compatible fix
later.


### `manifest.uuid`

If you have [the `manifest` setting][mset] enabled, Fossil writes a file
called `manifest.uuid` at the root of the check-out tree containing the
commit hash for the current checked-out version. Because this is a

public interface, we are unwilling to rename the file for correctness.


[cin]:  ./checkin_names.wiki
[ctkt]: ./custom_ticket.wiki
[hpol]: ./hashpolicy.wiki

[jart]: ./json-api/api-artifact.md
[jtim]: ./json-api/api-timeline.md
[mset]: /help?cmd=manifest


[tvb]:  ./branching.wiki
[uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier
[v4]:   https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)




|
<

>
|
<
|
|
>






|
<
<






|
>
>

|
|
|





|
<
<
<



>
>
>








|

|
|
|
>

<
|

>
>
|
<
<
>
|
>

>
>
>
|

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




|
>
|
|
|
<
<
<
<
<

<
<
|
|
>
|
<
>
>
>


|
<
|
|
|
<
|
<
<
<
<




|
>
|
|
>

|
<
<
>
|
<
>
>
|




|
|
|
|
<
<







>
|





>



>
>


<
1
2
3
4
5

6
7
8

9
10
11
12
13
14
15
16
17
18


19
20
21
22
23
24
25
26
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
73
74
75
76
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
103
104
105

106
107
108

109




110
111
112
113
114
115
116
117
118
119
120


121
122

123
124
125
126
127
128
129
130
131
132
133


134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

# Hashes: Fossil Artifact Identification

All artifacts in Fossil are identified by a unique hash, currently using
[the SHA3 algorithm by default][hpol], but historically using the SHA1
algorithm:


<table border="1" cellspacing="0" cellpadding="10">
<tr><th>Algorithm<</th><th>Raw Bits</th> <th>Hexadecimal digits</th></tr>

<tr><td>SHA3-256</td>  <td>256</td>      <td>64</td></tr>
<tr><td>SHA1</td>      <td>160</td>      <td>40</td></tr>
</table>

There are many types of artifacts in Fossil: commits (a.k.a. check-ins),
tickets, ticket comments, wiki articles, forum postings, file data
belonging to check-ins, etc. ([More info...](./concepts.wiki#artifacts)).

There is a loose hierarchy of terms used instead of “hash” in various
parts of the Fossil UI, which we cover in the sections below.




## Names

Several Fossil interfaces accept [a wide variety of check-in
names][cin]: commit artifact hashes, ISO8601 date strings, branch names,
etc. Fossil interfaces that accept any of these options usually
document the parameter as “NAME”, so we will use that form to refer to
this specialized use.

Artifact hashes are only one of many different types of NAME.  We use
the broad term “NAME” to refer to the whole class of options. We use
more specific terms when we mean one particular type of NAME.


## Versions

When an artifact hash refers to a specific commit, Fossil sometimes
calls it a “VERSION,” a “commit ID,” or a “check-in ID.”



We may eventually settle on one of these terms, but all three are
currently in common use within Fossil’s docs, UI, and programming
interfaces.

A VERSION is a specific type of artifact hash, distinct
from, let us say, a wiki article artifact hash.

A unique prefix of a VERSION hash is itself a VERSION. That is, if your
repository has exactly one commit artifact with a hash prefix of
“abc123”, then that is a valid version string as long as it remains
unambiguous.



## <a id="uvh"></a>UUIDs

Fossil uses the term “UUID” as a short alias for “artifact hash” in its
internals. There are a few places where this leaks out into external
interfaces, which we cover in the sections below. Going forward, we
prefer one of the terms above in public interfaces instead.


Whether this short alias is correct is debateable.

One argument is that since "UUID" is an acronym for “Univerially Unique
Identifier,” and both SHA1 and SHA3-256 are larger and stronger than the
128-bit algorithms used by “proper” UUIDs, Fossil artifact hashes are


*more universally unique*. It is therefore quibbling to say that Fossil
UUIDs are not actually UUIDs. One wag suggested that Fossil artifact
hashes be called MUIDs: multiversally unique IDs.

The common counterargument is that the acronym “UUID” was created for [a
particular type of universally-unique ID][uuid], with particular ASCII
and bitfield formats, and with particular meaning given to certain of
its bits. In that sense, no Fossil “UUID” can be used as a proper UUID.

Be warned: attempting to advance the second position on the Fossil
discussion forum will get you nowhere at this late date. We’ve had the
debates, we’ve done the engineering, and we’ve made our evaluation. It’s
a settled matter: internally within Fossil, “UUID” is defined as in this
section’s leading paragraph.

To those who remain unconvinced, “fixing” this would require touching
almost every source code file in Fossil in a total of about a thousand
separate locations. (Not exaggeration, actual data.) This would be a
massive undertaking simply to deal with a small matter of terminology,
with a high risk of creating bugs and downstream incompatibilities.
Therefore, we are highly unlikely to change this ourselves, and we are
also unlikely to accept a patch that attempts to fix it.


### Repository DB Schema

The primary place where you find "UUID" in Fossil is in the `blob.uuid`
table column, in code dealing with that column, and in code manipulating
*other* data that *refers* to that column. This is a key lookup column
in the most important Fossil DB table, so it influences broad swaths of
the Fossil internals.








For example, C code that refers to SQL result data on `blob.uuid`
usually calls the variable `zUuid`. That value may then be inserted into
a table like `ticket.tkt_uuid`, creating a reference back to
`blob.uuid`, and then be passed to a function like `uuid_to_rid()`.

There is no point renaming a single one of these in isolation: it would
create needless terminology conflicts, making the code hard to read and
understand, risking the creation of new bugs.

You may have local SQL code that digs into the repository DB using these
column names. While you may rest easy, assured now that we are highly

unlikely to ever rename these columns, the Fossil repository DB schema
is not considered an external user interface, and internal interfaces
are subject to change at any time. We suggest switching to a more stable

API: [the JSON API][japi], [`timeline.rss`][trss], [TH1][th1], etc.






### TH1 Scripting Interfaces

Some [TH1][th1] interfaces expose Fossil internals flowing from
`blob.uuid`, so “UUID” is a short alias for “artifact hash” in TH1.  For
example, the `$tkt_uuid` variable &mdash; available when [customizing
the ticket system][ctkt] &mdash; is a ticket artifact hash, exposing the
`ticket.tkt_uuid` column, which has a SQL relation to `blob.uuid`.

TH1 is a longstanding public programming interface. We cannot rename its


interfaces without breaking existing TH1 Fossil customizations. We are
also unlikely to provide a parallel set of variables with “better

names, since that would create a mismatch with respect to the internals
they expose, creating a different sort of developer confusion in its
place.


### JSON API Parameters and Outputs

[The JSON API][japi] frequently uses the term “UUID” in the same sort of way,
most commonly in [artifact][jart] and [timeline][jtim] APIs. As with
TH1, we can’t change this without breaking code that uses the JSON
API as originally designed, so we take the same stance.




### `manifest.uuid`

If you have [the `manifest` setting][mset] enabled, Fossil writes a file
called `manifest.uuid` at the root of the check-out tree containing the
commit hash for the current checked-out version. Because this is a
public interface that existing code depends on, we are unwilling to
rename the file.


[cin]:  ./checkin_names.wiki
[ctkt]: ./custom_ticket.wiki
[hpol]: ./hashpolicy.wiki
[japi]: ./json-api/
[jart]: ./json-api/api-artifact.md
[jtim]: ./json-api/api-timeline.md
[mset]: /help?cmd=manifest
[th1]:  ./th1.md
[trss]: /help?cmd=/timeline.rss
[tvb]:  ./branching.wiki
[uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier

Changes to www/json-api/api-artifact.md.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
73
74
75
76
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

<a id="checkin"></a>
# Checkin Artifacts (Commits)

Returns information about checkin artifacts (commits).

**Status:** implemented 201110xx

**Request:** `/json/artifact/COMMIT_UUID`

**Required permissions:** "o" (was "h" prior to 20120408)

**Response payload example: (CHANGED SIGNIFICANTLY ON 20120713)**

```json
{
"type":"checkin",
"name":"18dd383e5e7684e", // as given on CLI
"uuid":"18dd383e5e7684ecee327d3de7d3ff846069d1b2",
"isLeaf":false,
"user":"drh",
"comment":"Merge wideAnnotateUser and jsonWarnings into trunk.",
"timestamp":1330090810,
"parents":[
  // 1st entry is primary parent UUID:
  "3a44f95f40a193739aaafc2409f155df43e74a6f",
  // Remaining entries are merged-in branch UUIDs:
  "86f6e675eb3f8761d70d8b82b052ce2b297fffd2",\
  "dbf4ecf414881c9aad6f4f125dab9762589ef3d7"\
],
"tags":["trunk"],
"files":[{
    "name":"src/diff.c",
    // BLOB uuids, NOT commit UUIDs:
    "uuid":"78c74c3b37e266f8f7e570d5cf476854b7af9d76",
    "parent":"b1fa7e636cf4e7b6ed20bba2d2680397f80c096a",
    "state":"modified",
    "downloadPath":"/raw/src/diff.c?name=78c74c3b37e266f8f7e570d5cf476854b7af9d76"
  },
  ...]
}
```

The "parents" property lists the parent UUIDs of the checkin. The
"parent" property of file entries refers to the parent UUID of that
file. In the case of a merge there may be essentially an arbitrary
number. The first entry in the list is the "primary" parent. The primary
parent is the parent which was not pulled in via a merge operation. The
ordering of remaining entries is unspecified and may even change between
calls. For example: if, from branch C, we merge in A and B and then
commit, then in the artifact response for that commit the UUID of branch
C will be in the first (primary) position, with the UUIDs for branches A
and B in the following entries (in an unspecified, and possibly
unstable, order).

Note that the "uuid" and "parent" properties of the "files" entries
refer to raw blob uuids, not commit uuids (i.e. not checkins).


<a id="file"></a>
# File Artifacts

Fetches information about file artifacts.

**FIXME:** the content type guessing is currently very primitive, and
may (but i haven't seen this) mis-diagnose some non-binary files as
binary. Fossil doesn't yet have a mechanism for mime-type mappings.

**Status:** implemented 20111020

**Required permissions:** "o"

**Request:** `/json/artifact/FILE_UUID`

**Request options:**

-   `format=(raw|html|none)` (default=none). If set, the contents of the
    artifact are included if they are text, else they are not (JSON does
    not do binary). The "html" flag runs it through the wiki parser. The
    results of doing so are unspecified for non-embedded-doc files. The
    "raw" format means to return the content as-is. "none" is the same
    as not specifying this flag, and elides the content from the
    response.
-   DEPRECATED (use format instead): `includeContent=bool` (=false) (CLI:
    `--content|-c`). If true, the full content of the artifact is returned
    for text-only artifacts (but not for non-text artifacts). As of
    20120713 this option is only inspected if "format" is not specified.

**Response payload example: (CHANGED SIGNIFICANTLY ON 20120713)**

```json
{
"type":"file",
"name":"same name specified as FILE_UUID argument",
"size": 12345, // in bytes, independent of format=...
"parent": "uuid of parent file blob. Not set for first generation.",
"checkins":[{
  "name":"src/json_detail.h",
  "timestamp":1319058803,
  "comment":"...",
  "user":"stephan",
  "checkin":"d2c1ae23a90b24f6ca1d7637193a59d5ecf3e680",
  "branch":"json",
  "state":"added|modified|removed"
  },
  ...],
/* The following "content" properties are only set if format=raw|html */
"content": "file contents",
"contentSize": "size of content field, in bytes. Affected by the format option!",
"contentType": "text/plain", /* currently always text/plain */
"contentFormat": "html|raw"
}
```

The "checkins" array lists all checkins which include this file, and a
file might have different names across different branches. The size and
uuid, however, are the same across all checkins for a given blob.

<a id="wiki"></a>
# Wiki Artifacts

Returns information about wiki artifacts.

**Status:** implemented 20111020, fixed to return the requested version
(instead of the latest) on 20120302.

**Request:** `/json/artifact/WIKI_UUID`

**Required permissions:** "j"

**Options:**

-   DEPRECATED (use format instead): `bool includeContent` (=false). If
    true then the raw content is returned with the wiki page, else no
    content is returned.\  
    CLI: `--includeContent|-c`
-   The `--format` option works as for
    [`/json/wiki/get`](api-wiki.md#get), and if set then it
    implies the `includeContent` option.

**Response payload example:**

Currently the same as [`/json/wiki/get`](api-wiki.md#get).









|















|

|






|









|
|





|
|




|
>














|




















|

|




















|









|

















>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
73
74
75
76
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<a id="checkin"></a>
# Checkin Artifacts (Commits)

Returns information about checkin artifacts (commits).

**Status:** implemented 201110xx

**Request:** `/json/artifact/COMMIT_HASH`

**Required permissions:** "o" (was "h" prior to 20120408)

**Response payload example: (CHANGED SIGNIFICANTLY ON 20120713)**

```json
{
"type":"checkin",
"name":"18dd383e5e7684e", // as given on CLI
"uuid":"18dd383e5e7684ecee327d3de7d3ff846069d1b2",
"isLeaf":false,
"user":"drh",
"comment":"Merge wideAnnotateUser and jsonWarnings into trunk.",
"timestamp":1330090810,
"parents":[
  // 1st entry is primary parent hash:
  "3a44f95f40a193739aaafc2409f155df43e74a6f",
  // Remaining entries are merged-in branch hashes:
  "86f6e675eb3f8761d70d8b82b052ce2b297fffd2",\
  "dbf4ecf414881c9aad6f4f125dab9762589ef3d7"\
],
"tags":["trunk"],
"files":[{
    "name":"src/diff.c",
    // BLOB hashes, NOT commit hashes:
    "uuid":"78c74c3b37e266f8f7e570d5cf476854b7af9d76",
    "parent":"b1fa7e636cf4e7b6ed20bba2d2680397f80c096a",
    "state":"modified",
    "downloadPath":"/raw/src/diff.c?name=78c74c3b37e266f8f7e570d5cf476854b7af9d76"
  },
  ...]
}
```

The "parents" property lists the parent hashes of the checkin. The
"parent" property of file entries refers to the parent hash of that
file. In the case of a merge there may be essentially an arbitrary
number. The first entry in the list is the "primary" parent. The primary
parent is the parent which was not pulled in via a merge operation. The
ordering of remaining entries is unspecified and may even change between
calls. For example: if, from branch C, we merge in A and B and then
commit, then in the artifact response for that commit the hash of branch
C will be in the first (primary) position, with the hashes for branches A
and B in the following entries (in an unspecified, and possibly
unstable, order).

Note that the "uuid" and "parent" properties of the "files" entries
refer to raw blob hashes, not commit (a.k.a. check-in) hashes. See also
[the UUID vs. Hash discussion][uvh].

<a id="file"></a>
# File Artifacts

Fetches information about file artifacts.

**FIXME:** the content type guessing is currently very primitive, and
may (but i haven't seen this) mis-diagnose some non-binary files as
binary. Fossil doesn't yet have a mechanism for mime-type mappings.

**Status:** implemented 20111020

**Required permissions:** "o"

**Request:** `/json/artifact/FILE_HASH`

**Request options:**

-   `format=(raw|html|none)` (default=none). If set, the contents of the
    artifact are included if they are text, else they are not (JSON does
    not do binary). The "html" flag runs it through the wiki parser. The
    results of doing so are unspecified for non-embedded-doc files. The
    "raw" format means to return the content as-is. "none" is the same
    as not specifying this flag, and elides the content from the
    response.
-   DEPRECATED (use format instead): `includeContent=bool` (=false) (CLI:
    `--content|-c`). If true, the full content of the artifact is returned
    for text-only artifacts (but not for non-text artifacts). As of
    20120713 this option is only inspected if "format" is not specified.

**Response payload example: (CHANGED SIGNIFICANTLY ON 20120713)**

```json
{
"type":"file",
"name":"same name specified as FILE_HASH argument",
"size": 12345, // in bytes, independent of format=...
"parent": "hash of parent file blob. Not set for first generation.",
"checkins":[{
  "name":"src/json_detail.h",
  "timestamp":1319058803,
  "comment":"...",
  "user":"stephan",
  "checkin":"d2c1ae23a90b24f6ca1d7637193a59d5ecf3e680",
  "branch":"json",
  "state":"added|modified|removed"
  },
  ...],
/* The following "content" properties are only set if format=raw|html */
"content": "file contents",
"contentSize": "size of content field, in bytes. Affected by the format option!",
"contentType": "text/plain", /* currently always text/plain */
"contentFormat": "html|raw"
}
```

The "checkins" array lists all checkins which include this file, and a
file might have different names across different branches. The size and
hash, however, are the same across all checkins for a given blob.

<a id="wiki"></a>
# Wiki Artifacts

Returns information about wiki artifacts.

**Status:** implemented 20111020, fixed to return the requested version
(instead of the latest) on 20120302.

**Request:** `/json/artifact/WIKI_HASH`

**Required permissions:** "j"

**Options:**

-   DEPRECATED (use format instead): `bool includeContent` (=false). If
    true then the raw content is returned with the wiki page, else no
    content is returned.\  
    CLI: `--includeContent|-c`
-   The `--format` option works as for
    [`/json/wiki/get`](api-wiki.md#get), and if set then it
    implies the `includeContent` option.

**Response payload example:**

Currently the same as [`/json/wiki/get`](api-wiki.md#get).

[uvh]: ../hashes.md#uvh
Changes to www/json-api/api-diff.md.
31
32
33
34
35
36
37
38
39
40
41
42
43
44
"to":"96920e7c04746c55ceed6e24fc82879886cb8197",
"diff":"@@ -1,7 +1,7 @@\\n-C factored\\\\sout..."
}
```

TODOs:

-   Unlike the standard diff command, which apparently requires commit
    UUID, this one diffs individual file versions. If a commit UUID is
    provided, a diff of the manifests is returned. (That should be
    considered a bug - we should return a combined diff in that case.)
-   If UUIDs from two different types of artifacts are given, results
    are unspecified. Garbage in, garbage out, and all that.
-   For file diffs, add the file name(s) to the response payload.







|
|


|


31
32
33
34
35
36
37
38
39
40
41
42
43
44
"to":"96920e7c04746c55ceed6e24fc82879886cb8197",
"diff":"@@ -1,7 +1,7 @@\\n-C factored\\\\sout..."
}
```

TODOs:

-   Unlike the standard diff command, which apparently requires a commit
    hash, this one diffs individual file versions. If a commit hash is
    provided, a diff of the manifests is returned. (That should be
    considered a bug - we should return a combined diff in that case.)
-   If hashes from two different types of artifacts are given, results
    are unspecified. Garbage in, garbage out, and all that.
-   For file diffs, add the file name(s) to the response payload.
Changes to www/json-api/api-tag.md.
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
"value":"abc",
"propagate":true,
"raw":false,
"appliedTo":"626ab2f3743543122cc11bc082a0603d2b5b2b1b"
}
```

The appliedTo property is the UUID of the checkin to which the tag was
applied. This is the "resolved" version of the checkin name provided by
the client.

<a id="cancel"></a>
# Cancel Tag

**Status:** implemented 20111006








|
|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
"value":"abc",
"propagate":true,
"raw":false,
"appliedTo":"626ab2f3743543122cc11bc082a0603d2b5b2b1b"
}
```

The `appliedTo` property is the hash of the check-in to which the tag was
applied. This is the "resolved" version of the check-in name provided by
the client.

<a id="cancel"></a>
# Cancel Tag

**Status:** implemented 20111006

97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
112
-   `name=string` The tag name to search for. Can optionally be the 3rd
    path element.
-   `limit=int` (defalt=0) Limits the number of results (0=no limit).
    Since they are ordered from oldest to newest, the newest N results
    will be returned.
-   `type=string` (default=`*`) Searches only for the given type of
    artifact (using fossil's conventional type naming: ci, e, t, w.
-   `raw=bool` (=false) If enabled, the response is an array of UUID

    strings, else it is an array of higher-level objects. If this is
    true, the "name" property is interpretted as-is. If it is false, the
    name is automatically prepended with "sym-" (meaning a branch).
    (FIXME: the current semantics are confusing and hard to remember.
    Re-do them.)

**Response payload example, in RAW mode: (expect this format to change
at some point!)**







|
>
|







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
-   `name=string` The tag name to search for. Can optionally be the 3rd
    path element.
-   `limit=int` (defalt=0) Limits the number of results (0=no limit).
    Since they are ordered from oldest to newest, the newest N results
    will be returned.
-   `type=string` (default=`*`) Searches only for the given type of
    artifact (using fossil's conventional type naming: ci, e, t, w.
-   `raw=bool` (=false) If enabled, the response is an array of hashes
    of the requested artifact type; otherwise,
    it is an array of higher-level objects. If this is
    true, the "name" property is interpretted as-is. If it is false, the
    name is automatically prepended with "sym-" (meaning a branch).
    (FIXME: the current semantics are confusing and hard to remember.
    Re-do them.)

**Response payload example, in RAW mode: (expect this format to change
at some point!)**
Changes to www/json-api/api-timeline.md.
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  "uuid":"be700e84336941ef1bcd08d676310b75b9070f43",
  "timestamp":1317094090,
  "comment":"Added /json/timeline/ci showFiles to ajax test page.",
  "user":"stephan",
  "isLeaf":true,
  "bgColor":null, /* not quite sure why this is null? */
  "type":"ci",
  "parents": ["primary parent UUID", "...other parent UUIDs"],
  "tags":["json"],
  "files":[{
    "name":"ajax/index.html",
    "uuid":"9f00773a94cea6191dc3289aa24c0811b6d0d8fe",
    "parent":"50e337c33c27529e08a7037a8679fb84b976ad0b",
    "state":"modified"
   }]







|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  "uuid":"be700e84336941ef1bcd08d676310b75b9070f43",
  "timestamp":1317094090,
  "comment":"Added /json/timeline/ci showFiles to ajax test page.",
  "user":"stephan",
  "isLeaf":true,
  "bgColor":null, /* not quite sure why this is null? */
  "type":"ci",
  "parents": ["primary parent hash", "...other parent hashes"],
  "tags":["json"],
  "files":[{
    "name":"ajax/index.html",
    "uuid":"9f00773a94cea6191dc3289aa24c0811b6d0d8fe",
    "parent":"50e337c33c27529e08a7037a8679fb84b976ad0b",
    "state":"modified"
   }]
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
commit. The first entry in the array is the "primary parent" - the one
which was not involved in a merge with the child.

**Request options:**

-   `files=bool` toggles the addition of a "files" array property which
    contains objects describing the files changed by the commit,
    including their uuid, previous uuid, and state change type
    (modified, added, or removed).\  
    CLI mode: `--show-files|-f`
-   `tag|branch=string` selects only entries with the given tag or "close
    to" the given branch. Only one of these may be specified and if both
    are specified, which one takes precedence is unspecified. If the
    given tag/branch does not exist, an error response is generated. The
    difference between the two is subtle - tag filters only on the given
    tag (analog to the HTML interface's "r" option) whereas branch can







|
|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
commit. The first entry in the array is the "primary parent" - the one
which was not involved in a merge with the child.

**Request options:**

-   `files=bool` toggles the addition of a "files" array property which
    contains objects describing the files changed by the commit,
    including their hash, previous hash, and state change type
    (modified, added, or removed). ([“uuid” here means hash][uvh])\  
    CLI mode: `--show-files|-f`
-   `tag|branch=string` selects only entries with the given tag or "close
    to" the given branch. Only one of these may be specified and if both
    are specified, which one takes precedence is unspecified. If the
    given tag/branch does not exist, an error response is generated. The
    difference between the two is subtle - tag filters only on the given
    tag (analog to the HTML interface's "r" option) whereas branch can
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
    "comment":"Ticket [b64435dba9] &lt;i&gt;How to...&lt;/i&gt;",
    "briefComment":"Ticket [b64435dba9]: 2 changes",
    "ticketUuid":"b64435dba9cceb709bd54fbc5883884d73f93491"
  },...]
}
```

**Notice that there are two UUIDs for tickets** - `uuid` is the change
UUID and `ticketUuid` is the actual ticket. This is an unfortunate
discrepancy vis-a-vis the other timeline entries, which only have one
uuid. We may want to swap uuid to mean the ticket uuid and change uuid
to commitUuid.

<a id="wiki"></a>
# Wiki Timeline

**Status:** implemented 201109xx

**Required privileges:** "j" or "o"







|
|

|
|







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
    "comment":"Ticket [b64435dba9] &lt;i&gt;How to...&lt;/i&gt;",
    "briefComment":"Ticket [b64435dba9]: 2 changes",
    "ticketUuid":"b64435dba9cceb709bd54fbc5883884d73f93491"
  },...]
}
```

**Notice that there are two [hashes][uvh] for tickets** - `uuid` is the change
hash and `ticketUuid` is the actual ticket’s hash. This is an unfortunate
discrepancy vis-a-vis the other timeline entries, which only have one
hash. We may want to swap `uuid` to mean the ticket hash and change `uuid`
to `commitHash`.

<a id="wiki"></a>
# Wiki Timeline

**Status:** implemented 201109xx

**Required privileges:** "j" or "o"
204
205
206
207
208
209
210
211
212
213

  "user":"stephan",
  "eventType":"w"
 },...]
}
```

The `uuid` of each entry can be passed to `/json/artifact` or
`/json/wiki/get?uuid=...` to fetch the raw page and the uuid of the
parent version.









|


>
204
205
206
207
208
209
210
211
212
213
214
  "user":"stephan",
  "eventType":"w"
 },...]
}
```

The `uuid` of each entry can be passed to `/json/artifact` or
`/json/wiki/get?uuid=...` to fetch the raw page and the hash of the
parent version.

[uvh]: ../hashes.md#uvh
Changes to www/json-api/conventions.md.
582
583
584
585
586
587
588
589

590
591
592
593
594
595
596
**`FOSSIL-3000`: Usage Error Category**

- `FOSSIL-3001`: Invalid argument/parameter type(s) or value(s) in
  request
- `FOSSIL-3002`: Required argument(s)/parameter(s) missing from
  request
- `FOSSIL-3003`: Requested resource identifier is ambiguous (e.g. a
  shortened UUID can be ambiguous).

- `FOSSIL-3004`: Unresolved resource identifier. A branch/tag/uuid
  provided by client code could not be resolved. This is a special
  case of #3006.
- `FOSSIL-3005`: Resource already exists and overwriting/replacing is
  not allowed. e.g. trying to create a wiki page or user which already
  exists. FIXME? Consolidate this and resource-not-found into a
  separate category for dumb-down purposes?







|
>







582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
**`FOSSIL-3000`: Usage Error Category**

- `FOSSIL-3001`: Invalid argument/parameter type(s) or value(s) in
  request
- `FOSSIL-3002`: Required argument(s)/parameter(s) missing from
  request
- `FOSSIL-3003`: Requested resource identifier is ambiguous (e.g. a
  shortened hash that matches multiple artifacts, an abbreviated
  date that matches multiple commits, etc.)
- `FOSSIL-3004`: Unresolved resource identifier. A branch/tag/uuid
  provided by client code could not be resolved. This is a special
  case of #3006.
- `FOSSIL-3005`: Resource already exists and overwriting/replacing is
  not allowed. e.g. trying to create a wiki page or user which already
  exists. FIXME? Consolidate this and resource-not-found into a
  separate category for dumb-down purposes?