Fossil

Check-in [d1983dd24c]
Login

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

Overview
Comment:Merge updates from trunk.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | warningFix
Files: files | file ages | folders
SHA1: d1983dd24ccb0c1c2ecbc5d9332374e05aa55ca3
User & Date: mistachkin 2014-10-21 03:00:25.628
Context
2014-10-21
03:05
Fix compiler warning and clarify use of local variables. ... (check-in: e7518b44eb user: mistachkin tags: trunk)
03:00
Merge updates from trunk. ... (Closed-Leaf check-in: d1983dd24c user: mistachkin tags: warningFix)
02:52
Add the codecheck1 program to the build tools. This program validates the format strings on printf-style internal interfaces, verifying that the correct number of arguments follow and that SQL is protected from injection attacks. ... (check-in: 49b0ff1592 user: drh tags: trunk)
2014-10-16
02:16
Merge updates from trunk. ... (check-in: 1b6dfe8178 user: mistachkin tags: warningFix)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/allrepo.c.
255
256
257
258
259
260
261
262


263
264

265
266
267
268
269
270
271
272
273
274
275
276
277
    quiet = 1;
  }else if( strncmp(zCmd, "ignore", n)==0 ){
    int j;
    useCheckouts = find_option("ckout","c",0)!=0;
    verify_all_options();
    db_begin_transaction();
    for(j=3; j<g.argc; j++){
      char *zSql = mprintf("DELETE FROM global_config"


                           " WHERE name GLOB '%s:%q'",
                           useCheckouts?"ckout":"repo", g.argv[j]);

      if( dryRunFlag ){
        fossil_print("%s\n", zSql);
      }else{
        db_multi_exec("%s", zSql);
      }
      fossil_free(zSql);
    }
    db_end_transaction(0);
    return;
  }else if( strncmp(zCmd, "info", n)==0 ){
    zCmd = "info";
    showLabel = 1;
    quiet = 1;







|
>
>
|
|
>

|

|

|







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
    quiet = 1;
  }else if( strncmp(zCmd, "ignore", n)==0 ){
    int j;
    useCheckouts = find_option("ckout","c",0)!=0;
    verify_all_options();
    db_begin_transaction();
    for(j=3; j<g.argc; j++){
      Blob sql;
      blob_zero(&sql);
      blob_append_sql(&sql, 
         "DELETE FROM global_config WHERE name GLOB '%s:%q'",
         useCheckouts?"ckout":"repo", g.argv[j]
      );
      if( dryRunFlag ){
        fossil_print("%s\n", blob_sql_text(&sql));
      }else{
        db_multi_exec("%s", blob_sql_text(&sql));
      }
      blob_reset(&sql);
    }
    db_end_transaction(0);
    return;
  }else if( strncmp(zCmd, "info", n)==0 ){
    zCmd = "info";
    showLabel = 1;
    quiet = 1;
344
345
346
347
348
349
350
351
352
353
354
  ** be found, remove those names from the ~/.fossil file.
  */
  if( nToDel>0 ){
    const char *zSql = "DELETE FROM global_config WHERE name IN toDel";
    if( dryRunFlag ){
      fossil_print("%s\n", zSql);
    }else{
      db_multi_exec(zSql);
    }
  }
}







|



347
348
349
350
351
352
353
354
355
356
357
  ** be found, remove those names from the ~/.fossil file.
  */
  if( nToDel>0 ){
    const char *zSql = "DELETE FROM global_config WHERE name IN toDel";
    if( dryRunFlag ){
      fossil_print("%s\n", zSql);
    }else{
      db_multi_exec("%s", zSql /*safe-for-%s*/ );
    }
  }
}
Changes to src/attach.c.
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
  const char *zTkt = P("tkt");
  Blob sql;
  Stmt q;

  if( zPage && zTkt ) zTkt = 0;
  login_check_credentials();
  blob_zero(&sql);
  blob_appendf(&sql,
     "SELECT datetime(mtime%s), src, target, filename,"
     "       comment, user,"
     "       (SELECT uuid FROM blob WHERE rid=attachid), attachid"
     "  FROM attachment",
     timeline_utc()
  );
  if( zPage ){
    if( g.perm.RdWiki==0 ) login_needed();
    style_header("Attachments To %h", zPage);
    blob_appendf(&sql, " WHERE target=%Q", zPage);
  }else if( zTkt ){
    if( g.perm.RdTkt==0 ) login_needed();
    style_header("Attachments To Ticket %S", zTkt);
    blob_appendf(&sql, " WHERE target GLOB '%q*'", zTkt);
  }else{
    if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ) login_needed();
    style_header("All Attachments");
  }
  blob_appendf(&sql, " ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_str(&sql));
  @ <ol>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zSrc = db_column_text(&q, 1);
    const char *zTarget = db_column_text(&q, 2);
    const char *zFilename = db_column_text(&q, 3);
    const char *zComment = db_column_text(&q, 4);







|









|



|




|
|







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
  const char *zTkt = P("tkt");
  Blob sql;
  Stmt q;

  if( zPage && zTkt ) zTkt = 0;
  login_check_credentials();
  blob_zero(&sql);
  blob_append_sql(&sql,
     "SELECT datetime(mtime%s), src, target, filename,"
     "       comment, user,"
     "       (SELECT uuid FROM blob WHERE rid=attachid), attachid"
     "  FROM attachment",
     timeline_utc()
  );
  if( zPage ){
    if( g.perm.RdWiki==0 ) login_needed();
    style_header("Attachments To %h", zPage);
    blob_append_sql(&sql, " WHERE target=%Q", zPage);
  }else if( zTkt ){
    if( g.perm.RdTkt==0 ) login_needed();
    style_header("Attachments To Ticket %S", zTkt);
    blob_append_sql(&sql, " WHERE target GLOB '%q*'", zTkt);
  }else{
    if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ) login_needed();
    style_header("All Attachments");
  }
  blob_append_sql(&sql, " ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  @ <ol>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zSrc = db_column_text(&q, 1);
    const char *zTarget = db_column_text(&q, 2);
    const char *zFilename = db_column_text(&q, 3);
    const char *zComment = db_column_text(&q, 4);
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
  rid = name_to_rid_www("name");
  if( rid==0 ){ fossil_redirect_home(); }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
#if 0
  /* Shunning here needs to get both the attachment control artifact and
  ** the object that is attached. */
  if( g.perm.Admin ){
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
#endif
  pAttach = manifest_get(rid, CFTYPE_ATTACHMENT, 0);
  if( pAttach==0 ) fossil_redirect_home();
  zTarget = pAttach->zAttachTarget;
  zSrc = pAttach->zAttachSrc;
  ridSrc = db_int(0,"SELECT rid FROM blob WHERE uuid='%s'", zSrc);
  zName = pAttach->zAttachName;
  zDesc = pAttach->zComment;
  zMime = mimetype_from_name(zName);
  fShowContent = zMime ? strncmp(zMime,"text/", 5)==0 : 0;
  if( validate16(zTarget, strlen(zTarget))
   && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%s'", zTarget)
  ){
    zTktUuid = zTarget;
    if( !g.perm.RdTkt ){ login_needed(); return; }
    if( g.perm.WrTkt ){
      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
    }
  }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){







|












|





|







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
  rid = name_to_rid_www("name");
  if( rid==0 ){ fossil_redirect_home(); }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
#if 0
  /* Shunning here needs to get both the attachment control artifact and
  ** the object that is attached. */
  if( g.perm.Admin ){
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%q'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
#endif
  pAttach = manifest_get(rid, CFTYPE_ATTACHMENT, 0);
  if( pAttach==0 ) fossil_redirect_home();
  zTarget = pAttach->zAttachTarget;
  zSrc = pAttach->zAttachSrc;
  ridSrc = db_int(0,"SELECT rid FROM blob WHERE uuid='%q'", zSrc);
  zName = pAttach->zAttachName;
  zDesc = pAttach->zComment;
  zMime = mimetype_from_name(zName);
  fShowContent = zMime ? strncmp(zMime,"text/", 5)==0 : 0;
  if( validate16(zTarget, strlen(zTarget))
   && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%q'", zTarget)
  ){
    zTktUuid = zTarget;
    if( !g.perm.RdTkt ){ login_needed(); return; }
    if( g.perm.WrTkt ){
      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
    }
  }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){
Changes to src/blob.c.
32
33
34
35
36
37
38

39
40
41
42





43
44
45
46
47
48
49
** A Blob can hold a string or a binary object of arbitrary size.  The
** size changes as necessary.
*/
struct Blob {
  unsigned int nUsed;            /* Number of bytes used in aData[] */
  unsigned int nAlloc;           /* Number of bytes allocated for aData[] */
  unsigned int iCursor;          /* Next character of input to parse */

  char *aData;                   /* Where the information is stored */
  void (*xRealloc)(Blob*, unsigned int); /* Function to reallocate the buffer */
};






/*
** The current size of a Blob
*/
#define blob_size(X)  ((X)->nUsed)

/*
** The buffer holding the blob data







>




>
>
>
>
>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
** A Blob can hold a string or a binary object of arbitrary size.  The
** size changes as necessary.
*/
struct Blob {
  unsigned int nUsed;            /* Number of bytes used in aData[] */
  unsigned int nAlloc;           /* Number of bytes allocated for aData[] */
  unsigned int iCursor;          /* Next character of input to parse */
  unsigned int blobFlags;        /* One or more BLOBFLAG_* bits */
  char *aData;                   /* Where the information is stored */
  void (*xRealloc)(Blob*, unsigned int); /* Function to reallocate the buffer */
};

/*
** Allowed values for Blob.blobFlags
*/
#define BLOBFLAG_NotSQL  0x0001      /* Non-SQL text */

/*
** The current size of a Blob
*/
#define blob_size(X)  ((X)->nUsed)

/*
** The buffer holding the blob data
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
void blobReallocMalloc(Blob *pBlob, unsigned int newSize){
  if( newSize==0 ){
    free(pBlob->aData);
    pBlob->aData = 0;
    pBlob->nAlloc = 0;
    pBlob->nUsed = 0;
    pBlob->iCursor = 0;

  }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){
    char *pNew = fossil_realloc(pBlob->aData, newSize);
    pBlob->aData = pNew;
    pBlob->nAlloc = newSize;
    if( pBlob->nUsed>pBlob->nAlloc ){
      pBlob->nUsed = pBlob->nAlloc;
    }
  }
}

/*
** An initializer for Blobs
*/
#if INTERFACE
#define BLOB_INITIALIZER  {0,0,0,0,blobReallocMalloc}
#endif
const Blob empty_blob = BLOB_INITIALIZER;

/*
** A reallocation function for when the initial string is in unmanaged
** space.  Copy the string to memory obtained from malloc().
*/







>














|







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
void blobReallocMalloc(Blob *pBlob, unsigned int newSize){
  if( newSize==0 ){
    free(pBlob->aData);
    pBlob->aData = 0;
    pBlob->nAlloc = 0;
    pBlob->nUsed = 0;
    pBlob->iCursor = 0;
    pBlob->blobFlags = 0;
  }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){
    char *pNew = fossil_realloc(pBlob->aData, newSize);
    pBlob->aData = pNew;
    pBlob->nAlloc = newSize;
    if( pBlob->nUsed>pBlob->nAlloc ){
      pBlob->nUsed = pBlob->nAlloc;
    }
  }
}

/*
** An initializer for Blobs
*/
#if INTERFACE
#define BLOB_INITIALIZER  {0,0,0,0,0,blobReallocMalloc}
#endif
const Blob empty_blob = BLOB_INITIALIZER;

/*
** A reallocation function for when the initial string is in unmanaged
** space.  Copy the string to memory obtained from malloc().
*/
215
216
217
218
219
220
221

222
223
224
225
226
227
228
  if( zData==0 ){
    *pBlob = empty_blob;
  }else{
    if( size<=0 ) size = strlen(zData);
    pBlob->nUsed = pBlob->nAlloc = size;
    pBlob->aData = (char*)zData;
    pBlob->iCursor = 0;

    pBlob->xRealloc = blobReallocStatic;
  }
}

/*
** Initialize a blob to a nul-terminated string.
** Any prior data in the blob is discarded.







>







222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  if( zData==0 ){
    *pBlob = empty_blob;
  }else{
    if( size<=0 ) size = strlen(zData);
    pBlob->nUsed = pBlob->nAlloc = size;
    pBlob->aData = (char*)zData;
    pBlob->iCursor = 0;
    pBlob->blobFlags = 0;
    pBlob->xRealloc = blobReallocStatic;
  }
}

/*
** Initialize a blob to a nul-terminated string.
** Any prior data in the blob is discarded.
246
247
248
249
250
251
252

253
254
255
256
257
258
259
void blob_zero(Blob *pBlob){
  static const char zEmpty[] = "";
  assert_blob_is_reset(pBlob);
  pBlob->nUsed = 0;
  pBlob->nAlloc = 1;
  pBlob->aData = (char*)zEmpty;
  pBlob->iCursor = 0;

  pBlob->xRealloc = blobReallocStatic;
}

/*
** Append text or data to the end of a blob.
*/
void blob_append(Blob *pBlob, const char *aData, int nData){







>







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
void blob_zero(Blob *pBlob){
  static const char zEmpty[] = "";
  assert_blob_is_reset(pBlob);
  pBlob->nUsed = 0;
  pBlob->nAlloc = 1;
  pBlob->aData = (char*)zEmpty;
  pBlob->iCursor = 0;
  pBlob->blobFlags = 0;
  pBlob->xRealloc = blobReallocStatic;
}

/*
** Append text or data to the end of a blob.
*/
void blob_append(Blob *pBlob, const char *aData, int nData){
290
291
292
293
294
295
296














297
298
299
300
301
302
303
    p->nUsed = 0;
  }
  if( p->aData[p->nUsed]!=0 ){
    blob_materialize(p);
  }
  return p->aData;
}















/*
** Return a pointer to a null-terminated string for a blob.
**
** WARNING:  If the blob is ephemeral, it might cause a '\000'
** character to be inserted into the middle of the parent blob.
** Example:  Suppose p is a token extracted from some larger







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







299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
    p->nUsed = 0;
  }
  if( p->aData[p->nUsed]!=0 ){
    blob_materialize(p);
  }
  return p->aData;
}

/*
** Return a pointer to a null-terminated string for a blob that has
** been created using blob_append_sql() and not blob_appendf().  If
** text was ever added using blob_appendf() then throw an error.
*/
char *blob_sql_text(Blob *p){
  blob_is_init(p);
  if( (p->blobFlags & BLOBFLAG_NotSQL) ){
    fossil_fatal("Internal error: Use of blob_appendf() to construct SQL text");
  }
  return blob_str(p);
}


/*
** Return a pointer to a null-terminated string for a blob.
**
** WARNING:  If the blob is ephemeral, it might cause a '\000'
** character to be inserted into the middle of the parent blob.
** Example:  Suppose p is a token extracted from some larger
669
670
671
672
673
674
675



676
677
678









679
680
681
682
683
684
685
  int i;
  for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
  return i;
}

/*
** Do printf-style string rendering and append the results to a blob.



*/
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
  if( pBlob ){









    va_list ap;
    va_start(ap, zFormat);
    vxprintf(pBlob, zFormat, ap);
    va_end(ap);
  }
}
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){







>
>
>



>
>
>
>
>
>
>
>
>







692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
  int i;
  for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
  return i;
}

/*
** Do printf-style string rendering and append the results to a blob.
**
** The blob_appendf() version sets the BLOBFLAG_NotSQL bit in Blob.blobFlags
** whereas blob_append_sql() does not.
*/
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
  if( pBlob ){
    va_list ap;
    va_start(ap, zFormat);
    vxprintf(pBlob, zFormat, ap);
    va_end(ap);
    pBlob->blobFlags |= BLOBFLAG_NotSQL;
  }
}
void blob_append_sql(Blob *pBlob, const char *zFormat, ...){
  if( pBlob ){
    va_list ap;
    va_start(ap, zFormat);
    vxprintf(pBlob, zFormat, ap);
    va_end(ap);
  }
}
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
Changes to src/branch.c.
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  if( colorTest ){
    showClosed = 0;
    showAll = 1;
  }

  style_header(showClosed ? "Closed Branches" :
                  showAll ? "All Branches" : "Open Branches");
  style_submenu_element("Timeline", "Timeline", "brtimeline");
  if( showClosed ){
    style_submenu_element("All", "All", "brlist?all");
    style_submenu_element("Open","Open","brlist");
  }else if( showAll ){
    style_submenu_element("Closed", "Closed", "brlist?closed");
    style_submenu_element("Open","Open","brlist");







|
|







298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  if( colorTest ){
    showClosed = 0;
    showAll = 1;
  }

  style_header("%s", showClosed ? "Closed Branches" :
                        showAll ? "All Branches" : "Open Branches");
  style_submenu_element("Timeline", "Timeline", "brtimeline");
  if( showClosed ){
    style_submenu_element("All", "All", "brlist?all");
    style_submenu_element("Open","Open","brlist");
  }else if( showAll ){
    style_submenu_element("Closed", "Closed", "brlist?closed");
    style_submenu_element("Open","Open","brlist");
Changes to src/browse.c.
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
  zName = P("name");
  if( zName==0 ) zName = "tip";
  rid = symbolic_name_to_rid(zName, "ci");
  if( rid==0 ){
    fossil_fatal("not a valid check-in: %s", zName);
  }
  style_submenu_element("Tree-View", "Tree-View", "%R/tree?ci=%T", zName);
  style_header("File Ages", zName);
  zGlob = P("glob");
  compute_fileage(rid,zGlob);
  baseTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
  zBaseTime = db_text("","SELECT datetime(%.20g%s)", baseTime, timeline_utc());
  @ <h2>File Ages For Check-in
  @ %z(href("%R/info?name=%T",zName))%h(zName)</a></h2>
  @







|







826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
  zName = P("name");
  if( zName==0 ) zName = "tip";
  rid = symbolic_name_to_rid(zName, "ci");
  if( rid==0 ){
    fossil_fatal("not a valid check-in: %s", zName);
  }
  style_submenu_element("Tree-View", "Tree-View", "%R/tree?ci=%T", zName);
  style_header("File Ages");
  zGlob = P("glob");
  compute_fileage(rid,zGlob);
  baseTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
  zBaseTime = db_text("","SELECT datetime(%.20g%s)", baseTime, timeline_utc());
  @ <h2>File Ages For Check-in
  @ %z(href("%R/info?name=%T",zName))%h(zName)</a></h2>
  @
Changes to src/cache.c.
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  }
  rc = sqlite3_open(zDbName, &db);
  fossil_free(zDbName);
  if( rc ){
    sqlite3_close(db);
    return 0;
  }
  rc = sqlite3_exec(db, 
     "PRAGMA page_size=8192;"
     "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);"
     "CREATE TABLE IF NOT EXISTS cache("
       "key TEXT PRIMARY KEY,"     /* Key used to access the cache */
       "id INT REFERENCES blob,"   /* The cache content */
       "sz INT,"                   /* Size of content in bytes */
       "tm INT,"                   /* Last access time (unix timestampe) */







|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  }
  rc = sqlite3_open(zDbName, &db);
  fossil_free(zDbName);
  if( rc ){
    sqlite3_close(db);
    return 0;
  }
  rc = sqlite3_exec(db,
     "PRAGMA page_size=8192;"
     "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);"
     "CREATE TABLE IF NOT EXISTS cache("
       "key TEXT PRIMARY KEY,"     /* Key used to access the cache */
       "id INT REFERENCES blob,"   /* The cache content */
       "sz INT,"                   /* Size of content in bytes */
       "tm INT,"                   /* Last access time (unix timestampe) */
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  int rc;

  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  if( rc ){
    sqlite3_finalize(pStmt);
    pStmt = 0;
  }
  return pStmt;  
}

/*
** This routine implements an SQL function that renders a large integer
** compactly:  ex: 12.3MB
*/
static void cache_sizename(







|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  int rc;

  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  if( rc ){
    sqlite3_finalize(pStmt);
    pStmt = 0;
  }
  return pStmt;
}

/*
** This routine implements an SQL function that renders a large integer
** compactly:  ex: 12.3MB
*/
static void cache_sizename(
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0);
  pStmt = cacheStmt(db, "INSERT INTO blob(data) VALUES(?1)");
  if( pStmt==0 ) goto cache_write_end;
  sqlite3_bind_blob(pStmt, 1, blob_buffer(pContent), blob_size(pContent),
                    SQLITE_STATIC);
  if( sqlite3_step(pStmt)!=SQLITE_DONE ) goto cache_write_end;
  sqlite3_finalize(pStmt);
  pStmt = cacheStmt(db, 
      "INSERT OR IGNORE INTO cache(key,sz,tm,nref,id)"
      "VALUES(?1,?2,strftime('%s','now'),1,?3)"
  );
  if( pStmt==0 ) goto cache_write_end;
  sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC);
  sqlite3_bind_int(pStmt, 2, blob_size(pContent));
  sqlite3_bind_int(pStmt, 3, sqlite3_last_insert_rowid(db));







|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0);
  pStmt = cacheStmt(db, "INSERT INTO blob(data) VALUES(?1)");
  if( pStmt==0 ) goto cache_write_end;
  sqlite3_bind_blob(pStmt, 1, blob_buffer(pContent), blob_size(pContent),
                    SQLITE_STATIC);
  if( sqlite3_step(pStmt)!=SQLITE_DONE ) goto cache_write_end;
  sqlite3_finalize(pStmt);
  pStmt = cacheStmt(db,
      "INSERT OR IGNORE INTO cache(key,sz,tm,nref,id)"
      "VALUES(?1,?2,strftime('%s','now'),1,?3)"
  );
  if( pStmt==0 ) goto cache_write_end;
  sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC);
  sqlite3_bind_int(pStmt, 2, blob_size(pContent));
  sqlite3_bind_int(pStmt, 3, sqlite3_last_insert_rowid(db));
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
  sqlite3_stmt *pStmt;
  int rc = 0;

  db = cacheOpen(0);
  if( db==0 ) return 0;
  sqlite3_busy_timeout(db, 10000);
  sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0);
  pStmt = cacheStmt(db, 
    "SELECT blob.data FROM cache, blob"
    " WHERE cache.key=?1 AND cache.id=blob.id");
  if( pStmt==0 ) goto cache_read_done;
  sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC);
  if( sqlite3_step(pStmt)==SQLITE_ROW ){
    blob_append(pContent, sqlite3_column_blob(pStmt, 0),
                          sqlite3_column_bytes(pStmt, 0));
    rc = 1;
    sqlite3_reset(pStmt);
    pStmt = cacheStmt(db, 
              "UPDATE cache SET nref=nref+1, tm=strftime('%s','now')"
              " WHERE key=?1");
    if( pStmt ){
      sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC);
      sqlite3_step(pStmt);
    }  
  }
  sqlite3_finalize(pStmt);
cache_read_done:
  sqlite3_exec(db, "COMMIT", 0, 0, 0);
  sqlite3_close(db);
  return rc;
}







|









|





|







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
  sqlite3_stmt *pStmt;
  int rc = 0;

  db = cacheOpen(0);
  if( db==0 ) return 0;
  sqlite3_busy_timeout(db, 10000);
  sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0);
  pStmt = cacheStmt(db,
    "SELECT blob.data FROM cache, blob"
    " WHERE cache.key=?1 AND cache.id=blob.id");
  if( pStmt==0 ) goto cache_read_done;
  sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC);
  if( sqlite3_step(pStmt)==SQLITE_ROW ){
    blob_append(pContent, sqlite3_column_blob(pStmt, 0),
                          sqlite3_column_bytes(pStmt, 0));
    rc = 1;
    sqlite3_reset(pStmt);
    pStmt = cacheStmt(db,
              "UPDATE cache SET nref=nref+1, tm=strftime('%s','now')"
              " WHERE key=?1");
    if( pStmt ){
      sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC);
      sqlite3_step(pStmt);
    }
  }
  sqlite3_finalize(pStmt);
cache_read_done:
  sqlite3_exec(db, "COMMIT", 0, 0, 0);
  sqlite3_close(db);
  return rc;
}
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  int nCmd;
  sqlite3 *db;
  sqlite3_stmt *pStmt;

  db_find_and_open_repository(0,0);
  zCmd = g.argc>=3 ? g.argv[2] : "";
  nCmd = (int)strlen(zCmd);
  if( nCmd<=1 ){ 
    fossil_fatal("Usage: %s cache SUBCOMMAND", g.argv[0]);
  }
  if( strncmp(zCmd, "init", nCmd)==0 ){
    db = cacheOpen(0);
    sqlite3_close(db);
    if( db ){
      fossil_print("cache already exists in file %z\n", cacheName());







|







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  int nCmd;
  sqlite3 *db;
  sqlite3_stmt *pStmt;

  db_find_and_open_repository(0,0);
  zCmd = g.argc>=3 ? g.argv[2] : "";
  nCmd = (int)strlen(zCmd);
  if( nCmd<=1 ){
    fossil_fatal("Usage: %s cache SUBCOMMAND", g.argv[0]);
  }
  if( strncmp(zCmd, "init", nCmd)==0 ){
    db = cacheOpen(0);
    sqlite3_close(db);
    if( db ){
      fossil_print("cache already exists in file %z\n", cacheName());
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
    db = cacheOpen(0);
    if( db==0 ){
      fossil_print("cache does not exist\n");
    }else{
      int nEntry = 0;
      char *zDbName = cacheName();
      cache_register_sizename(db);
      pStmt = cacheStmt(db, 
           "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')"
           "  FROM cache"
           " ORDER BY tm DESC"
      );
      if( pStmt ){
        while( sqlite3_step(pStmt)==SQLITE_ROW ){
          fossil_print("%s %4d %8s %s\n",







|







288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
    db = cacheOpen(0);
    if( db==0 ){
      fossil_print("cache does not exist\n");
    }else{
      int nEntry = 0;
      char *zDbName = cacheName();
      cache_register_sizename(db);
      pStmt = cacheStmt(db,
           "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')"
           "  FROM cache"
           " ORDER BY tm DESC"
      );
      if( pStmt ){
        while( sqlite3_step(pStmt)==SQLITE_ROW ){
          fossil_print("%s %4d %8s %s\n",
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  style_header("Web Cache Status");
  db = cacheOpen(0);
  if( db==0 ){
    @ The web-page cache is disabled for this repository
  }else{
    char *zDbName = cacheName();
    cache_register_sizename(db);
    pStmt = cacheStmt(db, 
         "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')"
         "  FROM cache"
         " ORDER BY tm DESC"
    );
    if( pStmt ){
      @ <ol>
      while( sqlite3_step(pStmt)==SQLITE_ROW ){







|







336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  style_header("Web Cache Status");
  db = cacheOpen(0);
  if( db==0 ){
    @ The web-page cache is disabled for this repository
  }else{
    char *zDbName = cacheName();
    cache_register_sizename(db);
    pStmt = cacheStmt(db,
         "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')"
         "  FROM cache"
         " ORDER BY tm DESC"
    );
    if( pStmt ){
      @ <ol>
      while( sqlite3_step(pStmt)==SQLITE_ROW ){
Changes to src/cgi.c.
1710
1711
1712
1713
1714
1715
1716

1717
1718
1719
1720
1721
1722
1723
1724
  }
  if( iPort>mxPort ) return 1;
  listen(listener,10);
  fossil_print("Listening for %s requests on TCP port %d\n",
     (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP",  iPort);
  fflush(stdout);
  if( zBrowser ){

    zBrowser = mprintf(zBrowser, iPort);
#if defined(__CYGWIN__)
    /* On Cygwin, we can do better than "echo" */
    if( strncmp(zBrowser, "echo ", 5)==0 ){
      wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5);
      wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */
      if( (size_t)ShellExecuteW(0, L"open", wUrl, 0, 0, 1)<33 ){
        fossil_warning("cannot start browser\n");







>
|







1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
  }
  if( iPort>mxPort ) return 1;
  listen(listener,10);
  fossil_print("Listening for %s requests on TCP port %d\n",
     (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP",  iPort);
  fflush(stdout);
  if( zBrowser ){
    assert( strstr(zBrowser,"%d")!=0 );
    zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
#if defined(__CYGWIN__)
    /* On Cygwin, we can do better than "echo" */
    if( strncmp(zBrowser, "echo ", 5)==0 ){
      wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5);
      wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */
      if( (size_t)ShellExecuteW(0, L"open", wUrl, 0, 0, 1)<33 ){
        fossil_warning("cannot start browser\n");
Changes to src/checkin.c.
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
    Blob fname;
    file_tree_name(g.argv[i], &fname, 1);
    zName = blob_str(&fname);
    if( fossil_strcmp(zName, ".")==0 ) {
      blob_reset(&where);
      break;
    }

    blob_appendf(&where, " %s (pathname=%Q %s) "
                 "OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
                 (blob_size(&where)>0) ? "OR" : "AND", zName,
                 filename_collation(), zName, filename_collation(),
                 zName, filename_collation());

  }

  db_prepare(&q,
    "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)"
    "  FROM vfile "
    " WHERE is_selected(id) %s"
    "   AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1",
    blob_str(&where)
  );
  blob_zero(&rewrittenPathname);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    const char *zDisplayName = zPathname;
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);







>
|
|
|
|
|
>







|







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
    Blob fname;
    file_tree_name(g.argv[i], &fname, 1);
    zName = blob_str(&fname);
    if( fossil_strcmp(zName, ".")==0 ) {
      blob_reset(&where);
      break;
    }
    blob_append_sql(&where,
      " %s (pathname=%Q %s) "
      "OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
      (blob_size(&where)>0) ? "OR" : "AND", zName,
      filename_collation(), zName, filename_collation(),
      zName, filename_collation()
    );
  }

  db_prepare(&q,
    "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)"
    "  FROM vfile "
    " WHERE is_selected(id) %s"
    "   AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1",
    blob_sql_text(&where)
  );
  blob_zero(&rewrittenPathname);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    const char *zDisplayName = zPathname;
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);
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
    Blob fname;
    file_tree_name(g.argv[i], &fname, 1);
    zName = blob_str(&fname);
    if( fossil_strcmp(zName, ".")==0 ) {
      blob_reset(&where);
      break;
    }

    blob_appendf(&where, " %s (pathname=%Q %s) "
                 "OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
                 (blob_size(&where)>0) ? "OR" : "WHERE", zName,
                 filename_collation(), zName, filename_collation(),
                 zName, filename_collation());

  }
  vfile_check_signature(vid, 0);
  if( showAge ){
    db_prepare(&q,
       "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0),"
       "       datetime(checkin_mtime(%d,rid),'unixepoch'%s)"
       "  FROM vfile %s"
       " ORDER BY %s", vid, timeline_utc(), blob_str(&where), zOrderBy

    );
  }else{
    db_prepare(&q,
       "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
       "  FROM vfile %s"
       " ORDER BY %s", blob_str(&where), zOrderBy
    );
  }
  blob_reset(&where);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    int isDeleted = db_column_int(&q, 1);
    int isNew = db_column_int(&q,2)==0;







>
|
|
|
|
|
>







|
>





|







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
    Blob fname;
    file_tree_name(g.argv[i], &fname, 1);
    zName = blob_str(&fname);
    if( fossil_strcmp(zName, ".")==0 ) {
      blob_reset(&where);
      break;
    }
    blob_append_sql(&where,
       " %s (pathname=%Q %s) "
       "OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
       (blob_size(&where)>0) ? "OR" : "WHERE", zName,
       filename_collation(), zName, filename_collation(),
       zName, filename_collation()
    );
  }
  vfile_check_signature(vid, 0);
  if( showAge ){
    db_prepare(&q,
       "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0),"
       "       datetime(checkin_mtime(%d,rid),'unixepoch'%s)"
       "  FROM vfile %s"
       " ORDER BY %s",
       vid, timeline_utc(), blob_sql_text(&where), zOrderBy /*safe-for-%s*/
    );
  }else{
    db_prepare(&q,
       "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
       "  FROM vfile %s"
       " ORDER BY %s", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
    );
  }
  blob_reset(&where);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    int isDeleted = db_column_int(&q, 1);
    int isNew = db_column_int(&q,2)==0;
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
int select_commit_files(void){
  int result = 0;
  assert( g.aCommitFile==0 );
  if( g.argc>2 ){
    int ii, jj=0;
    Blob fname;
    Stmt q;
    const char *zCollate;
    Bag toCommit;

    zCollate = filename_collation();
    blob_zero(&fname);
    bag_init(&toCommit);
    for(ii=2; ii<g.argc; ii++){
      int cnt = 0;
      file_tree_name(g.argv[ii], &fname, 1);
      if( fossil_strcmp(blob_str(&fname),".")==0 ){
        bag_clear(&toCommit);
        return result;
      }
      db_prepare(&q,
        "SELECT id FROM vfile WHERE pathname=%Q %s"
        " OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
        blob_str(&fname), zCollate, blob_str(&fname),
        zCollate, blob_str(&fname), zCollate);
      while( db_step(&q)==SQLITE_ROW ){
        cnt++;
        bag_insert(&toCommit, db_column_int(&q, 0));
      }
      db_finalize(&q);
      if( cnt==0 ){
        fossil_warning("fossil knows nothing about: %s", g.argv[ii]);







<


<












|
|







911
912
913
914
915
916
917

918
919

920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
int select_commit_files(void){
  int result = 0;
  assert( g.aCommitFile==0 );
  if( g.argc>2 ){
    int ii, jj=0;
    Blob fname;
    Stmt q;

    Bag toCommit;


    blob_zero(&fname);
    bag_init(&toCommit);
    for(ii=2; ii<g.argc; ii++){
      int cnt = 0;
      file_tree_name(g.argv[ii], &fname, 1);
      if( fossil_strcmp(blob_str(&fname),".")==0 ){
        bag_clear(&toCommit);
        return result;
      }
      db_prepare(&q,
        "SELECT id FROM vfile WHERE pathname=%Q %s"
        " OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
        blob_str(&fname), filename_collation(), blob_str(&fname),
        filename_collation(), blob_str(&fname), filename_collation());
      while( db_step(&q)==SQLITE_ROW ){
        cnt++;
        bag_insert(&toCommit, db_column_int(&q, 0));
      }
      db_finalize(&q);
      if( cnt==0 ){
        fossil_warning("fossil knows nothing about: %s", g.argv[ii]);
Changes to src/checkout.c.
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
*/
int load_vfile(const char *zName, int forceMissingFlag){
  Blob uuid;
  int vid;

  blob_init(&uuid, zName, -1);
  if( name_to_uuid(&uuid, 1, "ci") ){
    fossil_fatal(g.zErrMsg);
  }
  vid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
  if( vid==0 ){
    fossil_fatal("no such check-in: %s", g.argv[2]);
  }
  if( !is_a_version(vid) ){
    fossil_fatal("object [%S] is not a check-in", blob_str(&uuid));







|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
*/
int load_vfile(const char *zName, int forceMissingFlag){
  Blob uuid;
  int vid;

  blob_init(&uuid, zName, -1);
  if( name_to_uuid(&uuid, 1, "ci") ){
    fossil_fatal("%s", g.zErrMsg);
  }
  vid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
  if( vid==0 ){
    fossil_fatal("no such check-in: %s", g.argv[2]);
  }
  if( !is_a_version(vid) ){
    fossil_fatal("object [%S] is not a check-in", blob_str(&uuid));
Added src/codecheck1.c.






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
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
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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
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
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
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
559
560
561
562
563
/*
** Copyright (c) 2014 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This program reads Fossil source code files and tries to verify that
** printf-style format strings are correct.
**
** This program implements a compile-time validation step on the Fossil
** source code.  Running this program is entirely optional.  Its role is
** similar to the -Wall compiler switch on gcc, or the scan-build utility
** of clang, or other static analyzers.  The purpose is to try to identify
** problems in the source code at compile-time.  The difference is that this
** static checker is specifically designed for the particular printf formatter
** implementation used by Fossil.
**
** Checks include:
**
**    *  Verify that vararg formatting routines like blob_printf() or
**       db_multi_exec() have the correct number of arguments for their
**       format string.
**
**    *  For routines designed to generate SQL, warn about the use of %s
**       which might allow SQL injection.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>

/*
** Malloc, aborting if it fails.
*/
void *safe_malloc(int nByte){
  void *x = malloc(nByte);
  if( x==0 ){
    fprintf(stderr, "failed to allocate %d bytes\n", nByte);
    exit(1);
  }
  return x;
}
void *safe_realloc(void *pOld, int nByte){
  void *x = realloc(pOld, nByte);
  if( x==0 ){
    fprintf(stderr, "failed to allocate %d bytes\n", nByte);
    exit(1);
  }
  return x;
}

/*
** Read the entire content of the file named zFilename into memory obtained
** from malloc().   Add a zero-terminator to the end.
** Return a pointer to that memory.
*/
static char *read_file(const char *zFilename){
  FILE *in;
  char *z;
  int nByte;
  int got;
  in = fopen(zFilename, "rb");
  if( in==0 ){
    return 0;
  }
  fseek(in, 0, SEEK_END);
  nByte = ftell(in);
  fseek(in, 0, SEEK_SET);
  z = safe_malloc( nByte+1 );
  got = fread(z, 1, nByte, in);
  z[got] = 0;
  fclose(in);
  return z;
}

/*
** When parsing the input file, the following token types are recognized.
*/
#define TK_SPACE      1      /* Whitespace or comments */
#define TK_ID         2      /* An identifier */
#define TK_STR        3      /* A string literal in double-quotes */
#define TK_OTHER      4      /* Any other token */
#define TK_EOF       99      /* End of file */

/*
** Determine the length and type of the token beginning at z[0]
*/
static int token_length(const char *z, int *pType, int *pLN){
  int i;
  if( z[0]==0 ){
    *pType = TK_EOF;
    return 0;
  }
  if( z[0]=='"' || z[0]=='\'' ){
    for(i=1; z[i] && z[i]!=z[0]; i++){
      if( z[i]=='\\' && z[i+1]!=0 ){
        if( z[i+1]=='\n' ) (*pLN)++;
        i++;
      }
    }
    if( z[i]!=0 ) i++;
    *pType = z[0]=='"' ? TK_STR : TK_OTHER;
    return i;
  }
  if( isalnum(z[0]) || z[0]=='_' ){
    for(i=1; isalnum(z[i]) || z[i]=='_'; i++){}
    *pType = isalpha(z[0]) || z[0]=='_' ? TK_ID : TK_OTHER;
    return i;
  }
  if( isspace(z[0]) ){
    if( z[0]=='\n' ) (*pLN)++;
    for(i=1; isspace(z[i]); i++){
      if( z[i]=='\n' ) (*pLN)++;
    }
    *pType = TK_SPACE;
    return i;
  }
  if( z[0]=='/' && z[1]=='*' ){
    for(i=2; z[i] && (z[i]!='*' || z[i+1]!='/'); i++){
      if( z[i]=='\n' ) (*pLN)++;
    }
    if( z[i] ) i += 2;
    *pType = TK_SPACE;
    return i;
  }
  if( z[0]=='/' && z[1]=='/' ){
    for(i=2; z[i] && z[i]!='\n'; i++){}
    if( z[i] ){
      (*pLN)++;
      i++;
    }
    *pType = TK_SPACE;
    return i;
  }
  *pType = TK_OTHER;
  return 1;
}

/*
** Return the next non-whitespace token
*/
const char *next_non_whitespace(const char *z, int *pLen, int *pType){
  int len;
  int eType;
  int ln = 0;
  while( (len = token_length(z, &eType, &ln))>0 && eType==TK_SPACE ){
    z += len;
  }
  *pLen = len;
  *pType = eType;
  return z;
}

/*
** Return index into z[] for the first balanced TK_OTHER token with
** value cValue.
*/
static int distance_to(const char *z, char cVal){
  int len;
  int dist = 0;
  int eType;
  int nNest = 0;
  int ln = 0;
  while( z[0] && (len = token_length(z, &eType, &ln))>0 ){
    if( eType==TK_OTHER ){
      if( z[0]==cVal && nNest==0 ){
        break;
      }else if( z[0]=='(' ){
        nNest++;
      }else if( z[0]==')' ){
        nNest--;
      }
    }
    dist += len;
    z += len;
  }
  return dist;
}

/*
** Return the first non-whitespace characters in z[]
*/
static const char *skip_space(const char *z){
  while( isspace(z[0]) ){ z++; }
  return z;
}

/*
** Return true if the input is a string literal.
*/
static int is_string_lit(const char *z){
  int nu1, nu2;
  z = next_non_whitespace(z, &nu1, &nu2);
  return z[0]=='"';
}

/*
** Return true if the input is an expression of string literals:
**
**      EXPR ? "..." : "..."
*/
static int is_string_expr(const char *z){
  int len = 0, eType;
  const char *zOrig = z;
  len = distance_to(z, '?');
  if( z[len]==0 && skip_space(z)[0]=='(' ){
    z = skip_space(z) + 1;
    len = distance_to(z, '?');
  }
  z += len;
  if( z[0]=='?' ){
    z++;
    z = next_non_whitespace(z, &len, &eType);
    if( eType==TK_STR ){
      z += len;
      z = next_non_whitespace(z, &len, &eType);
      if( eType==TK_OTHER && z[0]==':' ){
        z += len;
        z = next_non_whitespace(z, &len, &eType);
        if( eType==TK_STR ){
          z += len;
          z = next_non_whitespace(z, &len, &eType);
          if( eType==TK_EOF ) return 1;
          if( eType==TK_OTHER && z[0]==')' && skip_space(zOrig)[0]=='(' ){
            z += len;
            z = next_non_whitespace(z, &len, &eType);
            if( eType==TK_EOF ) return 1;
          }
        }
      }
    }
  }
  return 0;
}

/*
** A list of functions that return strings that are safe to insert into
** SQL using %s.
*/
static const char *azSafeFunc[] = {
  "filename_collation",
  "db_name",
  "timeline_utc",
  "leaf_is_closed_sql",
  "timeline_query_for_www",
  "timeline_query_for_tty",
  "blob_sql_text",
  "glob_expr",
  "fossil_all_reserved_names",
  "configure_inop_rhs",
  "db_setting_inop_rhs",
};

/*
** Return true if the input is an argument that is safe to use with %s
** while building an SQL statement.
*/
static int is_s_safe(const char *z){
  int len, eType;
  int i;

  /* A string literal is safe for use with %s */
  if( is_string_lit(z) ) return 1;

  /* Certain functions are guaranteed to return a string that is safe
  ** for use with %s */
  z = next_non_whitespace(z, &len, &eType);
  for(i=0; i<sizeof(azSafeFunc)/sizeof(azSafeFunc[0]); i++){
    if( eType==TK_ID 
     && strncmp(z, azSafeFunc[i], len)==0
     && strlen(azSafeFunc[i])==len
    ){
      return 1;
    }
  }

  /* Expressions of the form:  EXPR ? "..." : "...." can count as
  ** a string literal. */
  if( is_string_expr(z) ) return 1;

  /* If the "safe-for-%s" comment appears in the argument, then
  ** let it through */
  if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1;
    
  return 0;
}

/*
** Processing flags
*/
#define FMT_NO_S   0x00001     /* Do not allow %s substitutions */

/*
** 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[] = {
  { "blob_append_sql",         2, FMT_NO_S },
  { "blob_appendf",            2, 0 },
  { "cgi_panic",               1, 0 },
  { "cgi_redirectf",           1, 0 },
  { "db_blob",                 2, FMT_NO_S },
  { "db_double",               2, FMT_NO_S },
  { "db_err",                  1, 0 },
  { "db_exists",               1, FMT_NO_S },
  { "db_int",                  2, FMT_NO_S },
  { "db_int64",                2, FMT_NO_S },
  { "db_multi_exec",           1, FMT_NO_S },
  { "db_optional_sql",         2, FMT_NO_S },
  { "db_prepare",              2, FMT_NO_S },
  { "db_prepare_ignore_error", 2, FMT_NO_S },
  { "db_static_prepare",       2, FMT_NO_S },
  { "db_text",                 2, FMT_NO_S },
  { "form_begin",              2, 0 },
  { "fossil_error",            2, 0 },
  { "fossil_errorlog",         1, 0 },
  { "fossil_fatal",            1, 0 },
  { "fossil_fatal_recursive",  1, 0 },
  { "fossil_panic",            1, 0 },
  { "fossil_print",            1, 0 },
  { "fossil_trace",            1, 0 },
  { "fossil_warning",          1, 0 },
  { "href",                    1, 0 },
  { "json_new_string_f",       1, 0 },
  { "mprintf",                 1, 0 },
  { "socket_set_errmsg",       1, 0 },
  { "ssl_set_errmsg",          1, 0 },
  { "style_header",            1, 0 },
  { "style_set_current_page",  1, 0 },
  { "webpage_error",           1, 0 },
  { "xhref",                   2, 0 },
};

/*
** 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.
*/
static int isFormatFunc(const char *zIdent, int nIdent, unsigned *pFlags){
  int upr, lwr;
  lwr = 0;
  upr = sizeof(aFmtFunc)/sizeof(aFmtFunc[0]) - 1;
  while( lwr<=upr ){
    unsigned x = (lwr + upr)/2;
    int c = strncmp(zIdent, aFmtFunc[x].zFName, nIdent);
    if( c==0 ){
      if( aFmtFunc[x].zFName[nIdent]==0 ){
        *pFlags = aFmtFunc[x].fmtFlags;
        return aFmtFunc[x].iFmtArg;
      }
      c = -1;
    }
    if( c<0 ){
      upr = x - 1;
    }else{
      lwr = x + 1;
    }
  }
  *pFlags = 0;
  return 0;
}

/*
** Return the expected number of arguments for the format string.
** Return -1 if the value cannot be computed.
**
** For each argument less than nType, store the conversion character
** for that argument in cType[i].
*/
static int formatArgCount(const char *z, int nType, char *cType){
  int nArg = 0;
  int i, k;
  int len;
  int eType;
  int ln = 0;
  while( z[0] ){
    len = token_length(z, &eType, &ln);
    if( eType==TK_STR ){
      for(i=1; i<len-1; i++){
        if( z[i]!='%' ) continue;
        if( z[i+1]=='%' ){ i++; continue; }
        for(k=i+1; k<len && !isalpha(z[k]); k++){
          if( z[k]=='*' || z[k]=='#' ){
            if( nArg<nType ) cType[nArg] = z[k];
            nArg++;
          }
        }
        if( z[k]!='R' ){
          if( nArg<nType ) cType[nArg] = z[k];
          nArg++;
        }
      }
    }
    z += len;
  }
  return nArg;
}

/*
** The function call that begins at zFCall[0] (which is on line lnFCall of the
** original file) is a function that uses a printf-style format string
** on argument number fmtArg.  It has processings flags fmtFlags.  Do
** compile-time checking on this function, output any errors, and return
** the number of errors.
*/
static int checkFormatFunc(
  const char *zFilename, /* Name of the file being processed */
  const char *zFCall,    /* Pointer to start of function call */
  int lnFCall,           /* Line number that holds z[0] */
  int fmtArg,            /* Format string should be this argument */
  int fmtFlags           /* Extra processing flags */
){
  int szFName;
  int eToken;
  int ln = lnFCall;
  int len;
  const char *zStart;
  char *z;
  char *zCopy;
  int nArg = 0;
  char const **azArg = 0;
  int i, k;
  int nErr = 0;
  char *acType;

  szFName = 	token_length(zFCall, &eToken, &ln);
  zStart = next_non_whitespace(zFCall+szFName, &len, &eToken);
  assert( zStart[0]=='(' && len==1 );
  len = distance_to(zStart+1, ')');
  zCopy = safe_malloc( len + 1 );
  memcpy(zCopy, zStart+1, len);
  zCopy[len] = 0;
  azArg = 0;
  nArg = 0;
  z = zCopy;
  while( z[0] ){
    len = distance_to(z, ',');
    azArg = safe_realloc(azArg, (sizeof(azArg[0])+1)*(nArg+1));
    azArg[nArg++] = skip_space(z);
    if( z[len]==0 ) break;
    z[len] = 0;
    for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; }
    z += len + 1;
  }
  acType = (char*)&azArg[nArg];
  if( fmtArg>nArg ){
    printf("%s:%d: too few arguments to %.*s()\n", 
           zFilename, lnFCall, szFName, zFCall);
    nErr++;
  }else{
    const char *zFmt = azArg[fmtArg-1];
    const char *zOverride = strstr(zFmt, "/*works-like:");
    if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1;
    if( !is_string_lit(zFmt) ){
      printf("%s:%d: %.*s() has non-constant format string\n",
             zFilename, lnFCall, szFName, zFCall);
      nErr++;
    }else if( (k = formatArgCount(zFmt, nArg, acType))>=0
             && nArg!=fmtArg+k ){
      printf("%s:%d: too %s arguments to %.*s() "
             "- got %d and expected %d\n",
             zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"),
             szFName, zFCall, nArg, fmtArg+k);
      nErr++;
    }else if( fmtFlags & FMT_NO_S ){
      for(i=0; i<nArg && i<k; i++){
        if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b')
         && !is_s_safe(azArg[fmtArg+i])
        ){
           printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
             zFilename, lnFCall, i+fmtArg, szFName, zFCall);
           nErr++;
        }
      }
    }
  }
  if( nErr ){
    for(i=0; i<nArg; i++){
      printf("   arg[%d]: %s\n", i, azArg[i]);
    }
  }

  free(azArg);
  free(zCopy);
  return nErr;
}


/*
** Do a design-rule check of format strings for the file named zName
** with content zContent.  Write errors on standard output.  Return
** the number of errors.
*/
static int scan_file(const char *zName, const char *zContent){
  const char *z;
  int ln = 0;
  int szToken;
  int eToken;
  const char *zPrev;
  int ePrev;
  int szPrev;
  int lnPrev;
  int nCurly = 0;
  int x;
  unsigned fmtFlags = 0;
  int nErr = 0;

  if( zContent==0 ){
    printf("cannot read file: %s\n", zName);
    return 1;
  }
  for(z=zContent; z[0]; z += szToken){
    szToken = token_length(z, &eToken, &ln);
    if( eToken==TK_SPACE ) continue;
    if( eToken==TK_OTHER ){
      if( z[0]=='{' ){
        nCurly++;
      }else if( z[0]=='}' ){
        nCurly--;
      }else if( nCurly>0 && z[0]=='(' && ePrev==TK_ID
            && (x = isFormatFunc(zPrev,szPrev,&fmtFlags))>0 ){
        nErr += checkFormatFunc(zName, zPrev, lnPrev, x, fmtFlags);
      }
    }    
    zPrev = z;
    ePrev = eToken;
    szPrev = szToken;
    lnPrev = ln;
  }
  return nErr;
}

/*
** Check for format-string design rule violations on all files listed
** on the command-line.
*/
int main(int argc, char **argv){
  int i;
  int nErr = 0;
  for(i=1; i<argc; i++){
    char *zFile = read_file(argv[i]);
    nErr += scan_file(argv[i], zFile);
    free(zFile);
  }
  return nErr;
}
Changes to src/comformat.c.
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
    if( zOrigText ){
      blob_read_from_file(&fileData, zOrigText);
      zOrigText = mprintf("%s", blob_str(&fileData));
      blob_reset(&fileData);
    }
  }
  if( decode ){
    zText = mprintf(fromFile ? "%z" : "%s", zText);
    defossilize(zText);
    if( zOrigText ){
      zOrigText = mprintf(fromFile ? "%z" : "%s", zOrigText);
      defossilize(zOrigText);
    }
  }
  if( indent<0 ){
    indent = strlen(zPrefix);
  }
  if( zPrefix && *zPrefix ){







|


|







495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
    if( zOrigText ){
      blob_read_from_file(&fileData, zOrigText);
      zOrigText = mprintf("%s", blob_str(&fileData));
      blob_reset(&fileData);
    }
  }
  if( decode ){
    zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
    defossilize(zText);
    if( zOrigText ){
      zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText);
      defossilize(zOrigText);
    }
  }
  if( indent<0 ){
    indent = strlen(zPrefix);
  }
  if( zPrefix && *zPrefix ){
Changes to src/configure.c.
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
*/
const char *configure_inop_rhs(int iMask){
  Blob x;
  int i;
  const char *zSep = "";

  blob_zero(&x);
  blob_append(&x, "(", 1);
  for(i=0; i<count(aConfig); i++){
    if( (aConfig[i].groupMask & iMask)==0 ) continue;
    if( aConfig[i].zName[0]=='@' ) continue;
    blob_appendf(&x, "%s'%s'", zSep, aConfig[i].zName);
    zSep = ",";
  }
  blob_append(&x, ")", 1);
  return blob_str(&x);
}

/*
** Return the mask for the named configuration parameter if it can be
** safely exported.  Return 0 if the parameter is not safe to export.
**
** "Safe" in the previous paragraph means the permission is created to







|



|


|
|







192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
*/
const char *configure_inop_rhs(int iMask){
  Blob x;
  int i;
  const char *zSep = "";

  blob_zero(&x);
  blob_append_sql(&x, "(");
  for(i=0; i<count(aConfig); i++){
    if( (aConfig[i].groupMask & iMask)==0 ) continue;
    if( aConfig[i].zName[0]=='@' ) continue;
    blob_append_sql(&x, "%s'%q'", zSep/*safe-for-%s*/, aConfig[i].zName);
    zSep = ",";
  }
  blob_append_sql(&x, ")");
  return blob_sql_text(&x);
}

/*
** Return the mask for the named configuration parameter if it can be
** safely exported.  Return 0 if the parameter is not safe to export.
**
** "Safe" in the previous paragraph means the permission is created to
359
360
361
362
363
364
365

366
367
368
369
370
371
372
373
    @   photo BLOB                      -- JPEG image of this user
    @ );
    @ INSERT INTO _xfer_reportfmt
    @    SELECT rn,owner,title,cols,sqlcode FROM reportfmt;
    @ INSERT INTO _xfer_user
    @    SELECT uid,login,pw,cap,cookie,ipaddr,cexpire,info,photo FROM user;
  ;

  db_multi_exec(zSQL1);

  /* When the replace flag is set, add triggers that run the first time
  ** that new data is seen.  The triggers run only once and delete all the
  ** existing data.
  */
  if( replaceFlag ){
    static const char zSQL2[] =







>
|







359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    @   photo BLOB                      -- JPEG image of this user
    @ );
    @ INSERT INTO _xfer_reportfmt
    @    SELECT rn,owner,title,cols,sqlcode FROM reportfmt;
    @ INSERT INTO _xfer_user
    @    SELECT uid,login,pw,cap,cookie,ipaddr,cexpire,info,photo FROM user;
  ;
  assert( strchr(zSQL1,'%')==0 );
  db_multi_exec(zSQL1 /*works-like:""*/);

  /* When the replace flag is set, add triggers that run the first time
  ** that new data is seen.  The triggers run only once and delete all the
  ** existing data.
  */
  if( replaceFlag ){
    static const char zSQL2[] =
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
      @ END;
    ;
    sqlite3_create_function(g.db, "config_is_reset", 1, SQLITE_UTF8, 0,
         config_is_reset_function, 0, 0);
    sqlite3_create_function(g.db, "config_reset", 1, SQLITE_UTF8, 0,
         config_reset_function, 0, 0);
    configHasBeenReset = 0;

    db_multi_exec(zSQL2);
  }
}

/*
** After receiving configuration data, call this routine to transfer
** the results into the main database.
*/
void configure_finalize_receive(void){
  static const char zSQL[] =
    @ DELETE FROM user;
    @ INSERT INTO user SELECT * FROM _xfer_user;
    @ DELETE FROM reportfmt;
    @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt;
    @ DROP TABLE _xfer_user;
    @ DROP TABLE _xfer_reportfmt;
  ;

  db_multi_exec(zSQL);
}

/*
** Mask of modified configuration sets
*/
static int rebuildMask = 0;








>
|
















>
|







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
      @ END;
    ;
    sqlite3_create_function(g.db, "config_is_reset", 1, SQLITE_UTF8, 0,
         config_is_reset_function, 0, 0);
    sqlite3_create_function(g.db, "config_reset", 1, SQLITE_UTF8, 0,
         config_reset_function, 0, 0);
    configHasBeenReset = 0;
    assert( strchr(zSQL2,'%')==0 );
    db_multi_exec(zSQL2 /*works-like:""*/);
  }
}

/*
** After receiving configuration data, call this routine to transfer
** the results into the main database.
*/
void configure_finalize_receive(void){
  static const char zSQL[] =
    @ DELETE FROM user;
    @ INSERT INTO user SELECT * FROM _xfer_user;
    @ DELETE FROM reportfmt;
    @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt;
    @ DROP TABLE _xfer_user;
    @ DROP TABLE _xfer_reportfmt;
  ;
  assert( strchr(zSQL,'%')==0 );
  db_multi_exec(zSQL /*works-like:""*/);
}

/*
** Mask of modified configuration sets
*/
static int rebuildMask = 0;

561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579

580
581
582
583
584
585

586
587

588
589
590
591

592
593
594
595
596
597
598
599
      thisMask = configure_is_exportable(aType[ii].zName);
    }
    if( (thisMask & groupMask)==0 ) return;

    blob_zero(&sql);
    if( groupMask & CONFIGSET_OVERWRITE ){
      if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
        db_multi_exec("DELETE FROM %s", &aType[ii].zName[1]);
        configHasBeenReset |= thisMask;
      }
      blob_append(&sql, "REPLACE INTO ", -1);
    }else{
      blob_append(&sql, "INSERT OR IGNORE INTO ", -1);
    }
    blob_appendf(&sql, "%s(%s, mtime", &zName[1], aType[ii].zPrimKey);
    for(jj=2; jj<nToken; jj+=2){
       blob_appendf(&sql, ",%s", azToken[jj]);
    }
    blob_appendf(&sql,") VALUES(%s,%s", azToken[1], azToken[0]);

    for(jj=2; jj<nToken; jj+=2){
       blob_appendf(&sql, ",%s", azToken[jj+1]);
    }
    db_multi_exec("%s)", blob_str(&sql));
    if( db_changes()==0 ){
      blob_reset(&sql);

      blob_appendf(&sql, "UPDATE %s SET mtime=%s", &zName[1], azToken[0]);
      for(jj=2; jj<nToken; jj+=2){

        blob_appendf(&sql, ", %s=%s", azToken[jj], azToken[jj+1]);
      }
      blob_appendf(&sql, " WHERE %s=%s AND mtime<%s",
                   aType[ii].zPrimKey, azToken[1], azToken[0]);

      db_multi_exec("%s", blob_str(&sql));
    }
    blob_reset(&sql);
    rebuildMask |= thisMask;
  }else{
    /* Otherwise, the old format */
    if( (configure_is_exportable(zName) & groupMask)==0 ) return;
    if( fossil_strcmp(zName, "logo-image")==0 ){







|


|

|

|

|

|
>

|

|


>
|

>
|

|
|
>
|







564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
      thisMask = configure_is_exportable(aType[ii].zName);
    }
    if( (thisMask & groupMask)==0 ) return;

    blob_zero(&sql);
    if( groupMask & CONFIGSET_OVERWRITE ){
      if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
        db_multi_exec("DELETE FROM \"%w\"", &aType[ii].zName[1]);
        configHasBeenReset |= thisMask;
      }
      blob_append_sql(&sql, "REPLACE INTO ");
    }else{
      blob_append_sql(&sql, "INSERT OR IGNORE INTO ");
    }
    blob_append_sql(&sql, "\"%w\"(\"%w\", mtime", &zName[1], aType[ii].zPrimKey);
    for(jj=2; jj<nToken; jj+=2){
       blob_append_sql(&sql, ",\"%w\"", azToken[jj]);
    }
    blob_append_sql(&sql,") VALUES(%s,%s",
       azToken[1] /*safe-for-%s*/, azToken[0] /*safe-for-%s*/);
    for(jj=2; jj<nToken; jj+=2){
       blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
    }
    db_multi_exec("%s)", blob_sql_text(&sql));
    if( db_changes()==0 ){
      blob_reset(&sql);
      blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s",
                      &zName[1], azToken[0]/*safe-for-%s*/);
      for(jj=2; jj<nToken; jj+=2){
        blob_append_sql(&sql, ", \"%w\"=%s",
                        azToken[jj], azToken[jj+1]/*safe-for-%s*/);
      }
      blob_append_sql(&sql, " WHERE \"%w\"=%s AND mtime<%s",
                   aType[ii].zPrimKey, azToken[1]/*safe-for-%s*/,
                   azToken[0]/*safe-for-%s*/);
      db_multi_exec("%s", blob_sql_text(&sql));
    }
    blob_reset(&sql);
    rebuildMask |= thisMask;
  }else{
    /* Otherwise, the old format */
    if( (configure_is_exportable(zName) & groupMask)==0 ) return;
    if( fossil_strcmp(zName, "logo-image")==0 ){
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
      db_finalize(&ins);
    }else if( zName[0]=='@' ){
      /* Notice that we are evaluating arbitrary SQL received from the
      ** client.  But this can only happen if the client has authenticated
      ** as an administrator, so presumably we trust the client at this
      ** point.
      */
      db_multi_exec("%s", blob_str(pContent));
    }else{
      db_multi_exec(
         "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
         zName, blob_str(pContent)
      );
    }
  }







|







614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
      db_finalize(&ins);
    }else if( zName[0]=='@' ){
      /* Notice that we are evaluating arbitrary SQL received from the
      ** client.  But this can only happen if the client has authenticated
      ** as an administrator, so presumably we trust the client at this
      ** point.
      */
      db_multi_exec("%s", blob_str(pContent) /*safe-for-%s*/);
    }else{
      db_multi_exec(
         "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
         zName, blob_str(pContent)
      );
    }
  }
964
965
966
967
968
969
970

971
972
973
974
975
976
977
978
        db_create_default_users(0, 0);
      }else if( fossil_strcmp(zName,"@concealed")==0 ){
        db_multi_exec("DELETE FROM concealed");
      }else if( fossil_strcmp(zName,"@shun")==0 ){
        db_multi_exec("DELETE FROM shun");
      }else if( fossil_strcmp(zName,"@reportfmt")==0 ){
        db_multi_exec("DELETE FROM reportfmt");

        db_multi_exec(zRepositorySchemaDefaultReports);
      }
    }
    db_end_transaction(0);
    fossil_print("Configuration reset to factory defaults.\n");
    fossil_print("To recover, use:  %s %s import %s\n",
            g.argv[0], g.argv[1], zBackup);
    rebuildMask |= mask;







>
|







971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
        db_create_default_users(0, 0);
      }else if( fossil_strcmp(zName,"@concealed")==0 ){
        db_multi_exec("DELETE FROM concealed");
      }else if( fossil_strcmp(zName,"@shun")==0 ){
        db_multi_exec("DELETE FROM shun");
      }else if( fossil_strcmp(zName,"@reportfmt")==0 ){
        db_multi_exec("DELETE FROM reportfmt");
        assert( strchr(zRepositorySchemaDefaultReports,'%')==0 );
        db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/);
      }
    }
    db_end_transaction(0);
    fossil_print("Configuration reset to factory defaults.\n");
    fossil_print("To recover, use:  %s %s import %s\n",
            g.argv[0], g.argv[1], zBackup);
    rebuildMask |= mask;
Changes to src/content.c.
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
      isDephantomize = 1;
      content_mark_available(rid);
    }
  }else{
    /* We are creating a new entry */
    db_prepare(&s1,
      "INSERT INTO blob(rcvid,size,uuid,content)"
      "VALUES(%d,%d,'%b',:data)",
       g.rcvid, size, &hash
    );
    db_bind_blob(&s1, ":data", &cmpr);
    db_exec(&s1);
    rid = db_last_insert_rowid();
    if( !pBlob ){
      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
    }







|
|







556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
      isDephantomize = 1;
      content_mark_available(rid);
    }
  }else{
    /* We are creating a new entry */
    db_prepare(&s1,
      "INSERT INTO blob(rcvid,size,uuid,content)"
      "VALUES(%d,%d,'%q',:data)",
       g.rcvid, size, blob_str(&hash)
    );
    db_bind_blob(&s1, ":data", &cmpr);
    db_exec(&s1);
    rid = db_last_insert_rowid();
    if( !pBlob ){
      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
    }
Changes to src/db.c.
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    }
    for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){
      db.doRollback |= db.aHook[i].xHook();
    }
    while( db.pAllStmt ){
      db_finalize(db.pAllStmt);
    }
    db_multi_exec(db.doRollback ? "ROLLBACK" : "COMMIT");
    db.doRollback = 0;
  }
}

/*
** Force a rollback and shutdown the database
*/







|







175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    }
    for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){
      db.doRollback |= db.aHook[i].xHook();
    }
    while( db.pAllStmt ){
      db_finalize(db.pAllStmt);
    }
    db_multi_exec("%s", db.doRollback ? "ROLLBACK" : "COMMIT");
    db.doRollback = 0;
  }
}

/*
** Force a rollback and shutdown the database
*/
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
  const char *zSql;
  va_list ap;

  db = db_open(zFileName);
  sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
  rc = sqlite3_exec(db, zSchema, 0, 0, 0);
  if( rc!=SQLITE_OK ){
    db_err(sqlite3_errmsg(db));
  }
  va_start(ap, zSchema);
  while( (zSql = va_arg(ap, const char*))!=0 ){
    rc = sqlite3_exec(db, zSql, 0, 0, 0);
    if( rc!=SQLITE_OK ){
      db_err(sqlite3_errmsg(db));
    }
  }
  va_end(ap);
  sqlite3_exec(db, "COMMIT", 0, 0, 0);
  sqlite3_close(db);
}








|





|







662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
  const char *zSql;
  va_list ap;

  db = db_open(zFileName);
  sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
  rc = sqlite3_exec(db, zSchema, 0, 0, 0);
  if( rc!=SQLITE_OK ){
    db_err("%s", sqlite3_errmsg(db));
  }
  va_start(ap, zSchema);
  while( (zSql = va_arg(ap, const char*))!=0 ){
    rc = sqlite3_exec(db, zSql, 0, 0, 0);
    if( rc!=SQLITE_OK ){
      db_err("%s", sqlite3_errmsg(db));
    }
  }
  va_end(ap);
  sqlite3_exec(db, "COMMIT", 0, 0, 0);
  sqlite3_close(db);
}

748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
}


/*
** Detaches the zLabel database.
*/
void db_detach(const char *zLabel){
  db_multi_exec("DETACH DATABASE %s", zLabel);
}

/*
** zDbName is the name of a database file.  Attach zDbName using
** the name zLabel.
*/
void db_attach(const char *zDbName, const char *zLabel){
  db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel);
}

/*
** zDbName is the name of a database file.  If no other database
** file is open, then open this one.  If another database file is
** already open, then attach zDbName using the name zLabel.
*/







|







|







748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
}


/*
** Detaches the zLabel database.
*/
void db_detach(const char *zLabel){
  db_multi_exec("DETACH DATABASE %Q", zLabel);
}

/*
** zDbName is the name of a database file.  Attach zDbName using
** the name zLabel.
*/
void db_attach(const char *zDbName, const char *zLabel){
  db_multi_exec("ATTACH DATABASE %Q AS %Q", zDbName, zLabel);
}

/*
** zDbName is the name of a database file.  If no other database
** file is open, then open this one.  If another database file is
** already open, then attach zDbName using the name zLabel.
*/
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
** zColumn
*/
static int db_local_table_exists_but_lacks_column(
  const char *zTable,
  const char *zColumn
){
  char *zDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
                   " WHERE name=='%s' /*scan*/",
                   db_name("localdb"), zTable);
  int rc = 0;
  if( zDef ){
    char *zPattern = mprintf("* %s *", zColumn);
    rc = sqlite3_strglob(zPattern, zDef)!=0;
    fossil_free(zPattern);
    fossil_free(zDef);







|







885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
** zColumn
*/
static int db_local_table_exists_but_lacks_column(
  const char *zTable,
  const char *zColumn
){
  char *zDef = db_text(0, "SELECT sql FROM %s.sqlite_master"
                   " WHERE name==%Q /*scan*/",
                   db_name("localdb"), zTable);
  int rc = 0;
  if( zDef ){
    char *zPattern = mprintf("* %s *", zColumn);
    rc = sqlite3_strglob(zPattern, zDef)!=0;
    fossil_free(zPattern);
    fossil_free(zDef);
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131

/*
** Return TRUE if the schema is out-of-date
*/
int db_schema_is_outofdate(void){
  return db_exists("SELECT 1 FROM config"
                   " WHERE name='aux-schema'"
                   "   AND value<>'%s'", AUX_SCHEMA);
}

/*
** Return true if the database is writeable
*/
int db_is_writeable(const char *zName){
  return g.db!=0 && !sqlite3_db_readonly(g.db, db_name(zName));







|







1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131

/*
** Return TRUE if the schema is out-of-date
*/
int db_schema_is_outofdate(void){
  return db_exists("SELECT 1 FROM config"
                   " WHERE name='aux-schema'"
                   "   AND value<>%Q", AUX_SCHEMA);
}

/*
** Return true if the database is writeable
*/
int db_is_writeable(const char *zName){
  return g.db!=0 && !sqlite3_db_readonly(g.db, db_name(zName));
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
*/
const char *db_setting_inop_rhs(){
  Blob x;
  int i;
  const char *zSep = "";

  blob_zero(&x);
  blob_append(&x, "(", 1);
  for(i=0; ctrlSettings[i].name; i++){
    blob_appendf(&x, "%s'%s'", zSep, ctrlSettings[i].name);
    zSep = ",";
  }
  blob_append(&x, ")", 1);
  return blob_str(&x);
}

/*
** Fill an empty repository database with the basic information for a
** repository. This function is shared between 'create_repository_cmd'
** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
** new repositories.







|

|


|
|







1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
*/
const char *db_setting_inop_rhs(){
  Blob x;
  int i;
  const char *zSep = "";

  blob_zero(&x);
  blob_append_sql(&x, "(");
  for(i=0; ctrlSettings[i].name; i++){
    blob_append_sql(&x, "%s%Q", zSep/*safe-for-%s*/, ctrlSettings[i].name);
    zSep = ",";
  }
  blob_append_sql(&x, ")");
  return blob_sql_text(&x);
}

/*
** Fill an empty repository database with the basic information for a
** repository. This function is shared between 'create_repository_cmd'
** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
** new repositories.
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
** as follows:
**
**       ckout:%s
**
** Where %s is the checkout root.  The value is the repository file.
*/
void db_record_repository_filename(const char *zName){
  const char *zCollation;
  char *zRepoSetting;
  char *zCkoutSetting;
  Blob full;
  if( zName==0 ){
    if( !g.localOpen ) return;
    zName = db_repository_filename();
  }
  file_canonical_name(zName, &full, 0);
  zCollation = filename_collation();
  db_swap_connections();
  zRepoSetting = mprintf("repo:%q", blob_str(&full));
  db_multi_exec(
     "DELETE FROM global_config WHERE name %s = '%s';",
     zCollation, zRepoSetting
  );
  db_multi_exec(
     "INSERT OR IGNORE INTO global_config(name,value)"
     "VALUES('%s',1);",
     zRepoSetting
  );
  fossil_free(zRepoSetting);
  if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){
    Blob localRoot;
    file_canonical_name(g.zLocalRoot, &localRoot, 1);
    zCkoutSetting = mprintf("ckout:%q", blob_str(&localRoot));
    db_multi_exec(
       "DELETE FROM global_config WHERE name %s = '%s';",
       zCollation, zCkoutSetting
    );
    db_multi_exec(
      "REPLACE INTO global_config(name, value)"
      "VALUES('%s','%q');",
      zCkoutSetting, blob_str(&full)
    );
    db_swap_connections();
    db_optional_sql("repository",
        "DELETE FROM config WHERE name %s = '%s';",
        zCollation, zCkoutSetting
    );
    db_optional_sql("repository",
        "REPLACE INTO config(name,value,mtime)"
        "VALUES('%s',1,now());",
        zCkoutSetting
    );
    fossil_free(zCkoutSetting);
    blob_reset(&localRoot);
  }else{
    db_swap_connections();
  }







<








|



|
|



|








|
|



|




|
|



|







2001
2002
2003
2004
2005
2006
2007

2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
** as follows:
**
**       ckout:%s
**
** Where %s is the checkout root.  The value is the repository file.
*/
void db_record_repository_filename(const char *zName){

  char *zRepoSetting;
  char *zCkoutSetting;
  Blob full;
  if( zName==0 ){
    if( !g.localOpen ) return;
    zName = db_repository_filename();
  }
  file_canonical_name(zName, &full, 0);
  (void)filename_collation();  /* Initialize before connection swap */
  db_swap_connections();
  zRepoSetting = mprintf("repo:%q", blob_str(&full));
  db_multi_exec(
     "DELETE FROM global_config WHERE name %s = %Q;",
     filename_collation(), zRepoSetting
  );
  db_multi_exec(
     "INSERT OR IGNORE INTO global_config(name,value)"
     "VALUES(%Q,1);",
     zRepoSetting
  );
  fossil_free(zRepoSetting);
  if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){
    Blob localRoot;
    file_canonical_name(g.zLocalRoot, &localRoot, 1);
    zCkoutSetting = mprintf("ckout:%q", blob_str(&localRoot));
    db_multi_exec(
       "DELETE FROM global_config WHERE name %s = %Q;",
       filename_collation(), zCkoutSetting
    );
    db_multi_exec(
      "REPLACE INTO global_config(name, value)"
      "VALUES(%Q,%Q);",
      zCkoutSetting, blob_str(&full)
    );
    db_swap_connections();
    db_optional_sql("repository",
        "DELETE FROM config WHERE name %s = %Q;",
        filename_collation(), zCkoutSetting
    );
    db_optional_sql("repository",
        "REPLACE INTO config(name,value,mtime)"
        "VALUES(%Q,1,now());",
        zCkoutSetting
    );
    fossil_free(zCkoutSetting);
    blob_reset(&localRoot);
  }else{
    db_swap_connections();
  }
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
          blob_append(&newSql, zOrigSql, j);
          blob_append(&newSql, "PRIMARY KEY", -1);
          zOrigSql += j+6;
          j = -1;
        }
      }
      blob_append(&newSql, zOrigSql, -1);
      blob_appendf(&allSql,
         "ALTER TABLE %s RENAME TO x_%s;\n"
         "%s WITHOUT ROWID;\n"
         "INSERT INTO %s SELECT * FROM x_%s;\n"
         "DROP TABLE x_%s;\n",
         zTName, zTName, blob_str(&newSql), zTName, zTName, zTName
      );
      fossil_print("Converting table %s of %s to WITHOUT ROWID.\n", zTName, g.argv[i]);
      blob_reset(&newSql);
    }
    blob_appendf(&allSql, "COMMIT;\n");
    db_finalize(&q);
    if( dryRun ){
      fossil_print("SQL that would have been evaluated:\n");
      fossil_print("-------------------------------------------------------------\n");
      fossil_print("%s", blob_str(&allSql));
    }else{
      db_multi_exec("%s", blob_str(&allSql));
    }
    blob_reset(&allSql);
    db_close(1);
  }
}







|
|

|
|
|




|




|

|





2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
          blob_append(&newSql, zOrigSql, j);
          blob_append(&newSql, "PRIMARY KEY", -1);
          zOrigSql += j+6;
          j = -1;
        }
      }
      blob_append(&newSql, zOrigSql, -1);
      blob_append_sql(&allSql,
         "ALTER TABLE \"%w\" RENAME TO \"x_%w\";\n"
         "%s WITHOUT ROWID;\n"
         "INSERT INTO \"%w\" SELECT * FROM \"x_%w\";\n"
         "DROP TABLE \"x_%w\";\n",
         zTName, zTName, blob_sql_text(&newSql), zTName, zTName, zTName
      );
      fossil_print("Converting table %s of %s to WITHOUT ROWID.\n", zTName, g.argv[i]);
      blob_reset(&newSql);
    }
    blob_append_sql(&allSql, "COMMIT;\n");
    db_finalize(&q);
    if( dryRun ){
      fossil_print("SQL that would have been evaluated:\n");
      fossil_print("-------------------------------------------------------------\n");
      fossil_print("%s", blob_sql_text(&allSql));
    }else{
      db_multi_exec("%s", blob_sql_text(&allSql));
    }
    blob_reset(&allSql);
    db_close(1);
  }
}
Changes to src/delta.c.
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
#if INTERFACE
/*
** The "u32" type must be an unsigned 32-bit integer.  Adjust this
*/
typedef unsigned int u32;

/*
** Must be a 16-bit value 
*/
typedef short int s16;
typedef unsigned short int u16;

#endif /* INTERFACE */

/*
** The width of a hash window in bytes.  The algorithm only works if this
** is a power of 2.
*/
#define NHASH 16

/*
** The current state of the rolling hash.
**
** z[] holds the values that have been hashed.  z[] is a circular buffer.
** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of 
** the window.
**
** Hash.a is the sum of all elements of hash.z[].  Hash.b is a weighted
** sum.  Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1.
** (Each index for z[] should be module NHASH, of course.  The %NHASH operator
** is omitted in the prior expression for brevity.)
*/







|
















|







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
#if INTERFACE
/*
** The "u32" type must be an unsigned 32-bit integer.  Adjust this
*/
typedef unsigned int u32;

/*
** Must be a 16-bit value
*/
typedef short int s16;
typedef unsigned short int u16;

#endif /* INTERFACE */

/*
** The width of a hash window in bytes.  The algorithm only works if this
** is a power of 2.
*/
#define NHASH 16

/*
** The current state of the rolling hash.
**
** z[] holds the values that have been hashed.  z[] is a circular buffer.
** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of
** the window.
**
** Hash.a is the sum of all elements of hash.z[].  Hash.b is a weighted
** sum.  Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1.
** (Each index for z[] should be module NHASH, of course.  The %NHASH operator
** is omitted in the prior expression for brevity.)
*/
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16);
}

/*
** Write an base-64 integer into the given buffer.
*/
static void putInt(unsigned int v, char **pz){
  static const char zDigits[] = 
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
  /*  123456789 123456789 123456789 123456789 123456789 123456789 123 */
  int i, j;
  char zBuf[20];
  if( v==0 ){
    *(*pz)++ = '0';
    return;







|







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16);
}

/*
** Write an base-64 integer into the given buffer.
*/
static void putInt(unsigned int v, char **pz){
  static const char zDigits[] =
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
  /*  123456789 123456789 123456789 123456789 123456789 123456789 123 */
  int i, j;
  char zBuf[20];
  if( v==0 ){
    *(*pz)++ = '0';
    return;
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
  }
  return sum3;
}

/*
** Create a new delta.
**
** The delta is written into a preallocated buffer, zDelta, which 
** should be at least 60 bytes longer than the target file, zOut.
** The delta string will be NUL-terminated, but it might also contain
** embedded NUL characters if either the zSrc or zOut files are
** binary.  This function returns the length of the delta string
** in bytes, excluding the final NUL terminator character.
**
** Output Format:
**
** The delta begins with a base64 number followed by a newline.  This
** number is the number of bytes in the TARGET file.  Thus, given a
** delta file z, a program can compute the size of the output file
** simply by reading the first line and decoding the base-64 number
** found there.  The delta_output_size() routine does exactly this.
**
** After the initial size number, the delta consists of a series of
** literal text segments and commands to copy from the SOURCE file.  
** A copy command looks like this:
**
**     NNN@MMM,
**
** where NNN is the number of bytes to be copied and MMM is the offset
** into the source file of the first byte (both base-64).   If NNN is 0
** it means copy the rest of the input file.  Literal text is like this:







|















|







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
  }
  return sum3;
}

/*
** Create a new delta.
**
** The delta is written into a preallocated buffer, zDelta, which
** should be at least 60 bytes longer than the target file, zOut.
** The delta string will be NUL-terminated, but it might also contain
** embedded NUL characters if either the zSrc or zOut files are
** binary.  This function returns the length of the delta string
** in bytes, excluding the final NUL terminator character.
**
** Output Format:
**
** The delta begins with a base64 number followed by a newline.  This
** number is the number of bytes in the TARGET file.  Thus, given a
** delta file z, a program can compute the size of the output file
** simply by reading the first line and decoding the base-64 number
** found there.  The delta_output_size() routine does exactly this.
**
** After the initial size number, the delta consists of a series of
** literal text segments and commands to copy from the SOURCE file.
** A copy command looks like this:
**
**     NNN@MMM,
**
** where NNN is the number of bytes to be copied and MMM is the offset
** into the source file of the first byte (both base-64).   If NNN is 0
** it means copy the rest of the input file.  Literal text is like this:
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
** Next we begin scanning the target file using a sliding 16-byte
** window.  The hash of the 16-byte window in the target is used to
** search for a matching section in the source file.  When a match
** is found, a copy command is added to the delta.  An effort is
** made to extend the matching section to regions that come before
** and after the 16-byte hash window.  A copy command is only issued
** if the result would use less space that just quoting the text
** literally. Literal text is added to the delta for sections that 
** do not match or which can not be encoded efficiently using copy
** commands.
*/
int delta_create(
  const char *zSrc,      /* The source or pattern file */
  unsigned int lenSrc,   /* Length of the source file */
  const char *zOut,      /* The target file */







|







281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
** Next we begin scanning the target file using a sliding 16-byte
** window.  The hash of the 16-byte window in the target is used to
** search for a matching section in the source file.  When a match
** is found, a copy command is added to the delta.  An effort is
** made to extend the matching section to regions that come before
** and after the 16-byte hash window.  A copy command is only issued
** if the result would use less space that just quoting the text
** literally. Literal text is added to the delta for sections that
** do not match or which can not be encoded efficiently using copy
** commands.
*/
int delta_create(
  const char *zSrc,      /* The source or pattern file */
  unsigned int lenSrc,   /* Length of the source file */
  const char *zOut,      /* The target file */
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
      int limit = 250;

      hv = hash_32bit(&h) % nHash;
      DEBUG2( printf("LOOKING: %4d [%s]\n", base+i, print16(&zOut[base+i])); )
      iBlock = landmark[hv];
      while( iBlock>=0 && (limit--)>0 ){
        /*
        ** The hash window has identified a potential match against 
        ** landmark block iBlock.  But we need to investigate further.
        ** 
        ** Look for a region in zOut that matches zSrc. Anchor the search
        ** at zSrc[iSrc] and zOut[base+i].  Do not include anything prior to
        ** zOut[base] or after zOut[outLen] nor anything after zSrc[srcLen].
        **
        ** Set cnt equal to the length of the match and set ofst so that
        ** zSrc[ofst] is the first element of the match.  litsz is the number
        ** of characters between zOut[base] and the beginning of the match.







|

|







354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
      int limit = 250;

      hv = hash_32bit(&h) % nHash;
      DEBUG2( printf("LOOKING: %4d [%s]\n", base+i, print16(&zOut[base+i])); )
      iBlock = landmark[hv];
      while( iBlock>=0 && (limit--)>0 ){
        /*
        ** The hash window has identified a potential match against
        ** landmark block iBlock.  But we need to investigate further.
        **
        ** Look for a region in zOut that matches zSrc. Anchor the search
        ** at zSrc[iSrc] and zOut[base+i].  Do not include anything prior to
        ** zOut[base] or after zOut[outLen] nor anything after zSrc[srcLen].
        **
        ** Set cnt equal to the length of the match and set ofst so that
        ** zSrc[ofst] is the first element of the match.  litsz is the number
        ** of characters between zOut[base] and the beginning of the match.
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    memcpy(zDelta, &zOut[base], lenOut-base);
    zDelta += lenOut-base;
  }
  /* Output the final checksum record. */
  putInt(checksum(zOut, lenOut), &zDelta);
  *(zDelta++) = ';';
  fossil_free(collide);
  return zDelta - zOrigDelta; 
}

/*
** Return the size (in bytes) of the output from applying
** a delta. 
**
** This routine is provided so that an procedure that is able
** to call delta_apply() can learn how much space is required
** for the output and hence allocate nor more space that is really
** needed.
*/
int delta_output_size(const char *zDelta, int lenDelta){







|




|







466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    memcpy(zDelta, &zOut[base], lenOut-base);
    zDelta += lenOut-base;
  }
  /* Output the final checksum record. */
  putInt(checksum(zOut, lenOut), &zDelta);
  *(zDelta++) = ';';
  fossil_free(collide);
  return zDelta - zOrigDelta;
}

/*
** Return the size (in bytes) of the output from applying
** a delta.
**
** This routine is provided so that an procedure that is able
** to call delta_apply() can learn how much space is required
** for the output and hence allocate nor more space that is really
** needed.
*/
int delta_output_size(const char *zDelta, int lenDelta){
Changes to src/descendants.c.
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
  
  /* We should be done with options.. */
  verify_all_options();

  if( recomputeFlag ) leaf_rebuild();
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_tty(), -1);
  blob_appendf(&sql, " AND blob.rid IN leaf");
  if( showClosed ){
    blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
  }else if( !showAll ){
    blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
  }
  if( byBranch ){
    db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase,"
                   " event.mtime DESC",
                   blob_str(&sql));
  }else{
    db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
  }
  blob_reset(&sql);
  n = 0;
  while( db_step(&q)==SQLITE_ROW ){
    const char *zId = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);







|

|

|




|

|







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
  
  /* We should be done with options.. */
  verify_all_options();

  if( recomputeFlag ) leaf_rebuild();
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_tty(), -1);
  blob_append_sql(&sql, " AND blob.rid IN leaf");
  if( showClosed ){
    blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
  }else if( !showAll ){
    blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
  }
  if( byBranch ){
    db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase,"
                   " event.mtime DESC",
                   blob_sql_text(&sql));
  }else{
    db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
  }
  blob_reset(&sql);
  n = 0;
  while( db_step(&q)==SQLITE_ROW ){
    const char *zId = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zCom = db_column_text(&q, 3);
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  }else if( showClosed ){
    @ <h1>Closed leaves:</h1>
  }else{
    @ <h1>Open leaves:</h1>
  }
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_appendf(&sql, " AND blob.rid IN leaf");
  if( showClosed ){
    blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
  }else if( !showAll ){
    blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
  }
  db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
  blob_reset(&sql);
  www_print_timeline(&q, TIMELINE_LEAFONLY, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_footer();
}








|

|

|

|







472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  }else if( showClosed ){
    @ <h1>Closed leaves:</h1>
  }else{
    @ <h1>Open leaves:</h1>
  }
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND blob.rid IN leaf");
  if( showClosed ){
    blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
  }else if( !showAll ){
    blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
  }
  db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
  blob_reset(&sql);
  www_print_timeline(&q, TIMELINE_LEAFONLY, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_footer();
}

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  Bag pending;
  Stmt ins;
  Stmt q;
  int rid;

  bag_init(&seen);
  bag_init(&pending);
  db_prepare(&ins, "INSERT OR IGNORE INTO \"%s\" VALUES(:rid)", zTab);
  db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
  while( db_step(&q)==SQLITE_ROW ){
    int mid = db_column_int(&q, 0);
    bag_insert(&pending, mid);
    bag_insert(&seen, mid);
    db_bind_int(&ins, ":rid", mid);
    db_step(&ins);







|







506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  Bag pending;
  Stmt ins;
  Stmt q;
  int rid;

  bag_init(&seen);
  bag_init(&pending);
  db_prepare(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES(:rid)", zTab);
  db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
  while( db_step(&q)==SQLITE_ROW ){
    int mid = db_column_int(&q, 0);
    bag_insert(&pending, mid);
    bag_insert(&seen, mid);
    db_bind_int(&ins, ":rid", mid);
    db_step(&ins);
Changes to src/diffcmd.c.
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
    int cnt = 0;
    Blob nameFile1;    /* Name of temporary file to old pFile1 content */
    Blob cmd;          /* Text of command to run */

    if( !fIncludeBinary ){
      Blob file2;
      if( isBin1 ){
        fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( zBinGlob ){
        Glob *pBinary = glob_create(zBinGlob);
        if( glob_match(pBinary, zName) ){
          fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
      blob_zero(&file2);
      if( file_wd_size(zFile2)>=0 ){
        if( file_wd_islink(zFile2) ){
          blob_read_link(&file2, zFile2);
        }else{
          blob_read_from_file(&file2, zFile2);
        }
      }
      if( looks_like_binary(&file2) ){
        fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
        blob_reset(&file2);
        return;
      }
      blob_reset(&file2);
    }

    /* Construct a temporary file to hold pFile1 based on the name of







|





|














|







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
    int cnt = 0;
    Blob nameFile1;    /* Name of temporary file to old pFile1 content */
    Blob cmd;          /* Text of command to run */

    if( !fIncludeBinary ){
      Blob file2;
      if( isBin1 ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( zBinGlob ){
        Glob *pBinary = glob_create(zBinGlob);
        if( glob_match(pBinary, zName) ){
          fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
      blob_zero(&file2);
      if( file_wd_size(zFile2)>=0 ){
        if( file_wd_islink(zFile2) ){
          blob_read_link(&file2, zFile2);
        }else{
          blob_read_from_file(&file2, zFile2);
        }
      }
      if( looks_like_binary(&file2) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        blob_reset(&file2);
        return;
      }
      blob_reset(&file2);
    }

    /* Construct a temporary file to hold pFile1 based on the name of
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  }else{
    Blob cmd;
    char zTemp1[300];
    char zTemp2[300];

    if( !fIncludeBinary ){
      if( isBin1 || isBin2 ){
        fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( zBinGlob ){
        Glob *pBinary = glob_create(zBinGlob);
        if( glob_match(pBinary, zName) ){
          fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
    }








|





|







236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  }else{
    Blob cmd;
    char zTemp1[300];
    char zTemp2[300];

    if( !fIncludeBinary ){
      if( isBin1 || isBin2 ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( zBinGlob ){
        Glob *pBinary = glob_create(zBinGlob);
        if( glob_match(pBinary, zName) ){
          fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
    }

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  Blob content;
  int isLink;
  int isBin;
  file_tree_name(zFileTreeName, &fname, 1);
  historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
                             fIncludeBinary ? 0 : &isBin, 0);
  if( !isLink != !file_wd_islink(zFrom) ){
    fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
  }else{
    diff_file(&content, isBin, zFileTreeName, zFileTreeName,
              zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
  }
  blob_reset(&content);
  blob_reset(&fname);
}







|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  Blob content;
  int isLink;
  int isBin;
  file_tree_name(zFileTreeName, &fname, 1);
  historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
                             fIncludeBinary ? 0 : &isBin, 0);
  if( !isLink != !file_wd_islink(zFrom) ){
    fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
  }else{
    diff_file(&content, isBin, zFileTreeName, zFileTreeName,
              zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
  }
  blob_reset(&content);
  blob_reset(&fname);
}
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
  db_begin_transaction();
  if( zFrom ){
    int rid = name_to_typed_rid(zFrom, "ci");
    if( !is_a_version(rid) ){
      fossil_fatal("no such check-in: %s", zFrom);
    }
    load_vfile_from_rid(rid);
    blob_appendf(&sql,
      "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid, v1.islink"
      "  FROM vfile v1, vfile v2 "
      " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
      "   AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)"
      "UNION "
      "SELECT pathname, 1, 0, 0, 0, islink"
      "  FROM vfile v1"
      " WHERE v1.vid=%d"
      "   AND NOT EXISTS(SELECT 1 FROM vfile v2"
                        " WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
      "UNION "
      "SELECT pathname, 0, 0, 1, 0, islink"
      "  FROM vfile v2"
      " WHERE v2.vid=%d"
      "   AND NOT EXISTS(SELECT 1 FROM vfile v1"
                        " WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
      " ORDER BY 1",
      rid, vid, rid, vid, vid, rid
    );
  }else{
    blob_appendf(&sql,
      "SELECT pathname, deleted, chnged , rid==0, rid, islink"
      "  FROM vfile"
      " WHERE vid=%d"
      "   AND (deleted OR chnged OR rid==0)"
      " ORDER BY pathname",
      vid
    );
  }
  db_prepare(&q, blob_str(&sql));
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);
    int isNew = db_column_int(&q,3);
    int srcid = db_column_int(&q, 4);
    int isLink = db_column_int(&q, 5);







|




















|








|







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
  db_begin_transaction();
  if( zFrom ){
    int rid = name_to_typed_rid(zFrom, "ci");
    if( !is_a_version(rid) ){
      fossil_fatal("no such check-in: %s", zFrom);
    }
    load_vfile_from_rid(rid);
    blob_append_sql(&sql,
      "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid, v1.islink"
      "  FROM vfile v1, vfile v2 "
      " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
      "   AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)"
      "UNION "
      "SELECT pathname, 1, 0, 0, 0, islink"
      "  FROM vfile v1"
      " WHERE v1.vid=%d"
      "   AND NOT EXISTS(SELECT 1 FROM vfile v2"
                        " WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
      "UNION "
      "SELECT pathname, 0, 0, 1, 0, islink"
      "  FROM vfile v2"
      " WHERE v2.vid=%d"
      "   AND NOT EXISTS(SELECT 1 FROM vfile v1"
                        " WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
      " ORDER BY 1",
      rid, vid, rid, vid, vid, rid
    );
  }else{
    blob_append_sql(&sql,
      "SELECT pathname, deleted, chnged , rid==0, rid, islink"
      "  FROM vfile"
      " WHERE vid=%d"
      "   AND (deleted OR chnged OR rid==0)"
      " ORDER BY pathname",
      vid
    );
  }
  db_prepare(&q, "%s", blob_sql_text(&sql));
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);
    int isNew = db_column_int(&q,3);
    int srcid = db_column_int(&q, 4);
    int isLink = db_column_int(&q, 5);
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
    }
    if( showDiff ){
      Blob content;
      int isBin;
      if( !isLink != !file_wd_islink(zFullName) ){
        diff_print_index(zPathname, diffFlags);
        diff_print_filenames(zPathname, zPathname, diffFlags);
        fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
        continue;
      }
      if( srcid>0 ){
        content_get(srcid, &content);
      }else{
        blob_zero(&content);
      }







|







406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
    }
    if( showDiff ){
      Blob content;
      int isBin;
      if( !isLink != !file_wd_islink(zFullName) ){
        diff_print_index(zPathname, diffFlags);
        diff_print_filenames(zPathname, zPathname, diffFlags);
        fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
        continue;
      }
      if( srcid>0 ){
        content_get(srcid, &content);
      }else{
        blob_zero(&content);
      }
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  zName = blob_str(&fname);
  historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
                             fIncludeBinary ? 0 : &isBin1, 0);
  historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
                             fIncludeBinary ? 0 : &isBin2, 0);
  if( isLink1 != isLink2 ){
    diff_print_filenames(zName, zName, diffFlags);
    fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
  }else{
    diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
                  zBinGlob, fIncludeBinary, diffFlags);
  }
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&fname);







|







460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  zName = blob_str(&fname);
  historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
                             fIncludeBinary ? 0 : &isBin1, 0);
  historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
                             fIncludeBinary ? 0 : &isBin2, 0);
  if( isLink1 != isLink2 ){
    diff_print_filenames(zName, zName, diffFlags);
    fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
  }else{
    diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
                  zBinGlob, fIncludeBinary, diffFlags);
  }
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&fname);
Changes to src/doc.c.
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

/*
** Try to guess the mimetype from content.
**
** If the content is pure text, return NULL.
**
** For image types, attempt to return an appropriate mimetype
** name like "image/gif" or "image/jpeg".  
**
** For any other binary type, return "unknown/unknown".
*/
const char *mimetype_from_content(Blob *pBlob){
  int i;
  int n;
  const unsigned char *x;







|







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

/*
** Try to guess the mimetype from content.
**
** If the content is pure text, return NULL.
**
** For image types, attempt to return an appropriate mimetype
** name like "image/gif" or "image/jpeg".
**
** For any other binary type, return "unknown/unknown".
*/
const char *mimetype_from_content(Blob *pBlob){
  int i;
  int n;
  const unsigned char *x;
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
const char *mimetype_from_name(const char *zName){
  const char *z;
  int i;
  int first, last;
  int len;
  char zSuffix[20];

  /* A table of mimetypes based on file suffixes. 
  ** Suffixes must be in sorted order so that we can do a binary
  ** search to find the mime-type
  */
  static const struct {
    const char *zSuffix;       /* The file suffix */
    int size;                  /* Length of the suffix */
    const char *zMimetype;     /* The corresponding mimetype */







|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
const char *mimetype_from_name(const char *zName){
  const char *z;
  int i;
  int first, last;
  int len;
  char zSuffix[20];

  /* A table of mimetypes based on file suffixes.
  ** Suffixes must be in sorted order so that we can do a binary
  ** search to find the mime-type
  */
  static const struct {
    const char *zSuffix;       /* The file suffix */
    int size;                  /* Length of the suffix */
    const char *zMimetype;     /* The corresponding mimetype */
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
520
521
522
523
524
525
526
527
528
529
530
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
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
      goto doc_not_found;
    }
    db_end_transaction(0);
  }
  blob_to_utf8_no_bom(&filebody, 0);

  /* The file is now contained in the filebody blob.  Deliver the
  ** file to the user 
  */
  zMime = P("mimetype");
  if( zMime==0 ){
    zMime = mimetype_from_name(zName);
  }
  Th_Store("doc_name", zName);
  Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
                                     "  FROM blob WHERE rid=%d", vid));
  Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
                                  " WHERE objid=%d AND type='ci'", vid));
  if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
    Blob title, tail;
    if( wiki_find_title(&filebody, &title, &tail) ){
      style_header(blob_str(&title));
      wiki_convert(&tail, 0, WIKI_BUTTONS);
    }else{
      style_header("Documentation");
      wiki_convert(&filebody, 0, WIKI_BUTTONS);
    }
    style_footer();
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob title = BLOB_INITIALIZER;
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(&filebody, &title, &tail);
    if( blob_size(&title)>0 ){
      style_header(blob_str(&title));
    }else{
      style_header("Documentation");
    }
    blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail));
    style_footer();
  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
    style_header("Documentation");
    @ <blockquote><pre>
    @ %h(blob_str(&filebody))
    @ </pre></blockquote>
    style_footer();
#ifdef FOSSIL_ENABLE_TH1_DOCS
  }else if( db_get_boolean("th1-docs", 0) &&
            fossil_strcmp(zMime, "application/x-th1")==0 ){
    char *zHtml = htmlize(zName, -1);
    style_header(zHtml);
    Th_Render(blob_str(&filebody));
    style_footer();
    fossil_free(zHtml);
#endif
  }else{
    cgi_set_content_type(zMime);
    cgi_set_content(&filebody);
  }
  return;

doc_not_found:
  /* Jump here when unable to locate the document */
  db_end_transaction(0);
  style_header("Document Not Found");
  @ <p>No such document: %h(zName)</p>
  style_footer();
  return;  
}

/*
** The default logo.
*/
static const unsigned char aLogo[] = {
    71,  73,  70,  56,  55,  97,  62,   0,  71,   0, 244,   0,   0,  85, 
   129, 149,  95, 136, 155,  99, 139, 157, 106, 144, 162, 113, 150, 166, 
   116, 152, 168, 127, 160, 175, 138, 168, 182, 148, 176, 188, 159, 184, 
   195, 170, 192, 202, 180, 199, 208, 184, 202, 210, 191, 207, 215, 201, 
   215, 221, 212, 223, 228, 223, 231, 235, 226, 227, 226, 226, 234, 237, 
   233, 239, 241, 240, 244, 246, 244, 247, 248, 255, 255, 255,   0,   0, 
     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  44,   0,   0, 
     0,   0,  62,   0,  71,   0,   0,   5, 255,  96, 100, 141, 100, 105, 
   158, 168,  37,  41, 132, 192, 164, 112,  44, 207, 102,  99,   0,  56, 
    16,  84, 116, 239, 199, 141,  65, 110, 232, 248,  25, 141, 193, 161, 
    82, 113, 108, 202,  32,  55, 229, 210,  73,  61,  41, 164,  88, 102, 
   181,  10,  41,  96, 179,  91, 106,  35, 240,   5, 135, 143, 137, 242, 
    87, 123, 246,  33, 190,  81, 108, 163, 237, 198,  14,  30, 113, 233, 
   131,  78, 115,  72,  11, 115,  87, 101,  19, 124,  51,  66,  74,   8, 
    19,  16,  67, 100,  74, 133,  50,  15, 101, 135,  56,  11,  74,   6, 
   143,  49, 126, 106,  56,   8, 145,  67,   9, 152,  48, 139, 155,   5, 
    22,  13,  74, 115, 161,  41, 147, 101,  13, 130,  57, 132, 170,  40, 
   167, 155,   0,  94,  57,   3, 178,  48, 183, 181,  57, 160, 186,  40, 
    19, 141, 189,   0,  69, 192,  40,  16, 195, 155, 185, 199,  41, 201, 
   189, 191, 205, 193, 188, 131, 210,  49, 175,  88, 209, 214,  38,  19, 
     3,  11,  19, 111, 127,  60, 219,  39,  55, 204,  19,  11,   6, 100, 
     5,  10, 227, 228,  37, 163,   0, 239, 117,  56, 238, 243,  49, 195, 
   177, 247,  48, 158,  56, 251,  50, 216, 254, 197,  56, 128, 107, 158, 
     2, 125, 171, 114,  92, 218, 246,  96,  66,   3,   4,  50, 134, 176, 
   145,   6,  97,  64, 144,  24,  19, 136, 108,  91, 177, 160,   0, 194, 
    19, 253,   0, 216, 107, 214, 224, 192, 129,   5,  16,  83, 255, 244, 
    43, 213, 195,  24, 159,  27, 169,  64, 230,  88, 208, 227, 129, 182, 
    54,   4,  89, 158,  24, 181, 163, 199,   1, 155,  52, 233,   8, 130, 
   176,  83,  24, 128, 137,  50,  18,  32,  48,  48, 114,  11, 173, 137, 
    19, 110,   4,  64, 105,   1, 194,  30, 140,  68,  15,  24,  24, 224, 
    50,  76,  70,   0,  11, 171,  54,  26, 160, 181, 194, 149, 148,  40, 
   174, 148, 122,  64, 180, 208, 161,  17, 207, 112, 164,   1, 128,  96, 
   148,  78,  18,  21, 194,  33, 229,  51, 247,  65, 133,  97,   5, 250, 
    69, 229, 100,  34, 220, 128, 166, 116, 190,  62,   8, 167, 195, 170, 
    47, 163,   0, 130,  90, 152,  11, 160, 173, 170,  27, 154,  26,  91, 
   232, 151, 171,  18,  14, 162, 253,  98, 170,  18,  70, 171,  64, 219, 
    10,  67, 136, 134, 187, 116,  75, 180,  46, 179, 174, 135,   4, 189, 
   229, 231,  78,  40,  10,  62, 226, 164, 172,  64, 240, 167, 170,  10, 
    18, 124, 188,  10, 107,  65, 193,  94,  11,  93, 171,  28, 248,  17, 
   239,  46, 140,  78,  97,  34,  25, 153,  36,  99,  65, 130,   7, 203, 
   183, 168,  51,  34, 136,  25, 140,  10,   6,  16,  28, 255, 145, 241, 
   230, 140,  10,  66, 178, 167, 112,  48, 192, 128, 129,   9,  31, 141, 
    84, 138,  63, 163, 162,   2, 203, 206, 240,  56,  55,  98, 192, 188, 
    15, 185,  50, 160,   6,   0, 125,  62,  33, 214, 195,  33,   5,  24, 
   184,  25, 231,  14, 201, 245, 144,  23, 126, 104, 228,   0, 145,   2, 
    13, 140, 244, 212,  17,  21,  20, 176, 159,  17,  95, 225, 160, 128, 
    16,   1,  32, 224, 142,  32, 227, 125,  87,  64,   0,  16,  54, 129, 
   205,   2, 141,  76,  53, 130, 103,  37, 166,  64, 144, 107,  78, 196, 
     5, 192,   0,  54,  50, 229,   9, 141,  49,  84, 194,  35,  12, 196, 
   153,  48, 192, 137,  57,  84,  24,   7,  87, 159, 249, 240, 215, 143, 
   105, 241, 118, 149,   9, 139,   4,  64, 203, 141,  35, 140, 129, 131, 
    16, 222, 125, 231, 128,   2, 238,  17, 152,  66,   3,   5,  56, 224, 
   159, 103,  16,  76,  25,  75,   5,  11, 164, 215,  96,   9,  14,  16, 
    36, 225,  15,  11,  40, 144, 192, 156,  41,  10, 178, 199,   3,  66, 
    64,  80, 193,   3, 124,  90,  48, 129, 129, 102, 177,  18, 192, 154, 
    49,  84, 240, 208,  92,  22, 149,  96,  39,   9,  31,  74,  17,  94, 
     3,   8, 177, 199,  72,  59,  85,  76,  25, 216,   8, 139, 194, 197, 
   138, 163,  69,  96, 115,   0, 147,  72,  72,  84,  28,  14,  79,  86, 
   233, 230,  23, 113,  26, 160, 128,   3,  10,  58, 129, 103,  14, 159, 
   214, 163, 146, 117, 238, 213, 154, 128, 151, 109,  84,  64, 217,  13, 
    27,  10, 228,  39,   2, 235, 164, 168,  74,   8,   0,  59, 
};

/*
** WEBPAGE: logo
**
** Return the logo image.  This image is available to anybody who can see
** the login page.  It is designed for use in the upper left-hand corner







|













|











|














<
|
















|






|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







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
520
521
522
523
524
525
526
527
528
529
530
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
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
      goto doc_not_found;
    }
    db_end_transaction(0);
  }
  blob_to_utf8_no_bom(&filebody, 0);

  /* The file is now contained in the filebody blob.  Deliver the
  ** file to the user
  */
  zMime = P("mimetype");
  if( zMime==0 ){
    zMime = mimetype_from_name(zName);
  }
  Th_Store("doc_name", zName);
  Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
                                     "  FROM blob WHERE rid=%d", vid));
  Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
                                  " WHERE objid=%d AND type='ci'", vid));
  if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
    Blob title, tail;
    if( wiki_find_title(&filebody, &title, &tail) ){
      style_header("%s", blob_str(&title));
      wiki_convert(&tail, 0, WIKI_BUTTONS);
    }else{
      style_header("Documentation");
      wiki_convert(&filebody, 0, WIKI_BUTTONS);
    }
    style_footer();
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob title = BLOB_INITIALIZER;
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(&filebody, &title, &tail);
    if( blob_size(&title)>0 ){
      style_header("%s", blob_str(&title));
    }else{
      style_header("Documentation");
    }
    blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail));
    style_footer();
  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
    style_header("Documentation");
    @ <blockquote><pre>
    @ %h(blob_str(&filebody))
    @ </pre></blockquote>
    style_footer();
#ifdef FOSSIL_ENABLE_TH1_DOCS
  }else if( db_get_boolean("th1-docs", 0) &&
            fossil_strcmp(zMime, "application/x-th1")==0 ){

    style_header("%h", zName);
    Th_Render(blob_str(&filebody));
    style_footer();
    fossil_free(zHtml);
#endif
  }else{
    cgi_set_content_type(zMime);
    cgi_set_content(&filebody);
  }
  return;

doc_not_found:
  /* Jump here when unable to locate the document */
  db_end_transaction(0);
  style_header("Document Not Found");
  @ <p>No such document: %h(zName)</p>
  style_footer();
  return;
}

/*
** The default logo.
*/
static const unsigned char aLogo[] = {
    71,  73,  70,  56,  55,  97,  62,   0,  71,   0, 244,   0,   0,  85,
   129, 149,  95, 136, 155,  99, 139, 157, 106, 144, 162, 113, 150, 166,
   116, 152, 168, 127, 160, 175, 138, 168, 182, 148, 176, 188, 159, 184,
   195, 170, 192, 202, 180, 199, 208, 184, 202, 210, 191, 207, 215, 201,
   215, 221, 212, 223, 228, 223, 231, 235, 226, 227, 226, 226, 234, 237,
   233, 239, 241, 240, 244, 246, 244, 247, 248, 255, 255, 255,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
     0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  44,   0,   0,
     0,   0,  62,   0,  71,   0,   0,   5, 255,  96, 100, 141, 100, 105,
   158, 168,  37,  41, 132, 192, 164, 112,  44, 207, 102,  99,   0,  56,
    16,  84, 116, 239, 199, 141,  65, 110, 232, 248,  25, 141, 193, 161,
    82, 113, 108, 202,  32,  55, 229, 210,  73,  61,  41, 164,  88, 102,
   181,  10,  41,  96, 179,  91, 106,  35, 240,   5, 135, 143, 137, 242,
    87, 123, 246,  33, 190,  81, 108, 163, 237, 198,  14,  30, 113, 233,
   131,  78, 115,  72,  11, 115,  87, 101,  19, 124,  51,  66,  74,   8,
    19,  16,  67, 100,  74, 133,  50,  15, 101, 135,  56,  11,  74,   6,
   143,  49, 126, 106,  56,   8, 145,  67,   9, 152,  48, 139, 155,   5,
    22,  13,  74, 115, 161,  41, 147, 101,  13, 130,  57, 132, 170,  40,
   167, 155,   0,  94,  57,   3, 178,  48, 183, 181,  57, 160, 186,  40,
    19, 141, 189,   0,  69, 192,  40,  16, 195, 155, 185, 199,  41, 201,
   189, 191, 205, 193, 188, 131, 210,  49, 175,  88, 209, 214,  38,  19,
     3,  11,  19, 111, 127,  60, 219,  39,  55, 204,  19,  11,   6, 100,
     5,  10, 227, 228,  37, 163,   0, 239, 117,  56, 238, 243,  49, 195,
   177, 247,  48, 158,  56, 251,  50, 216, 254, 197,  56, 128, 107, 158,
     2, 125, 171, 114,  92, 218, 246,  96,  66,   3,   4,  50, 134, 176,
   145,   6,  97,  64, 144,  24,  19, 136, 108,  91, 177, 160,   0, 194,
    19, 253,   0, 216, 107, 214, 224, 192, 129,   5,  16,  83, 255, 244,
    43, 213, 195,  24, 159,  27, 169,  64, 230,  88, 208, 227, 129, 182,
    54,   4,  89, 158,  24, 181, 163, 199,   1, 155,  52, 233,   8, 130,
   176,  83,  24, 128, 137,  50,  18,  32,  48,  48, 114,  11, 173, 137,
    19, 110,   4,  64, 105,   1, 194,  30, 140,  68,  15,  24,  24, 224,
    50,  76,  70,   0,  11, 171,  54,  26, 160, 181, 194, 149, 148,  40,
   174, 148, 122,  64, 180, 208, 161,  17, 207, 112, 164,   1, 128,  96,
   148,  78,  18,  21, 194,  33, 229,  51, 247,  65, 133,  97,   5, 250,
    69, 229, 100,  34, 220, 128, 166, 116, 190,  62,   8, 167, 195, 170,
    47, 163,   0, 130,  90, 152,  11, 160, 173, 170,  27, 154,  26,  91,
   232, 151, 171,  18,  14, 162, 253,  98, 170,  18,  70, 171,  64, 219,
    10,  67, 136, 134, 187, 116,  75, 180,  46, 179, 174, 135,   4, 189,
   229, 231,  78,  40,  10,  62, 226, 164, 172,  64, 240, 167, 170,  10,
    18, 124, 188,  10, 107,  65, 193,  94,  11,  93, 171,  28, 248,  17,
   239,  46, 140,  78,  97,  34,  25, 153,  36,  99,  65, 130,   7, 203,
   183, 168,  51,  34, 136,  25, 140,  10,   6,  16,  28, 255, 145, 241,
   230, 140,  10,  66, 178, 167, 112,  48, 192, 128, 129,   9,  31, 141,
    84, 138,  63, 163, 162,   2, 203, 206, 240,  56,  55,  98, 192, 188,
    15, 185,  50, 160,   6,   0, 125,  62,  33, 214, 195,  33,   5,  24,
   184,  25, 231,  14, 201, 245, 144,  23, 126, 104, 228,   0, 145,   2,
    13, 140, 244, 212,  17,  21,  20, 176, 159,  17,  95, 225, 160, 128,
    16,   1,  32, 224, 142,  32, 227, 125,  87,  64,   0,  16,  54, 129,
   205,   2, 141,  76,  53, 130, 103,  37, 166,  64, 144, 107,  78, 196,
     5, 192,   0,  54,  50, 229,   9, 141,  49,  84, 194,  35,  12, 196,
   153,  48, 192, 137,  57,  84,  24,   7,  87, 159, 249, 240, 215, 143,
   105, 241, 118, 149,   9, 139,   4,  64, 203, 141,  35, 140, 129, 131,
    16, 222, 125, 231, 128,   2, 238,  17, 152,  66,   3,   5,  56, 224,
   159, 103,  16,  76,  25,  75,   5,  11, 164, 215,  96,   9,  14,  16,
    36, 225,  15,  11,  40, 144, 192, 156,  41,  10, 178, 199,   3,  66,
    64,  80, 193,   3, 124,  90,  48, 129, 129, 102, 177,  18, 192, 154,
    49,  84, 240, 208,  92,  22, 149,  96,  39,   9,  31,  74,  17,  94,
     3,   8, 177, 199,  72,  59,  85,  76,  25, 216,   8, 139, 194, 197,
   138, 163,  69,  96, 115,   0, 147,  72,  72,  84,  28,  14,  79,  86,
   233, 230,  23, 113,  26, 160, 128,   3,  10,  58, 129, 103,  14, 159,
   214, 163, 146, 117, 238, 213, 154, 128, 151, 109,  84,  64, 217,  13,
    27,  10, 228,  39,   2, 235, 164, 168,  74,   8,   0,  59,
};

/*
** WEBPAGE: logo
**
** Return the logo image.  This image is available to anybody who can see
** the login page.  It is designed for use in the upper left-hand corner
Changes to src/encode.c.
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
    }
    i++;
  }
  i = 0;
  zOut = fossil_malloc( count+1 );
  while( n-->0 && (c = *zIn)!=0 ){
    switch( c ){
      case '<':   
        zOut[i++] = '&';
        zOut[i++] = 'l';
        zOut[i++] = 't';
        zOut[i++] = ';';
        break;
      case '>':   
        zOut[i++] = '&';
        zOut[i++] = 'g';
        zOut[i++] = 't';
        zOut[i++] = ';';
        break;
      case '&':   
        zOut[i++] = '&';
        zOut[i++] = 'a';
        zOut[i++] = 'm';
        zOut[i++] = 'p';
        zOut[i++] = ';';
        break;
      case '"':   
        zOut[i++] = '&';
        zOut[i++] = 'q';
        zOut[i++] = 'u';
        zOut[i++] = 'o';
        zOut[i++] = 't';
        zOut[i++] = ';';
        break;







|





|





|






|







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
    }
    i++;
  }
  i = 0;
  zOut = fossil_malloc( count+1 );
  while( n-->0 && (c = *zIn)!=0 ){
    switch( c ){
      case '<':
        zOut[i++] = '&';
        zOut[i++] = 'l';
        zOut[i++] = 't';
        zOut[i++] = ';';
        break;
      case '>':
        zOut[i++] = '&';
        zOut[i++] = 'g';
        zOut[i++] = 't';
        zOut[i++] = ';';
        break;
      case '&':
        zOut[i++] = '&';
        zOut[i++] = 'a';
        zOut[i++] = 'm';
        zOut[i++] = 'p';
        zOut[i++] = ';';
        break;
      case '"':
        zOut[i++] = '&';
        zOut[i++] = 'q';
        zOut[i++] = 'u';
        zOut[i++] = 'o';
        zOut[i++] = 't';
        zOut[i++] = ';';
        break;
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
}

/*
** Convert the input string into a form that is suitable for use as
** a token in the HTTP protocol.  Spaces are encoded as '+' and special
** characters are encoded as "%HH" where HH is a two-digit hexidecimal
** representation of the character.  The "/" character is not encoded
** by this routine. 
*/
char *urlize(const char *z, int n){
  return EncodeHttp(z, n, 0);
}

/*
** Convert a single HEX digit to an integer







|







179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
}

/*
** Convert the input string into a form that is suitable for use as
** a token in the HTTP protocol.  Spaces are encoded as '+' and special
** characters are encoded as "%HH" where HH is a two-digit hexidecimal
** representation of the character.  The "/" character is not encoded
** by this routine.
*/
char *urlize(const char *z, int n){
  return EncodeHttp(z, n, 0);
}

/*
** Convert a single HEX digit to an integer
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  if( z[j] ) z[j] = 0;
}


/*
** The characters used for HTTP base64 encoding.
*/
static unsigned char zBase[] = 
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/*
** Encode a string using a base-64 encoding.
** The encoding can be reversed using the <b>decode64</b> function.
**
** Space to hold the result comes from malloc().







|







325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  if( z[j] ) z[j] = 0;
}


/*
** The characters used for HTTP base64 encoding.
*/
static unsigned char zBase[] =
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/*
** Encode a string using a base-64 encoding.
** The encoding can be reversed using the <b>decode64</b> function.
**
** Space to hold the result comes from malloc().
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
    z64[n++] = '=';
  }
  z64[n] = 0;
  return z64;
}

/*
** COMMAND: test-encode64 
** Usage: %fossil test-encode64 STRING
*/
void test_encode64_cmd(void){
  char *z;
  int i;
  for(i=2; i<g.argc; i++){
    z = encode64(g.argv[i], -1);







|







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
    z64[n++] = '=';
  }
  z64[n] = 0;
  return z64;
}

/*
** COMMAND: test-encode64
** Usage: %fossil test-encode64 STRING
*/
void test_encode64_cmd(void){
  char *z;
  int i;
  for(i=2; i<g.argc; i++){
    z = encode64(g.argv[i], -1);
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  }
  zData[j] = 0;
  *pnByte = j;
  return zData;
}

/*
** COMMAND: test-decode64 
** Usage: %fossil test-decode64 STRING
*/
void test_decode64_cmd(void){
  char *z;
  int i, n;
  for(i=2; i<g.argc; i++){
    z = decode64(g.argv[i], &n);







|







429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  }
  zData[j] = 0;
  *pnByte = j;
  return zData;
}

/*
** COMMAND: test-decode64
** Usage: %fossil test-decode64 STRING
*/
void test_decode64_cmd(void){
  char *z;
  int i, n;
  for(i=2; i<g.argc; i++){
    z = decode64(g.argv[i], &n);
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
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
**         0123456789abcdef
**
*/

/*
** The array used for encoding
*/                           /* 123456789 12345  */
static const char zEncode[] = "0123456789abcdef"; 

/*
** Encode a N-digit base-256 in base-16.  Return zero on success
** and non-zero if there is an error.
*/
int encode16(const unsigned char *pIn, unsigned char *zOut, int N){
  int i;
  for(i=0; i<N; i++){
    *(zOut++) = zEncode[pIn[i]>>4];
    *(zOut++) = zEncode[pIn[i]&0xf];
  }
  *zOut = 0;
  return 0;
}

/*
** An array for translating single base-16 characters into a value.
** Disallowed input characters have a value of 64.  Upper and lower
** case is the same. 
*/
static const char zDecode[] = {
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
   0,  1,  2,  3,  4,  5,  6,  7,   8,  9, 64, 64, 64, 64, 64, 64, 
  64, 10, 11, 12, 13, 14, 15, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 10, 11, 12, 13, 14, 15, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 
};

/*
** Decode a N-character base-16 number into base-256.  N must be a 
** multiple of 2.  The output buffer must be at least N/2 characters
** in length
*/
int decode16(const unsigned char *zIn, unsigned char *pOut, int N){
  int i, j;
  if( (N&1)!=0 ) return 1;
  for(i=j=0; i<N; i += 2, j++){







|


















|


|
|
|
|




|
|
|
|
|
|
|
|



|







452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
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
**         0123456789abcdef
**
*/

/*
** The array used for encoding
*/                           /* 123456789 12345  */
static const char zEncode[] = "0123456789abcdef";

/*
** Encode a N-digit base-256 in base-16.  Return zero on success
** and non-zero if there is an error.
*/
int encode16(const unsigned char *pIn, unsigned char *zOut, int N){
  int i;
  for(i=0; i<N; i++){
    *(zOut++) = zEncode[pIn[i]>>4];
    *(zOut++) = zEncode[pIn[i]&0xf];
  }
  *zOut = 0;
  return 0;
}

/*
** An array for translating single base-16 characters into a value.
** Disallowed input characters have a value of 64.  Upper and lower
** case is the same.
*/
static const char zDecode[] = {
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
   0,  1,  2,  3,  4,  5,  6,  7,   8,  9, 64, 64, 64, 64, 64, 64,
  64, 10, 11, 12, 13, 14, 15, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 10, 11, 12, 13, 14, 15, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64,
};

/*
** Decode a N-character base-16 number into base-256.  N must be a
** multiple of 2.  The output buffer must be at least N/2 characters
** in length
*/
int decode16(const unsigned char *zIn, unsigned char *pOut, int N){
  int i, j;
  if( (N&1)!=0 ) return 1;
  for(i=j=0; i<N; i += 2, j++){
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
}

/* Randomness used for XOR-ing by the obscure() and unobscure() routines */
static const unsigned char aObscurer[16] = {
    0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86,
    0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85
};
 

/*
** Obscure plain text so that it is not easily readable.
**
** This is used for storing sensitive information (such as passwords) in a
** way that prevents their exposure through idle browsing.  This is not
** encryption.  Anybody who really wants the password can still get it.
**
** The text is XOR-ed with a repeating pattern then converted to hex.
** Space to hold the returned string is obtained from malloc and should
** be freed by the caller.
*/
char *obscure(const char *zIn){
  int n, i;
  unsigned char salt;
  char *zOut;
  
  if( zIn==0 ) return 0;
  n = strlen(zIn);
  zOut = fossil_malloc( n*2+3 );
  sqlite3_randomness(1, &salt);
  zOut[n+1] = (char)salt;
  for(i=0; i<n; i++) zOut[i+n+2] = zIn[i]^aObscurer[i&0x0f]^salt;
  encode16((unsigned char*)&zOut[n+1], (unsigned char*)zOut, n+1);
  return zOut;
}

/*
** Undo the obscuring of text performed by obscure().  Or, if the input is
** not hexadecimal (meaning the input is not the output of obscure()) then
** do the equivalent of strdup().
**
** The result is memory obtained from malloc that should be freed by the caller. 
*/
char *unobscure(const char *zIn){
  int n, i;
  unsigned char salt;
  char *zOut;
  
  if( zIn==0 ) return 0;
  n = strlen(zIn);
  zOut = fossil_malloc( n + 1 );
  if( n<2
    || decode16((unsigned char*)zIn, &salt, 2)
    || decode16((unsigned char*)&zIn[2], (unsigned char*)zOut, n-2)
  ){







|
















|















|





|







543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
}

/* Randomness used for XOR-ing by the obscure() and unobscure() routines */
static const unsigned char aObscurer[16] = {
    0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86,
    0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85
};


/*
** Obscure plain text so that it is not easily readable.
**
** This is used for storing sensitive information (such as passwords) in a
** way that prevents their exposure through idle browsing.  This is not
** encryption.  Anybody who really wants the password can still get it.
**
** The text is XOR-ed with a repeating pattern then converted to hex.
** Space to hold the returned string is obtained from malloc and should
** be freed by the caller.
*/
char *obscure(const char *zIn){
  int n, i;
  unsigned char salt;
  char *zOut;

  if( zIn==0 ) return 0;
  n = strlen(zIn);
  zOut = fossil_malloc( n*2+3 );
  sqlite3_randomness(1, &salt);
  zOut[n+1] = (char)salt;
  for(i=0; i<n; i++) zOut[i+n+2] = zIn[i]^aObscurer[i&0x0f]^salt;
  encode16((unsigned char*)&zOut[n+1], (unsigned char*)zOut, n+1);
  return zOut;
}

/*
** Undo the obscuring of text performed by obscure().  Or, if the input is
** not hexadecimal (meaning the input is not the output of obscure()) then
** do the equivalent of strdup().
**
** The result is memory obtained from malloc that should be freed by the caller.
*/
char *unobscure(const char *zIn){
  int n, i;
  unsigned char salt;
  char *zOut;

  if( zIn==0 ) return 0;
  n = strlen(zIn);
  zOut = fossil_malloc( n + 1 );
  if( n<2
    || decode16((unsigned char*)zIn, &salt, 2)
    || decode16((unsigned char*)&zIn[2], (unsigned char*)zOut, n-2)
  ){
Changes to src/event.c.
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  */
  pEvent = manifest_get(rid, CFTYPE_EVENT, 0);
  if( pEvent==0 ){
    fossil_fatal("Object #%d is not an event", rid);
  }
  blob_init(&fullbody, pEvent->zWiki, -1);
  if( wiki_find_title(&fullbody, &title, &tail) ){
    style_header(blob_str(&title));
  }else{
    style_header("Event %S", zEventId);
    tail = fullbody;
  }
  if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
    style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
                          g.zTop, zEventId);







|







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  */
  pEvent = manifest_get(rid, CFTYPE_EVENT, 0);
  if( pEvent==0 ){
    fossil_fatal("Object #%d is not an event", rid);
  }
  blob_init(&fullbody, pEvent->zWiki, -1);
  if( wiki_find_title(&fullbody, &title, &tail) ){
    style_header("%s", blob_str(&title));
  }else{
    style_header("Event %S", zEventId);
    tail = fullbody;
  }
  if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){
    style_submenu_element("Edit", "Edit", "%s/eventedit?name=%s",
                          g.zTop, zEventId);
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
** Edit an event.  If name is omitted, create a new event.
*/
void eventedit_page(void){
  char *zTag;
  int rid = 0;
  Blob event;
  const char *zEventId;
  char *zHtmlPageName;
  int n;
  const char *z;
  char *zBody = (char*)P("w");
  char *zETime = (char*)P("t");
  const char *zComment = P("c");
  const char *zTags = P("g");
  const char *zClr;







<







201
202
203
204
205
206
207

208
209
210
211
212
213
214
** Edit an event.  If name is omitted, create a new event.
*/
void eventedit_page(void){
  char *zTag;
  int rid = 0;
  Blob event;
  const char *zEventId;

  int n;
  const char *z;
  char *zBody = (char*)P("w");
  char *zETime = (char*)P("t");
  const char *zComment = P("c");
  const char *zTags = P("g");
  const char *zClr;
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  if( P("cancel")!=0 ){
    cgi_redirectf("event?name=%T", zEventId);
    return;
  }
  if( zBody==0 ){
    zBody = mprintf("<i>Event Text</i>");
  }
  zHtmlPageName = mprintf("Edit Event %S", zEventId);
  style_header(zHtmlPageName);
  if( P("preview")!=0 ){
    Blob title, tail, com;
    @ <p><b>Timeline comment preview:</b></p>
    @ <blockquote>
    @ <table border="0">
    if( zClr && zClr[0] ){
      @ <tr><td style="background-color: %h(zClr);">







|
<







361
362
363
364
365
366
367
368

369
370
371
372
373
374
375
  if( P("cancel")!=0 ){
    cgi_redirectf("event?name=%T", zEventId);
    return;
  }
  if( zBody==0 ){
    zBody = mprintf("<i>Event Text</i>");
  }
  style_header("Edit Event %S", zEventId);

  if( P("preview")!=0 ){
    Blob title, tail, com;
    @ <p><b>Timeline comment preview:</b></p>
    @ <blockquote>
    @ <table border="0">
    if( zClr && zClr[0] ){
      @ <tr><td style="background-color: %h(zClr);">
Changes to src/finfo.c.
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
  if( baseCheckin ) firstChngOnly = 1;
  if( !firstChngOnly ) url_add_parameter(&url, "fco", "0");

  zPrevDate[0] = 0;
  zFilename = PD("name","");
  url_add_parameter(&url, "name", zFilename);
  blob_zero(&sql);
  blob_appendf(&sql,
    "SELECT"
    " datetime(event.mtime%s),"                      /* 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 */
    " (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* 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)," /* Tags */
    " mlink.mid,"                                    /* check-in ID */
    " mlink.pfnid",                                  /* Previous filename */
    timeline_utc(), TAG_BRANCH
  );
  if( firstChngOnly ){
#if 0
    blob_appendf(&sql, ", min(event.mtime)");
#else
    blob_appendf(&sql,
        ", min(CASE (SELECT value FROM tagxref"
                    " WHERE tagtype>0 AND tagid=%d"
                    "   AND tagxref.rid=mlink.mid)"
             " WHEN 'trunk' THEN event.mtime-10000 ELSE event.mtime END)",
    TAG_BRANCH);
#endif
  }
  blob_appendf(&sql,
    "  FROM mlink, event"
    " WHERE mlink.fnid IN (SELECT fnid FROM filename WHERE name=%Q)"
    "   AND event.objid=mlink.mid",
    zFilename
  );
  if( baseCheckin ){
    compute_direct_ancestors(baseCheckin, 10000000);
    blob_appendf(&sql,"  AND mlink.mid IN (SELECT rid FROM ancestor)");
  }
  if( (zA = P("a"))!=0 ){
    blob_appendf(&sql, " AND event.mtime>=julianday('%q')", zA);
    url_add_parameter(&url, "a", zA);
  }
  if( (zB = P("b"))!=0 ){
    blob_appendf(&sql, " AND event.mtime<=julianday('%q')", zB);
    url_add_parameter(&url, "b", zB);
  }
  if( firstChngOnly ){
    blob_appendf(&sql, " GROUP BY mlink.fid");
  }
  blob_appendf(&sql," ORDER BY event.mtime DESC /*sort*/");
  if( (n = atoi(PD("n","0")))>0 ){
    blob_appendf(&sql, " LIMIT %d", n);
    url_add_parameter(&url, "n", P("n"));
  }
  if( baseCheckin==0 ){
    if( firstChngOnly ){
      style_submenu_element("Full", "Show all changes","%s",
                            url_render(&url, "fco", "0", 0, 0));
    }else{
      style_submenu_element("Simplified",
                            "Show only first use of a change","%s",
                            url_render(&url, "fco", 0, 0, 0));
    }
  }
  db_prepare(&q, blob_str(&sql));
  if( P("showsql")!=0 ){
    @ <p>SQL: %h(blob_str(&sql))</p>
  }
  blob_reset(&sql);
  blob_zero(&title);
  if( baseCheckin ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);







|


















|

|







|







|


|



|



|

|

|












|







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
  if( baseCheckin ) firstChngOnly = 1;
  if( !firstChngOnly ) url_add_parameter(&url, "fco", "0");

  zPrevDate[0] = 0;
  zFilename = PD("name","");
  url_add_parameter(&url, "name", zFilename);
  blob_zero(&sql);
  blob_append_sql(&sql,
    "SELECT"
    " datetime(event.mtime%s),"                      /* 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 */
    " (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* 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)," /* Tags */
    " mlink.mid,"                                    /* check-in ID */
    " mlink.pfnid",                                  /* Previous filename */
    timeline_utc(), TAG_BRANCH
  );
  if( firstChngOnly ){
#if 0
    blob_append_sql(&sql, ", min(event.mtime)");
#else
    blob_append_sql(&sql,
        ", min(CASE (SELECT value FROM tagxref"
                    " WHERE tagtype>0 AND tagid=%d"
                    "   AND tagxref.rid=mlink.mid)"
             " WHEN 'trunk' THEN event.mtime-10000 ELSE event.mtime END)",
    TAG_BRANCH);
#endif
  }
  blob_append_sql(&sql,
    "  FROM mlink, event"
    " WHERE mlink.fnid IN (SELECT fnid FROM filename WHERE name=%Q)"
    "   AND event.objid=mlink.mid",
    zFilename
  );
  if( baseCheckin ){
    compute_direct_ancestors(baseCheckin, 10000000);
    blob_append_sql(&sql,"  AND mlink.mid IN (SELECT rid FROM ancestor)");
  }
  if( (zA = P("a"))!=0 ){
    blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zA);
    url_add_parameter(&url, "a", zA);
  }
  if( (zB = P("b"))!=0 ){
    blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zB);
    url_add_parameter(&url, "b", zB);
  }
  if( firstChngOnly ){
    blob_append_sql(&sql, " GROUP BY mlink.fid");
  }
  blob_append_sql(&sql," ORDER BY event.mtime DESC /*sort*/");
  if( (n = atoi(PD("n","0")))>0 ){
    blob_append_sql(&sql, " LIMIT %d", n);
    url_add_parameter(&url, "n", P("n"));
  }
  if( baseCheckin==0 ){
    if( firstChngOnly ){
      style_submenu_element("Full", "Show all changes","%s",
                            url_render(&url, "fco", "0", 0, 0));
    }else{
      style_submenu_element("Simplified",
                            "Show only first use of a change","%s",
                            url_render(&url, "fco", 0, 0, 0));
    }
  }
  db_prepare(&q, "%s", blob_sql_text(&sql));
  if( P("showsql")!=0 ){
    @ <p>SQL: %h(blob_str(&sql))</p>
  }
  blob_reset(&sql);
  blob_zero(&title);
  if( baseCheckin ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin);
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
        fossil_free(zNewName);
      }else{
        @ <b>Deleted</b> by check-in
      }
    }
    hyperlink_to_uuid(zCkin);
    @ %w(zCom) (user:
    hyperlink_to_user(zUser, zDate, "");
    @ branch: %h(zBr))
    if( g.perm.Hyperlink && zUuid ){
      const char *z = zFilename;
      @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))







|







471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
        @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in
        fossil_free(zNewName);
      }else{
        @ <b>Deleted</b> by check-in
      }
    }
    hyperlink_to_uuid(zCkin);
    @ %W(zCom) (user:
    hyperlink_to_user(zUser, zDate, "");
    @ branch: %h(zBr))
    if( g.perm.Hyperlink && zUuid ){
      const char *z = zFilename;
      @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
Changes to src/http.c.
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  char *zUser;
  char *zPw;
  char *zPrompt;
  char *zHttpAuth = 0;
  if( !isatty(fileno(stdin)) ) return 0;
  zPrompt = mprintf("\n%s authorization required by\n%s\n",
    g.url.isHttps==1 ? "Encrypted HTTPS" : "Unencrypted HTTP", g.url.canonical);
  fossil_print(zPrompt);
  free(zPrompt);
  if ( g.url.user && g.url.passwd && use_fossil_creds_for_httpauth_prompt() ){
    zHttpAuth = mprintf("%s:%s", g.url.user, g.url.passwd);
  }else{
    prompt_user("Basic Authorization user: ", &x);
    zUser = mprintf("%b", &x);
    zPrompt = mprintf("HTTP password for %b: ", &x);







|







165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  char *zUser;
  char *zPw;
  char *zPrompt;
  char *zHttpAuth = 0;
  if( !isatty(fileno(stdin)) ) return 0;
  zPrompt = mprintf("\n%s authorization required by\n%s\n",
    g.url.isHttps==1 ? "Encrypted HTTPS" : "Unencrypted HTTP", g.url.canonical);
  fossil_print("%s", zPrompt);
  free(zPrompt);
  if ( g.url.user && g.url.passwd && use_fossil_creds_for_httpauth_prompt() ){
    zHttpAuth = mprintf("%s:%s", g.url.user, g.url.passwd);
  }else{
    prompt_user("Basic Authorization user: ", &x);
    zUser = mprintf("%b", &x);
    zPrompt = mprintf("HTTP password for %b: ", &x);
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  int iHttpVersion;     /* Which version of HTTP protocol server uses */
  char *zLine;          /* A single line of the reply header */
  int i;                /* Loop counter */
  int isError = 0;      /* True if the reply is an error message */
  int isCompressed = 1; /* True if the reply is compressed */

  if( transport_open(&g.url) ){
    fossil_warning(transport_errmsg(&g.url));
    return 1;
  }

  /* Construct the login card and prepare the complete payload */
  blob_zero(&login);
  if( useLogin ) http_build_login_card(pSend, &login);
  if( g.fHttpTrace ){







|







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  int iHttpVersion;     /* Which version of HTTP protocol server uses */
  char *zLine;          /* A single line of the reply header */
  int i;                /* Loop counter */
  int isError = 0;      /* True if the reply is an error message */
  int isCompressed = 1; /* True if the reply is compressed */

  if( transport_open(&g.url) ){
    fossil_warning("%s", transport_errmsg(&g.url));
    return 1;
  }

  /* Construct the login card and prepare the complete payload */
  blob_zero(&login);
  if( useLogin ) http_build_login_card(pSend, &login);
  if( g.fHttpTrace ){
Changes to src/info.c.
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     timeline_utc(), timeline_utc(), rid, rid
  );
  sideBySide = !is_false(PD("sbs","1"));
  if( db_step(&q1)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q1, 0);
    char *zTitle = mprintf("Check-in [%S]", zUuid);
    char *zEUser, *zEComment;
    const char *zUser;
    const char *zComment;
    const char *zDate;
    const char *zOrigDate;

    style_header(zTitle);
    login_anonymous_available();
    free(zTitle);
    zEUser = db_text(0,
                   "SELECT value FROM tagxref"
                   " WHERE tagid=%d AND rid=%d AND tagtype>0",
                    TAG_USER, rid);
    zEComment = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_COMMENT, rid);







<






|

<







533
534
535
536
537
538
539

540
541
542
543
544
545
546
547

548
549
550
551
552
553
554
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     timeline_utc(), timeline_utc(), rid, rid
  );
  sideBySide = !is_false(PD("sbs","1"));
  if( db_step(&q1)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q1, 0);

    char *zEUser, *zEComment;
    const char *zUser;
    const char *zComment;
    const char *zDate;
    const char *zOrigDate;

    style_header("Check-in [%s]", zUuid);
    login_anonymous_available();

    zEUser = db_text(0,
                   "SELECT value FROM tagxref"
                   " WHERE tagid=%d AND rid=%d AND tagtype>0",
                    TAG_USER, rid);
    zEComment = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_COMMENT, rid);
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594

  rid = name_to_rid_www("name");
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  if( rid==0 ) fossil_redirect_home();
  if( g.perm.Admin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#delshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }







|







1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592

  rid = name_to_rid_www("name");
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  if( rid==0 ) fossil_redirect_home();
  if( g.perm.Admin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#delshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
  }

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  if( rid==0 ) fossil_redirect_home();
  if( g.perm.Admin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#accshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
  if( descOnly || P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  style_header(descOnly ? "Artifact Description" : "Artifact Content");
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( g.perm.Setup ){
    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
  }else{
    @ <h2>Artifact %s(zUuid):</h2>
  }
  blob_zero(&downloadName);







|








|







1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
  }

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  if( rid==0 ) fossil_redirect_home();
  if( g.perm.Admin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#accshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
  if( descOnly || P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  style_header("%s", descOnly ? "Artifact Description" : "Artifact Content");
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( g.perm.Setup ){
    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
  }else{
    @ <h2>Artifact %s(zUuid):</h2>
  }
  blob_zero(&downloadName);
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
  char *zTktTitle;
  login_check_credentials();
  if( !g.perm.RdTkt ){ login_needed(); return; }
  rid = name_to_rid_www("name");
  if( rid==0 ){ fossil_redirect_home(); }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( g.perm.Admin ){
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#accshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }







|







1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
  char *zTktTitle;
  login_check_credentials();
  if( !g.perm.RdTkt ){ login_needed(); return; }
  rid = name_to_rid_www("name");
  if( rid==0 ){ fossil_redirect_home(); }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( g.perm.Admin ){
    if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#accshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
    return;
  }else if( rc==2 ){
    cgi_set_parameter("src","info");
    ambiguous_page();
    return;
  }
  zName = blob_str(&uuid);
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid='%s'", zName);
  if( rid==0 ){
    style_header("Broken Link");
    @ <p>No such object: %h(zName)</p>
    style_footer();
    return;
  }
  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){







|







2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
    return;
  }else if( rc==2 ){
    cgi_set_parameter("src","info");
    ambiguous_page();
    return;
  }
  zName = blob_str(&uuid);
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
  if( rid==0 ){
    style_header("Broken Link");
    @ <p>No such object: %h(zName)</p>
    style_footer();
    return;
  }
  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
Changes to src/json.c.
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
** to simplify the trivial use-cases (which don't need a Blob).
*/
cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt,
                                      char resetBlob){
  Stmt q = empty_Stmt;
  cson_value * pay = NULL;
  assert( blob_size(pSql) > 0 );
  db_prepare(&q, "%s", blob_str(pSql));
  if(resetBlob){
    blob_reset(pSql);
  }
  pay = json_stmt_to_array_of_obj(&q, pTgt);
  db_finalize(&q);
  return pay;








|







1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
** to simplify the trivial use-cases (which don't need a Blob).
*/
cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt,
                                      char resetBlob){
  Stmt q = empty_Stmt;
  cson_value * pay = NULL;
  assert( blob_size(pSql) > 0 );
  db_prepare(&q, "%s", blob_str(pSql) /*safe-for-%s*/);
  if(resetBlob){
    blob_reset(pSql);
  }
  pay = json_stmt_to_array_of_obj(&q, pTgt);
  db_finalize(&q);
  return pay;

1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
  jv2 = cson_value_new_object();
  jo2 = cson_value_get_object(jv2);
  cson_object_set(jo, "sqlite", jv2);
  sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
                   sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion());
  SETBUF(jo2, "version");
  zDb = db_name("repository");
  cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA %s.page_count", zDb)));
  cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA %s.page_size", zDb)));
  cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA %s.freelist_count", zDb)));
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.encoding", zDb));
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.journal_mode", zDb));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}










|
|
|
|

|







1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
  jv2 = cson_value_new_object();
  jo2 = cson_value_get_object(jv2);
  cson_object_set(jo, "sqlite", jv2);
  sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
                   sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion());
  SETBUF(jo2, "version");
  zDb = db_name("repository");
  cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".page_count", zDb)));
  cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".page_size", zDb)));
  cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".freelist_count", zDb)));
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA \"%w\".encoding", zDb));
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA \"%w\".journal_mode", zDb));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}



2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
                                   Blob * pOut, int filterByMode){
  int i = 0;
  for( ; zPages->name; ++zPages, ++i ){
    if(filterByMode){
      if(g.isHTTP && zPages->runMode < 0) continue;
      else if(zPages->runMode > 0) continue;
    }
    blob_appendf(pOut, zPages->name, -1);
    if((zPages+1)->name){
      blob_append(pOut, ", ",2);
    }
  }
  return i;
}








|







2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
                                   Blob * pOut, int filterByMode){
  int i = 0;
  for( ; zPages->name; ++zPages, ++i ){
    if(filterByMode){
      if(g.isHTTP && zPages->runMode < 0) continue;
      else if(zPages->runMode > 0) continue;
    }
    blob_append(pOut, zPages->name, -1);
    if((zPages+1)->name){
      blob_append(pOut, ", ",2);
    }
  }
  return i;
}

Changes to src/json_config.c.
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
    const struct JsonConfigProperty * prop = &JsonConfigProperties[0];
    blob_append(&sql," OR name IN (",-1);
    for( i = 0; prop->name; ++prop ){
      if(prop->groupMask & confMask){
        if( i++ ){
          blob_append(&sql,",",1);
        }
        blob_appendf(&sql, "%Q", prop->name);
      }
    }
    blob_append(&sql,") ", -1);
  }


  if( optSkinBackups ){
    blob_append(&sql, " OR name GLOB 'skin:*'", -1);
  }
  blob_append(&sql," ORDER BY name", -1);
  db_prepare(&q, blob_str(&sql));
  blob_reset(&sql);
  pay = cson_new_object();
  while( (SQLITE_ROW==db_step(&q)) ){
    cson_object_set(pay,
                    db_column_text(&q,0),
                    json_new_string(db_column_text(&q,1)));
  }







|










|







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
    const struct JsonConfigProperty * prop = &JsonConfigProperties[0];
    blob_append(&sql," OR name IN (",-1);
    for( i = 0; prop->name; ++prop ){
      if(prop->groupMask & confMask){
        if( i++ ){
          blob_append(&sql,",",1);
        }
        blob_append_sql(&sql, "%Q", prop->name);
      }
    }
    blob_append(&sql,") ", -1);
  }


  if( optSkinBackups ){
    blob_append(&sql, " OR name GLOB 'skin:*'", -1);
  }
  blob_append(&sql," ORDER BY name", -1);
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  pay = cson_new_object();
  while( (SQLITE_ROW==db_step(&q)) ){
    cson_object_set(pay,
                    db_column_text(&q,0),
                    json_new_string(db_column_text(&q,1)));
  }
Changes to src/json_finfo.c.
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  }
  
  zBefore = json_find_option_cstr("before",NULL,"b");
  zAfter = json_find_option_cstr("after",NULL,"a");
  limit = json_find_option_int("limit",NULL,"n", -1);
  zCheckin = json_find_option_cstr("checkin",NULL,"ci");

  blob_appendf(&sql, 
/*0*/   "SELECT b.uuid,"
/*1*/   "   ci.uuid,"
/*2*/   "   (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* Current file uuid */
/*3*/   "   cast(strftime('%%s',event.mtime) AS INTEGER),"
/*4*/   "   coalesce(event.euser, event.user),"
/*5*/   "   coalesce(event.ecomment, event.comment),"
/*6*/   " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */







|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  }
  
  zBefore = json_find_option_cstr("before",NULL,"b");
  zAfter = json_find_option_cstr("after",NULL,"a");
  limit = json_find_option_int("limit",NULL,"n", -1);
  zCheckin = json_find_option_cstr("checkin",NULL,"ci");

  blob_append_sql(&sql, 
/*0*/   "SELECT b.uuid,"
/*1*/   "   ci.uuid,"
/*2*/   "   (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* Current file uuid */
/*3*/   "   cast(strftime('%%s',event.mtime) AS INTEGER),"
/*4*/   "   coalesce(event.euser, event.user),"
/*5*/   "   coalesce(event.ecomment, event.comment),"
/*6*/   " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */
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
    /*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,
                   "Checkin UUID %s.", (rc<0) ? "is ambiguous" : "not found");
      blob_reset(&sql);
      return NULL;
    }
    blob_appendf(&sql, " AND ci.uuid='%q'", zU);
    free(zU);
  }else{
    if( zAfter && *zAfter ){
      blob_appendf(&sql, " AND event.mtime>=julianday('%q')", zAfter);
      sort = 1;
    }else if( zBefore && *zBefore ){
      blob_appendf(&sql, " AND event.mtime<=julianday('%q')", zBefore);
    }
  }

  blob_appendf(&sql," ORDER BY event.mtime %s /*sort*/", (sort>0?"ASC":"DESC"));
  /*printf("SQL=\n%s\n",blob_str(&sql));*/
  db_prepare(&q, "%s", blob_str(&sql)/*extra %s to avoid double-expanding
                                       SQL escapes*/);
  blob_reset(&sql);

  pay = cson_new_object();
  cson_object_set(pay, "name", json_new_string(zFilename));
  if( limit > 0 ){
    cson_object_set(pay, "limit", json_new_int(limit));
  }







|



|


|



|

|
<







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
    /*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,
                   "Checkin 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 ){
      blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zAfter);
      sort = 1;
    }else if( zBefore && *zBefore ){
      blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zBefore);
    }
  }

  blob_append_sql(&sql," ORDER BY event.mtime %s /*sort*/", (sort>0?"ASC":"DESC"));
  /*printf("SQL=\n%s\n",blob_str(&sql));*/
  db_prepare(&q, "%s", blob_sql_text(&sql));

  blob_reset(&sql);

  pay = cson_new_object();
  cson_object_set(pay, "name", json_new_string(zFilename));
  if( limit > 0 ){
    cson_object_set(pay, "limit", json_new_int(limit));
  }
Changes to src/json_query.c.
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "'sql' (-s) argument is missing.");
    return NULL;
  }

  zFmt = json_find_option_cstr2("format",NULL,"f",3);
  if(!zFmt) zFmt = "o";
  db_prepare(&q,"%s", zSql);
  if( 0 == sqlite3_column_count( q.pStmt ) ){
      json_set_err(FSL_JSON_E_USAGE,
                   "Input query has no result columns. "
                   "Only SELECT-like queries are supported.");
      db_finalize(&q);
      return NULL;
  }







|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "'sql' (-s) argument is missing.");
    return NULL;
  }

  zFmt = json_find_option_cstr2("format",NULL,"f",3);
  if(!zFmt) zFmt = "o";
  db_prepare(&q,"%s", zSql/*safe-for-%s*/);
  if( 0 == sqlite3_column_count( q.pStmt ) ){
      json_set_err(FSL_JSON_E_USAGE,
                   "Input query has no result columns. "
                   "Only SELECT-like queries are supported.");
      db_finalize(&q);
      return NULL;
  }
Changes to src/json_report.c.
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  limit = json_find_option_int("limit",NULL,"n",-1);

  
  /* Copy over report's SQL...*/
  blob_append(&sql, db_column_text(&q,0), -1);
  zTitle = mprintf("%s", db_column_text(&q,1));
  db_finalize(&q);
  db_prepare(&q, "%s", blob_str(&sql));

  /** Build the response... */
  pay = cson_new_object();

  cson_object_set(pay, "report", json_new_int(nReport));
  cson_object_set(pay, "title", json_new_string(zTitle));
  if(limit>0){







|







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  limit = json_find_option_int("limit",NULL,"n",-1);

  
  /* Copy over report's SQL...*/
  blob_append(&sql, db_column_text(&q,0), -1);
  zTitle = mprintf("%s", db_column_text(&q,1));
  db_finalize(&q);
  db_prepare(&q, "%s", blob_sql_text(&sql));

  /** Build the response... */
  pay = cson_new_object();

  cson_object_set(pay, "report", json_new_int(nReport));
  cson_object_set(pay, "title", json_new_string(zTitle));
  if(limit>0){
Changes to src/json_tag.c.
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
               "  AND event.type GLOB '%q'"
               "  AND blob.rid IN ("
               "    SELECT rid FROM tagxref"
               "      WHERE tagtype>0 AND tagid=%d"
               "  )"
               " ORDER BY event.mtime DESC"
               "%s LIMIT %d",
               zSqlBase, zType, tagid,
               (limit>0)?"":"--", limit
               );
    listV = json_stmt_to_array_of_obj(&q, NULL);
    db_finalize(&q);
  }

  if(!listV) {







|







299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
               "  AND event.type GLOB '%q'"
               "  AND blob.rid IN ("
               "    SELECT rid FROM tagxref"
               "      WHERE tagtype>0 AND tagid=%d"
               "  )"
               " ORDER BY event.mtime DESC"
               "%s LIMIT %d",
               zSqlBase /*safe-for-%s*/, zType, tagid,
               (limit>0)?"":"--", limit
               );
    listV = json_stmt_to_array_of_obj(&q, NULL);
    db_finalize(&q);
  }

  if(!listV) {
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
                -1
                );
    if(!fTicket){
      blob_append(&sql, " AND tagname NOT GLOB('tkt-*') ", -1);
    }
    blob_append(&sql,
                " ORDER BY tagname", -1);
    db_prepare(&q, blob_buffer(&sql));
    blob_reset(&sql);
    cson_object_set(pay, "includeTickets", cson_value_new_bool(fTicket) );
    while( SQLITE_ROW == db_step(&q) ){
      const char *zName = db_column_text(&q, 0);
      if(NULL==arV){
        arV = cson_value_new_array();
        ar = cson_value_get_array(arV);







|







440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
                -1
                );
    if(!fTicket){
      blob_append(&sql, " AND tagname NOT GLOB('tkt-*') ", -1);
    }
    blob_append(&sql,
                " ORDER BY tagname", -1);
    db_prepare(&q, "%s", blob_sql_text(&sql));
    blob_reset(&sql);
    cson_object_set(pay, "includeTickets", cson_value_new_bool(fTicket) );
    while( SQLITE_ROW == db_step(&q) ){
      const char *zName = db_column_text(&q, 0);
      if(NULL==arV){
        arV = cson_value_new_array();
        ar = cson_value_get_array(arV);
Changes to src/json_timeline.c.
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    @   bgColor TEXT,
    @   eventType TEXT,
    @   tags TEXT,
    @   tagId INTEGER,
    @   brief TEXT
    @ )
  ;
  db_multi_exec(zSql);
}

/*
** Return a pointer to a constant string that forms the basis
** for a timeline query for the JSON interface.
*/
char const * json_timeline_query(void){







|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    @   bgColor TEXT,
    @   eventType TEXT,
    @   tags TEXT,
    @   tagId INTEGER,
    @   brief TEXT
    @ )
  ;
  db_multi_exec("%s", zSql /*safe-for-%s*/);
}

/*
** Return a pointer to a constant string that forms the basis
** for a timeline query for the JSON interface.
*/
char const * json_timeline_query(void){
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
              "  coalesce(euser, user) as user,"
              "  blob.rid IN leaf as isLeaf,"
              "  bgcolor as bgColor"
              " FROM event JOIN blob"
              " WHERE blob.rid=event.objid",
              -1);

  blob_appendf(&sql,
               " AND event.type='ci'"
               " AND blob.rid IN (SELECT rid FROM tagxref"
               "  WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
               " ORDER BY event.mtime DESC",
               TAG_BRANCH);
  limit = json_timeline_limit(20);
  if(limit>0){
    blob_appendf(&sql," LIMIT %d ",limit);
  }
  db_prepare(&q,"%s", blob_str(&sql));
  blob_reset(&sql);
  pay = json_stmt_to_array_of_obj(&q, NULL);
  db_finalize(&q);
  assert(NULL != pay);
  if(pay){
    /* get the array-form tags of each record. */
    cson_string * tags = cson_new_string("tags",4);







|







|

|







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
              "  coalesce(euser, user) as user,"
              "  blob.rid IN leaf as isLeaf,"
              "  bgcolor as bgColor"
              " FROM event JOIN blob"
              " WHERE blob.rid=event.objid",
              -1);

  blob_append_sql(&sql,
               " AND event.type='ci'"
               " AND blob.rid IN (SELECT rid FROM tagxref"
               "  WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
               " ORDER BY event.mtime DESC",
               TAG_BRANCH);
  limit = json_timeline_limit(20);
  if(limit>0){
    blob_append_sql(&sql," LIMIT %d ",limit);
  }
  db_prepare(&q,"%s", blob_sql_text(&sql));
  blob_reset(&sql);
  pay = json_stmt_to_array_of_obj(&q, NULL);
  db_finalize(&q);
  assert(NULL != pay);
  if(pay){
    /* get the array-form tags of each record. */
    cson_string * tags = cson_new_string("tags",4);
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
  } (void)0

#if 0
  /* only for testing! */
  tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
  SET("timelineSql");
#endif
  db_multi_exec(blob_buffer(&sql));
  blob_reset(&sql);
  db_prepare(&q, "SELECT "
             " rid AS rid"
             " FROM json_timeline"
             " ORDER BY rowid");
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);







|







480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
  } (void)0

#if 0
  /* only for testing! */
  tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
  SET("timelineSql");
#endif
  db_multi_exec("%s", blob_buffer(&sql)/*safe-for-%s*/);
  blob_reset(&sql);
  db_prepare(&q, "SELECT "
             " rid AS rid"
             " FROM json_timeline"
             " ORDER BY rowid");
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
  }

#if 0
  /* only for testing! */
  tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
  SET("timelineSql");
#endif
  db_multi_exec(blob_buffer(&sql));
  blob_reset(&sql);
  db_prepare(&q, "SELECT"
             " uuid AS uuid,"
             " mtime AS timestamp,"
#if 0
             " timestampString AS timestampString,"
#endif
             " comment AS comment, "
             " user AS user,"
             " eventType AS eventType"
#if 0
             /* can wiki pages have tags? */
             " tags AS tags," /*FIXME: split this into
                                a JSON array*/
             " tagId AS tagId,"
#endif
             " FROM json_timeline"
             " ORDER BY rowid",
             -1);
  list = cson_new_array();
  json_stmt_to_array_of_obj(&q, list);
  cson_object_set(pay, "timeline", cson_array_value(list));
  goto ok;
  error:
  assert( 0 != g.json.resultCode );
  cson_value_free(payV);







|

















|
<







545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570

571
572
573
574
575
576
577
  }

#if 0
  /* only for testing! */
  tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)));
  SET("timelineSql");
#endif
  db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
  blob_reset(&sql);
  db_prepare(&q, "SELECT"
             " uuid AS uuid,"
             " mtime AS timestamp,"
#if 0
             " timestampString AS timestampString,"
#endif
             " comment AS comment, "
             " user AS user,"
             " eventType AS eventType"
#if 0
             /* can wiki pages have tags? */
             " tags AS tags," /*FIXME: split this into
                                a JSON array*/
             " tagId AS tagId,"
#endif
             " FROM json_timeline"
             " ORDER BY rowid");

  list = cson_new_array();
  json_stmt_to_array_of_obj(&q, list);
  cson_object_set(pay, "timeline", cson_array_value(list));
  goto ok;
  error:
  assert( 0 != g.json.resultCode );
  cson_value_free(payV);
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "t", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }

  db_multi_exec(blob_buffer(&sql));
#define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \
    json_set_err((cson_rc.AllocError==check)        \
                 ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN,      \
                 "Object property insertion failed."); \
    goto error;\
  } (void)0








|







604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "t", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }

  db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
#define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \
    json_set_err((cson_rc.AllocError==check)        \
                 ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN,      \
                 "Object property insertion failed."); \
    goto error;\
  } (void)0

636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
             " timestampString AS timestampString,"
#endif
             " user AS user,"
             " eventType AS eventType,"
             " comment AS comment,"
             " brief AS briefComment"
             " FROM json_timeline"
             " ORDER BY rowid",
             -1);
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  tmp = listV;
  SET("timeline");
  while( (SQLITE_ROW == db_step(&q) )){
    /* convert each row into a JSON object...*/
    int rc;







|
<







635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
             " timestampString AS timestampString,"
#endif
             " user AS user,"
             " eventType AS eventType,"
             " comment AS comment,"
             " brief AS briefComment"
             " FROM json_timeline"
             " ORDER BY rowid");

  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  tmp = listV;
  SET("timeline");
  while( (SQLITE_ROW == db_step(&q) )){
    /* convert each row into a JSON object...*/
    int rc;
Changes to src/json_user.c.
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
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
      }
      forceLogout = cson_value_true()
        /* reminders: 1) does not allocate.
           2) we do this because changing a name
           invalidates any login token because the old name
           is part of the token hash.
        */;
      blob_appendf(&sql, ", login=%Q", zNameNew);
      ++gotFields;
    }
  }

  if( zCap && *zCap ){
    if(!g.perm.Admin || !g.perm.Setup){
      /* we "could" arguably silently ignore cap in this case. */
      json_set_err(FSL_JSON_E_DENIED,
                   "Changing capabilities requires 'a' or 's' privileges.");
      goto error;
    }
    blob_appendf(&sql, ", cap=%Q", zCap);
    ++gotFields;
  }

  if( zPW && *zPW ){
    if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){
      json_set_err( FSL_JSON_E_DENIED,
                    "Password change requires 'a', 's', "
                    "or 'p' permissions.");
      goto error;
    }else{
#define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */
#if !TRY_LOGIN_GROUP
      char * zPWHash = NULL;
      ++gotFields;
      zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL);
      blob_appendf(&sql, ", pw=%Q", zPWHash);
      free(zPWHash);
#else
      ++gotFields;
      blob_appendf(&sql, ", pw=coalesce(shared_secret(%Q,%Q,"
                   "(SELECT value FROM config WHERE name='project-code')))",
                   zPW, zNameNew ? zNameNew : zName);
      /* shared_secret() func is undefined? */
#endif
    }
  }

  if( zInfo ){
    blob_appendf(&sql, ", info=%Q", zInfo);
    ++gotFields;
  }

  if((g.perm.Admin || g.perm.Setup)
     && forceLogout && cson_value_get_bool(forceLogout)){
    blob_append(&sql, ", cookie=NULL, cexpire=NULL", -1);
    ++gotFields;
  }
  
  if(!gotFields){
    json_set_err( FSL_JSON_E_MISSING_ARGS,
                  "Required user data are missing.");
    goto error;
  }
  assert(uid>0);
#if !TRY_LOGIN_GROUP
  blob_appendf(&sql, " WHERE uid=%d", uid);
#else /* need name for login group support :/ */
  blob_appendf(&sql, " WHERE login=%Q", zName);
#endif
#if 0
  puts(blob_str(&sql));
  cson_output_FILE( cson_object_value(pUser), stdout, NULL );
#endif
  db_prepare(&q, "%s", blob_str(&sql));
  db_exec(&q);
  db_finalize(&q);
#if TRY_LOGIN_GROUP
  if( zPW || cson_value_get_bool(forceLogout) ){
    Blob groupSql = empty_blob;
    char * zErr = NULL;
    blob_appendf(&groupSql,
      "INSERT INTO user(login)"
      "  SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);",
      zName, zName
    );
    blob_append(&groupSql, blob_str(&sql), blob_size(&sql));
    login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr);
    blob_reset(&groupSql);







|











|















|



|








|
















|

|





|






|







284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
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
      }
      forceLogout = cson_value_true()
        /* reminders: 1) does not allocate.
           2) we do this because changing a name
           invalidates any login token because the old name
           is part of the token hash.
        */;
      blob_append_sql(&sql, ", login=%Q", zNameNew);
      ++gotFields;
    }
  }

  if( zCap && *zCap ){
    if(!g.perm.Admin || !g.perm.Setup){
      /* we "could" arguably silently ignore cap in this case. */
      json_set_err(FSL_JSON_E_DENIED,
                   "Changing capabilities requires 'a' or 's' privileges.");
      goto error;
    }
    blob_append_sql(&sql, ", cap=%Q", zCap);
    ++gotFields;
  }

  if( zPW && *zPW ){
    if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){
      json_set_err( FSL_JSON_E_DENIED,
                    "Password change requires 'a', 's', "
                    "or 'p' permissions.");
      goto error;
    }else{
#define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */
#if !TRY_LOGIN_GROUP
      char * zPWHash = NULL;
      ++gotFields;
      zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL);
      blob_append_sql(&sql, ", pw=%Q", zPWHash);
      free(zPWHash);
#else
      ++gotFields;
      blob_append_sql(&sql, ", pw=coalesce(shared_secret(%Q,%Q,"
                   "(SELECT value FROM config WHERE name='project-code')))",
                   zPW, zNameNew ? zNameNew : zName);
      /* shared_secret() func is undefined? */
#endif
    }
  }

  if( zInfo ){
    blob_append_sql(&sql, ", info=%Q", zInfo);
    ++gotFields;
  }

  if((g.perm.Admin || g.perm.Setup)
     && forceLogout && cson_value_get_bool(forceLogout)){
    blob_append(&sql, ", cookie=NULL, cexpire=NULL", -1);
    ++gotFields;
  }
  
  if(!gotFields){
    json_set_err( FSL_JSON_E_MISSING_ARGS,
                  "Required user data are missing.");
    goto error;
  }
  assert(uid>0);
#if !TRY_LOGIN_GROUP
  blob_append_sql(&sql, " WHERE uid=%d", uid);
#else /* need name for login group support :/ */
  blob_append_sql(&sql, " WHERE login=%Q", zName);
#endif
#if 0
  puts(blob_str(&sql));
  cson_output_FILE( cson_object_value(pUser), stdout, NULL );
#endif
  db_prepare(&q, "%s", blob_sql_text(&sql));
  db_exec(&q);
  db_finalize(&q);
#if TRY_LOGIN_GROUP
  if( zPW || cson_value_get_bool(forceLogout) ){
    Blob groupSql = empty_blob;
    char * zErr = NULL;
    blob_append_sql(&groupSql,
      "INSERT INTO user(login)"
      "  SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);",
      zName, zName
    );
    blob_append(&groupSql, blob_str(&sql), blob_size(&sql));
    login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr);
    blob_reset(&groupSql);
Changes to src/json_wiki.c.
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
  }
  blob_append(&sql,"SELECT"
              " substr(tagname,6) as name"
              " FROM tag WHERE tagname GLOB 'wiki-*'",
              -1);
  zGlob = json_find_option_cstr("glob",NULL,"g");
  if(zGlob && *zGlob){
    blob_appendf(&sql," AND name %s GLOB %Q",
                 fInvert ? "NOT" : "", zGlob);
  }else{
    zGlob = json_find_option_cstr("like",NULL,"l");
    if(zGlob && *zGlob){
      blob_appendf(&sql," AND name %s LIKE %Q",
                   fInvert ? "NOT" : "",
                   zGlob);
    }
  }
  blob_append(&sql," ORDER BY lower(name)", -1);
  db_prepare(&q,"%s", blob_str(&sql));
  blob_reset(&sql);
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  while( SQLITE_ROW == db_step(&q) ){
    cson_value * v;
    if( verbose ){
      char const * name = db_column_text(&q,0);







|
|



|
|
<



|







442
443
444
445
446
447
448
449
450
451
452
453
454
455

456
457
458
459
460
461
462
463
464
465
466
  }
  blob_append(&sql,"SELECT"
              " substr(tagname,6) as name"
              " FROM tag WHERE tagname GLOB 'wiki-*'",
              -1);
  zGlob = json_find_option_cstr("glob",NULL,"g");
  if(zGlob && *zGlob){
    blob_append_sql(&sql," AND name %s GLOB %Q",
                    fInvert ? "NOT" : "", zGlob);
  }else{
    zGlob = json_find_option_cstr("like",NULL,"l");
    if(zGlob && *zGlob){
      blob_append_sql(&sql," AND name %s LIKE %Q",
                      fInvert ? "NOT" : "", zGlob);

    }
  }
  blob_append(&sql," ORDER BY lower(name)", -1);
  db_prepare(&q,"%s", blob_sql_text(&sql));
  blob_reset(&sql);
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  while( SQLITE_ROW == db_step(&q) ){
    cson_value * v;
    if( verbose ){
      char const * name = db_column_text(&q,0);
Changes to src/leaf.c.
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
    @ SELECT 1 FROM plink
    @  WHERE pid=%d
    @    AND coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
    @       =coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
  ;

  rc = db_int(0, zSql, rid, TAG_BRANCH, TAG_BRANCH);
  return rc==0;
}

/*
** Count the number of primary non-branch children for the given check-in.
**
** A primary child is one where the parent is the primary parent, not







>
|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    @ SELECT 1 FROM plink
    @  WHERE pid=%d
    @    AND coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
    @       =coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
  ;
  rc = db_int(0, zSql /*works-like:"%d,%d,%d"*/,
              rid, TAG_BRANCH, TAG_BRANCH);
  return rc==0;
}

/*
** Count the number of primary non-branch children for the given check-in.
**
** A primary child is one where the parent is the primary parent, not
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
    @ SELECT count(*) FROM plink
    @  WHERE pid=:pid AND isprim
    @    AND coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
    @       =coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
  ;
  db_static_prepare(&q, zSql, TAG_BRANCH, TAG_BRANCH);
  db_bind_int(&q, ":pid", pid);
  if( db_step(&q)==SQLITE_ROW ){
    nNonBranch = db_column_int(&q, 0);
  }
  db_reset(&q);
  return nNonBranch;
}







|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    @ SELECT count(*) FROM plink
    @  WHERE pid=:pid AND isprim
    @    AND coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.pid), 'trunk')
    @       =coalesce((SELECT value FROM tagxref
    @                   WHERE tagid=%d AND rid=plink.cid), 'trunk')
  ;
  db_static_prepare(&q, zSql /*works-like: "%d,%d"*/, TAG_BRANCH, TAG_BRANCH);
  db_bind_int(&q, ":pid", pid);
  if( db_step(&q)==SQLITE_ROW ){
    nNonBranch = db_column_int(&q, 0);
  }
  db_reset(&q);
  return nNonBranch;
}
Changes to src/login.c.
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
        @ %s(zUsername) already exists.
        @ </span></p>
      }else{
        char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
        int uid;
        db_multi_exec(
            "INSERT INTO user(login,pw,cap,info,mtime)"
            "VALUES(%B,%Q,%B,%B,strftime('%s','now'))",
            &login, zPw, &caps, &contact
            );
        free(zPw);

        /* The user is registered, now just log him in. */
        uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
        login_set_user_cookie( zUsername, uid, NULL );







|







1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
        @ %s(zUsername) already exists.
        @ </span></p>
      }else{
        char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
        int uid;
        db_multi_exec(
            "INSERT INTO user(login,pw,cap,info,mtime)"
            "VALUES(%B,%Q,%B,%B,strftime('%%s','now'))",
            &login, zPw, &caps, &contact
            );
        free(zPw);

        /* The user is registered, now just log him in. */
        uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername);
        login_set_user_cookie( zUsername, uid, NULL );
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
  const char *zSelf;         /* The ATTACH name of our repository */

  *pzErrMsg = 0;   /* Default to no errors */
  zSelf = db_name("repository");

  /* Get the full pathname of the other repository */
  file_canonical_name(zRepo, &fullName, 0);
  zRepo = mprintf(blob_str(&fullName));
  blob_reset(&fullName);

  /* Get the full pathname for our repository.  Also the project code
  ** and project name for ourself. */
  file_canonical_name(g.zRepositoryName, &fullName, 0);
  zSelfRepo = mprintf(blob_str(&fullName));
  blob_reset(&fullName);
  zSelfProjCode = db_get("project-code", "unknown");
  zSelfLabel = db_get("project-name", 0);
  if( zSelfLabel==0 ){
    zSelfLabel = zSelfProjCode;
  }








|





|







1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
  const char *zSelf;         /* The ATTACH name of our repository */

  *pzErrMsg = 0;   /* Default to no errors */
  zSelf = db_name("repository");

  /* Get the full pathname of the other repository */
  file_canonical_name(zRepo, &fullName, 0);
  zRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);

  /* Get the full pathname for our repository.  Also the project code
  ** and project name for ourself. */
  file_canonical_name(g.zRepositoryName, &fullName, 0);
  zSelfRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);
  zSelfProjCode = db_get("project-code", "unknown");
  zSelfLabel = db_get("project-name", 0);
  if( zSelfLabel==0 ){
    zSelfLabel = zSelfProjCode;
  }

1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
  }
  rc = sqlite3_open_v2(
       zRepo, &pOther,
       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
       g.zVfsName
  );
  if( rc!=SQLITE_OK ){
    *pzErrMsg = mprintf(sqlite3_errmsg(pOther));
  }else{
    rc = sqlite3_exec(pOther, "SELECT count(*) FROM user", 0, 0, pzErrMsg);
  }
  sqlite3_close(pOther);
  if( rc ) return;

  /* Attach the other repository.  Make sure the username/password is







|







1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
  }
  rc = sqlite3_open_v2(
       zRepo, &pOther,
       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
       g.zVfsName
  );
  if( rc!=SQLITE_OK ){
    *pzErrMsg = fossil_strdup(sqlite3_errmsg(pOther));
  }else{
    rc = sqlite3_exec(pOther, "SELECT count(*) FROM user", 0, 0, pzErrMsg);
  }
  sqlite3_close(pOther);
  if( rc ) return;

  /* Attach the other repository.  Make sure the username/password is
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
  /* Create all the necessary CONFIG table entries on both the
  ** other repository and on our own repository.
  */
  zSelfProjCode = abbreviated_project_code(zSelfProjCode);
  zOtherProjCode = abbreviated_project_code(zOtherProjCode);
  db_begin_transaction();
  db_multi_exec(
    "DELETE FROM %s.config WHERE name GLOB 'peer-*';"
    "INSERT INTO %s.config(name,value) VALUES('peer-repo-%s',%Q);"
    "INSERT INTO %s.config(name,value) "
    "  SELECT 'peer-name-%q', value FROM other.config"
    "   WHERE name='project-name';",
    zSelf,
    zSelf, zOtherProjCode, zRepo,
    zSelf, zOtherProjCode
  );
  db_multi_exec(
    "INSERT OR IGNORE INTO other.config(name,value)"
    " VALUES('login-group-name',%Q);"
    "INSERT OR IGNORE INTO other.config(name,value)"
    " VALUES('login-group-code',lower(hex(randomblob(8))));",
    zNewName
  );
  db_multi_exec(
    "REPLACE INTO %s.config(name,value)"
    "  SELECT name, value FROM other.config"
    "   WHERE name GLOB 'peer-*' OR name GLOB 'login-group-*'",
    zSelf
  );
  db_end_transaction(0);
  db_multi_exec("DETACH other");








|
|
|














|







1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
  /* Create all the necessary CONFIG table entries on both the
  ** other repository and on our own repository.
  */
  zSelfProjCode = abbreviated_project_code(zSelfProjCode);
  zOtherProjCode = abbreviated_project_code(zOtherProjCode);
  db_begin_transaction();
  db_multi_exec(
    "DELETE FROM \"%w\".config WHERE name GLOB 'peer-*';"
    "INSERT INTO \"%w\".config(name,value) VALUES('peer-repo-%q',%Q);"
    "INSERT INTO \"%w\".config(name,value) "
    "  SELECT 'peer-name-%q', value FROM other.config"
    "   WHERE name='project-name';",
    zSelf,
    zSelf, zOtherProjCode, zRepo,
    zSelf, zOtherProjCode
  );
  db_multi_exec(
    "INSERT OR IGNORE INTO other.config(name,value)"
    " VALUES('login-group-name',%Q);"
    "INSERT OR IGNORE INTO other.config(name,value)"
    " VALUES('login-group-code',lower(hex(randomblob(8))));",
    zNewName
  );
  db_multi_exec(
    "REPLACE INTO \"%w\".config(name,value)"
    "  SELECT name, value FROM other.config"
    "   WHERE name GLOB 'peer-*' OR name GLOB 'login-group-*'",
    zSelf
  );
  db_end_transaction(0);
  db_multi_exec("DETACH other");

Changes to src/main.c.
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */
  int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
  int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
  int *aCommitFile;       /* Array of files to be committed */
  int markPrivate;        /* All new artifacts are private if true */
  int clockSkewSeen;      /* True if clocks on client and server out of sync */
  int wikiFlags;          /* Wiki conversion flags applied to %w and %W */
  char isHTTP;            /* True if server/CGI modes, else assume CLI. */
  char javascriptHyperlink; /* If true, set href= using script, not HTML */
  Blob httpHeader;        /* Complete text of the HTTP request header */
  UrlData url;            /* Information about current URL */
  const char *zLogin;     /* Login name.  NULL or "" if not logged in. */
  const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of
                             ** SSL client identity */







|







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */
  int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
  int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
  int *aCommitFile;       /* Array of files to be committed */
  int markPrivate;        /* All new artifacts are private if true */
  int clockSkewSeen;      /* True if clocks on client and server out of sync */
  int wikiFlags;          /* Wiki conversion flags applied to %W */
  char isHTTP;            /* True if server/CGI modes, else assume CLI. */
  char javascriptHyperlink; /* If true, set href= using script, not HTML */
  Blob httpHeader;        /* Complete text of the HTTP request header */
  UrlData url;            /* Information about current URL */
  const char *zLogin;     /* Login name.  NULL or "" if not logged in. */
  const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of
                             ** SSL client identity */
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
  if( zName && validate16(zName, strlen(zName)) ){
    for(i=0; i<nRedirect; i++){
      if( fossil_strcmp(azRedirect[i*2],"*")==0 ){
        zNotFound = azRedirect[i*2+1];
        continue;
      }
      db_open_repository(azRedirect[i*2]);
      if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%s*'", zName) ){
        cgi_redirectf(azRedirect[i*2+1], zName);
        return;
      }
      db_close(1);
    }
  }
  if( zNotFound ){
    cgi_redirectf(zNotFound, zName);
  }else{
    @ <html>
    @ <head><title>No Such Object</title></head>
    @ <body>
    @ <p>No such object: <b>%h(zName)</b></p>
    @ </body>
    cgi_reply();







|
|






|







1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
  if( zName && validate16(zName, strlen(zName)) ){
    for(i=0; i<nRedirect; i++){
      if( fossil_strcmp(azRedirect[i*2],"*")==0 ){
        zNotFound = azRedirect[i*2+1];
        continue;
      }
      db_open_repository(azRedirect[i*2]);
      if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'", zName) ){
        cgi_redirectf(azRedirect[i*2+1] /*works-like:"%s"*/, zName);
        return;
      }
      db_close(1);
    }
  }
  if( zNotFound ){
    cgi_redirectf(zNotFound /*works-like:"%s"*/, zName);
  }else{
    @ <html>
    @ <head><title>No Such Object</title></head>
    @ <body>
    @ <p>No such object: <b>%h(zName)</b></p>
    @ </body>
    cgi_reply();
Changes to src/main.mk.
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

all:	$(OBJDIR) $(APPNAME)

install:	$(APPNAME)
	mkdir -p $(INSTALLDIR)
	mv $(APPNAME) $(INSTALLDIR)




$(OBJDIR):
	-mkdir $(OBJDIR)

$(OBJDIR)/translate:	$(SRCDIR)/translate.c
	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c

$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c

$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c

$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
	$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c




# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion







>
>
>















>
>
>







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

all:	$(OBJDIR) $(APPNAME)

install:	$(APPNAME)
	mkdir -p $(INSTALLDIR)
	mv $(APPNAME) $(INSTALLDIR)

codecheck:	$(TRANS_SRC) $(OBJDIR)/codecheck1
	$(OBJDIR)/codecheck1 $(TRANS_SRC)

$(OBJDIR):
	-mkdir $(OBJDIR)

$(OBJDIR)/translate:	$(SRCDIR)/translate.c
	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c

$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c

$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c

$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
	$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c

$(OBJDIR)/codecheck1:	$(SRCDIR)/codecheck1.c
	$(BCC) -o $(OBJDIR)/codecheck1 $(SRCDIR)/codecheck1.c

# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
428
429
430
431
432
433
434
435

436
437
438
439
440
441
442
 $(OBJDIR)/shell.o \
 $(OBJDIR)/th.o \
 $(OBJDIR)/th_lang.o \
 $(OBJDIR)/th_tcl.o \
 $(OBJDIR)/cson_amalgamation.o


$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)

	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop







|
>







434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
 $(OBJDIR)/shell.o \
 $(OBJDIR)/th.o \
 $(OBJDIR)/th_lang.o \
 $(OBJDIR)/th_tcl.o \
 $(OBJDIR)/cson_amalgamation.o


$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(OBJ) $(EXTRAOBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop
Changes to src/makemake.tcl.
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
    <<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n                "]] {
all:	$(OBJDIR) $(APPNAME)

install:	$(APPNAME)
	mkdir -p $(INSTALLDIR)
	mv $(APPNAME) $(INSTALLDIR)




$(OBJDIR):
	-mkdir $(OBJDIR)

$(OBJDIR)/translate:	$(SRCDIR)/translate.c
	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c

$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c

$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c

$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
	$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c




# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion







>
>
>















>
>
>







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
    <<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n                "]] {
all:	$(OBJDIR) $(APPNAME)

install:	$(APPNAME)
	mkdir -p $(INSTALLDIR)
	mv $(APPNAME) $(INSTALLDIR)

codecheck:	$(TRANS_SRC) $(OBJDIR)/codecheck1
	$(OBJDIR)/codecheck1 $(TRANS_SRC)

$(OBJDIR):
	-mkdir $(OBJDIR)

$(OBJDIR)/translate:	$(SRCDIR)/translate.c
	$(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c

$(OBJDIR)/makeheaders:	$(SRCDIR)/makeheaders.c
	$(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c

$(OBJDIR)/mkindex:	$(SRCDIR)/mkindex.c
	$(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c

$(OBJDIR)/mkversion:	$(SRCDIR)/mkversion.c
	$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c

$(OBJDIR)/codecheck1:	$(SRCDIR)/codecheck1.c
	$(BCC) -o $(OBJDIR)/codecheck1 $(SRCDIR)/codecheck1.c

# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
302
303
304
305
306
307
308
309

310
311
312
313
314
315
316
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {
$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)

	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop







|
>







308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {
$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(OBJ) $(EXTRAOBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop
751
752
753
754
755
756
757

758
759
760
761
762
763
764
765
766
767
768
769

770
771
772
773
774
775
776
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
VERSION     = $(subst /,\,$(OBJDIR)/version.exe)

CAT         = type
CP          = copy
GREP        = find
MV          = copy
RM          = del /Q
MKDIR       = -mkdir
RMDIR       = rmdir /S /Q
else
TRANSLATE   = $(OBJDIR)/translate.exe
MAKEHEADERS = $(OBJDIR)/makeheaders.exe
MKINDEX     = $(OBJDIR)/mkindex.exe
VERSION     = $(OBJDIR)/version.exe

CAT         = cat
CP          = cp
GREP        = grep
MV          = mv
RM          = rm -f
MKDIR       = -mkdir -p
RMDIR       = rm -rf







>












>







758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
VERSION     = $(subst /,\,$(OBJDIR)/version.exe)
CODECHECK1  = $(subst /,\,$(OBJDIR)/codecheck1.exe)
CAT         = type
CP          = copy
GREP        = find
MV          = copy
RM          = del /Q
MKDIR       = -mkdir
RMDIR       = rmdir /S /Q
else
TRANSLATE   = $(OBJDIR)/translate.exe
MAKEHEADERS = $(OBJDIR)/makeheaders.exe
MKINDEX     = $(OBJDIR)/mkindex.exe
VERSION     = $(OBJDIR)/version.exe
CODECHECK1  = $(OBJDIR)/codecheck1.exe
CAT         = cat
CP          = cp
GREP        = grep
MV          = mv
RM          = rm -f
MKDIR       = -mkdir -p
RMDIR       = rm -rf
817
818
819
820
821
822
823



824
825
826
827
828
829
830

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(BCC) -o $(MKINDEX) $(SRCDIR)/mkindex.c

$(VERSION): $(SRCDIR)/mkversion.c
	$(BCC) -o $(VERSION) $(SRCDIR)/mkversion.c




# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)







>
>
>







826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(BCC) -o $(MKINDEX) $(SRCDIR)/mkindex.c

$(VERSION): $(SRCDIR)/mkversion.c
	$(BCC) -o $(VERSION) $(SRCDIR)/mkversion.c

$(CODECHECK1):	$(SRCDIR)/codecheck1.c
	$(BCC) -o $(CODECHECK1) $(SRCDIR)/codecheck1.c

# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
884
885
886
887
888
889
890
891

892
893
894
895
896
897
898

APPTARGETS += $(LIBTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(APPTARGETS)

	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop







|
>







896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911

APPTARGETS += $(LIBTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(CODECHECK1) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(APPTARGETS)
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop
1035
1036
1037
1038
1039
1040
1041
1042
1043

1044
1045
1046
1047
1048
1049
1050
RC=$(DMDIR)\bin\rcc
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__

APPNAME = $(OBJDIR)\fossil$(E)

all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E headers  $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR)

	$(DMDIR)\bin\link @link

$(OBJDIR)\fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**

$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res}
writeln -nonewline "\t+echo "







|

>







1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
RC=$(DMDIR)\bin\rcc
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__

APPNAME = $(OBJDIR)\fossil$(E)

all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E codecheck1$E headers  $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR)
	codecheck1$E $(SRC)
	$(DMDIR)\bin\link @link

$(OBJDIR)\fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**

$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res}
writeln -nonewline "\t+echo "
1064
1065
1066
1067
1068
1069
1070
1071



1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) -o$@ $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) -o$@ $**

version$E: $B\src\mkversion.c



	$(BCC) -o$@ $**

$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
	$(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $**

$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
	$(TCC) -o$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**

$(OBJDIR)\th$O : $(SRCDIR)\th.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
	cp $@ $@

VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
	+$** > $@

page_index.h: mkindex$E $(SRC)
	+$** > $@

clean:
	-del $(OBJDIR)\*.obj
	-del *.obj *_.c *.h *.map

realclean:
	-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E

$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h







|
>
>
>

















|










|







1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) -o$@ $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) -o$@ $**

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) -o$@ $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) -o$@ $**

$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
	$(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $**

$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
	$(TCC) -o$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**

$(OBJDIR)\th$O : $(SRCDIR)\th.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
	cp $@ $@

VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
	+$** > $@

page_index.h: mkindex$E $(SRC)
	+$** > $@

clean:
	-del $(OBJDIR)\*.obj
	-del *.obj *_.c *.h *.map

realclean:
	-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E codecheck1$E

$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
1396
1397
1398
1399
1400
1401
1402
1403
1404

1405
1406
1407
1408
1409
1410
1411

!ifdef FOSSIL_ENABLE_SSL
!ifdef FOSSIL_BUILD_SSL
APPTARGETS = $(APPTARGETS) openssl
!endif
!endif

$(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts
	cd $(OX)

	link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts

$(OX)\linkopts: $B\win\Makefile.msc}
set redir {>}
foreach s [lsort [concat $src $AdditionalObj]] {
  writeln "\techo \$(OX)\\$s.obj $redir \$@"
  set redir {>>}







|

>







1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429

!ifdef FOSSIL_ENABLE_SSL
!ifdef FOSSIL_BUILD_SSL
APPTARGETS = $(APPTARGETS) openssl
!endif
!endif

$(APPNAME) : $(APPTARGETS) translate$E mkindex$E codecheck1$E headers $(OBJ) $(OX)\linkopts
	cd $(OX)
	codecheck1$E $(SRC)
	link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts

$(OX)\linkopts: $B\win\Makefile.msc}
set redir {>}
foreach s [lsort [concat $src $AdditionalObj]] {
  writeln "\techo \$(OX)\\$s.obj $redir \$@"
  set redir {>>}
1424
1425
1426
1427
1428
1429
1430
1431



1432
1433
1434
1435
1436
1437
1438

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) $**

mkversion$E: $B\src\mkversion.c



	$(BCC) $**

$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
	$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c

$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
	$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c







|
>
>
>







1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) $**

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) $**

$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
	$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c

$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
	$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
1476
1477
1478
1479
1480
1481
1482


1483
1484
1485
1486
1487
1488
1489
	-del translate$P
	-del mkindex$E
	-del mkindex$P
	-del makeheaders$E
	-del makeheaders$P
	-del mkversion$E
	-del mkversion$P



$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h







>
>







1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
	-del translate$P
	-del mkindex$E
	-del mkindex$P
	-del makeheaders$E
	-del makeheaders$P
	-del mkversion$E
	-del mkversion$P
	-del codecheck1$E
	-del codecheck1$P

$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
Changes to src/manifest.c.
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392

  /* Every control artifact ends with a '\n' character.  Exit early
  ** if that is not the case for this artifact.
  */
  if( !isRepeat ) g.parseCnt[0]++;
  z = blob_materialize(pContent);
  n = blob_size(pContent);
  if( n<=0 || z[n-1]!='\n' ){
    blob_reset(pContent);
    blob_appendf(pErr, n ? "not terminated with \\n" : "zero-length");
    return 0;
  }

  /* Strip off the PGP signature if there is one.
  */
  remove_pgp_signature(&z, &n);








|

|







376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392

  /* Every control artifact ends with a '\n' character.  Exit early
  ** if that is not the case for this artifact.
  */
  if( !isRepeat ) g.parseCnt[0]++;
  z = blob_materialize(pContent);
  n = blob_size(pContent);
  if( pErr && (n<=0 || z[n-1]!='\n') ){
    blob_reset(pContent);
    blob_append(pErr, n ? "not terminated with \\n" : "zero-length", -1);
    return 0;
  }

  /* Strip off the PGP signature if there is one.
  */
  remove_pgp_signature(&z, &n);

1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
  blob_zero(&brief);
  if( once ){
    once = 0;
    zTitleExpr = db_get("ticket-title-expr", "title");
    zStatusColumn = db_get("ticket-status-column", "status");
  }
  zTitle = db_text("unknown",
    "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
    zTitleExpr, pManifest->zTicketUuid
  );
  if( !isNew ){
    for(i=0; i<pManifest->nField; i++){
      if( fossil_strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
        zNewStatus = pManifest->aField[i].zValue;
      }
    }
    if( zNewStatus ){
      blob_appendf(&comment, "%h ticket [%s|%S]: <i>%h</i>",
         zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle
      );
      if( pManifest->nField>1 ){
        blob_appendf(&comment, " plus %d other change%s",
          pManifest->nField-1, pManifest->nField==2 ? "" : "s");
      }
      blob_appendf(&brief, "%h ticket [%s|%S].",
                   zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid);
    }else{
      zNewStatus = db_text("unknown",
         "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
         zStatusColumn, pManifest->zTicketUuid
      );
      blob_appendf(&comment, "Ticket [%s|%S] <i>%h</i> status still %h with "
           "%d other change%s",
           pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle, zNewStatus,
           pManifest->nField, pManifest->nField==1 ? "" : "s"
      );







|




















|







1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
  blob_zero(&brief);
  if( once ){
    once = 0;
    zTitleExpr = db_get("ticket-title-expr", "title");
    zStatusColumn = db_get("ticket-status-column", "status");
  }
  zTitle = db_text("unknown",
    "SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q",
    zTitleExpr, pManifest->zTicketUuid
  );
  if( !isNew ){
    for(i=0; i<pManifest->nField; i++){
      if( fossil_strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
        zNewStatus = pManifest->aField[i].zValue;
      }
    }
    if( zNewStatus ){
      blob_appendf(&comment, "%h ticket [%s|%S]: <i>%h</i>",
         zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle
      );
      if( pManifest->nField>1 ){
        blob_appendf(&comment, " plus %d other change%s",
          pManifest->nField-1, pManifest->nField==2 ? "" : "s");
      }
      blob_appendf(&brief, "%h ticket [%s|%S].",
                   zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid);
    }else{
      zNewStatus = db_text("unknown",
         "SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q",
         zStatusColumn, pManifest->zTicketUuid
      );
      blob_appendf(&comment, "Ticket [%s|%S] <i>%h</i> status still %h with "
           "%d other change%s",
           pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle, zNewStatus,
           pManifest->nField, pManifest->nField==1 ? "" : "s"
      );
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
      "                  bgcolor,euser,ecomment)"
      "VALUES('w',%.17g,%d,%Q,%Q,"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
      p->rDate, rid, p->zUser, zComment,
      TAG_BGCOLOR, rid,
      TAG_BGCOLOR, rid,
      TAG_USER, rid,
      TAG_COMMENT, rid
    );
    fossil_free(zComment);
  }
  if( p->type==CFTYPE_EVENT ){
    char *zTag = mprintf("event-%s", p->zEventId);







<







1892
1893
1894
1895
1896
1897
1898

1899
1900
1901
1902
1903
1904
1905
      "                  bgcolor,euser,ecomment)"
      "VALUES('w',%.17g,%d,%Q,%Q,"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),"
      "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
      p->rDate, rid, p->zUser, zComment,
      TAG_BGCOLOR, rid,

      TAG_USER, rid,
      TAG_COMMENT, rid
    );
    fossil_free(zComment);
  }
  if( p->type==CFTYPE_EVENT ){
    char *zTag = mprintf("event-%s", p->zEventId);
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
      if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){
        blob_appendf(&comment,
           " Edit [%s|%S]:",
           zTagUuid, zTagUuid);
        branchMove = 0;
        if( permitHooks && db_exists("SELECT 1 FROM event, blob"
            " WHERE event.type='ci' AND event.objid=blob.rid"
            " AND blob.uuid='%s'", zTagUuid) ){
          zScript = xfer_commit_code();
          zUuid = zTagUuid;
        }
      }
      zName = p->aTag[i].zName;
      zValue = p->aTag[i].zValue;
      if( strcmp(zName, "*branch")==0 ){







|







2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
      if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){
        blob_appendf(&comment,
           " Edit [%s|%S]:",
           zTagUuid, zTagUuid);
        branchMove = 0;
        if( permitHooks && db_exists("SELECT 1 FROM event, blob"
            " WHERE event.type='ci' AND event.objid=blob.rid"
            " AND blob.uuid=%Q", zTagUuid) ){
          zScript = xfer_commit_code();
          zUuid = zTagUuid;
        }
      }
      zName = p->aTag[i].zName;
      zValue = p->aTag[i].zValue;
      if( strcmp(zName, "*branch")==0 ){
Changes to src/merge3.c.
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
      */
      int sz = 1;    /* Size of the conflict in lines */
      nConflict++;
      while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
        sz++;
      }
      DEBUG( printf("CONFLICT %d\n", sz); )
      blob_appendf(pOut, mergeMarker[0]);
      i1 = output_one_side(pOut, pV1, aC1, i1, sz);
      blob_appendf(pOut, mergeMarker[1]);
      blob_copy_lines(pOut, pPivot, sz);
      blob_appendf(pOut, mergeMarker[2]);
      i2 = output_one_side(pOut, pV2, aC2, i2, sz);
      blob_appendf(pOut, mergeMarker[3]);
   }

    /* If we are finished with an edit triple, advance to the next
    ** triple.
    */
    if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
    if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;







|

|

|

|







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
      */
      int sz = 1;    /* Size of the conflict in lines */
      nConflict++;
      while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
        sz++;
      }
      DEBUG( printf("CONFLICT %d\n", sz); )
      blob_append(pOut, mergeMarker[0], -1);
      i1 = output_one_side(pOut, pV1, aC1, i1, sz);
      blob_append(pOut, mergeMarker[1], -1);
      blob_copy_lines(pOut, pPivot, sz);
      blob_append(pOut, mergeMarker[2], -1);
      i2 = output_one_side(pOut, pV2, aC2, i2, sz);
      blob_append(pOut, mergeMarker[3], -1);
   }

    /* If we are finished with an edit triple, advance to the next
    ** triple.
    */
    if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
    if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
Changes to src/moderate.c.
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
     "mlink",      "mid",
     "mlink",      "fid",
     "tagxref",    "srcid",
     "tagxref",    "rid",
  };
  int i;
  for(i=0; i<sizeof(aTabField)/sizeof(aTabField[0]); i+=2){
    if( db_exists("SELECT 1 FROM %s WHERE %s=%d",
                  aTabField[i], aTabField[i+1], rid) ) return 1;
  }
  return 0;
}

/*
** Delete a moderation item given by objid







|







71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
     "mlink",      "mid",
     "mlink",      "fid",
     "tagxref",    "srcid",
     "tagxref",    "rid",
  };
  int i;
  for(i=0; i<sizeof(aTabField)/sizeof(aTabField[0]); i+=2){
    if( db_exists("SELECT 1 FROM \"%w\" WHERE \"%w\"=%d",
                  aTabField[i], aTabField[i+1], rid) ) return 1;
  }
  return 0;
}

/*
** Delete a moderation item given by objid
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

  login_check_credentials();
  if( !g.perm.RdWiki && !g.perm.RdTkt ){ login_needed(); return; }
  style_header("Pending Moderation Requests");
  @ <h2>All Pending Moderation Requests</h2>
  if( moderation_table_exists() ){
    blob_init(&sql, timeline_query_for_www(), -1);
    blob_appendf(&sql,
        " AND event.objid IN (SELECT objid FROM modreq)"
        " ORDER BY event.mtime DESC"
    );
    db_prepare(&q, blob_str(&sql));
    www_print_timeline(&q, 0, 0, 0, 0);
    db_finalize(&q);
  }
  style_footer();
}







|



|





150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

  login_check_credentials();
  if( !g.perm.RdWiki && !g.perm.RdTkt ){ login_needed(); return; }
  style_header("Pending Moderation Requests");
  @ <h2>All Pending Moderation Requests</h2>
  if( moderation_table_exists() ){
    blob_init(&sql, timeline_query_for_www(), -1);
    blob_append_sql(&sql,
        " AND event.objid IN (SELECT objid FROM modreq)"
        " ORDER BY event.mtime DESC"
    );
    db_prepare(&q, "%s", blob_sql_text(&sql));
    www_print_timeline(&q, 0, 0, 0, 0);
    db_finalize(&q);
  }
  style_footer();
}
Changes to src/name.c.
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
  if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){
    Stmt q;
    char zUuid[UUID_SIZE+1];
    memcpy(zUuid, zTag, nTag+1);
    canonical16(zUuid, nTag);
    rid = 0;
    if( zType[0]=='*' ){
      db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid);
    }else{
      db_prepare(&q,
        "SELECT blob.rid"
        "  FROM blob, event"
        " WHERE blob.uuid GLOB '%s*'"
        "   AND event.objid=blob.rid"
        "   AND event.type GLOB '%q'",
        zUuid, zType
      );
    }
    if( db_step(&q)==SQLITE_ROW ){
      rid = db_column_int(&q, 0);







|




|







214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
  if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){
    Stmt q;
    char zUuid[UUID_SIZE+1];
    memcpy(zUuid, zTag, nTag+1);
    canonical16(zUuid, nTag);
    rid = 0;
    if( zType[0]=='*' ){
      db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid);
    }else{
      db_prepare(&q,
        "SELECT blob.rid"
        "  FROM blob, event"
        " WHERE blob.uuid GLOB '%q*'"
        "   AND event.objid=blob.rid"
        "   AND event.type GLOB '%q'",
        zUuid, zType
      );
    }
    if( db_step(&q)==SQLITE_ROW ){
      rid = db_column_int(&q, 0);
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
      if( strcmp(zType,"*")==0 ){
        rid = atoi(zTag);
      }else{
        rid = db_int(0,
          "SELECT event.objid"
          "  FROM event"
          " WHERE event.objid=%s"
          "   AND event.type GLOB '%q'", zTag, zType);
      }
    }
  }
  return rid;
}

/*







|







257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
      if( strcmp(zType,"*")==0 ){
        rid = atoi(zTag);
      }else{
        rid = db_int(0,
          "SELECT event.objid"
          "  FROM event"
          " WHERE event.objid=%s"
          "   AND event.type GLOB '%q'", zTag /*safe-for-%s*/, zType);
      }
    }
  }
  return rid;
}

/*
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
  /* Report any HIDDEN, PRIVATE, CLUSTER, or CLOSED tags on this artifact */
  db_prepare(&q,
    "SELECT tagname"
    "  FROM tag JOIN tagxref ON tag.tagid=tagxref.tagid"
    " WHERE tagxref.rid=%d"
    "   AND tag.tagid IN (5,6,7,9)"
    " ORDER BY 1",
    rid, rid
  );
  cnt = 0;
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPrefix = cnt++ ? ", " : "raw-tags:   ";
    fossil_print("%s%s", zPrefix, db_column_text(&q,0));
  }
  if( cnt ) fossil_print("\n");







|







552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
  /* Report any HIDDEN, PRIVATE, CLUSTER, or CLOSED tags on this artifact */
  db_prepare(&q,
    "SELECT tagname"
    "  FROM tag JOIN tagxref ON tag.tagid=tagxref.tagid"
    " WHERE tagxref.rid=%d"
    "   AND tag.tagid IN (5,6,7,9)"
    " ORDER BY 1",
    rid
  );
  cnt = 0;
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPrefix = cnt++ ? ", " : "raw-tags:   ";
    fossil_print("%s%s", zPrefix, db_column_text(&q,0));
  }
  if( cnt ) fossil_print("\n");
Changes to src/path.c.
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

/*
** Compute the shortest path from iFrom to iTo
**
** If directOnly is true, then use only the "primary" links from parent to
** child.  In other words, ignore merges.
**
** Return a pointer to the beginning of the path (the iFrom node).  
** Elements of the path can be traversed by following the PathNode.u.pTo
** pointer chain.
**
** Return NULL if no path is found.
*/
PathNode *path_shortest(
  int iFrom,          /* Path starts here */







|







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

/*
** Compute the shortest path from iFrom to iTo
**
** If directOnly is true, then use only the "primary" links from parent to
** child.  In other words, ignore merges.
**
** Return a pointer to the beginning of the path (the iFrom node).
** Elements of the path can be traversed by following the PathNode.u.pTo
** pointer chain.
**
** Return NULL if no path is found.
*/
PathNode *path_shortest(
  int iFrom,          /* Path starts here */
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
  path_reset();
  path.pStart = path_new_node(iFrom, 0, 0);
  if( iTo==iFrom ){
    path.pEnd = path.pStart;
    return path.pStart;
  }
  if( oneWayOnly && directOnly ){
    db_prepare(&s, 
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim"
    );
  }else if( oneWayOnly ){
    db_prepare(&s, 
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
    );
  }else if( directOnly ){
    db_prepare(&s, 
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim"
    );
  }else{
    db_prepare(&s, 
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE cid=:pid"
    );
  }
  while( path.pCurrent ){
    path.nStep++;







|



|



|





|







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
  path_reset();
  path.pStart = path_new_node(iFrom, 0, 0);
  if( iTo==iFrom ){
    path.pEnd = path.pStart;
    return path.pStart;
  }
  if( oneWayOnly && directOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim"
    );
  }else if( oneWayOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
    );
  }else if( directOnly ){
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim"
    );
  }else{
    db_prepare(&s,
        "SELECT cid, 1 FROM plink WHERE pid=:pid "
        "UNION ALL "
        "SELECT pid, 0 FROM plink WHERE cid=:pid"
    );
  }
  while( path.pCurrent ){
    path.nStep++;
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
      "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
      "  FROM blob, event"
      " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'",
      p->rid, p->rid);
    fossil_print("%4d: %5d %s", n, p->rid, z);
    fossil_free(z);
    if( p->u.pTo ){
      fossil_print(" is a %s of\n", 
                   p->u.pTo->fromIsParent ? "parent" : "child");
    }else{
      fossil_print("\n");
    }
  }
}








|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
      "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
      "  FROM blob, event"
      " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'",
      p->rid, p->rid);
    fossil_print("%4d: %5d %s", n, p->rid, z);
    fossil_free(z);
    if( p->u.pTo ){
      fossil_print(" is a %s of\n",
                   p->u.pTo->fromIsParent ? "parent" : "child");
    }else{
      fossil_print("\n");
    }
  }
}

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
};

/*
** Compute all file name changes that occur going from checkin iFrom
** to checkin iTo.
**
** The number of name changes is written into *pnChng.  For each name
** change, two integers are allocated for *piChng.  The first is the 
** filename.fnid for the original name as seen in check-in iFrom and
** the second is for new name as it is used in check-in iTo.
**
** Space to hold *piChng is obtained from fossil_malloc() and should
** be released by the caller.
**
** This routine really has nothing to do with path.  It is located







|







351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
};

/*
** Compute all file name changes that occur going from checkin iFrom
** to checkin iTo.
**
** The number of name changes is written into *pnChng.  For each name
** change, two integers are allocated for *piChng.  The first is the
** filename.fnid for the original name as seen in check-in iFrom and
** the second is for new name as it is used in check-in iTo.
**
** Space to hold *piChng is obtained from fossil_malloc() and should
** be released by the caller.
**
** This routine really has nothing to do with path.  It is located
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
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
559
560
561
562
563
    fossil_free(aChng);
    g.argv += 2;
    g.argc -= 2;
  }
}

/* Query to extract all rename operations */
static const char zRenameQuery[] = 
@ SELECT
@     datetime(event.mtime),
@     F.name AS old_name,
@     T.name AS new_name,
@     blob.uuid
@   FROM mlink, filename F, filename T, event, blob
@  WHERE coalesce(mlink.pfnid,0)!=0 AND mlink.pfnid!=mlink.fnid
@    AND F.fnid=mlink.pfnid
@    AND T.fnid=mlink.fnid
@    AND event.objid=mlink.mid
@    AND event.type='ci'
@    AND blob.rid=mlink.mid
@  ORDER BY 1 DESC, 2;
;
  
/*
** WEBPAGE: test-rename-list
**
** Print a list of all file rename operations throughout history.
** This page is intended for for testing purposes only and may change
** or be discontinued without notice.
*/
void test_rename_list_page(void){
  Stmt q;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  style_header("List Of File Name Changes");
  @ <h3>NB: Experimental Page</h3>
  @ <table border="1" width="100%%">
  @ <tr><th>Date &amp; Time</th>
  @ <th>Old Name</th>
  @ <th>New Name</th>
  @ <th>Check-in</th></tr>
  db_prepare(&q, zRenameQuery);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zOld = db_column_text(&q, 1);
    const char *zNew = db_column_text(&q, 2);
    const char *zUuid = db_column_text(&q, 3);
    @ <tr>
    @ <td>%z(href("%R/timeline?c=%t",zDate))%s(zDate)</a></td>







|














|



















|







514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
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
559
560
561
562
563
    fossil_free(aChng);
    g.argv += 2;
    g.argc -= 2;
  }
}

/* Query to extract all rename operations */
static const char zRenameQuery[] =
@ SELECT
@     datetime(event.mtime),
@     F.name AS old_name,
@     T.name AS new_name,
@     blob.uuid
@   FROM mlink, filename F, filename T, event, blob
@  WHERE coalesce(mlink.pfnid,0)!=0 AND mlink.pfnid!=mlink.fnid
@    AND F.fnid=mlink.pfnid
@    AND T.fnid=mlink.fnid
@    AND event.objid=mlink.mid
@    AND event.type='ci'
@    AND blob.rid=mlink.mid
@  ORDER BY 1 DESC, 2;
;

/*
** WEBPAGE: test-rename-list
**
** Print a list of all file rename operations throughout history.
** This page is intended for for testing purposes only and may change
** or be discontinued without notice.
*/
void test_rename_list_page(void){
  Stmt q;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(); return; }
  style_header("List Of File Name Changes");
  @ <h3>NB: Experimental Page</h3>
  @ <table border="1" width="100%%">
  @ <tr><th>Date &amp; Time</th>
  @ <th>Old Name</th>
  @ <th>New Name</th>
  @ <th>Check-in</th></tr>
  db_prepare(&q, "%s", zRenameQuery/*safe-for-%s*/);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zOld = db_column_text(&q, 1);
    const char *zNew = db_column_text(&q, 2);
    const char *zUuid = db_column_text(&q, 3);
    @ <tr>
    @ <td>%z(href("%R/timeline?c=%t",zDate))%s(zDate)</a></td>
Changes to src/printf.c.
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#define etERROR      10 /* Used to indicate no such conversion type */
/* The rest are extensions, not normally found in printf() */
#define etBLOB       11 /* Blob objects.  %b */
#define etBLOBSQL    12 /* Blob objects quoted for SQL.  %B */
#define etSQLESCAPE  13 /* Strings with '\'' doubled.  %q */
#define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '',
                          NULL pointers replaced by SQL NULL.  %Q */

#define etPOINTER    15 /* The %p conversion */
#define etHTMLIZE    16 /* Make text safe for HTML */
#define etHTTPIZE    17 /* Make text safe for HTTP.  "/" encoded as %2f */
#define etURLIZE     18 /* Make text safe for HTTP.  "/" not encoded */
#define etFOSSILIZE  19 /* The fossil header encoding format. */
#define etPATH       20 /* Path type */
#define etWIKISTR    21 /* 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: % */


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








>
|
|
|
|
|
|
|

|







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#define etERROR      10 /* Used to indicate no such conversion type */
/* The rest are extensions, not normally found in printf() */
#define etBLOB       11 /* Blob objects.  %b */
#define etBLOBSQL    12 /* Blob objects quoted for SQL.  %B */
#define etSQLESCAPE  13 /* Strings with '\'' doubled.  %q */
#define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '',
                          NULL pointers replaced by SQL NULL.  %Q */
#define etSQLESCAPE3 15 /* Double '"' characters within an indentifier.  %w */
#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 */


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

94
95
96
97
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 6, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etSQLESCAPE,  0,  0 },
  {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },
  {  'b',  0, 2, etBLOB,       0,  0 },
  {  'B',  0, 2, etBLOBSQL,    0,  0 },
  {  'w',  0, 2, etWIKISTR,    0,  0 },
  {  'h',  0, 4, etHTMLIZE,    0,  0 },
  {  'R',  0, 0, etROOT,       0,  0 },
  {  't',  0, 4, etHTTPIZE,    0,  0 },  /* "/" -> "%2F" */
  {  'T',  0, 4, etURLIZE,     0,  0 },  /* "/" unchanged */

  {  'F',  0, 4, etFOSSILIZE,  0,  0 },
  {  'S',  0, 4, etSTRINGID,   0,  0 },
  {  'c',  0, 0, etCHARX,      0,  0 },
  {  'o',  8, 0, etRADIX,      0,  2 },
  {  'u', 10, 0, etRADIX,      0,  0 },
  {  'x', 16, 0, etRADIX,      16, 1 },
  {  'X', 16, 0, etRADIX,      0,  4 },







|




>







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 6, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etSQLESCAPE,  0,  0 },
  {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },
  {  'b',  0, 2, etBLOB,       0,  0 },
  {  'B',  0, 2, etBLOBSQL,    0,  0 },
  {  'W',  0, 2, etWIKISTR,    0,  0 },
  {  'h',  0, 4, etHTMLIZE,    0,  0 },
  {  'R',  0, 0, etROOT,       0,  0 },
  {  't',  0, 4, etHTTPIZE,    0,  0 },  /* "/" -> "%2F" */
  {  'T',  0, 4, etURLIZE,     0,  0 },  /* "/" unchanged */
  {  'w',  0, 4, etSQLESCAPE3, 0,  0 },
  {  'F',  0, 4, etFOSSILIZE,  0,  0 },
  {  'S',  0, 4, etSTRINGID,   0,  0 },
  {  'c',  0, 0, etCHARX,      0,  0 },
  {  'o',  8, 0, etRADIX,      0,  2 },
  {  'u', 10, 0, etRADIX,      0,  0 },
  {  'x', 16, 0, etRADIX,      16, 1 },
  {  'X', 16, 0, etRADIX,      0,  4 },
659
660
661
662
663
664
665

666
667
668
669

670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
        }
        bufpt[j++] = '\'';
        length = j;
        assert( length==n+cnt+2 );
        break;
      }
      case etSQLESCAPE:

      case etSQLESCAPE2: {
        int i, j, n, ch, isnull;
        int needQuote;
        int limit = flag_alternateform ? va_arg(ap,int) : -1;

        char *escarg = va_arg(ap,char*);
        isnull = escarg==0;
        if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
        if( limit<0 ) limit = strlen(escarg);
        for(i=n=0; i<limit; i++){
          if( escarg[i]=='\'' )  n++;
        }
        needQuote = !isnull && xtype==etSQLESCAPE2;
        n += i + 1 + needQuote*2;
        if( n>etBUFSIZE ){
          bufpt = zExtra = fossil_malloc( n );
        }else{
          bufpt = buf;
        }
        j = 0;
        if( needQuote ) bufpt[j++] = '\'';
        for(i=0; i<limit; i++){
          bufpt[j++] = ch = escarg[i];
          if( ch=='\'' ) bufpt[j++] = ch;
        }
        if( needQuote ) bufpt[j++] = '\'';
        bufpt[j] = 0;
        length = j;
        if( precision>=0 && precision<length ) length = precision;
        break;
      }
      case etHTMLIZE: {
        int limit = flag_alternateform ? va_arg(ap,int) : -1;







>
|



>





|









|


|

|







661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
        }
        bufpt[j++] = '\'';
        length = j;
        assert( length==n+cnt+2 );
        break;
      }
      case etSQLESCAPE:
      case etSQLESCAPE2:
      case etSQLESCAPE3: {
        int i, j, n, ch, isnull;
        int needQuote;
        int limit = flag_alternateform ? va_arg(ap,int) : -1;
        char q = ((xtype==etSQLESCAPE3)?'"':'\'');  /* Quote characters */
        char *escarg = va_arg(ap,char*);
        isnull = escarg==0;
        if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
        if( limit<0 ) limit = strlen(escarg);
        for(i=n=0; i<limit; i++){
          if( escarg[i]==q )  n++;
        }
        needQuote = !isnull && xtype==etSQLESCAPE2;
        n += i + 1 + needQuote*2;
        if( n>etBUFSIZE ){
          bufpt = zExtra = fossil_malloc( n );
        }else{
          bufpt = buf;
        }
        j = 0;
        if( needQuote ) bufpt[j++] = q;
        for(i=0; i<limit; i++){
          bufpt[j++] = ch = escarg[i];
          if( ch==q ) bufpt[j++] = ch;
        }
        if( needQuote ) bufpt[j++] = q;
        bufpt[j] = 0;
        length = j;
        if( precision>=0 && precision<length ) length = precision;
        break;
      }
      case etHTMLIZE: {
        int limit = flag_alternateform ? va_arg(ap,int) : -1;
Changes to src/rebuild.c.
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@    cols TEXT,               -- A color-key specification
@    sqlcode TEXT             -- An SQL SELECT statement for this report
@ );
;

static void rebuild_update_schema(void){
  int rc;
  db_multi_exec(zSchemaUpdates1);
  db_multi_exec(zSchemaUpdates2);

  rc = db_exists("SELECT 1 FROM sqlite_master"
                 " WHERE name='user' AND sql GLOB '* mtime *'");
  if( rc==0 ){
    db_multi_exec(
      "CREATE TEMP TABLE temp_user AS SELECT * FROM user;"
      "DROP TABLE user;"







|
|







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@    cols TEXT,               -- A color-key specification
@    sqlcode TEXT             -- An SQL SELECT statement for this report
@ );
;

static void rebuild_update_schema(void){
  int rc;
  db_multi_exec("%s", zSchemaUpdates1 /*safe-for-%s*/);
  db_multi_exec("%s", zSchemaUpdates2 /*safe-for-%s*/);

  rc = db_exists("SELECT 1 FROM sqlite_master"
                 " WHERE name='user' AND sql GLOB '* mtime *'");
  if( rc==0 ){
    db_multi_exec(
      "CREATE TEMP TABLE temp_user AS SELECT * FROM user;"
      "DROP TABLE user;"
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  rc = db_exists("SELECT 1 FROM sqlite_master"
                 " WHERE name='reportfmt' AND sql GLOB '* mtime *'");
  if( rc==0 ){
    db_multi_exec(
      "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;"
      "DROP TABLE reportfmt;"
    );
    db_multi_exec(zSchemaUpdates2);
    db_multi_exec(
      "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
        " SELECT rn, owner, title, cols, sqlcode, now() FROM old_fmt;"
      "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
        " SELECT rn, owner, title || ' (' || rn || ')', cols, sqlcode, now()"
        "   FROM old_fmt;"
    );







|







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  rc = db_exists("SELECT 1 FROM sqlite_master"
                 " WHERE name='reportfmt' AND sql GLOB '* mtime *'");
  if( rc==0 ){
    db_multi_exec(
      "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;"
      "DROP TABLE reportfmt;"
    );
    db_multi_exec("%s", zSchemaUpdates2/*safe-for-%s*/);
    db_multi_exec(
      "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
        " SELECT rn, owner, title, cols, sqlcode, now() FROM old_fmt;"
      "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
        " SELECT rn, owner, title || ' (' || rn || ')', cols, sqlcode, now()"
        "   FROM old_fmt;"
    );
252
253
254
255
256
257
258
259

260
261
262
263
264
265
266
    }
    if( zFNameFormat==0 ){
      /* We are doing "fossil rebuild" */
      manifest_crosslink(rid, pUse, MC_NONE);
    }else{
      /* We are doing "fossil deconstruct" */
      char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
      char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength);

      blob_write_to_file(pUse,zFile);
      free(zFile);
      free(zUuid);
      blob_reset(pUse);
    }
    assert( blob_is_reset(pUse) );
    rebuild_step_done(rid);







|
>







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
    }
    if( zFNameFormat==0 ){
      /* We are doing "fossil rebuild" */
      manifest_crosslink(rid, pUse, MC_NONE);
    }else{
      /* We are doing "fossil deconstruct" */
      char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
      char *zFile = mprintf(zFNameFormat /*works-like:"%s:%s"*/,
                            zUuid, zUuid+prefixLength);
      blob_write_to_file(pUse,zFile);
      free(zFile);
      free(zUuid);
      blob_reset(pUse);
    }
    assert( blob_is_reset(pUse) );
    rebuild_step_done(rid);
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
       " AND name NOT GLOB 'sqlite_*'"
       " AND name NOT GLOB 'fx_*'"
    );
    if( zTable==0 ) break;
    db_multi_exec("DROP TABLE %Q", zTable);
    free(zTable);
  }
  db_multi_exec(zRepositorySchema2);
  ticket_create_table(0);
  shun_artifacts();

  db_multi_exec(
     "INSERT INTO unclustered"
     " SELECT rid FROM blob EXCEPT SELECT rid FROM private"
  );







|







354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
       " AND name NOT GLOB 'sqlite_*'"
       " AND name NOT GLOB 'fx_*'"
    );
    if( zTable==0 ) break;
    db_multi_exec("DROP TABLE %Q", zTable);
    free(zTable);
  }
  db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/);
  ticket_create_table(0);
  shun_artifacts();

  db_multi_exec(
     "INSERT INTO unclustered"
     " SELECT rid FROM blob EXCEPT SELECT rid FROM private"
  );
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  verify_all_options();

  db_begin_transaction();
  ttyOutput = 1;
  errCnt = rebuild_db(randomizeFlag, 1, doClustering);
  reconstruct_private_table();
  db_multi_exec(
    "REPLACE INTO config(name,value,mtime) VALUES('content-schema','%s',now());"
    "REPLACE INTO config(name,value,mtime) VALUES('aux-schema','%s',now());"
    "REPLACE INTO config(name,value,mtime) VALUES('rebuilt','%s',now());",
    CONTENT_SCHEMA, AUX_SCHEMA, get_version()
  );
  if( errCnt && !forceFlag ){
    fossil_print(
      "%d errors. Rolling back changes. Use --force to force a commit.\n",
      errCnt
    );







|
|
|







586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  verify_all_options();

  db_begin_transaction();
  ttyOutput = 1;
  errCnt = rebuild_db(randomizeFlag, 1, doClustering);
  reconstruct_private_table();
  db_multi_exec(
    "REPLACE INTO config(name,value,mtime) VALUES('content-schema',%Q,now());"
    "REPLACE INTO config(name,value,mtime) VALUES('aux-schema',%Q,now());"
    "REPLACE INTO config(name,value,mtime) VALUES('rebuilt',%Q,now());",
    CONTENT_SCHEMA, AUX_SCHEMA, get_version()
  );
  if( errCnt && !forceFlag ){
    fossil_print(
      "%d errors. Rolling back changes. Use --force to force a commit.\n",
      errCnt
    );
Changes to src/report.c.
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
    }
  }
  if( zOwner==0 ) zOwner = g.zLogin;
  style_submenu_element("Cancel", "Cancel", "reportlist");
  if( rn>0 ){
    style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
  }
  style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
  if( zErr ){
    @ <blockquote class="reportError">%h(zErr)</blockquote>
  }
  @ <form action="rptedit" method="post"><div>
  @ <input type="hidden" name="rn" value="%d(rn)" />
  @ <p>Report Title:<br />
  @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p>







|







434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
    }
  }
  if( zOwner==0 ) zOwner = g.zLogin;
  style_submenu_element("Cancel", "Cancel", "reportlist");
  if( rn>0 ){
    style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
  }
  style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
  if( zErr ){
    @ <blockquote class="reportError">%h(zErr)</blockquote>
  }
  @ <form action="rptedit" method="post"><div>
  @ <input type="hidden" name="rn" value="%d(rn)" />
  @ <p>Report Title:<br />
  @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p>
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
    if( g.perm.TktFmt ){
      style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
    }
    if( g.perm.NewTkt ){
      style_submenu_element("New Ticket", "Create a new ticket",
        "%s/tktnew", g.zTop);
    }
    style_header(zTitle);
    output_color_key(zClrKey, 1,
        "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
    @ <table border="1" cellpadding="2" cellspacing="0" class="report"
    @  id="reportTable">
    sState.rn = rn;
    sState.nCount = 0;
    report_restrict_sql(&zErr1);







|







1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
    if( g.perm.TktFmt ){
      style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
    }
    if( g.perm.NewTkt ){
      style_submenu_element("New Ticket", "Create a new ticket",
        "%s/tktnew", g.zTop);
    }
    style_header("%s", zTitle);
    output_color_key(zClrKey, 1,
        "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
    @ <table border="1" cellpadding="2" cellspacing="0" class="report"
    @  id="reportTable">
    sState.rn = rn;
    sState.nCount = 0;
    report_restrict_sql(&zErr1);
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134

/*
** show all reports, which can be used for ticket show.
** Output is written to stdout as tab delimited table
*/
void rpt_list_reports(void){
  Stmt q;
  const char aRptOutFrmt[] = "%s\t%s\n";

  fossil_print("Available reports:\n");
  fossil_print(aRptOutFrmt,"report number","report title");
  fossil_print(aRptOutFrmt,zFullTicketRptRn,zFullTicketRptTitle);
  db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zRn = db_column_text(&q, 0);
    const char *zTitle = db_column_text(&q, 1);

    fossil_print(aRptOutFrmt,zRn,zTitle);
  }
  db_finalize(&q);
}

/*
** user defined separator used by ticket show command
*/







<
<

|
|





|







1110
1111
1112
1113
1114
1115
1116


1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132

/*
** show all reports, which can be used for ticket show.
** Output is written to stdout as tab delimited table
*/
void rpt_list_reports(void){
  Stmt q;


  fossil_print("Available reports:\n");
  fossil_print("%s\t%s\n","report number","report title");
  fossil_print("%s\t%s\n",zFullTicketRptRn,zFullTicketRptTitle);
  db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zRn = db_column_text(&q, 0);
    const char *zTitle = db_column_text(&q, 1);

    fossil_print("%s\t%s\n",zRn,zTitle);
  }
  db_finalize(&q);
}

/*
** user defined separator used by ticket show command
*/
Changes to src/rss.c.
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  blob_zero(&bSQL);
  blob_append( &bSQL, zSQL1, -1 );

  if( zType[0]!='a' ){
    if( zType[0]=='c' && !g.perm.Read ) zType = "x";
    if( zType[0]=='w' && !g.perm.RdWiki ) zType = "x";
    if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x";
    blob_appendf(&bSQL, " AND event.type=%Q", zType);
  }else{
    if( !g.perm.Read ){
      if( g.perm.RdTkt && g.perm.RdWiki ){
        blob_append(&bSQL, " AND event.type!='ci'", -1);
      }else if( g.perm.RdTkt ){
        blob_append(&bSQL, " AND event.type=='t'", -1);
        







|







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  blob_zero(&bSQL);
  blob_append( &bSQL, zSQL1, -1 );

  if( zType[0]!='a' ){
    if( zType[0]=='c' && !g.perm.Read ) zType = "x";
    if( zType[0]=='w' && !g.perm.RdWiki ) zType = "x";
    if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x";
    blob_append_sql(&bSQL, " AND event.type=%Q", zType);
  }else{
    if( !g.perm.Read ){
      if( g.perm.RdTkt && g.perm.RdWiki ){
        blob_append(&bSQL, " AND event.type!='ci'", -1);
      }else if( g.perm.RdTkt ){
        blob_append(&bSQL, " AND event.type=='t'", -1);
        
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
      nTagId = -1;
    }
  }else{
    nTagId = 0;
  }

  if( nTagId==-1 ){
    blob_appendf(&bSQL, " AND 0");
  }else if( nTagId!=0 ){
    blob_appendf(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
  }

  if( zFilename ){
    blob_appendf(&bSQL,
      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)",
        zFilename, filename_collation()
    );
  }

  blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );








|

|




|







120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
      nTagId = -1;
    }
  }else{
    nTagId = 0;
  }

  if( nTagId==-1 ){
    blob_append_sql(&bSQL, " AND 0");
  }else if( nTagId!=0 ){
    blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
  }

  if( zFilename ){
    blob_append_sql(&bSQL,
      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)",
        zFilename, filename_collation()
    );
  }

  blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  @   <channel>
  @     <title>%h(zProjectName)</title>
  @     <link>%s(g.zBaseURL)</link>
  @     <description>%h(zProjectDescr)</description>
  @     <pubDate>%s(zPubDate)</pubDate>
  @     <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator>
  free(zPubDate);
  db_prepare(&q, blob_str(&bSQL));
  blob_reset( &bSQL );
  while( db_step(&q)==SQLITE_ROW && nLine<nLimit ){
    const char *zId = db_column_text(&q, 1);
    const char *zCom = db_column_text(&q, 3);
    const char *zAuthor = db_column_text(&q, 4);
    char *zPrefix = "";
    char *zDate;







|







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  @   <channel>
  @     <title>%h(zProjectName)</title>
  @     <link>%s(g.zBaseURL)</link>
  @     <description>%h(zProjectDescr)</description>
  @     <pubDate>%s(zPubDate)</pubDate>
  @     <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator>
  free(zPubDate);
  db_prepare(&q, "%s", blob_sql_text(&bSQL));
  blob_reset( &bSQL );
  while( db_step(&q)==SQLITE_ROW && nLine<nLimit ){
    const char *zId = db_column_text(&q, 1);
    const char *zCom = db_column_text(&q, 3);
    const char *zAuthor = db_column_text(&q, 4);
    char *zPrefix = "";
    char *zDate;
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  /* We should be done with options.. */
    verify_all_options();

  blob_zero(&bSQL);
  blob_append( &bSQL, zSQL1, -1 );

  if( zType[0]!='a' ){
    blob_appendf(&bSQL, " AND event.type=%Q", zType);
  }

  if( zTicketUuid ){
    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",
      zTicketUuid);
    if ( nTagId==0 ){
      nTagId = -1;







|







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  /* We should be done with options.. */
    verify_all_options();

  blob_zero(&bSQL);
  blob_append( &bSQL, zSQL1, -1 );

  if( zType[0]!='a' ){
    blob_append_sql(&bSQL, " AND event.type=%Q", zType);
  }

  if( zTicketUuid ){
    nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",
      zTicketUuid);
    if ( nTagId==0 ){
      nTagId = -1;
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
      nTagId = -1;
    }
  }else{
    nTagId = 0;
  }

  if( nTagId==-1 ){
    blob_appendf(&bSQL, " AND 0");
  }else if( nTagId!=0 ){
    blob_appendf(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
  }

  if( zFilename ){
    blob_appendf(&bSQL,
      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)",
        zFilename, filename_collation()
    );
  }

  blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );








|

|




|







304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
      nTagId = -1;
    }
  }else{
    nTagId = 0;
  }

  if( nTagId==-1 ){
    blob_append_sql(&bSQL, " AND 0");
  }else if( nTagId!=0 ){
    blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
  }

  if( zFilename ){
    blob_append_sql(&bSQL,
      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)",
        zFilename, filename_collation()
    );
  }

  blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );

341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
  fossil_print("<title>%h</title>\n", zProjectName);
  fossil_print("<link>%s</link>\n", zBaseURL);
  fossil_print("<description>%h</description>\n", zProjectDescr);
  fossil_print("<pubDate>%s</pubDate>\n", zPubDate);
  fossil_print("<generator>Fossil version %s %s</generator>\n",
               MANIFEST_VERSION, MANIFEST_DATE);
  free(zPubDate);
  db_prepare(&q, blob_str(&bSQL));
  blob_reset( &bSQL );
  while( db_step(&q)==SQLITE_ROW && nLine<nLimit ){
    const char *zId = db_column_text(&q, 1);
    const char *zCom = db_column_text(&q, 3);
    const char *zAuthor = db_column_text(&q, 4);
    char *zPrefix = "";
    char *zDate;







|







341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
  fossil_print("<title>%h</title>\n", zProjectName);
  fossil_print("<link>%s</link>\n", zBaseURL);
  fossil_print("<description>%h</description>\n", zProjectDescr);
  fossil_print("<pubDate>%s</pubDate>\n", zPubDate);
  fossil_print("<generator>Fossil version %s %s</generator>\n",
               MANIFEST_VERSION, MANIFEST_DATE);
  free(zPubDate);
  db_prepare(&q, "%s", blob_sql_text(&bSQL));
  blob_reset( &bSQL );
  while( db_step(&q)==SQLITE_ROW && nLine<nLimit ){
    const char *zId = db_column_text(&q, 1);
    const char *zCom = db_column_text(&q, 3);
    const char *zAuthor = db_column_text(&q, 4);
    char *zPrefix = "";
    char *zDate;
Changes to src/search.c.
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
     timeline_utc()
  );
  iBest = db_int(0, "SELECT max(x) FROM srch");
  blob_append(&sql,
              "SELECT rid, uuid, date, comment, 0, 0 FROM srch "
              "WHERE 1 ", -1);
  if(!fAll){
    blob_appendf(&sql,"AND x>%d ", iBest/3);
  }
  blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
  db_prepare(&q, blob_str(&sql));
  blob_reset(&sql);
  print_timeline(&q, nLimit, width, 0);
  db_finalize(&q);
}







|


|




229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
     timeline_utc()
  );
  iBest = db_int(0, "SELECT max(x) FROM srch");
  blob_append(&sql,
              "SELECT rid, uuid, date, comment, 0, 0 FROM srch "
              "WHERE 1 ", -1);
  if(!fAll){
    blob_append_sql(&sql,"AND x>%d ", iBest/3);
  }
  blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  print_timeline(&q, nLimit, width, 0);
  db_finalize(&q);
}
Changes to src/setup.c.
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
      @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
      style_footer();
      return;
    }
    login_verify_csrf_secret();
    db_multi_exec(
       "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
       "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s',now())",
      uid, P("login"), P("info"), zPw, zCap
    );
    if( atoi(PD("all","0"))>0 ){
      Blob sql;
      char *zErr = 0;
      blob_zero(&sql);
      if( zOldLogin==0 ){







|







373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
      @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
      style_footer();
      return;
    }
    login_verify_csrf_secret();
    db_multi_exec(
       "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
       "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",
      uid, P("login"), P("info"), zPw, zCap
    );
    if( atoi(PD("all","0"))>0 ){
      Blob sql;
      char *zErr = 0;
      blob_zero(&sql);
      if( zOldLogin==0 ){
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    free(z2);
  }

  /* Begin generating the page
  */
  style_submenu_element("Cancel", "Cancel", "setup_ulist");
  if( uid ){
    style_header(mprintf("Edit User %h", zLogin));
  }else{
    style_header("Add A New User");
  }
  @ <div class="ueditCapBox">
  @ <form action="%s(g.zPath)" method="post"><div>
  login_insert_csrf_secret();
  if( login_is_special(zLogin) ){







|







475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    free(z2);
  }

  /* Begin generating the page
  */
  style_submenu_element("Cancel", "Cancel", "setup_ulist");
  if( uid ){
    style_header("Edit User %h", zLogin);
  }else{
    style_header("Add A New User");
  }
  @ <div class="ueditCapBox">
  @ <form action="%s(g.zPath)" method="post"><div>
  login_insert_csrf_secret();
  if( login_is_special(zLogin) ){
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
  const char *zNewName = PD("newname", "New Login Group");

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed();
  }
  file_canonical_name(g.zRepositoryName, &fullName, 0);
  zSelfRepo = mprintf(blob_str(&fullName));
  blob_reset(&fullName);
  if( P("join")!=0 ){
    login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg);
  }else if( P("leave") ){
    login_group_leave(&zErrMsg);
  }
  style_header("Login Group Configuration");







|







1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
  const char *zNewName = PD("newname", "New Login Group");

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed();
  }
  file_canonical_name(g.zRepositoryName, &fullName, 0);
  zSelfRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);
  if( P("join")!=0 ){
    login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg);
  }else if( P("leave") ){
    login_group_leave(&zErrMsg);
  }
  style_header("Login Group Configuration");
1329
1330
1331
1332
1333
1334
1335



1336

1337
1338
1339
1340
1341
1342
1343
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed();
  }

  (void) aCmdHelp; /* NOTE: Silence compiler warning. */
  style_header("Settings");



  db_open_local(0);

  db_begin_transaction();
  @ <p>This page provides a simple interface to the "fossil setting" command.
  @ See the "fossil help setting" output below for further information on
  @ the meaning of each setting.</p><hr />
  @ <form action="%s(g.zTop)/setup_settings" method="post"><div>
  @ <table border="0"><tr><td valign="top">
  login_insert_csrf_secret();







>
>
>
|
>







1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed();
  }

  (void) aCmdHelp; /* NOTE: Silence compiler warning. */
  style_header("Settings");
  if(!g.repositoryOpen){
    /* Provide read-only access to versioned settings,
       but only if no repo file was explicitly provided. */
    db_open_local(0);
  }
  db_begin_transaction();
  @ <p>This page provides a simple interface to the "fossil setting" command.
  @ See the "fossil help setting" output below for further information on
  @ the meaning of each setting.</p><hr />
  @ <form action="%s(g.zTop)/setup_settings" method="post"><div>
  @ <table border="0"><tr><td valign="top">
  login_insert_csrf_secret();
Changes to src/shell.c.
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
#endif
      if( p->cnt++==0 && p->showHeader ){
        for(i=0; i<nArg; i++){
          output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
        }
        fprintf(p->out,"%s",p->newline);
      }
      if( azArg>0 ){
        for(i=0; i<nArg; i++){
          output_csv(p, azArg[i], i<nArg-1);
        }
        fprintf(p->out,"%s",p->newline);
      }
#if defined(WIN32) || defined(_WIN32)
      fflush(p->out);







|







878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
#endif
      if( p->cnt++==0 && p->showHeader ){
        for(i=0; i<nArg; i++){
          output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
        }
        fprintf(p->out,"%s",p->newline);
      }
      if( nArg>0 ){
        for(i=0; i<nArg; i++){
          output_csv(p, azArg[i], i<nArg-1);
        }
        fprintf(p->out,"%s",p->newline);
      }
#if defined(WIN32) || defined(_WIN32)
      fflush(p->out);
1348
1349
1350
1351
1352
1353
1354











1355
1356
1357
1358
1359
1360
1361
            fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2));
            fprintf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3));
          }
        }
        sqlite3_finalize(pExplain);
        sqlite3_free(zEQP);
      }












      /* If the shell is currently in ".explain" mode, gather the extra
      ** data required to add indents to the output.*/
      if( pArg && pArg->mode==MODE_Explain ){
        explain_data_prepare(pArg, pStmt);
      }








>
>
>
>
>
>
>
>
>
>
>







1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
            fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2));
            fprintf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3));
          }
        }
        sqlite3_finalize(pExplain);
        sqlite3_free(zEQP);
      }

#if USE_SYSTEM_SQLITE+0==1
      /* Output TESTCTRL_EXPLAIN text of requested */
      if( pArg && pArg->mode==MODE_Explain && sqlite3_libversion_number()<3008007 ){
        const char *zExplain = 0;
        sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain);
        if( zExplain && zExplain[0] ){
          fprintf(pArg->out, "%s", zExplain);
        }
      }
#endif

      /* If the shell is currently in ".explain" mode, gather the extra
      ** data required to add indents to the output.*/
      if( pArg && pArg->mode==MODE_Explain ){
        explain_data_prepare(pArg, pStmt);
      }

Changes to src/shun.c.
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  }
  style_header("Shunned Artifacts");
  if( zUuid && P("sub") ){
    const char *p = zUuid;
    int allExist = 1;
    login_verify_csrf_secret();
    while( *p ){
      db_multi_exec("DELETE FROM shun WHERE uuid='%s'", p);
      if( !db_exists("SELECT 1 FROM blob WHERE uuid='%s'", p) ){
        allExist = 0;
      }
      p += UUID_SIZE+1;
    }
    if( allExist ){
      @ <p class="noMoreShun">Artifact(s)<br />
      for( p = zUuid ; *p ; p += UUID_SIZE+1 ){







|
|







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  }
  style_header("Shunned Artifacts");
  if( zUuid && P("sub") ){
    const char *p = zUuid;
    int allExist = 1;
    login_verify_csrf_secret();
    while( *p ){
      db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p);
      if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){
        allExist = 0;
      }
      p += UUID_SIZE+1;
    }
    if( allExist ){
      @ <p class="noMoreShun">Artifact(s)<br />
      for( p = zUuid ; *p ; p += UUID_SIZE+1 ){
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  if( zUuid && P("add") ){
    const char *p = zUuid;
    int rid, tagid;
    login_verify_csrf_secret();
    while( *p ){
      db_multi_exec(
        "INSERT OR IGNORE INTO shun(uuid,mtime)"
        " VALUES('%s', now())", p);
      db_multi_exec("DELETE FROM attachment WHERE src=%Q", p);
      rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", p);
      if( rid ){
        db_multi_exec("DELETE FROM event WHERE objid=%d", rid);
      }
      tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", p);
      if( tagid ){







|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  if( zUuid && P("add") ){
    const char *p = zUuid;
    int rid, tagid;
    login_verify_csrf_secret();
    while( *p ){
      db_multi_exec(
        "INSERT OR IGNORE INTO shun(uuid,mtime)"
        " VALUES(%Q, now())", p);
      db_multi_exec("DELETE FROM attachment WHERE src=%Q", p);
      rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", p);
      if( rid ){
        db_multi_exec("DELETE FROM event WHERE objid=%d", rid);
      }
      tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", p);
      if( tagid ){
Changes to src/skins.c.
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
      );
    }
    seen = 0;
    for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
      if( fossil_strcmp(aBuiltinSkin[i].zName, z)==0 ){
        seen = 1;
        zCurrent = aBuiltinSkin[i].zValue;
        db_multi_exec("%s", zCurrent);
        break;
      }
    }
    if( !seen ){
      zName = skinVarName(z,0);
      zCurrent = db_get(zName, 0);
      db_multi_exec("%s", zCurrent);
    }
  }

  style_header("Skins");
  if( zErr ){
    @ <p><font color="red">%h(zErr)</font></p>
  }







|






|







1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
      );
    }
    seen = 0;
    for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
      if( fossil_strcmp(aBuiltinSkin[i].zName, z)==0 ){
        seen = 1;
        zCurrent = aBuiltinSkin[i].zValue;
        db_multi_exec("%s", zCurrent/*safe-for-%s*/);
        break;
      }
    }
    if( !seen ){
      zName = skinVarName(z,0);
      zCurrent = db_get(zName, 0);
      db_multi_exec("%s", zCurrent/*safe-for-%s*/);
    }
  }

  style_header("Skins");
  if( zErr ){
    @ <p><font color="red">%h(zErr)</font></p>
  }
Changes to src/sqlcmd.c.
146
147
148
149
150
151
152

153



154
155
156
157
158
159
160
*/
void cmd_sqlite3(void){
  extern int sqlite3_shell(int, char**);
  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  db_close(1);
  sqlite3_shutdown();
  sqlite3_shell(g.argc-1, g.argv+1);

  g.db = 0;



}

/*
** This routine is called by the patched sqlite3 command-line shell in order
** to load the name and database connection for the open Fossil database.
*/
void fossil_open(const char **pzRepoName){







>

>
>
>







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
*/
void cmd_sqlite3(void){
  extern int sqlite3_shell(int, char**);
  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  db_close(1);
  sqlite3_shutdown();
  sqlite3_shell(g.argc-1, g.argv+1);
  sqlite3_cancel_auto_extension((void(*)(void))sqlcmd_autoinit);
  g.db = 0;
  g.zMainDbType = 0;
  g.repositoryOpen = 0;
  g.localOpen = 0;
}

/*
** This routine is called by the patched sqlite3 command-line shell in order
** to load the name and database connection for the open Fossil database.
*/
void fossil_open(const char **pzRepoName){
Changes to src/sqlite3.c.
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.8.7"
#define SQLITE_VERSION_NUMBER 3008007
#define SQLITE_SOURCE_ID      "2014-10-15 15:28:27 3c1e70f4d55bc009ed9ed4cf6d756d7061985851"

/*
** 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







|







229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.8.7"
#define SQLITE_VERSION_NUMBER 3008007
#define SQLITE_SOURCE_ID      "2014-10-17 11:24:17 e4ab094f8afce0817f4074e823fabe59fc29ebb4"

/*
** 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
22089
22090
22091
22092
22093
22094
22095
22096
22097
22098
22099
22100
22101
22102
22103
}

#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
/******************************** End Unix Pthreads *************************/


/********************************* Win32 Threads ****************************/
#if SQLITE_OS_WIN && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0

#define SQLITE_THREADS_IMPLEMENTED 1  /* Prevent the single-thread code below */
#include <process.h>

/* A running thread */
struct SQLiteThread {
  void *tid;               /* The thread handle */







|







22089
22090
22091
22092
22093
22094
22095
22096
22097
22098
22099
22100
22101
22102
22103
}

#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
/******************************** End Unix Pthreads *************************/


/********************************* Win32 Threads ****************************/
#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0

#define SQLITE_THREADS_IMPLEMENTED 1  /* Prevent the single-thread code below */
#include <process.h>

/* A running thread */
struct SQLiteThread {
  void *tid;               /* The thread handle */
22182
22183
22184
22185
22186
22187
22188
22189
22190
22191
22192
22193
22194
22195
22196
    assert( bRc );
  }
  if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
  sqlite3_free(p);
  return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
}

#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINRT */
/******************************** End Win32 Threads *************************/


/********************************* Single-Threaded **************************/
#ifndef SQLITE_THREADS_IMPLEMENTED
/*
** This implementation does not actually create a new thread.  It does the







|







22182
22183
22184
22185
22186
22187
22188
22189
22190
22191
22192
22193
22194
22195
22196
    assert( bRc );
  }
  if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
  sqlite3_free(p);
  return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
}

#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */
/******************************** End Win32 Threads *************************/


/********************************* Single-Threaded **************************/
#ifndef SQLITE_THREADS_IMPLEMENTED
/*
** This implementation does not actually create a new thread.  It does the
33485
33486
33487
33488
33489
33490
33491

33492



33493
33494
33495
33496
33497
33498
33499
#else
  { "WaitForSingleObject",     (SYSCALL)0,                       0 },
#endif

#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
        DWORD))aSyscall[63].pCurrent)


  { "WaitForSingleObjectEx",   (SYSCALL)WaitForSingleObjectEx,   0 },




#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
        BOOL))aSyscall[64].pCurrent)

#if SQLITE_OS_WINRT
  { "SetFilePointerEx",        (SYSCALL)SetFilePointerEx,        0 },
#else







>

>
>
>







33485
33486
33487
33488
33489
33490
33491
33492
33493
33494
33495
33496
33497
33498
33499
33500
33501
33502
33503
#else
  { "WaitForSingleObject",     (SYSCALL)0,                       0 },
#endif

#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
        DWORD))aSyscall[63].pCurrent)

#if !SQLITE_OS_WINCE
  { "WaitForSingleObjectEx",   (SYSCALL)WaitForSingleObjectEx,   0 },
#else
  { "WaitForSingleObjectEx",   (SYSCALL)0,                       0 },
#endif

#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
        BOOL))aSyscall[64].pCurrent)

#if SQLITE_OS_WINRT
  { "SetFilePointerEx",        (SYSCALL)SetFilePointerEx,        0 },
#else
33828
33829
33830
33831
33832
33833
33834
33835

33836
33837
33838
33839
33840
33841
33842
  assert( sleepObj!=NULL );
  osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE);
#else
  osSleep(milliseconds);
#endif
}

#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0

SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
  DWORD rc;
  while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
                                       TRUE))==WAIT_IO_COMPLETION ){}
  return rc;
}
#endif







|
>







33832
33833
33834
33835
33836
33837
33838
33839
33840
33841
33842
33843
33844
33845
33846
33847
  assert( sleepObj!=NULL );
  osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE);
#else
  osSleep(milliseconds);
#endif
}

#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
        SQLITE_THREADSAFE>0
SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
  DWORD rc;
  while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
                                       TRUE))==WAIT_IO_COMPLETION ){}
  return rc;
}
#endif
78220
78221
78222
78223
78224
78225
78226
78227
78228
78229
78230
78231
78232
78233
78234

  rc = vdbeSorterMergeTreeBuild(pSorter, &pMain);
  if( rc==SQLITE_OK ){
#if SQLITE_MAX_WORKER_THREADS
    assert( pSorter->bUseThreads==0 || pSorter->nTask>1 );
    if( pSorter->bUseThreads ){
      int iTask;
      PmaReader *pReadr;
      SortSubtask *pLast = &pSorter->aTask[pSorter->nTask-1];
      rc = vdbeSortAllocUnpacked(pLast);
      if( rc==SQLITE_OK ){
        pReadr = (PmaReader*)sqlite3DbMallocZero(db, sizeof(PmaReader));
        pSorter->pReader = pReadr;
        if( pReadr==0 ) rc = SQLITE_NOMEM;
      }







|







78225
78226
78227
78228
78229
78230
78231
78232
78233
78234
78235
78236
78237
78238
78239

  rc = vdbeSorterMergeTreeBuild(pSorter, &pMain);
  if( rc==SQLITE_OK ){
#if SQLITE_MAX_WORKER_THREADS
    assert( pSorter->bUseThreads==0 || pSorter->nTask>1 );
    if( pSorter->bUseThreads ){
      int iTask;
      PmaReader *pReadr = 0;
      SortSubtask *pLast = &pSorter->aTask[pSorter->nTask-1];
      rc = vdbeSortAllocUnpacked(pLast);
      if( rc==SQLITE_OK ){
        pReadr = (PmaReader*)sqlite3DbMallocZero(db, sizeof(PmaReader));
        pSorter->pReader = pReadr;
        if( pReadr==0 ) rc = SQLITE_NOMEM;
      }
Changes to src/sqlite3.h.
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.8.7"
#define SQLITE_VERSION_NUMBER 3008007
#define SQLITE_SOURCE_ID      "2014-10-15 15:28:27 3c1e70f4d55bc009ed9ed4cf6d756d7061985851"

/*
** 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







|







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.8.7"
#define SQLITE_VERSION_NUMBER 3008007
#define SQLITE_SOURCE_ID      "2014-10-17 11:24:17 e4ab094f8afce0817f4074e823fabe59fc29ebb4"

/*
** 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/stash.c.
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <assert.h>


/*
** SQL code to implement the tables needed by the stash.
*/
static const char zStashInit[] =
@ CREATE TABLE IF NOT EXISTS %s.stash(
@   stashid INTEGER PRIMARY KEY,     -- Unique stash identifier
@   vid INTEGER,                     -- The baseline check-out for this stash
@   comment TEXT,                    -- Comment for this stash.  Or NULL
@   ctime TIMESTAMP                  -- When the stash was created
@ );
@ CREATE TABLE IF NOT EXISTS %s.stashfile(
@   stashid INTEGER REFERENCES stash,  -- Stash that contains this file
@   rid INTEGER,                       -- Baseline content in BLOB table or 0.
@   isAdded BOOLEAN,                   -- True if this is an added file
@   isRemoved BOOLEAN,                 -- True if this file is deleted
@   isExec BOOLEAN,                    -- True if file is executable
@   isLink BOOLEAN,                    -- True if file is a symlink
@   origname TEXT,                     -- Original filename







|





|







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


/*
** SQL code to implement the tables needed by the stash.
*/
static const char zStashInit[] =
@ CREATE TABLE IF NOT EXISTS "%w".stash(
@   stashid INTEGER PRIMARY KEY,     -- Unique stash identifier
@   vid INTEGER,                     -- The baseline check-out for this stash
@   comment TEXT,                    -- Comment for this stash.  Or NULL
@   ctime TIMESTAMP                  -- When the stash was created
@ );
@ CREATE TABLE IF NOT EXISTS "%w".stashfile(
@   stashid INTEGER REFERENCES stash,  -- Stash that contains this file
@   rid INTEGER,                       -- Baseline content in BLOB table or 0.
@   isAdded BOOLEAN,                   -- True if this is an added file
@   isRemoved BOOLEAN,                 -- True if this file is deleted
@   isExec BOOLEAN,                    -- True if file is executable
@   isLink BOOLEAN,                    -- True if file is a symlink
@   origname TEXT,                     -- Original filename
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
  Stmt q;               /* Query against the vfile table */
  Stmt ins;             /* Insert statement */

  zFile = mprintf("%/", zFName);
  file_tree_name(zFile, &fname, 1);
  zTreename = blob_str(&fname);
  blob_zero(&sql);
  blob_appendf(&sql,
    "SELECT deleted, isexe, islink, mrid, pathname, coalesce(origname,pathname)"
    "  FROM vfile"
    " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)",
    vid
  );
  if( fossil_strcmp(zTreename,".")!=0 ){
    blob_appendf(&sql,
      "   AND (pathname GLOB '%q/*' OR origname GLOB '%q/*'"
            "  OR pathname=%Q OR origname=%Q)",
      zTreename, zTreename, zTreename, zTreename
    );
  }
  db_prepare(&q, blob_str(&sql));
  blob_reset(&sql);
  db_prepare(&ins,
     "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
                           "origname, newname, delta)"
     "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
     stashid
  );







|






|





|







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
  Stmt q;               /* Query against the vfile table */
  Stmt ins;             /* Insert statement */

  zFile = mprintf("%/", zFName);
  file_tree_name(zFile, &fname, 1);
  zTreename = blob_str(&fname);
  blob_zero(&sql);
  blob_append_sql(&sql,
    "SELECT deleted, isexe, islink, mrid, pathname, coalesce(origname,pathname)"
    "  FROM vfile"
    " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)",
    vid
  );
  if( fossil_strcmp(zTreename,".")!=0 ){
    blob_append_sql(&sql,
      "   AND (pathname GLOB '%q/*' OR origname GLOB '%q/*'"
            "  OR pathname=%Q OR origname=%Q)",
      zTreename, zTreename, zTreename, zTreename
    );
  }
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  db_prepare(&ins,
     "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
                           "origname, newname, delta)"
     "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
     stashid
  );
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  int nCmd;
  int stashid = 0;
  undo_capture_command_line();
  db_must_be_within_tree();
  db_open_config(0);
  db_begin_transaction();
  zDb = db_name("localdb");
  db_multi_exec(zStashInit, zDb, zDb);
  if( g.argc<=2 ){
    zCmd = "save";
  }else{
    zCmd = g.argv[2];
  }
  nCmd = strlen(zCmd);
  if( memcmp(zCmd, "save", nCmd)==0 ){







|







479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  int nCmd;
  int stashid = 0;
  undo_capture_command_line();
  db_must_be_within_tree();
  db_open_config(0);
  db_begin_transaction();
  zDb = db_name("localdb");
  db_multi_exec(zStashInit /*works-like:"%w,%w"*/, zDb, zDb);
  if( g.argc<=2 ){
    zCmd = "save";
  }else{
    zCmd = g.argv[2];
  }
  nCmd = strlen(zCmd);
  if( memcmp(zCmd, "save", nCmd)==0 ){
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
    if( !verboseFlag ){
      verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
    }
    verify_all_options();
    db_prepare(&q,
       "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
       "       comment, datetime(ctime) FROM stash"
       " ORDER BY ctime DESC"
    );
    if( verboseFlag ){
      db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
                      "  FROM stashfile WHERE stashid=$id");
    }
    while( db_step(&q)==SQLITE_ROW ){
      int stashid = db_column_int(&q, 0);







|







532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
    if( !verboseFlag ){
      verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
    }
    verify_all_options();
    db_prepare(&q,
       "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
       "       comment, datetime(ctime) FROM stash"
       " ORDER BY ctime"
    );
    if( verboseFlag ){
      db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
                      "  FROM stashfile WHERE stashid=$id");
    }
    while( db_step(&q)==SQLITE_ROW ){
      int stashid = db_column_int(&q, 0);
Changes to src/stat.c.
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  @ <tr><th>SQLite&nbsp;Version:</th><td>%.19s(sqlite3_sourceid())
  @ [%.10s(&sqlite3_sourceid()[20])] (%s(sqlite3_libversion()))</td></tr>
  @ <tr><th>Repository Rebuilt:</th><td>
  @ %h(db_get_mtime("rebuilt","%Y-%m-%d %H:%M:%S","Never"))
  @ By Fossil %h(db_get("rebuilt","Unknown"))</td></tr>
  @ <tr><th>Database&nbsp;Stats:</th><td>
  zDb = db_name("repository");
  @ %d(db_int(0, "PRAGMA %s.page_count", zDb)) pages,
  @ %d(db_int(0, "PRAGMA %s.page_size", zDb)) bytes/page,
  @ %d(db_int(0, "PRAGMA %s.freelist_count", zDb)) free pages,
  @ %s(db_text(0, "PRAGMA %s.encoding", zDb)),
  @ %s(db_text(0, "PRAGMA %s.journal_mode", zDb)) mode
  @ </td></tr>

  @ </table>
  style_footer();
}

/*







|
|
|
|
|







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  @ <tr><th>SQLite&nbsp;Version:</th><td>%.19s(sqlite3_sourceid())
  @ [%.10s(&sqlite3_sourceid()[20])] (%s(sqlite3_libversion()))</td></tr>
  @ <tr><th>Repository Rebuilt:</th><td>
  @ %h(db_get_mtime("rebuilt","%Y-%m-%d %H:%M:%S","Never"))
  @ By Fossil %h(db_get("rebuilt","Unknown"))</td></tr>
  @ <tr><th>Database&nbsp;Stats:</th><td>
  zDb = db_name("repository");
  @ %d(db_int(0, "PRAGMA \"%w\".page_count", zDb)) pages,
  @ %d(db_int(0, "PRAGMA \"%w\".page_size", zDb)) bytes/page,
  @ %d(db_int(0, "PRAGMA \"%w\".freelist_count", zDb)) free pages,
  @ %s(db_text(0, "PRAGMA \"%w\".encoding", zDb)),
  @ %s(db_text(0, "PRAGMA \"%w\".journal_mode", zDb)) mode
  @ </td></tr>

  @ </table>
  style_footer();
}

/*
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
                 sqlite3_sourceid(), &sqlite3_sourceid()[20],
                 sqlite3_libversion());
  }
  zDb = db_name("repository");
  fossil_print("%*s%d pages, %d bytes/pg, %d free pages, "
               "%s, %s mode\n",
               colWidth, "database-stats:",
               db_int(0, "PRAGMA %s.page_count", zDb),
               db_int(0, "PRAGMA %s.page_size", zDb),
               db_int(0, "PRAGMA %s.freelist_count", zDb),
               db_text(0, "PRAGMA %s.encoding", zDb),
               db_text(0, "PRAGMA %s.journal_mode", zDb));
  if( dbCheck ){
    fossil_print("%*s%s\n", colWidth, "database-check:",
                 db_text(0, "PRAGMA quick_check(1)"));
  }
}

/*







|
|
|
|
|







265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
                 sqlite3_sourceid(), &sqlite3_sourceid()[20],
                 sqlite3_libversion());
  }
  zDb = db_name("repository");
  fossil_print("%*s%d pages, %d bytes/pg, %d free pages, "
               "%s, %s mode\n",
               colWidth, "database-stats:",
               db_int(0, "PRAGMA \"%w\".page_count", zDb),
               db_int(0, "PRAGMA \"%w\".page_size", zDb),
               db_int(0, "PRAGMA \"%w\".freelist_count", zDb),
               db_text(0, "PRAGMA \"%w\".encoding", zDb),
               db_text(0, "PRAGMA \"%w\".journal_mode", zDb));
  if( dbCheck ){
    fossil_print("%*s%s\n", colWidth, "database-check:",
                 db_text(0, "PRAGMA quick_check(1)"));
  }
}

/*
Changes to src/tag.c.
211
212
213
214
215
216
217
218

219
220
221
222
223
224
225
      db_multi_exec(
        "INSERT OR IGNORE INTO private(rid) VALUES(%d);",
        rid
      );
    }
  }
  if( zCol ){
    db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);

    if( tagid==TAG_COMMENT ){
      char *zCopy = mprintf("%s", zValue);
      wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
      free(zCopy);
    }
  }
  if( tagid==TAG_DATE ){







|
>







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
      db_multi_exec(
        "INSERT OR IGNORE INTO private(rid) VALUES(%d);",
        rid
      );
    }
  }
  if( zCol ){
    db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
                  zCol, zValue, rid);
    if( tagid==TAG_COMMENT ){
      char *zCopy = mprintf("%s", zValue);
      wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
      free(zCopy);
    }
  }
  if( tagid==TAG_DATE ){
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
    const char *zType = find_option("type","t",1);
    Blob sql = empty_blob;
    if( zType==0 || zType[0]==0 ) zType = "*";
    if( g.argc!=4 ){
      usage("find ?--raw? ?-t|--type TYPE? ?-n|--limit #? TAGNAME");
    }
    if( fRaw ){
      blob_appendf(&sql,
        "SELECT blob.uuid FROM tagxref, blob"
        " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
        "   AND tagxref.tagtype>0"
        "   AND blob.rid=tagxref.rid",
        g.argv[3]
      );
      if( nFindLimit>0 ){
        blob_appendf(&sql, " LIMIT %d", nFindLimit);
      }
      db_prepare(&q, "%s", blob_str(&sql));
      blob_reset(&sql);
      while( db_step(&q)==SQLITE_ROW ){
        fossil_print("%s\n", db_column_text(&q, 0));
      }
      db_finalize(&q);
    }else{
      int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
                         g.argv[3]);
      if( tagid>0 ){
        blob_appendf(&sql,
          "%s"
          "  AND event.type GLOB '%q'"
          "  AND blob.rid IN ("
                    " SELECT rid FROM tagxref"
                    "  WHERE tagtype>0 AND tagid=%d"
                    ")"
          " ORDER BY event.mtime DESC",
          timeline_query_for_tty(), zType, tagid
        );
        db_prepare(&q, "%s", blob_str(&sql));
        blob_reset(&sql);
        print_timeline(&q, nFindLimit, 79, 0);
        db_finalize(&q);
      }
    }
  }else








|







|

|









|









|







436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
    const char *zType = find_option("type","t",1);
    Blob sql = empty_blob;
    if( zType==0 || zType[0]==0 ) zType = "*";
    if( g.argc!=4 ){
      usage("find ?--raw? ?-t|--type TYPE? ?-n|--limit #? TAGNAME");
    }
    if( fRaw ){
      blob_append_sql(&sql,
        "SELECT blob.uuid FROM tagxref, blob"
        " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
        "   AND tagxref.tagtype>0"
        "   AND blob.rid=tagxref.rid",
        g.argv[3]
      );
      if( nFindLimit>0 ){
        blob_append_sql(&sql, " LIMIT %d", nFindLimit);
      }
      db_prepare(&q, "%s", blob_sql_text(&sql));
      blob_reset(&sql);
      while( db_step(&q)==SQLITE_ROW ){
        fossil_print("%s\n", db_column_text(&q, 0));
      }
      db_finalize(&q);
    }else{
      int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
                         g.argv[3]);
      if( tagid>0 ){
        blob_append_sql(&sql,
          "%s"
          "  AND event.type GLOB '%q'"
          "  AND blob.rid IN ("
                    " SELECT rid FROM tagxref"
                    "  WHERE tagtype>0 AND tagid=%d"
                    ")"
          " ORDER BY event.mtime DESC",
          timeline_query_for_tty(), zType, tagid
        );
        db_prepare(&q, "%s", blob_sql_text(&sql));
        blob_reset(&sql);
        print_timeline(&q, nFindLimit, 79, 0);
        db_finalize(&q);
      }
    }
  }else

Changes to src/timeline.c.
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
      ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
      wiki_convert(&comment, 0, WIKI_INLINE);
    }else if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
      Blob truncated;
      blob_zero(&truncated);
      blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
      blob_append(&truncated, "...", 3);
      @ <span class="timelineComment">%w(blob_str(&truncated))</span>
      blob_reset(&truncated);
    }else{
      @ <span class="timelineComment">%w(blob_str(&comment))</span>
    }
    blob_reset(&comment);

    /* Generate the "user: USERNAME" at the end of the comment, together
    ** with a hyperlink to another timeline for that user.
    */
    if( zTagList && zTagList[0]==0 ) zTagList = 0;







|


|







391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
      ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
      wiki_convert(&comment, 0, WIKI_INLINE);
    }else if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
      Blob truncated;
      blob_zero(&truncated);
      blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
      blob_append(&truncated, "...", 3);
      @ <span class="timelineComment">%W(blob_str(&truncated))</span>
      blob_reset(&truncated);
    }else{
      @ <span class="timelineComment">%W(blob_str(&comment))</span>
    }
    blob_reset(&comment);

    /* Generate the "user: USERNAME" at the end of the comment, together
    ** with a hyperlink to another timeline for that user.
    */
    if( zTagList && zTagList[0]==0 ) zTagList = 0;
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
    @   etype TEXT,
    @   taglist TEXT,
    @   tagid INTEGER,
    @   short TEXT,
    @   sortby REAL
    @ )
  ;
  db_multi_exec(zSql);
}

/*
** Return a pointer to a constant string that forms the basis
** for a timeline query for the WWW interface.
*/
const char *timeline_query_for_www(void){







|







858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
    @   etype TEXT,
    @   taglist TEXT,
    @   tagid INTEGER,
    @   short TEXT,
    @   sortby REAL
    @ )
  ;
  db_multi_exec("%s", zSql/*safe-for-%s*/);
}

/*
** Return a pointer to a constant string that forms the basis
** for a timeline query for the WWW interface.
*/
const char *timeline_query_for_www(void){
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
    @   tagid AS tagid,
    @   brief AS brief,
    @   event.mtime AS mtime
    @  FROM event CROSS JOIN blob
    @ WHERE blob.rid=event.objid
  ;
  if( zBase==0 ){
    zBase = mprintf(zBaseSql, timeline_utc());
  }
  return zBase;
}

/*
** Generate a submenu element with a single parameter change.
*/







|







887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
    @   tagid AS tagid,
    @   brief AS brief,
    @   event.mtime AS mtime
    @  FROM event CROSS JOIN blob
    @ WHERE blob.rid=event.objid
  ;
  if( zBase==0 ){
    zBase = mprintf(zBaseSql /*works-like: "%s"*/, timeline_utc());
  }
  return zBase;
}

/*
** Generate a submenu element with a single parameter change.
*/
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
*/
char *names_of_file(const char *zUuid){
  Stmt q;
  Blob out;
  const char *zSep = "";
  db_prepare(&q,
    "SELECT DISTINCT filename.name FROM mlink, filename"
    " WHERE mlink.fid=(SELECT rid FROM blob WHERE uuid='%s')"
    "   AND filename.fnid=mlink.fnid",
    zUuid
  );
  blob_zero(&out);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFN = db_column_text(&q, 0);
    blob_appendf(&out, "%s%z%h</a>", zSep,







|







963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
*/
char *names_of_file(const char *zUuid){
  Stmt q;
  Blob out;
  const char *zSep = "";
  db_prepare(&q,
    "SELECT DISTINCT filename.name FROM mlink, filename"
    " WHERE mlink.fid=(SELECT rid FROM blob WHERE uuid=%Q)"
    "   AND filename.fnid=mlink.fnid",
    zUuid
  );
  blob_zero(&out);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFN = db_column_text(&q, 0);
    blob_appendf(&out, "%s%z%h</a>", zSep,
1127
1128
1129
1130
1131
1132
1133

1134
1135
1136

1137
1138
1139
1140
1141
1142
1143
  blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
  blob_append(&sql, timeline_query_for_www(), -1);
  if( P("fc")!=0 || P("v")!=0 || P("detail")!=0 ){
    tmFlags |= TIMELINE_FCHANGES;
    url_add_parameter(&url, "v", 0);
  }
  if( (tmFlags & TIMELINE_UNHIDE)==0 ){

    blob_appendf(&sql, " AND NOT EXISTS(SELECT 1 FROM tagxref"
                 "     WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
                 TAG_HIDDEN);

  }
  if( !useDividers ) url_add_parameter(&url, "nd", 0);
  if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){
    /* If from= and to= are present, display all nodes on a path connecting
    ** the two */
    PathNode *p = 0;
    const char *zFrom = 0;







>
|
|
|
>







1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
  blob_append(&sql, timeline_query_for_www(), -1);
  if( P("fc")!=0 || P("v")!=0 || P("detail")!=0 ){
    tmFlags |= TIMELINE_FCHANGES;
    url_add_parameter(&url, "v", 0);
  }
  if( (tmFlags & TIMELINE_UNHIDE)==0 ){
    blob_append_sql(&sql,
      " AND NOT EXISTS(SELECT 1 FROM tagxref"
      "     WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
      TAG_HIDDEN
    );
  }
  if( !useDividers ) url_add_parameter(&url, "nd", 0);
  if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){
    /* If from= and to= are present, display all nodes on a path connecting
    ** the two */
    PathNode *p = 0;
    const char *zFrom = 0;
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
        p = path_first();
      }
      zFrom = P("me");
      zTo = P("you");
    }
    blob_append(&sql, " AND event.objid IN (0", -1);
    while( p ){
      blob_appendf(&sql, ",%d", p->rid);
      p = p->u.pTo;
    }
    blob_append(&sql, ")", -1);
    path_reset();
    blob_append(&desc, "All nodes on the path from ", -1);
    blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h", zFrom), zFrom);
    blob_append(&desc, " to ", -1);
    blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
    tmFlags |= TIMELINE_DISJOINT;
    db_multi_exec("%s", blob_str(&sql));
  }else if( (p_rid || d_rid) && g.perm.Read ){
    /* If p= or d= is present, ignore all other parameters other than n= */
    char *zUuid;
    int np, nd;

    if( p_rid && d_rid ){
      if( p_rid!=d_rid ) p_rid = d_rid;
      if( P("n")==0 ) nEntry = 10;
    }
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
                         p_rid ? p_rid : d_rid);
    blob_appendf(&sql, " AND event.objid IN ok");
    nd = 0;
    if( d_rid ){
      compute_descendants(d_rid, nEntry+1);
      nd = db_int(0, "SELECT count(*)-1 FROM ok");
      if( nd>=0 ) db_multi_exec("%s", blob_str(&sql));
      if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
      if( useDividers ) timeline_add_dividers(0, d_rid);
      db_multi_exec("DELETE FROM ok");
    }
    if( p_rid ){
      compute_ancestors(p_rid, nEntry+1, 0);
      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestors", np);
        db_multi_exec("%s", blob_str(&sql));
      }
      if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid);
    }
    blob_appendf(&desc, " of %z[%S]</a>",
                   href("%R/info/%s", zUuid), zUuid);
    if( p_rid ){
      url_add_parameter(&url, "p", zUuid);







|









|














|




|










|







1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
        p = path_first();
      }
      zFrom = P("me");
      zTo = P("you");
    }
    blob_append(&sql, " AND event.objid IN (0", -1);
    while( p ){
      blob_append_sql(&sql, ",%d", p->rid);
      p = p->u.pTo;
    }
    blob_append(&sql, ")", -1);
    path_reset();
    blob_append(&desc, "All nodes on the path from ", -1);
    blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h", zFrom), zFrom);
    blob_append(&desc, " to ", -1);
    blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
    tmFlags |= TIMELINE_DISJOINT;
    db_multi_exec("%s", blob_sql_text(&sql));
  }else if( (p_rid || d_rid) && g.perm.Read ){
    /* If p= or d= is present, ignore all other parameters other than n= */
    char *zUuid;
    int np, nd;

    if( p_rid && d_rid ){
      if( p_rid!=d_rid ) p_rid = d_rid;
      if( P("n")==0 ) nEntry = 10;
    }
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
                         p_rid ? p_rid : d_rid);
    blob_append_sql(&sql, " AND event.objid IN ok");
    nd = 0;
    if( d_rid ){
      compute_descendants(d_rid, nEntry+1);
      nd = db_int(0, "SELECT count(*)-1 FROM ok");
      if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
      if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
      if( useDividers ) timeline_add_dividers(0, d_rid);
      db_multi_exec("DELETE FROM ok");
    }
    if( p_rid ){
      compute_ancestors(p_rid, nEntry+1, 0);
      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestors", np);
        db_multi_exec("%s", blob_sql_text(&sql));
      }
      if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid);
    }
    blob_appendf(&desc, " of %z[%S]</a>",
                   href("%R/info/%s", zUuid), zUuid);
    if( p_rid ){
      url_add_parameter(&url, "p", zUuid);
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306

1307
1308
1309

1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
       "INSERT INTO ok VALUES(%d);"
       "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;"
       "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;",
       f_rid, f_rid, f_rid
    );
    blob_appendf(&sql, " AND event.objid IN ok");
    db_multi_exec("%s", blob_str(&sql));
    if( useDividers ) timeline_add_dividers(0, f_rid);
    blob_appendf(&desc, "Parents and children of check-in ");
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid);
    blob_appendf(&desc, "%z[%S]</a>", href("%R/info/%s", zUuid), zUuid);
    tmFlags |= TIMELINE_DISJOINT;
    url_add_parameter(&url, "f", zUuid);
    if( tmFlags & TIMELINE_FCHANGES ){
      timeline_submenu(&url, "Hide Files", "v", 0, 0);
    }else{
      timeline_submenu(&url, "Show Files", "v", "", 0);
    }
    if( (tmFlags & TIMELINE_UNHIDE)==0 ){
      timeline_submenu(&url, "Unhide", "unhide", "", 0);
    }
  }else{
    /* Otherwise, a timeline based on a span of time */
    int n;
    const char *zEType = "timeline item";
    char *zDate;
    if( zUses ){
      blob_appendf(&sql, " AND event.objid IN usesfile ");
    }
    if( renameOnly ){
      blob_appendf(&sql, " AND event.objid IN rnfile ");
    }
    if( zYearMonth ){
      blob_appendf(&sql, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
                   zYearMonth);
    }
    else if( zYearWeek ){
      blob_appendf(&sql, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
                   zYearWeek);
    }
    if( tagid>0 ){
      blob_appendf(&sql,
        "AND (EXISTS(SELECT 1 FROM tagxref"
                    " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid);

      if( zBrName ){
        url_add_parameter(&url, "r", zBrName);
        /* The next two blob_appendf() calls add SQL that causes checkins that
        ** are not part of the branch which are parents or children of the
        ** branch to be included in the report.  This related check-ins are
        ** useful in helping to visualize what has happened on a quiescent
        ** branch that is infrequently merged with a much more activate branch.
        */
        blob_appendf(&sql,
          " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=cid"
                     " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)",
           tagid
        );
        if( (tmFlags & TIMELINE_UNHIDE)==0 ){
          blob_appendf(&sql,
            " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
                       " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)",
            TAG_HIDDEN
          );
        }
        if( P("mionly")==0 ){
          blob_appendf(&sql,
            " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=pid"
                       " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)",
            tagid
          );
          if( (tmFlags & TIMELINE_UNHIDE)==0 ){

            blob_appendf(&sql, " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
                       " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)",
                         TAG_HIDDEN);

          }
        }else{
          url_add_parameter(&url, "mionly", "1");
        }
      }else{
        url_add_parameter(&url, "t", zTagName);
      }
      blob_appendf(&sql, ")");
    }
    if( (zType[0]=='w' && !g.perm.RdWiki)
     || (zType[0]=='t' && !g.perm.RdTkt)
     || (zType[0]=='e' && !g.perm.RdWiki)
     || (zType[0]=='c' && !g.perm.Read)
     || (zType[0]=='g' && !g.perm.Read)
    ){
      zType = "all";
    }
    if( zType[0]=='a' ){
      if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){
        char cSep = '(';
        blob_appendf(&sql, " AND event.type IN ");
        if( g.perm.Read ){
          blob_appendf(&sql, "%c'ci','g'", cSep);
          cSep = ',';
        }
        if( g.perm.RdWiki ){
          blob_appendf(&sql, "%c'w','e'", cSep);
          cSep = ',';
        }
        if( g.perm.RdTkt ){
          blob_appendf(&sql, "%c't'", cSep);
          cSep = ',';
        }
        blob_appendf(&sql, ")");
      }
    }else{ /* zType!="all" */
      blob_appendf(&sql, " AND event.type=%Q", zType);
      url_add_parameter(&url, "y", zType);
      if( zType[0]=='c' ){
        zEType = "checkin";
      }else if( zType[0]=='w' ){
        zEType = "wiki edit";
      }else if( zType[0]=='t' ){
        zEType = "ticket change";
      }else if( zType[0]=='e' ){
        zEType = "event";
      }else if( zType[0]=='g' ){
        zEType = "tag";
      }
    }
    if( zUser ){
      blob_appendf(&sql, " AND (event.user=%Q OR event.euser=%Q)",
                   zUser, zUser);
      url_add_parameter(&url, "u", zUser);
      zThisUser = zUser;
    }
    if( zSearch ){
      blob_appendf(&sql,
        " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
        zSearch, zSearch);
      url_add_parameter(&url, "s", zSearch);
    }
    rBefore = symbolic_name_to_mtime(zBefore);
    rAfter = symbolic_name_to_mtime(zAfter);
    rCirca = symbolic_name_to_mtime(zCirca);
    if( rAfter>0.0 ){
      if( rBefore>0.0 ){
        blob_appendf(&sql,
           " AND event.mtime>=%.17g AND event.mtime<=%.17g"
           " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
        url_add_parameter(&url, "a", zAfter);
        url_add_parameter(&url, "b", zBefore);
        nEntry = 1000000;
      }else{
        blob_appendf(&sql,
           " AND event.mtime>=%.17g  ORDER BY event.mtime ASC",
           rAfter-ONE_SECOND);
        url_add_parameter(&url, "a", zAfter);
      }
    }else if( rBefore>0.0 ){
      blob_appendf(&sql,
         " AND event.mtime<=%.17g ORDER BY event.mtime DESC",
         rBefore+ONE_SECOND);
      url_add_parameter(&url, "b", zBefore);
    }else if( rCirca>0.0 ){
      Blob sql2;
      blob_init(&sql2, blob_str(&sql), -1);
      blob_appendf(&sql2,
          " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d",
          rCirca, (nEntry+1)/2
      );
      db_multi_exec("%s", blob_str(&sql2));
      blob_reset(&sql2);
      blob_appendf(&sql,
          " AND event.mtime>=%f ORDER BY event.mtime ASC",
          rCirca
      );
      nEntry -= (nEntry+1)/2;
      if( useDividers ) timeline_add_dividers(rCirca, 0);
      url_add_parameter(&url, "c", zCirca);
    }else{
      blob_appendf(&sql, " ORDER BY event.mtime DESC");
    }
    blob_appendf(&sql, " LIMIT %d", nEntry);
    db_multi_exec("%s", blob_str(&sql));

    n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
    if( zYearMonth ){
      blob_appendf(&desc, "%s events for %h", zEType, zYearMonth);
    }else if( zYearWeek ){
      blob_appendf(&desc, "%s events for year/week %h", zEType, zYearWeek);
    }else if( zAfter==0 && zBefore==0 && zCirca==0 ){







|
|




















|


|


|



|



|











|





|






|





>
|
|
|
>







|












|

|



|



|


|


|














|





|









|






|





|





|
|



|

|







|

|
|







1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
       "INSERT INTO ok VALUES(%d);"
       "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;"
       "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;",
       f_rid, f_rid, f_rid
    );
    blob_append_sql(&sql, " AND event.objid IN ok");
    db_multi_exec("%s", blob_sql_text(&sql));
    if( useDividers ) timeline_add_dividers(0, f_rid);
    blob_appendf(&desc, "Parents and children of check-in ");
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid);
    blob_appendf(&desc, "%z[%S]</a>", href("%R/info/%s", zUuid), zUuid);
    tmFlags |= TIMELINE_DISJOINT;
    url_add_parameter(&url, "f", zUuid);
    if( tmFlags & TIMELINE_FCHANGES ){
      timeline_submenu(&url, "Hide Files", "v", 0, 0);
    }else{
      timeline_submenu(&url, "Show Files", "v", "", 0);
    }
    if( (tmFlags & TIMELINE_UNHIDE)==0 ){
      timeline_submenu(&url, "Unhide", "unhide", "", 0);
    }
  }else{
    /* Otherwise, a timeline based on a span of time */
    int n;
    const char *zEType = "timeline item";
    char *zDate;
    if( zUses ){
      blob_append_sql(&sql, " AND event.objid IN usesfile ");
    }
    if( renameOnly ){
      blob_append_sql(&sql, " AND event.objid IN rnfile ");
    }
    if( zYearMonth ){
      blob_append_sql(&sql, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
                   zYearMonth);
    }
    else if( zYearWeek ){
      blob_append_sql(&sql, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
                   zYearWeek);
    }
    if( tagid>0 ){
      blob_append_sql(&sql,
        "AND (EXISTS(SELECT 1 FROM tagxref"
                    " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid);

      if( zBrName ){
        url_add_parameter(&url, "r", zBrName);
        /* The next two blob_appendf() calls add SQL that causes checkins that
        ** are not part of the branch which are parents or children of the
        ** branch to be included in the report.  This related check-ins are
        ** useful in helping to visualize what has happened on a quiescent
        ** branch that is infrequently merged with a much more activate branch.
        */
        blob_append_sql(&sql,
          " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=cid"
                     " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)",
           tagid
        );
        if( (tmFlags & TIMELINE_UNHIDE)==0 ){
          blob_append_sql(&sql,
            " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
                       " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)",
            TAG_HIDDEN
          );
        }
        if( P("mionly")==0 ){
          blob_append_sql(&sql,
            " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=pid"
                       " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)",
            tagid
          );
          if( (tmFlags & TIMELINE_UNHIDE)==0 ){
            blob_append_sql(&sql, 
              " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
              " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)",
              TAG_HIDDEN
            );
          }
        }else{
          url_add_parameter(&url, "mionly", "1");
        }
      }else{
        url_add_parameter(&url, "t", zTagName);
      }
      blob_append_sql(&sql, ")");
    }
    if( (zType[0]=='w' && !g.perm.RdWiki)
     || (zType[0]=='t' && !g.perm.RdTkt)
     || (zType[0]=='e' && !g.perm.RdWiki)
     || (zType[0]=='c' && !g.perm.Read)
     || (zType[0]=='g' && !g.perm.Read)
    ){
      zType = "all";
    }
    if( zType[0]=='a' ){
      if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){
        char cSep = '(';
        blob_append_sql(&sql, " AND event.type IN ");
        if( g.perm.Read ){
          blob_append_sql(&sql, "%c'ci','g'", cSep);
          cSep = ',';
        }
        if( g.perm.RdWiki ){
          blob_append_sql(&sql, "%c'w','e'", cSep);
          cSep = ',';
        }
        if( g.perm.RdTkt ){
          blob_append_sql(&sql, "%c't'", cSep);
          cSep = ',';
        }
        blob_append_sql(&sql, ")");
      }
    }else{ /* zType!="all" */
      blob_append_sql(&sql, " AND event.type=%Q", zType);
      url_add_parameter(&url, "y", zType);
      if( zType[0]=='c' ){
        zEType = "checkin";
      }else if( zType[0]=='w' ){
        zEType = "wiki edit";
      }else if( zType[0]=='t' ){
        zEType = "ticket change";
      }else if( zType[0]=='e' ){
        zEType = "event";
      }else if( zType[0]=='g' ){
        zEType = "tag";
      }
    }
    if( zUser ){
      blob_append_sql(&sql, " AND (event.user=%Q OR event.euser=%Q)",
                   zUser, zUser);
      url_add_parameter(&url, "u", zUser);
      zThisUser = zUser;
    }
    if( zSearch ){
      blob_append_sql(&sql,
        " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
        zSearch, zSearch);
      url_add_parameter(&url, "s", zSearch);
    }
    rBefore = symbolic_name_to_mtime(zBefore);
    rAfter = symbolic_name_to_mtime(zAfter);
    rCirca = symbolic_name_to_mtime(zCirca);
    if( rAfter>0.0 ){
      if( rBefore>0.0 ){
        blob_append_sql(&sql,
           " AND event.mtime>=%.17g AND event.mtime<=%.17g"
           " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
        url_add_parameter(&url, "a", zAfter);
        url_add_parameter(&url, "b", zBefore);
        nEntry = 1000000;
      }else{
        blob_append_sql(&sql,
           " AND event.mtime>=%.17g  ORDER BY event.mtime ASC",
           rAfter-ONE_SECOND);
        url_add_parameter(&url, "a", zAfter);
      }
    }else if( rBefore>0.0 ){
      blob_append_sql(&sql,
         " AND event.mtime<=%.17g ORDER BY event.mtime DESC",
         rBefore+ONE_SECOND);
      url_add_parameter(&url, "b", zBefore);
    }else if( rCirca>0.0 ){
      Blob sql2;
      blob_init(&sql2, blob_sql_text(&sql), -1);
      blob_append_sql(&sql2,
          " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d",
          rCirca, (nEntry+1)/2
      );
      db_multi_exec("%s", blob_sql_text(&sql2));
      blob_reset(&sql2);
      blob_append_sql(&sql,
          " AND event.mtime>=%f ORDER BY event.mtime ASC",
          rCirca
      );
      nEntry -= (nEntry+1)/2;
      if( useDividers ) timeline_add_dividers(rCirca, 0);
      url_add_parameter(&url, "c", zCirca);
    }else{
      blob_append_sql(&sql, " ORDER BY event.mtime DESC");
    }
    blob_append_sql(&sql, " LIMIT %d", nEntry);
    db_multi_exec("%s", blob_sql_text(&sql));

    n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
    if( zYearMonth ){
      blob_appendf(&desc, "%s events for %h", zEType, zYearMonth);
    }else if( zYearWeek ){
      blob_appendf(&desc, "%s events for year/week %h", zEType, zYearWeek);
    }else if( zAfter==0 && zBefore==0 && zCirca==0 ){
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
        if( (tmFlags & TIMELINE_UNHIDE)==0 ){
          timeline_submenu(&url, "Unhide", "unhide", "", 0);
        }
      }
    }
  }
  if( P("showsql") ){
    @ <blockquote>%h(blob_str(&sql))</blockquote>
  }
  blob_zero(&sql);
  db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
  @ <h2>%b(&desc)</h2>
  blob_reset(&desc);
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, 0);
  db_finalize(&q);







|







1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
        if( (tmFlags & TIMELINE_UNHIDE)==0 ){
          timeline_submenu(&url, "Unhide", "unhide", "", 0);
        }
      }
    }
  }
  if( P("showsql") ){
    @ <blockquote>%h(blob_sql_text(&sql))</blockquote>
  }
  blob_zero(&sql);
  db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
  @ <h2>%b(&desc)</h2>
  blob_reset(&desc);
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, 0);
  db_finalize(&q);
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
}

/*
** Return a pointer to a static string that forms the basis for
** a timeline query for display on a TTY.
*/
const char *timeline_query_for_tty(void){
  static const char *zBase = 0;
  static const char zBaseSql[] =
    @ SELECT
    @   blob.rid AS rid,
    @   uuid,
    @   datetime(event.mtime%s) AS mDateTime,
    @   coalesce(ecomment,comment)
    @     || ' (user: ' || coalesce(euser,user,'?')
    @     || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
    @           FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
    @                   FROM tag, tagxref
    @                  WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
    @                    AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
    @     || ')' as comment,
    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
    @        AS primPlinkCount,
    @   (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
    @   event.mtime AS mtime,
    @   tagxref.value AS branch
    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;
  if( zBase==0 ){
    zBase = mprintf(zBaseSql, timeline_utc());
  }
  return zBase;
}

/*
** Return true if the input string is a date in the ISO 8601 format:
** YYYY-MM-DD.
*/
static int isIsoDate(const char *z){







<



















|





<
|
<
<







1657
1658
1659
1660
1661
1662
1663

1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688

1689


1690
1691
1692
1693
1694
1695
1696
}

/*
** Return a pointer to a static string that forms the basis for
** a timeline query for display on a TTY.
*/
const char *timeline_query_for_tty(void){

  static const char zBaseSql[] =
    @ SELECT
    @   blob.rid AS rid,
    @   uuid,
    @   datetime(event.mtime%s) AS mDateTime,
    @   coalesce(ecomment,comment)
    @     || ' (user: ' || coalesce(euser,user,'?')
    @     || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
    @           FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
    @                   FROM tag, tagxref
    @                  WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
    @                    AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
    @     || ')' as comment,
    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
    @        AS primPlinkCount,
    @   (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
    @   event.mtime AS mtime,
    @   tagxref.value AS branch
    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @      LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;

  return mprintf(zBaseSql /*works-like: "%s"*/, timeline_utc());


}

/*
** Return true if the input string is a date in the ISO 8601 format:
** YYYY-MM-DD.
*/
static int isIsoDate(const char *z){
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756


1757
1758
1759
1760
1761
1762
1763
**                        >20 or 0 (= no limit, resulting in a single line per
**                        entry).
**   -R REPO_FILE         Specifies the repository db to use. Default is
**                        the current checkout's repository.
*/
void timeline_cmd(void){
  Stmt q;
  int n, k, width;
  const char *zLimit;
  const char *zWidth;
  const char *zOffset;
  const char *zType;
  char *zOrigin;
  char *zDate;
  Blob sql;
  int objid = 0;
  Blob uuid;
  int mode = 0 ;       /* 0:none  1: before  2:after  3:children  4:parents */
  int verboseFlag = 0 ;
  int iOffset;



  verboseFlag = find_option("verbose","v", 0)!=0;
  if( !verboseFlag){
    verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
  }
  db_find_and_open_repository(0, 0);
  zLimit = find_option("limit","n",1);







|












>
>







1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
**                        >20 or 0 (= no limit, resulting in a single line per
**                        entry).
**   -R REPO_FILE         Specifies the repository db to use. Default is
**                        the current checkout's repository.
*/
void timeline_cmd(void){
  Stmt q;
  int n, k, width, i;
  const char *zLimit;
  const char *zWidth;
  const char *zOffset;
  const char *zType;
  char *zOrigin;
  char *zDate;
  Blob sql;
  int objid = 0;
  Blob uuid;
  int mode = 0 ;       /* 0:none  1: before  2:after  3:children  4:parents */
  int verboseFlag = 0 ;
  int iOffset;
  const char *zFilePattern = 0;
  Blob treeName;

  verboseFlag = find_option("verbose","v", 0)!=0;
  if( !verboseFlag){
    verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
  }
  db_find_and_open_repository(0, 0);
  zLimit = find_option("limit","n",1);
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
  }
  zOffset = find_option("offset",0,1);
  iOffset = zOffset ? atoi(zOffset) : 0;

  /* We should be done with options.. */
  verify_all_options();




  if( g.argc>=4 ){
    k = strlen(g.argv[2]);

    if( strncmp(g.argv[2],"before",k)==0 ){
      mode = 1;
    }else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){
      mode = 2;
    }else if( strncmp(g.argv[2],"descendants",k)==0 ){
      mode = 3;
    }else if( strncmp(g.argv[2],"children",k)==0 ){
      mode = 3;
    }else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){
      mode = 4;
    }else if( strncmp(g.argv[2],"parents",k)==0 ){
      mode = 4;
    }else if(!zType && !zLimit){
      usage("?WHEN? ?BASELINE|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
            "?-W|--width WIDTH?");
    }
    if( '-' != *g.argv[3] ){
      zOrigin = g.argv[3];
    }else{
      zOrigin = "now";
    }

  }else if( g.argc==3 ){
    zOrigin = g.argv[2];
  }else{
    zOrigin = "now";


  }
  k = strlen(zOrigin);
  blob_zero(&uuid);
  blob_append(&uuid, zOrigin, -1);
  if( fossil_strcmp(zOrigin, "now")==0 ){
    if( mode==3 || mode==4 ){
      fossil_fatal("cannot compute descendants or ancestors of a date");







>
>
>
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
|
|
|
<
|
|
>
|
|
|
<
>
>







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
  }
  zOffset = find_option("offset",0,1);
  iOffset = zOffset ? atoi(zOffset) : 0;

  /* We should be done with options.. */
  verify_all_options();

  zOrigin = "now";
  zFilePattern = 0;
  for(i=2; i<g.argc; i++){
    char *zArg = g.argv[i];
    k = strlen(zArg);
    if( mode==0 ){
      if( strncmp(zArg,"before",k)==0 ){
        mode = 1;
      }else if( strncmp(zArg,"after",k)==0 && k>1 ){
        mode = 2;
      }else if( strncmp(zArg,"descendants",k)==0 ){
        mode = 3;
      }else if( strncmp(zArg,"children",k)==0 ){
        mode = 3;
      }else if( strncmp(zArg,"ancestors",k)==0 && k>1 ){
        mode = 4;
      }else if( strncmp(zArg,"parents",k)==0 ){
        mode = 4;



      }
      if( mode ){
        if( i<g.argc-1 ) zOrigin = g.argv[++i];

        continue;
      }
    }
    if( zFilePattern==0 ){
      zFilePattern = zArg;
    }else{

      usage("?WHEN? ?CHECKIN|DATETIME? ?FILE? ?OPTIONS?");
    }
  }
  k = strlen(zOrigin);
  blob_zero(&uuid);
  blob_append(&uuid, zOrigin, -1);
  if( fossil_strcmp(zOrigin, "now")==0 ){
    if( mode==3 || mode==4 ){
      fossil_fatal("cannot compute descendants or ancestors of a date");
1836
1837
1838
1839
1840
1841
1842















1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862




















1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
      fossil_fatal("cannot compute descendants or ancestors of a date");
    }
    if( mode==0 ){
      if( isIsoDate(zOrigin) ) zShift = ",'+1 day'";
    }
    zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift);
  }















  if( mode==0 ) mode = 1;
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_tty(), -1);
  blob_appendf(&sql, "  AND event.mtime %s %s",
     (mode==1 || mode==4) ? "<=" : ">=",
     zDate
  );

  if( mode==3 || mode==4 ){
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    if( mode==3 ){
      compute_descendants(objid, n);
    }else{
      compute_ancestors(objid, n, 0);
    }
    blob_appendf(&sql, " AND blob.rid IN ok");
  }
  if( zType && (zType[0]!='a') ){
    blob_appendf(&sql, " AND event.type=%Q ", zType);
  }




















  blob_appendf(&sql, " ORDER BY event.mtime DESC");
  if( iOffset>0 ){
    /* Don't handle LIMIT here, otherwise print_timeline()
     * will not determine the end-marker correctly! */
    blob_appendf(&sql, " LIMIT -1 OFFSET %d", iOffset);
  }
  db_prepare(&q, blob_str(&sql));
  blob_reset(&sql);
  print_timeline(&q, n, width, verboseFlag);
  db_finalize(&q);
}

/*
** Return one of two things:







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



|

|









|


|

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



|

|







1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
      fossil_fatal("cannot compute descendants or ancestors of a date");
    }
    if( mode==0 ){
      if( isIsoDate(zOrigin) ) zShift = ",'+1 day'";
    }
    zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift);
  }
  
  if( zFilePattern ){
    if( zType==0 ){
      /* When zFilePattern is specified and type is not specified, only show
       * file checkins */
      zType="ci";
    }
    file_tree_name(zFilePattern, &treeName, 1);
    if( fossil_strcmp(blob_str(&treeName), ".")==0 ){
      /* When zTreeName refers to g.zLocalRoot, it's like not specifying
       * zFilePattern. */
      zFilePattern = 0;
    }
  }

  if( mode==0 ) mode = 1;
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_tty(), -1);
  blob_append_sql(&sql, "  AND event.mtime %s %s",
     (mode==1 || mode==4) ? "<=" : ">=",
     zDate /*safe-for-%s*/
  );

  if( mode==3 || mode==4 ){
    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
    if( mode==3 ){
      compute_descendants(objid, n);
    }else{
      compute_ancestors(objid, n, 0);
    }
    blob_append_sql(&sql, "\n  AND blob.rid IN ok");
  }
  if( zType && (zType[0]!='a') ){
    blob_append_sql(&sql, "\n  AND event.type=%Q ", zType);
  }
  if( zFilePattern ){
    blob_append(&sql,
       "\n  AND EXISTS(SELECT 1 FROM mlink\n"
         "              WHERE mlink.mid=event.objid\n"
         "                AND mlink.fnid IN ", -1);
    if( filenames_are_case_sensitive() ){
      blob_append_sql(&sql,
        "(SELECT fnid FROM filename"
        " WHERE name=%Q"
        " OR name GLOB '%q/*')",
        blob_str(&treeName), blob_str(&treeName));
    }else{
      blob_append_sql(&sql,
        "(SELECT fnid FROM filename"
        " WHERE name=%Q COLLATE nocase"
        " OR lower(name) GLOB lower('%q/*'))",
        blob_str(&treeName), blob_str(&treeName));
    }
    blob_append(&sql, ")", -1);
  }
  blob_append_sql(&sql, "\nORDER BY event.mtime DESC");
  if( iOffset>0 ){
    /* Don't handle LIMIT here, otherwise print_timeline()
     * will not determine the end-marker correctly! */
    blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
  }
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  print_timeline(&q, n, width, verboseFlag);
  db_finalize(&q);
}

/*
** Return one of two things:
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
  int iterations = 0;                /* number of weeks/months we iterate
                                        over */
  stats_report_init_view();
  stats_report_event_types_menu( includeMonth ? "bymonth" : "byyear", NULL );
  blob_appendf(&header, "Timeline Events (%s) by year%s",
               stats_report_label_for_type(),
               (includeMonth ? "/month" : ""));
  blob_appendf(&sql,
               "SELECT substr(date(mtime),1,%d) AS timeframe, "
               "count(*) AS eventCount "
               "FROM v_reports ",
               includeMonth ? 7 : 4);
  if(zUserName&&*zUserName){
    blob_appendf(&sql, " WHERE user=%Q ", zUserName);
    blob_appendf(&header," for user %q", zUserName);
  }
  blob_append(&sql,
              " GROUP BY timeframe"
              " ORDER BY timeframe DESC",
              -1);
  db_prepare(&query, blob_str(&sql));
  blob_reset(&sql);
  @ <h1>%b(&header)</h1>
  @ <table class='statistics-report-table-events' border='0' cellpadding='2'
  @  cellspacing='0' id='statsTable'>
  @ <thead>
  @ <th>%s(zTimeLabel)</th>
  @ <th>Events</th>







|





|






|







2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
  int iterations = 0;                /* number of weeks/months we iterate
                                        over */
  stats_report_init_view();
  stats_report_event_types_menu( includeMonth ? "bymonth" : "byyear", NULL );
  blob_appendf(&header, "Timeline Events (%s) by year%s",
               stats_report_label_for_type(),
               (includeMonth ? "/month" : ""));
  blob_append_sql(&sql,
               "SELECT substr(date(mtime),1,%d) AS timeframe, "
               "count(*) AS eventCount "
               "FROM v_reports ",
               includeMonth ? 7 : 4);
  if(zUserName&&*zUserName){
    blob_append_sql(&sql, " WHERE user=%Q ", zUserName);
    blob_appendf(&header," for user %q", zUserName);
  }
  blob_append(&sql,
              " GROUP BY timeframe"
              " ORDER BY timeframe DESC",
              -1);
  db_prepare(&query, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  @ <h1>%b(&header)</h1>
  @ <table class='statistics-report-table-events' border='0' cellpadding='2'
  @  cellspacing='0' id='statsTable'>
  @ <thead>
  @ <th>%s(zTimeLabel)</th>
  @ <th>Events</th>
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
*/
static void stats_report_by_user(){
  Stmt query = empty_Stmt;
  int nRowNumber = 0;                /* current TR number */
  int nEventTotal = 0;               /* Total event count */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */
  Blob sql = empty_blob;             /* SQL */
  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */
  stats_report_init_view();
  stats_report_event_types_menu("byuser", NULL);
  blob_append(&sql,
               "SELECT user, "
               "COUNT(*) AS eventCount "
               "FROM v_reports "
               "GROUP BY user ORDER BY eventCount DESC",
              -1);
  db_prepare(&query, blob_str(&sql));
  blob_reset(&sql);
  @ <h1>Timeline Events
  @ (%s(stats_report_label_for_type())) by User</h1>
  @ <table class='statistics-report-table-events' border='0'
  @ cellpadding='2' cellspacing='0' id='statsTable'>
  @ <thead><tr>
  @ <th>User</th>
  @ <th>Events</th>







<




|



|
<
<
<







2358
2359
2360
2361
2362
2363
2364

2365
2366
2367
2368
2369
2370
2371
2372
2373



2374
2375
2376
2377
2378
2379
2380
*/
static void stats_report_by_user(){
  Stmt query = empty_Stmt;
  int nRowNumber = 0;                /* current TR number */
  int nEventTotal = 0;               /* Total event count */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */

  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */
  stats_report_init_view();
  stats_report_event_types_menu("byuser", NULL);
  db_prepare(&query,
               "SELECT user, "
               "COUNT(*) AS eventCount "
               "FROM v_reports "
               "GROUP BY user ORDER BY eventCount DESC");



  @ <h1>Timeline Events
  @ (%s(stats_report_label_for_type())) by User</h1>
  @ <table class='statistics-report-table-events' border='0'
  @ cellpadding='2' cellspacing='0' id='statsTable'>
  @ <thead><tr>
  @ <th>User</th>
  @ <th>Events</th>
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
*/
static void stats_report_day_of_week(){
  Stmt query = empty_Stmt;
  int nRowNumber = 0;                /* current TR number */
  int nEventTotal = 0;               /* Total event count */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */
  Blob sql = empty_blob;             /* SQL */
  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */
  static const char *const daysOfWeek[] = {
  "Monday", "Tuesday", "Wednesday", "Thursday",
  "Friday", "Saturday", "Sunday"
  };

  stats_report_init_view();
  stats_report_event_types_menu("byweekday", NULL);
  blob_append(&sql,
               "SELECT cast(mtime %% 7 AS INTEGER) dow, "
               "COUNT(*) AS eventCount "
               "FROM v_reports "
               "GROUP BY dow ORDER BY dow",
              -1);
  db_prepare(&query, blob_str(&sql));
  blob_reset(&sql);
  @ <h1>Timeline Events
  @ (%s(stats_report_label_for_type())) by Day of the Week</h1>
  @ <table class='statistics-report-table-events' border='0'
  @ cellpadding='2' cellspacing='0' id='statsTable'>
  @ <thead><tr>
  @ <th>DoW</th>
  @ <th>Day</th>







<









|



|
<
<
<







2421
2422
2423
2424
2425
2426
2427

2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441



2442
2443
2444
2445
2446
2447
2448
*/
static void stats_report_day_of_week(){
  Stmt query = empty_Stmt;
  int nRowNumber = 0;                /* current TR number */
  int nEventTotal = 0;               /* Total event count */
  int rowClass = 0;                  /* counter for alternating
                                        row colors */

  int nMaxEvents = 1;                /* max number of events for
                                        all rows. */
  static const char *const daysOfWeek[] = {
  "Monday", "Tuesday", "Wednesday", "Thursday",
  "Friday", "Saturday", "Sunday"
  };

  stats_report_init_view();
  stats_report_event_types_menu("byweekday", NULL);
  db_prepare(&query,
               "SELECT cast(mtime %% 7 AS INTEGER) dow, "
               "COUNT(*) AS eventCount "
               "FROM v_reports "
               "GROUP BY dow ORDER BY dow");



  @ <h1>Timeline Events
  @ (%s(stats_report_label_for_type())) by Day of the Week</h1>
  @ <table class='statistics-report-table-events' border='0'
  @ cellpadding='2' cellspacing='0' id='statsTable'>
  @ <thead><tr>
  @ <th>DoW</th>
  @ <th>Day</th>
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
  }else{
    stats_report_event_types_menu("byweek", NULL);
  }
  blob_append(&sql,
              "SELECT DISTINCT substr(date(mtime),1,4) AS y "
              "FROM v_reports WHERE 1 ", -1);
  if(zUserName&&*zUserName){
    blob_appendf(&sql,"AND user=%Q ", zUserName);
  }
  blob_append(&sql,"GROUP BY y ORDER BY y", -1);
  db_prepare(&qYears, blob_str(&sql));
  blob_reset(&sql);
  cgi_printf("Select year: ");
  while( SQLITE_ROW == db_step(&qYears) ){
    const char *zT = db_column_text(&qYears, 0);
    if( i++ ){
      cgi_printf(" ");
    }







|


|







2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
  }else{
    stats_report_event_types_menu("byweek", NULL);
  }
  blob_append(&sql,
              "SELECT DISTINCT substr(date(mtime),1,4) AS y "
              "FROM v_reports WHERE 1 ", -1);
  if(zUserName&&*zUserName){
    blob_append_sql(&sql,"AND user=%Q ", zUserName);
  }
  blob_append(&sql,"GROUP BY y ORDER BY y", -1);
  db_prepare(&qYears, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  cgi_printf("Select year: ");
  while( SQLITE_ROW == db_step(&qYears) ){
    const char *zT = db_column_text(&qYears, 0);
    if( i++ ){
      cgi_printf(" ");
    }
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
    Stmt stWeek = empty_Stmt;
    int rowCount = 0;
    int total = 0;
    Blob header = empty_blob;
    blob_appendf(&header, "Timeline events (%s) for the calendar weeks "
                 "of %h", stats_report_label_for_type(),
                 zYear);
    blob_appendf(&sql,
                 "SELECT DISTINCT strftime('%%%%W',mtime) AS wk, "
                 "count(*) AS n "
                 "FROM v_reports "
                 "WHERE %Q=substr(date(mtime),1,4) "
                 "AND mtime < current_timestamp ",
                 zYear);
    if(zUserName&&*zUserName){
      blob_appendf(&sql, " AND user=%Q ", zUserName);
      blob_appendf(&header," for user %h", zUserName);
    }
    blob_appendf(&sql, "GROUP BY wk ORDER BY wk DESC");
    cgi_printf("<h1>%h</h1>", blob_str(&header));
    blob_reset(&header);
    cgi_printf("<table class='statistics-report-table-events' "
               "border='0' cellpadding='2' width='100%%' "
               "cellspacing='0' id='statsTable'>");
    cgi_printf("<thead><tr>"
               "<th>Week</th>"
               "<th>Events</th>"
               "<th width='90%%'><!-- relative commits graph --></th>"
               "</tr></thead>"
               "<tbody>");
    db_prepare(&stWeek, blob_str(&sql));
    blob_reset(&sql);
    while( SQLITE_ROW == db_step(&stWeek) ){
      const int nCount = db_column_int(&stWeek, 1);
      if(nCount>nMaxEvents){
        nMaxEvents = nCount;
      }
      ++iterations;







|







|


|











|







2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
    Stmt stWeek = empty_Stmt;
    int rowCount = 0;
    int total = 0;
    Blob header = empty_blob;
    blob_appendf(&header, "Timeline events (%s) for the calendar weeks "
                 "of %h", stats_report_label_for_type(),
                 zYear);
    blob_append_sql(&sql,
                 "SELECT DISTINCT strftime('%%%%W',mtime) AS wk, "
                 "count(*) AS n "
                 "FROM v_reports "
                 "WHERE %Q=substr(date(mtime),1,4) "
                 "AND mtime < current_timestamp ",
                 zYear);
    if(zUserName&&*zUserName){
      blob_append_sql(&sql, " AND user=%Q ", zUserName);
      blob_appendf(&header," for user %h", zUserName);
    }
    blob_append_sql(&sql, "GROUP BY wk ORDER BY wk DESC");
    cgi_printf("<h1>%h</h1>", blob_str(&header));
    blob_reset(&header);
    cgi_printf("<table class='statistics-report-table-events' "
               "border='0' cellpadding='2' width='100%%' "
               "cellspacing='0' id='statsTable'>");
    cgi_printf("<thead><tr>"
               "<th>Week</th>"
               "<th>Events</th>"
               "<th width='90%%'><!-- relative commits graph --></th>"
               "</tr></thead>"
               "<tbody>");
    db_prepare(&stWeek, "%s", blob_sql_text(&sql));
    blob_reset(&sql);
    while( SQLITE_ROW == db_step(&stWeek) ){
      const int nCount = db_column_int(&stWeek, 1);
      if(nCount>nMaxEvents){
        nMaxEvents = nCount;
      }
      ++iterations;
Changes to src/tkt.c.
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
    db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
                  "VALUES(%Q, 0)", p->zTicketUuid);
    tktid = db_last_insert_rowid();
  }
  blob_zero(&sql1);
  blob_zero(&sql2);
  blob_zero(&sql3);
  blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
  if( haveTicketCTime ){
    blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
  }
  aUsed = fossil_malloc( nField );
  memset(aUsed, 0, nField);
  for(i=0; i<p->nField; i++){
    const char *zName = p->aField[i].zName;
    const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
    j = fieldId(zBaseName);
    if( j<0 ) continue;
    aUsed[j] = 1;
    if( aField[j].mUsed & USEDBY_TICKET ){
      if( zName[0]=='+' ){
        zName++;
        blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q",
                     zName, zName, p->aField[i].zValue);
      }else{
        blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue);
      }
    }
    if( aField[j].mUsed & USEDBY_TICKETCHNG ){
      blob_appendf(&sql2, ",%s", zName);
      blob_appendf(&sql3, ",%Q", p->aField[i].zValue);
    }
    if( rid>0 ){
      wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
    }
  }
  blob_appendf(&sql1, " WHERE tkt_id=%d", tktid);
  db_prepare(&q, "%s", blob_str(&sql1));
  db_bind_double(&q, ":mtime", p->rDate);
  db_step(&q);
  db_finalize(&q);
  blob_reset(&sql1);
  if( blob_size(&sql2)>0 || haveTicketChngRid ){
    int fromTkt = 0;
    if( haveTicketChngRid ){
      blob_append(&sql2, ",tkt_rid", -1);
      blob_appendf(&sql3, ",%d", rid);
    }
    for(i=0; i<nField; i++){
      if( aUsed[i]==0
       && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH
      ){
        const char *z = aField[i].zName;
        if( z[0]=='+' ) z++;
        fromTkt = 1;
        blob_appendf(&sql2, ",%s", z);
        blob_appendf(&sql3, ",%s", z);
      }
    }
    if( fromTkt ){
      db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
                     "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d",

                     blob_str(&sql2), tktid, blob_str(&sql3), tktid);
    }else{
      db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
                     "VALUES(%d,:mtime%s)",
                     blob_str(&sql2), tktid, blob_str(&sql3));
    }
    db_bind_double(&q, ":mtime", p->rDate);
    db_step(&q);
    db_finalize(&q);
  }
  blob_reset(&sql2);
  blob_reset(&sql3);







|

|












|


|



|
|





|
|








|








|
|





>
|



|







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
    db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
                  "VALUES(%Q, 0)", p->zTicketUuid);
    tktid = db_last_insert_rowid();
  }
  blob_zero(&sql1);
  blob_zero(&sql2);
  blob_zero(&sql3);
  blob_append_sql(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
  if( haveTicketCTime ){
    blob_append_sql(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
  }
  aUsed = fossil_malloc( nField );
  memset(aUsed, 0, nField);
  for(i=0; i<p->nField; i++){
    const char *zName = p->aField[i].zName;
    const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
    j = fieldId(zBaseName);
    if( j<0 ) continue;
    aUsed[j] = 1;
    if( aField[j].mUsed & USEDBY_TICKET ){
      if( zName[0]=='+' ){
        zName++;
        blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q",
                     zName, zName, p->aField[i].zValue);
      }else{
        blob_append_sql(&sql1,", \"%w\"=%Q", zName, p->aField[i].zValue);
      }
    }
    if( aField[j].mUsed & USEDBY_TICKETCHNG ){
      blob_append_sql(&sql2, ",\"%w\"", zName);
      blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
    }
    if( rid>0 ){
      wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
    }
  }
  blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid);
  db_prepare(&q, "%s", blob_sql_text(&sql1));
  db_bind_double(&q, ":mtime", p->rDate);
  db_step(&q);
  db_finalize(&q);
  blob_reset(&sql1);
  if( blob_size(&sql2)>0 || haveTicketChngRid ){
    int fromTkt = 0;
    if( haveTicketChngRid ){
      blob_append(&sql2, ",tkt_rid", -1);
      blob_append_sql(&sql3, ",%d", rid);
    }
    for(i=0; i<nField; i++){
      if( aUsed[i]==0
       && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH
      ){
        const char *z = aField[i].zName;
        if( z[0]=='+' ) z++;
        fromTkt = 1;
        blob_append_sql(&sql2, ",\"%w\"", z);
        blob_append_sql(&sql3, ",\"%w\"", z);
      }
    }
    if( fromTkt ){
      db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
                     "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d",
                     blob_sql_text(&sql2), tktid,
                     blob_sql_text(&sql3), tktid);
    }else{
      db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
                     "VALUES(%d,:mtime%s)",
                     blob_sql_text(&sql2), tktid, blob_sql_text(&sql3));
    }
    db_bind_double(&q, ":mtime", p->rDate);
    db_step(&q);
    db_finalize(&q);
  }
  blob_reset(&sql2);
  blob_reset(&sql3);
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
    "DROP TABLE IF EXISTS ticketchng;"
  );
  zSql = ticket_table_schema();
  if( separateConnection ){
    db_end_transaction(0);
    db_init_database(g.zRepositoryName, zSql, 0);
  }else{
    db_multi_exec("%s", zSql);
  }
}

/*
** Repopulate the TICKET and TICKETCHNG tables from scratch using all
** available ticket artifacts.
*/







|







367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    "DROP TABLE IF EXISTS ticketchng;"
  );
  zSql = ticket_table_schema();
  if( separateConnection ){
    db_end_transaction(0);
    db_init_database(g.zRepositoryName, zSql, 0);
  }else{
    db_multi_exec("%s", zSql/*safe-for-%s*/);
  }
}

/*
** Repopulate the TICKET and TICKETCHNG tables from scratch using all
** available ticket artifacts.
*/
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
  int rid = content_put_ex(pTicket, 0, 0, 0, needMod);
  if( rid==0 ){
    fossil_fatal("trouble committing ticket: %s", g.zErrMsg);
  }
  if( needMod ){
    moderation_table_create();
    db_multi_exec(
      "INSERT INTO modreq(objid, tktid) VALUES(%d,'%s')",
      rid, zTktId
    );
  }else{
    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
  }
  manifest_crosslink_begin();







|







550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
  int rid = content_put_ex(pTicket, 0, 0, 0, needMod);
  if( rid==0 ){
    fossil_fatal("trouble committing ticket: %s", g.zErrMsg);
  }
  if( needMod ){
    moderation_table_create();
    db_multi_exec(
      "INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)",
      rid, zTktId
    );
  }else{
    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
  }
  manifest_crosslink_begin();
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
  style_submenu_element("Status", "Status",
    "%s/info/%s", g.zTop, zUuid);
  if( zType[0]=='c' ){
    zTitle = mprintf("Check-Ins Associated With Ticket %h", zUuid);
  }else{
    zTitle = mprintf("Timeline Of Ticket %h", zUuid);
  }
  style_header(zTitle);
  free(zTitle);

  sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
  canonical16(zGlobPattern, strlen(zGlobPattern));
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_footer();







|
<







854
855
856
857
858
859
860
861

862
863
864
865
866
867
868
  style_submenu_element("Status", "Status",
    "%s/info/%s", g.zTop, zUuid);
  if( zType[0]=='c' ){
    zTitle = mprintf("Check-Ins Associated With Ticket %h", zUuid);
  }else{
    zTitle = mprintf("Timeline Of Ticket %h", zUuid);
  }
  style_header("%z", zTitle);


  sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
  canonical16(zGlobPattern, strlen(zGlobPattern));
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_footer();
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
                  "   AND '%s' GLOB (target||'*')"
         "   UNION SELECT attachid FROM attachment"
                  " WHERE target=%Q) "
         "ORDER BY mtime DESC",
         timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
    );
  }
  db_prepare(&q, zSQL);
  free(zSQL);
  www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH,
                     0, 0, 0);
  db_finalize(&q);
  style_footer();
}

/*







|
<







887
888
889
890
891
892
893
894

895
896
897
898
899
900
901
                  "   AND '%s' GLOB (target||'*')"
         "   UNION SELECT attachid FROM attachment"
                  " WHERE target=%Q) "
         "ORDER BY mtime DESC",
         timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
    );
  }
  db_prepare(&q, "%z", zSQL/*safe-for-%s*/);

  www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH,
                     0, 0, 0);
  db_finalize(&q);
  style_footer();
}

/*
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
  if( P("plaintext")!=0 ){
    style_submenu_element("Formatted", "Formatted",
                          "%R/tkthistory/%s", zUuid);
  }else{
    style_submenu_element("Plaintext", "Plaintext",
                          "%R/tkthistory/%s?plaintext", zUuid);
  }
  style_header(zTitle);
  free(zTitle);

  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_footer();
    return;
  }







|
<







924
925
926
927
928
929
930
931

932
933
934
935
936
937
938
  if( P("plaintext")!=0 ){
    style_submenu_element("Formatted", "Formatted",
                          "%R/tkthistory/%s", zUuid);
  }else{
    style_submenu_element("Plaintext", "Plaintext",
                          "%R/tkthistory/%s?plaintext", zUuid);
  }
  style_header("%z", zTitle);


  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_footer();
    return;
  }
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
        }else{
          eCmd = set;
        }
        if( g.argc==3 ){
          usage("set|change|history TICKETUUID");
        }
        zTktUuid = db_text(0,
          "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
        );
        if( !zTktUuid ){
          fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
        }
        i=4;
      }else if( strncmp(g.argv[2],"add",n)==0 ){
        eCmd = add;







|







1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
        }else{
          eCmd = set;
        }
        if( g.argc==3 ){
          usage("set|change|history TICKETUUID");
        }
        zTktUuid = db_text(0,
          "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%q*'", g.argv[3]
        );
        if( !zTktUuid ){
          fossil_fatal("unknown ticket: '%s'!",g.argv[3]);
        }
        i=4;
      }else if( strncmp(g.argv[2],"add",n)==0 ){
        eCmd = add;
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
                  fossil_print("  Append to ");
            z++;
          }else{
            fossil_print("  Change ");
          }
          fossil_print("%h: ",z);
          if( blob_size(&val)>50 || contains_newline(&val)) {
                  fossil_print("\n    ",blob_str(&val));
                  comment_print(blob_str(&val),0,4,-1,g.comFmtFlags);
                }else{
                  fossil_print("%s\n",blob_str(&val));
                }
                blob_reset(&val);
              }
            }







|







1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
                  fossil_print("  Append to ");
            z++;
          }else{
            fossil_print("  Change ");
          }
          fossil_print("%h: ",z);
          if( blob_size(&val)>50 || contains_newline(&val)) {
                  fossil_print("\n    ");
                  comment_print(blob_str(&val),0,4,-1,g.comFmtFlags);
                }else{
                  fossil_print("%s\n",blob_str(&val));
                }
                blob_reset(&val);
              }
            }
Changes to src/translate.c.
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
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** SYNOPSIS: 
**
** Input lines that begin with the "@" character are translated into
** either cgi_printf() statements or string literals and the
** translated code is written on standard output.
**
** The problem this program is attempt to solve is as follows:  When
** writing CGI programs in C, we typically want to output a lot of HTML
** text to standard output.  In pure C code, this involves doing a
** printf() with a big string containing all that text.  But we have
** to insert special codes (ex: \n and \") for many common characters,
** which interferes with the readability of the HTML.
**
** This tool allows us to put raw HTML, without the special codes, in
** the middle of a C program.  This program then translates the text
** into standard C by inserting all necessary backslashes and other
** punctuation.
**
** Enhancement #1:
**
** If the last non-whitespace character prior to the first "@" of a
** @-block is "=" or "," then the @-block is a string literal initializer 
** rather than text that is to be output via cgi_printf().  Render it
** as such.
**
** Enhancement #2:
**
** Comments of the form:  "/* @-comment: CC" cause CC to become a 
** comment character for the @-substitution.  Typical values for CC are
** "--" (for SQL text) or "#" (for TCL script) or "//" (for C++ code).
** Lines of subsequent @-blocks that begin with CC are omitted from the
** output.
** 
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

/*







|




















|





|




|







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
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** SYNOPSIS:
**
** Input lines that begin with the "@" character are translated into
** either cgi_printf() statements or string literals and the
** translated code is written on standard output.
**
** The problem this program is attempt to solve is as follows:  When
** writing CGI programs in C, we typically want to output a lot of HTML
** text to standard output.  In pure C code, this involves doing a
** printf() with a big string containing all that text.  But we have
** to insert special codes (ex: \n and \") for many common characters,
** which interferes with the readability of the HTML.
**
** This tool allows us to put raw HTML, without the special codes, in
** the middle of a C program.  This program then translates the text
** into standard C by inserting all necessary backslashes and other
** punctuation.
**
** Enhancement #1:
**
** If the last non-whitespace character prior to the first "@" of a
** @-block is "=" or "," then the @-block is a string literal initializer
** rather than text that is to be output via cgi_printf().  Render it
** as such.
**
** Enhancement #2:
**
** Comments of the form:  "/* @-comment: CC" cause CC to become a
** comment character for the @-substitution.  Typical values for CC are
** "--" (for SQL text) or "#" (for TCL script) or "//" (for C++ code).
** Lines of subsequent @-blocks that begin with CC are omitted from the
** output.
**
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

/*
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
      i++;
      if( isspace(zLine[i]) ){ i++; }
      indent = i - 2;
      if( indent<0 ) indent = 0;
      omitline = 0;
      for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
        if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
           omitline = 1; break; 
        }
        if( zLine[i]=='"' || zLine[i]=='\\' ){ zOut[j++] = '\\'; }
        zOut[j++] = zLine[i];
      }
      while( j>0 && isspace(zOut[j-1]) ){ j--; }
      zOut[j] = 0;
      if( j<=0 && omitline ){
        fprintf(out,"\n");
      }else{
        fprintf(out,"%*s\"%s\\n\"\n",indent, "", zOut);
      }
    }else{
      /* Otherwise (if the last non-whitespace was not '=') then generate
      ** a cgi_printf() statement whose format is the text following the '@'.
      ** Substrings of the form "%C(...)" (where C is any sequence of 
      ** characters other than \000 and '(') will put "%C" in the
      ** format and add the "(...)" as an argument to the cgi_printf call.
      */
      int indent;
      int nC;
      char c;
      i++;







|














|







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
      i++;
      if( isspace(zLine[i]) ){ i++; }
      indent = i - 2;
      if( indent<0 ) indent = 0;
      omitline = 0;
      for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
        if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
           omitline = 1; break;
        }
        if( zLine[i]=='"' || zLine[i]=='\\' ){ zOut[j++] = '\\'; }
        zOut[j++] = zLine[i];
      }
      while( j>0 && isspace(zOut[j-1]) ){ j--; }
      zOut[j] = 0;
      if( j<=0 && omitline ){
        fprintf(out,"\n");
      }else{
        fprintf(out,"%*s\"%s\\n\"\n",indent, "", zOut);
      }
    }else{
      /* Otherwise (if the last non-whitespace was not '=') then generate
      ** a cgi_printf() statement whose format is the text following the '@'.
      ** Substrings of the form "%C(...)" (where C is any sequence of
      ** characters other than \000 and '(') will put "%C" in the
      ** format and add the "(...)" as an argument to the cgi_printf call.
      */
      int indent;
      int nC;
      char c;
      i++;
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
      zOut[j] = 0;
      if( !inPrint ){
        fprintf(out,"%*scgi_printf(\"%s\\n\"",indent-2,"", zOut);
        inPrint = 1;
      }else{
        fprintf(out,"\n%*s\"%s\\n\"",indent+5, "", zOut);
      }
    }      
  }
}

int main(int argc, char **argv){
  if( argc==2 ){
    char *arg;
    FILE *in = fopen(argv[1], "r");







|







172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
      zOut[j] = 0;
      if( !inPrint ){
        fprintf(out,"%*scgi_printf(\"%s\\n\"",indent-2,"", zOut);
        inPrint = 1;
      }else{
        fprintf(out,"\n%*s\"%s\\n\"",indent+5, "", zOut);
      }
    }
  }
}

int main(int argc, char **argv){
  if( argc==2 ){
    char *arg;
    FILE *in = fopen(argv[1], "r");
Changes to src/undo.c.
141
142
143
144
145
146
147
148

149
150
151
152
153
154
155
    "CREATE TEMP TABLE undo_vmerge_2 AS SELECT * FROM vmerge;"
    "DELETE FROM vmerge;"
    "INSERT INTO vmerge SELECT * FROM undo_vmerge;"
    "DELETE FROM undo_vmerge;"
    "INSERT INTO undo_vmerge SELECT * FROM undo_vmerge_2;"
    "DROP TABLE undo_vmerge_2;"
  );
  if(db_exists("SELECT 1 FROM %s.sqlite_master WHERE name='undo_stash'", zDb) ){

    if( redoFlag ){
      db_multi_exec(
        "DELETE FROM stash WHERE stashid IN (SELECT stashid FROM undo_stash);"
        "DELETE FROM stashfile"
        " WHERE stashid NOT IN (SELECT stashid FROM stash);"
      );
    }else{







|
>







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
    "CREATE TEMP TABLE undo_vmerge_2 AS SELECT * FROM vmerge;"
    "DELETE FROM vmerge;"
    "INSERT INTO vmerge SELECT * FROM undo_vmerge;"
    "DELETE FROM undo_vmerge;"
    "INSERT INTO undo_vmerge SELECT * FROM undo_vmerge_2;"
    "DROP TABLE undo_vmerge_2;"
  );
  if(db_exists("SELECT 1 FROM \"%w\".sqlite_master"
               " WHERE name='undo_stash'", zDb) ){
    if( redoFlag ){
      db_multi_exec(
        "DELETE FROM stash WHERE stashid IN (SELECT stashid FROM undo_stash);"
        "DELETE FROM stashfile"
        " WHERE stashid NOT IN (SELECT stashid FROM stash);"
      );
    }else{
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  static const char zSql[] =
    @ DROP TABLE IF EXISTS undo;
    @ DROP TABLE IF EXISTS undo_vfile;
    @ DROP TABLE IF EXISTS undo_vmerge;
    @ DROP TABLE IF EXISTS undo_stash;
    @ DROP TABLE IF EXISTS undo_stashfile;
    ;
  db_multi_exec(zSql);
  db_lset_int("undo_available", 0);
  db_lset_int("undo_checkout", 0);
}

/*
** The following variable stores the original command-line of the
** command that is a candidate to be undone.







|







173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  static const char zSql[] =
    @ DROP TABLE IF EXISTS undo;
    @ DROP TABLE IF EXISTS undo_vfile;
    @ DROP TABLE IF EXISTS undo_vmerge;
    @ DROP TABLE IF EXISTS undo_stash;
    @ DROP TABLE IF EXISTS undo_stashfile;
    ;
  db_multi_exec(zSql /*works-like:""*/);
  db_lset_int("undo_available", 0);
  db_lset_int("undo_checkout", 0);
}

/*
** The following variable stores the original command-line of the
** command that is a candidate to be undone.
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
/*
** Begin capturing a snapshot that can be undone.
*/
void undo_begin(void){
  int cid;
  const char *zDb = db_name("localdb");
  static const char zSql[] = 
    @ CREATE TABLE %s.undo(
    @   pathname TEXT UNIQUE,             -- Name of the file
    @   redoflag BOOLEAN,                 -- 0 for undoable.  1 for redoable
    @   existsflag BOOLEAN,               -- True if the file exists
    @   isExe BOOLEAN,                    -- True if the file is executable
    @   isLink BOOLEAN,                   -- True if the file is symlink
    @   content BLOB                      -- Saved content
    @ );
    @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
    @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
  ;
  if( undoDisable ) return;
  undo_reset();
  db_multi_exec(zSql, zDb, zDb, zDb);
  cid = db_lget_int("checkout", 0);
  db_lset_int("undo_checkout", cid);
  db_lset_int("undo_available", 1);
  db_lset("undo_cmdline", undoCmd);
  undoActive = 1;
}








|







|
|



|







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
/*
** Begin capturing a snapshot that can be undone.
*/
void undo_begin(void){
  int cid;
  const char *zDb = db_name("localdb");
  static const char zSql[] = 
    @ CREATE TABLE "%w".undo(
    @   pathname TEXT UNIQUE,             -- Name of the file
    @   redoflag BOOLEAN,                 -- 0 for undoable.  1 for redoable
    @   existsflag BOOLEAN,               -- True if the file exists
    @   isExe BOOLEAN,                    -- True if the file is executable
    @   isLink BOOLEAN,                   -- True if the file is symlink
    @   content BLOB                      -- Saved content
    @ );
    @ CREATE TABLE "%w".undo_vfile AS SELECT * FROM vfile;
    @ CREATE TABLE "%w".undo_vmerge AS SELECT * FROM vmerge;
  ;
  if( undoDisable ) return;
  undo_reset();
  db_multi_exec(zSql/*works-like:"%w,%w,%w"*/, zDb, zDb, zDb);
  cid = db_lget_int("checkout", 0);
  db_lset_int("undo_checkout", cid);
  db_lset_int("undo_available", 1);
  db_lset("undo_cmdline", undoCmd);
  undoActive = 1;
}

299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

/*
** Make the current state of stashid undoable.
*/
void undo_save_stash(int stashid){
  const char *zDb = db_name("localdb");
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS %s.undo_stash"
    "  AS SELECT * FROM stash WHERE 0;"
    "INSERT INTO undo_stash"
    " SELECT * FROM stash WHERE stashid=%d;",
    zDb, stashid
  );
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS %s.undo_stashfile"
    "  AS SELECT * FROM stashfile WHERE 0;"
    "INSERT INTO undo_stashfile"
    " SELECT * FROM stashfile WHERE stashid=%d;",
    zDb, stashid
  );
}








|






|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321

/*
** Make the current state of stashid undoable.
*/
void undo_save_stash(int stashid){
  const char *zDb = db_name("localdb");
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS \"%w\".undo_stash"
    "  AS SELECT * FROM stash WHERE 0;"
    "INSERT INTO undo_stash"
    " SELECT * FROM stash WHERE stashid=%d;",
    zDb, stashid
  );
  db_multi_exec(
    "CREATE TABLE IF NOT EXISTS \"%w\".undo_stashfile"
    "  AS SELECT * FROM stashfile WHERE 0;"
    "INSERT INTO undo_stashfile"
    " SELECT * FROM stashfile WHERE stashid=%d;",
    zDb, stashid
  );
}

Changes to src/update.c.
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
    blob_zero(&sql);
    blob_append(&sql, "DELETE FROM fv WHERE ", -1);
    zSep = "";
    for(i=3; i<g.argc; i++){
      file_tree_name(g.argv[i], &treename, 1);
      if( file_wd_isdir(g.argv[i])==1 ){
        if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){
          blob_appendf(&sql, "%sfn NOT GLOB '%b/*' ", zSep, &treename);

        }else{
          blob_reset(&sql);
          break;
        }
      }else{
        blob_appendf(&sql, "%sfn<>%B ", zSep, &treename);

      }
      zSep = "AND ";
      blob_reset(&treename);
    }
    db_multi_exec(blob_str(&sql));
    blob_reset(&sql);
  }

  /*
  ** Alter the content of the checkout so that it conforms with the
  ** target
  */







|
>





|
>




|







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
    blob_zero(&sql);
    blob_append(&sql, "DELETE FROM fv WHERE ", -1);
    zSep = "";
    for(i=3; i<g.argc; i++){
      file_tree_name(g.argv[i], &treename, 1);
      if( file_wd_isdir(g.argv[i])==1 ){
        if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){
          blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ", 
                         zSep /*safe-for-%s*/, blob_str(&treename));
        }else{
          blob_reset(&sql);
          break;
        }
      }else{
        blob_append_sql(&sql, "%sfn<>%Q ",
                        zSep /*safe-for-%s*/, blob_str(&treename));
      }
      zSep = "AND ";
      blob_reset(&treename);
    }
    db_multi_exec("%s", blob_sql_text(&sql));
    blob_reset(&sql);
  }

  /*
  ** Alter the content of the checkout so that it conforms with the
  ** target
  */
Changes to src/user.c.
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
                  "(SELECT rowid FROM accesslog ORDER BY rowid DESC"
                  " LIMIT -1 OFFSET 200)");
    cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n);
    return;
  }
  style_header("Access Log");
  blob_zero(&sql);
  blob_appendf(&sql,
    "SELECT uname, ipaddr, datetime(mtime%s), success"
    "  FROM accesslog", timeline_utc()
  );
  if( y==1 ){
    blob_append(&sql, "  WHERE success", -1);
  }else if( y==2 ){
    blob_append(&sql, "  WHERE NOT success", -1);
  }
  blob_appendf(&sql,"  ORDER BY rowid DESC LIMIT %d OFFSET %d", n+1, skip);
  if( skip ){
    style_submenu_element("Newer", "Newer entries",
              "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0,
              n, y);
  }
  rc = db_prepare_ignore_error(&q, blob_str(&sql));
  @ <center><table border="1" cellpadding="5">
  @ <tr><th width="33%%">Date</th><th width="34%%">User</th>
  @ <th width="33%%">IP Address</th></tr>
  while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zIP = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);







|








|





|







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
                  "(SELECT rowid FROM accesslog ORDER BY rowid DESC"
                  " LIMIT -1 OFFSET 200)");
    cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n);
    return;
  }
  style_header("Access Log");
  blob_zero(&sql);
  blob_append_sql(&sql,
    "SELECT uname, ipaddr, datetime(mtime%s), success"
    "  FROM accesslog", timeline_utc()
  );
  if( y==1 ){
    blob_append(&sql, "  WHERE success", -1);
  }else if( y==2 ){
    blob_append(&sql, "  WHERE NOT success", -1);
  }
  blob_append_sql(&sql,"  ORDER BY rowid DESC LIMIT %d OFFSET %d", n+1, skip);
  if( skip ){
    style_submenu_element("Newer", "Newer entries",
              "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0,
              n, y);
  }
  rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql));
  @ <center><table border="1" cellpadding="5">
  @ <tr><th width="33%%">Date</th><th width="34%%">User</th>
  @ <th width="33%%">IP Address</th></tr>
  while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    const char *zIP = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
Changes to src/wiki.c.
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    }
    if( g.perm.Hyperlink ){
      style_submenu_element("History", "History", "%s/whistory?name=%T",
           g.zTop, zPageName);
    }
  }
  style_set_current_page("%T?name=%T", g.zPath, zPageName);
  style_header(zPageName);
  blob_init(&wiki, zBody, -1);
  wiki_render_by_mimetype(&wiki, zMimetype);
  blob_reset(&wiki);
  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
  manifest_destroy(pWiki);
  style_footer();
}







|







290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    }
    if( g.perm.Hyperlink ){
      style_submenu_element("History", "History", "%s/whistory?name=%T",
           g.zTop, zPageName);
    }
  }
  style_set_current_page("%T?name=%T", g.zPath, zPageName);
  style_header("%s", zPageName);
  blob_init(&wiki, zBody, -1);
  wiki_render_by_mimetype(&wiki, zMimetype);
  blob_reset(&wiki);
  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
  manifest_destroy(pWiki);
  style_footer();
}
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
    Blob cksum;
    Blob body;
    Blob wiki;
    Manifest *pWiki = 0;

    blob_zero(&body);
    if( isSandbox ){
      blob_appendf(&body, db_get("sandbox",""));
      appendRemark(&body, zMimetype);
      db_set("sandbox", blob_str(&body), 0);
    }else{
      login_verify_csrf_secret();
      pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
      if( pWiki ){
        blob_append(&body, pWiki->zWiki, -1);







|







647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
    Blob cksum;
    Blob body;
    Blob wiki;
    Manifest *pWiki = 0;

    blob_zero(&body);
    if( isSandbox ){
      blob_append(&body, db_get("sandbox",""), -1);
      appendRemark(&body, zMimetype);
      db_set("sandbox", blob_str(&body), 0);
    }else{
      login_verify_csrf_secret();
      pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
      if( pWiki ){
        blob_append(&body, pWiki->zWiki, -1);
744
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
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Show the complete change history for a single wiki page.
*/
void whistory_page(void){
  Stmt q;
  char *zTitle;
  char *zSQL;
  const char *zPageName;
  login_check_credentials();
  if( !g.perm.Hyperlink ){ login_needed(); return; }
  zPageName = PD("name","");
  zTitle = mprintf("History Of %s", zPageName);
  style_header(zTitle);
  free(zTitle);

  zSQL = mprintf("%s AND event.objid IN "
                 "  (SELECT rid FROM tagxref WHERE tagid="
                       "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
                 "   UNION SELECT attachid FROM attachment"
                          " WHERE target=%Q)"
                 "ORDER BY mtime DESC",
                 timeline_query_for_www(), zPageName, zPageName);
  db_prepare(&q, zSQL);
  free(zSQL);
  zWikiPageName = zPageName;
  www_print_timeline(&q, TIMELINE_ARTID, 0, 0, wiki_history_extra);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: wdiff
** URL: /whistory?name=PAGENAME&a=RID1&b=RID2
**
** Show the difference between two wiki pages.
*/
void wdiff_page(void){
  char *zTitle;
  int rid1, rid2;
  const char *zPageName;
  Manifest *pW1, *pW2 = 0;
  Blob w1, w2, d;
  u64 diffFlags;

  login_check_credentials();
  rid1 = atoi(PD("a","0"));
  if( !g.perm.Hyperlink ){ login_needed(); return; }
  if( rid1==0 ) fossil_redirect_home();
  rid2 = atoi(PD("b","0"));
  zPageName = PD("name","");
  zTitle = mprintf("Changes To %s", zPageName);
  style_header(zTitle);
  free(zTitle);

  if( rid2==0 ){
    rid2 = db_int(0,
      "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid="
                        "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
      " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
      " ORDER BY event.mtime DESC LIMIT 1",







<
<




|
<
<

|






<
<













<












|
<
<







744
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
771
772
773
774
775
776

777
778
779
780
781
782
783
784
785
786
787
788
789


790
791
792
793
794
795
796
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Show the complete change history for a single wiki page.
*/
void whistory_page(void){
  Stmt q;


  const char *zPageName;
  login_check_credentials();
  if( !g.perm.Hyperlink ){ login_needed(); return; }
  zPageName = PD("name","");
  style_header("History Of %s", zPageName);



  db_prepare(&q, "%s AND event.objid IN "
                 "  (SELECT rid FROM tagxref WHERE tagid="
                       "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
                 "   UNION SELECT attachid FROM attachment"
                          " WHERE target=%Q)"
                 "ORDER BY mtime DESC",
                 timeline_query_for_www(), zPageName, zPageName);


  zWikiPageName = zPageName;
  www_print_timeline(&q, TIMELINE_ARTID, 0, 0, wiki_history_extra);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: wdiff
** URL: /whistory?name=PAGENAME&a=RID1&b=RID2
**
** Show the difference between two wiki pages.
*/
void wdiff_page(void){

  int rid1, rid2;
  const char *zPageName;
  Manifest *pW1, *pW2 = 0;
  Blob w1, w2, d;
  u64 diffFlags;

  login_check_credentials();
  rid1 = atoi(PD("a","0"));
  if( !g.perm.Hyperlink ){ login_needed(); return; }
  if( rid1==0 ) fossil_redirect_home();
  rid2 = atoi(PD("b","0"));
  zPageName = PD("name","");
  style_header("Changes To %s", zPageName);



  if( rid2==0 ){
    rid2 = db_int(0,
      "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid="
                        "(SELECT tagid FROM tag WHERE tagname='wiki-%q')"
      " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)"
      " ORDER BY event.mtime DESC LIMIT 1",
Changes to src/wikiformat.c.
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
/*
** Begin a new paragraph if that something that is needed.
*/
static void startAutoParagraph(Renderer *p){
  if( p->wantAutoParagraph==0 ) return;
  if( p->state & WIKI_LINKSONLY ) return;
  if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
  blob_appendf(p->pOut, "<p>", -1);
  p->wantAutoParagraph = 0;
  p->inAutoParagraph = 1;
}

/*
** End a paragraph if we are in one.
*/







|







1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
/*
** Begin a new paragraph if that something that is needed.
*/
static void startAutoParagraph(Renderer *p){
  if( p->wantAutoParagraph==0 ) return;
  if( p->state & WIKI_LINKSONLY ) return;
  if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
  blob_append(p->pOut, "<p>", -1);
  p->wantAutoParagraph = 0;
  p->inAutoParagraph = 1;
}

/*
** End a paragraph if we are in one.
*/
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  memcpy(zUpper, zLower, n+1);
  zUpper[n-1]++;
  if( once ){
    const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
    db_static_prepare(&q,
      "SELECT %s FROM ticket "
      " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
      zClosedExpr
    );
    once = 0;
  }
  db_bind_text(&q, ":lwr", zLower);
  db_bind_text(&q, ":upr", zUpper);
  if( db_step(&q)==SQLITE_ROW ){
    rc = 1;







|







1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  memcpy(zUpper, zLower, n+1);
  zUpper[n-1]++;
  if( once ){
    const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
    db_static_prepare(&q,
      "SELECT %s FROM ticket "
      " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
      zClosedExpr /*safe-for-%s*/
    );
    once = 0;
  }
  db_bind_text(&q, ":lwr", zLower);
  db_bind_text(&q, ":upr", zUpper);
  if( db_step(&q)==SQLITE_ROW ){
    rc = 1;
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
          zTerm = "]";
        }
      }
    }else if( !in_this_repo(zTarget) ){
      if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
        zTerm = "";
      }else{
        blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
        zTerm = "]</span>";
      }
    }else if( g.perm.Hyperlink ){
      blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget));
      zTerm = "]</a>";
    }
  }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'







|







1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
          zTerm = "]";
        }
      }
    }else if( !in_this_repo(zTarget) ){
      if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
        zTerm = "";
      }else{
        blob_appendf(p->pOut, "<span class=\"brokenlink\">[");
        zTerm = "]</span>";
      }
    }else if( g.perm.Hyperlink ){
      blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget));
      zTerm = "]</a>";
    }
  }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
          blob_append(p->pOut, " &nbsp;&nbsp; ", -1);
        }else{
          if( p->wikiList ){
            popStackToTag(p, p->wikiList);
            p->wikiList = 0;
          }
          endAutoParagraph(p);
          blob_appendf(p->pOut, "\n\n", 1);
          p->wantAutoParagraph = 1;
        }
        p->state |= AT_PARAGRAPH|AT_NEWLINE;
        break;
      }
      case TOKEN_NEWLINE: {
        blob_append(p->pOut, "\n", 1);







|







1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
          blob_append(p->pOut, " &nbsp;&nbsp; ", -1);
        }else{
          if( p->wikiList ){
            popStackToTag(p, p->wikiList);
            p->wikiList = 0;
          }
          endAutoParagraph(p);
          blob_append(p->pOut, "\n\n", 1);
          p->wantAutoParagraph = 1;
        }
        p->state |= AT_PARAGRAPH|AT_NEWLINE;
        break;
      }
      case TOKEN_NEWLINE: {
        blob_append(p->pOut, "\n", 1);
Changes to src/winhttp.c.
56
57
58
59
60
61
62











63
64
65
66
67
68
69
        return atoi(&zHdr[17]);
      }
    }
    zHdr++;
  }
  return 0;
}












/*
** Process a single incoming HTTP request.
*/
static void win32_http_request(void *pAppData){
  HttpRequest *p = (HttpRequest*)pAppData;
  FILE *in = 0, *out = 0;







>
>
>
>
>
>
>
>
>
>
>







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
        return atoi(&zHdr[17]);
      }
    }
    zHdr++;
  }
  return 0;
}

/*
** Issue a fatal error.
*/
static NORETURN void winhttp_fatal(
  const char *zOp,
  const char *zService,
  const char *zErr
){
  fossil_fatal("unable to %s service '%s': %s", zOp, zService, zErr);
}

/*
** Process a single incoming HTTP request.
*/
static void win32_http_request(void *pAppData){
  HttpRequest *p = (HttpRequest*)pAppData;
  FILE *in = 0, *out = 0;
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
    fossil_fatal("unable to get path to the temporary directory.");
  }
  zTempPrefix = mprintf("%sfossil_server_P%d_",
                        fossil_unicode_to_utf8(zTmpPath), iPort);
  fossil_print("Listening for %s requests on TCP port %d\n",
               (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort);
  if( zBrowser ){
    zBrowser = mprintf(zBrowser, iPort);
    fossil_print("Launch webbrowser: %s\n", zBrowser);
    fossil_system(zBrowser);
  }
  fossil_print("Type Ctrl-C to stop the HTTP server\n");
  /* Set the service status to running and pass the listener socket to the
  ** service handling procedures. */
  win32_http_service_running(s);







|







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
    fossil_fatal("unable to get path to the temporary directory.");
  }
  zTempPrefix = mprintf("%sfossil_server_P%d_",
                        fossil_unicode_to_utf8(zTmpPath), iPort);
  fossil_print("Listening for %s requests on TCP port %d\n",
               (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort);
  if( zBrowser ){
    zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
    fossil_print("Launch webbrowser: %s\n", zBrowser);
    fossil_system(zBrowser);
  }
  fossil_print("Type Ctrl-C to stop the HTTP server\n");
  /* Set the service status to running and pass the listener socket to the
  ** service handling procedures. */
  win32_http_service_running(s);
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  n = strlen(zMethod);

  if( strncmp(zMethod, "create", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_DESCRIPTIONW
      svcDescr = {L"Fossil - Distributed Software Configuration Management"};
    char *zErrFmt = "unable to create service '%s': %s";
    DWORD dwStartType = SERVICE_DEMAND_START;
    const char *zDisplay    = find_option("display", "D", 1);
    const char *zStart      = find_option("start", "S", 1);
    const char *zUsername   = find_option("username", "U", 1);
    const char *zPassword   = find_option("password", "W", 1);
    const char *zPort       = find_option("port", "P", 1);
    const char *zNotFound   = find_option("notfound", 0, 1);







<







688
689
690
691
692
693
694

695
696
697
698
699
700
701
  n = strlen(zMethod);

  if( strncmp(zMethod, "create", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_DESCRIPTIONW
      svcDescr = {L"Fossil - Distributed Software Configuration Management"};

    DWORD dwStartType = SERVICE_DEMAND_START;
    const char *zDisplay    = find_option("display", "D", 1);
    const char *zStart      = find_option("start", "S", 1);
    const char *zUsername   = find_option("username", "U", 1);
    const char *zPassword   = find_option("password", "W", 1);
    const char *zPort       = find_option("port", "P", 1);
    const char *zNotFound   = find_option("notfound", 0, 1);
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
    }
    if( zStart ){
      if( strncmp(zStart, "auto", strlen(zStart))==0 ){
        dwStartType = SERVICE_AUTO_START;
      }else if( strncmp(zStart, "manual", strlen(zStart))==0 ){
        dwStartType = SERVICE_DEMAND_START;
      }else{
        fossil_fatal(zErrFmt, zSvcName,
                     "specify 'auto' or 'manual' for the '-S|--start' option");
      }
    }
    /* Process options for Fossil running as server. */
    if( zPort && (atoi(zPort)<=0) ){
      fossil_fatal(zErrFmt, zSvcName,
                   "port number must be in the range 1 - 65535.");
    }
    if( !zRepository ){
      db_must_be_within_tree();
    }else if( file_isdir(zRepository)==1 ){
      g.zRepositoryName = mprintf("%s", zRepository);
      file_simplify_name(g.zRepositoryName, -1, 0);







|





|







722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
    }
    if( zStart ){
      if( strncmp(zStart, "auto", strlen(zStart))==0 ){
        dwStartType = SERVICE_AUTO_START;
      }else if( strncmp(zStart, "manual", strlen(zStart))==0 ){
        dwStartType = SERVICE_DEMAND_START;
      }else{
        winhttp_fatal("create", zSvcName,
                     "specify 'auto' or 'manual' for the '-S|--start' option");
      }
    }
    /* Process options for Fossil running as server. */
    if( zPort && (atoi(zPort)<=0) ){
      winhttp_fatal("create", zSvcName,
                   "port number must be in the range 1 - 65535.");
    }
    if( !zRepository ){
      db_must_be_within_tree();
    }else if( file_isdir(zRepository)==1 ){
      g.zRepositoryName = mprintf("%s", zRepository);
      file_simplify_name(g.zRepositoryName, -1, 0);
741
742
743
744
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
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
    if( useSCGI ) blob_appendf(&binPath, " --scgi");
    if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound);
    if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob);
    if( zLocalAuth ) blob_append(&binPath, " --localauth", -1);
    blob_appendf(&binPath, " \"%s\"", g.zRepositoryName);
    /* Create the service. */
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    hSvc = CreateServiceW(
             hScm,                                    /* Handle to the SCM */
             fossil_utf8_to_unicode(zSvcName),        /* Name of the service */
             fossil_utf8_to_unicode(zDisplay),        /* Display name */
             SERVICE_ALL_ACCESS,                      /* Desired access */
             SERVICE_WIN32_OWN_PROCESS,               /* Service type */
             dwStartType,                             /* Start type */
             SERVICE_ERROR_NORMAL,                    /* Error control */
             fossil_utf8_to_unicode(blob_str(&binPath)), /* Binary path */
             NULL,                                    /* Load ordering group */
             NULL,                                    /* Tag value */
             NULL,                                    /* Service dependencies */
             zUsername ? fossil_utf8_to_unicode(zUsername) : 0, /* Account */
             fossil_utf8_to_unicode(zPassword)        /* Account password */
           );
    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    /* Set the service description. */
    ChangeServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr);
    fossil_print("Service '%s' successfully created.\n", zSvcName);
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "delete", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;
    char *zErrFmt = "unable to delete service '%s': %s";

    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for delete method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
      fossil_print("Stopping service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
        }
      }
      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }
      fossil_print("\nService '%s' stopped.\n", zSvcName);
    }
    if( !DeleteService(hSvc) ){
      if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
        fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
      }else{
        fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
      }
    }else{
      fossil_print("Service '%s' successfully deleted.\n", zSvcName);
    }
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "show", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;
    LPQUERY_SERVICE_CONFIGW pSvcConfig;
    LPSERVICE_DESCRIPTIONW pSvcDescr;
    BOOL bStatus;
    DWORD nRequired;
    const char *zErrFmt = "unable to show service '%s': %s";
    static const char *const zSvcTypes[] = {
      "Driver service",
      "File system driver service",
      "Service runs in its own process",
      "Service shares a process with other services",
      "Service can interact with the desktop"
    };







|















|










<








|


|





|













|















<







751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784

785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831

832
833
834
835
836
837
838
    if( useSCGI ) blob_appendf(&binPath, " --scgi");
    if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound);
    if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob);
    if( zLocalAuth ) blob_append(&binPath, " --localauth", -1);
    blob_appendf(&binPath, " \"%s\"", g.zRepositoryName);
    /* Create the service. */
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) winhttp_fatal("create", zSvcName, win32_get_last_errmsg());
    hSvc = CreateServiceW(
             hScm,                                    /* Handle to the SCM */
             fossil_utf8_to_unicode(zSvcName),        /* Name of the service */
             fossil_utf8_to_unicode(zDisplay),        /* Display name */
             SERVICE_ALL_ACCESS,                      /* Desired access */
             SERVICE_WIN32_OWN_PROCESS,               /* Service type */
             dwStartType,                             /* Start type */
             SERVICE_ERROR_NORMAL,                    /* Error control */
             fossil_utf8_to_unicode(blob_str(&binPath)), /* Binary path */
             NULL,                                    /* Load ordering group */
             NULL,                                    /* Tag value */
             NULL,                                    /* Service dependencies */
             zUsername ? fossil_utf8_to_unicode(zUsername) : 0, /* Account */
             fossil_utf8_to_unicode(zPassword)        /* Account password */
           );
    if( !hSvc ) winhttp_fatal("create", zSvcName, win32_get_last_errmsg());
    /* Set the service description. */
    ChangeServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr);
    fossil_print("Service '%s' successfully created.\n", zSvcName);
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "delete", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;


    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for delete method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
      fossil_print("Stopping service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
        }
      }
      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }
      fossil_print("\nService '%s' stopped.\n", zSvcName);
    }
    if( !DeleteService(hSvc) ){
      if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
        fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
      }else{
        winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
      }
    }else{
      fossil_print("Service '%s' successfully deleted.\n", zSvcName);
    }
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "show", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;
    LPQUERY_SERVICE_CONFIGW pSvcConfig;
    LPSERVICE_DESCRIPTIONW pSvcDescr;
    BOOL bStatus;
    DWORD nRequired;

    static const char *const zSvcTypes[] = {
      "Driver service",
      "File system driver service",
      "Service runs in its own process",
      "Service shares a process with other services",
      "Service can interact with the desktop"
    };
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for show method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, GENERIC_READ);
    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), GENERIC_READ);
    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    /* Get the service configuration */
    bStatus = QueryServiceConfigW(hSvc, NULL, 0, &nRequired);
    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
      fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    }
    pSvcConfig = fossil_malloc(nRequired);
    bStatus = QueryServiceConfigW(hSvc, pSvcConfig, nRequired, &nRequired);
    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    /* Translate the service type */
    switch( pSvcConfig->dwServiceType ){
      case SERVICE_KERNEL_DRIVER:       zSvcType = zSvcTypes[0]; break;
      case SERVICE_FILE_SYSTEM_DRIVER:  zSvcType = zSvcTypes[1]; break;
      case SERVICE_WIN32_OWN_PROCESS:   zSvcType = zSvcTypes[2]; break;
      case SERVICE_WIN32_SHARE_PROCESS: zSvcType = zSvcTypes[3]; break;
      case SERVICE_INTERACTIVE_PROCESS: zSvcType = zSvcTypes[4]; break;
    }
    /* Translate the service start type */
    switch( pSvcConfig->dwStartType ){
      case SERVICE_BOOT_START:    zSvcStartType = zSvcStartTypes[0]; break;
      case SERVICE_SYSTEM_START:  zSvcStartType = zSvcStartTypes[1]; break;
      case SERVICE_AUTO_START:    zSvcStartType = zSvcStartTypes[2]; break;
      case SERVICE_DEMAND_START:  zSvcStartType = zSvcStartTypes[3]; break;
      case SERVICE_DISABLED:      zSvcStartType = zSvcStartTypes[4]; break;
    }
    /* Get the service description. */
    bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
                                  NULL, 0, &nRequired);
    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
      fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    }
    pSvcDescr = fossil_malloc(nRequired);
    bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
                                  (LPBYTE)pSvcDescr, nRequired, &nRequired);
    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    /* Retrieves the current status of the specified service. */
    bStatus = QueryServiceStatus(hSvc, &sstat);
    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    /* Translate the current state. */
    switch( sstat.dwCurrentState ){
      case SERVICE_STOPPED:          zSvcState = zSvcStates[0]; break;
      case SERVICE_START_PENDING:    zSvcState = zSvcStates[1]; break;
      case SERVICE_STOP_PENDING:     zSvcState = zSvcStates[2]; break;
      case SERVICE_RUNNING:          zSvcState = zSvcStates[3]; break;
      case SERVICE_CONTINUE_PENDING: zSvcState = zSvcStates[4]; break;







|

|



|



|




















|




|


|







854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for show method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, GENERIC_READ);
    if( !hScm ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), GENERIC_READ);
    if( !hSvc ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
    /* Get the service configuration */
    bStatus = QueryServiceConfigW(hSvc, NULL, 0, &nRequired);
    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
      winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
    }
    pSvcConfig = fossil_malloc(nRequired);
    bStatus = QueryServiceConfigW(hSvc, pSvcConfig, nRequired, &nRequired);
    if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
    /* Translate the service type */
    switch( pSvcConfig->dwServiceType ){
      case SERVICE_KERNEL_DRIVER:       zSvcType = zSvcTypes[0]; break;
      case SERVICE_FILE_SYSTEM_DRIVER:  zSvcType = zSvcTypes[1]; break;
      case SERVICE_WIN32_OWN_PROCESS:   zSvcType = zSvcTypes[2]; break;
      case SERVICE_WIN32_SHARE_PROCESS: zSvcType = zSvcTypes[3]; break;
      case SERVICE_INTERACTIVE_PROCESS: zSvcType = zSvcTypes[4]; break;
    }
    /* Translate the service start type */
    switch( pSvcConfig->dwStartType ){
      case SERVICE_BOOT_START:    zSvcStartType = zSvcStartTypes[0]; break;
      case SERVICE_SYSTEM_START:  zSvcStartType = zSvcStartTypes[1]; break;
      case SERVICE_AUTO_START:    zSvcStartType = zSvcStartTypes[2]; break;
      case SERVICE_DEMAND_START:  zSvcStartType = zSvcStartTypes[3]; break;
      case SERVICE_DISABLED:      zSvcStartType = zSvcStartTypes[4]; break;
    }
    /* Get the service description. */
    bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
                                  NULL, 0, &nRequired);
    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
      winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
    }
    pSvcDescr = fossil_malloc(nRequired);
    bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
                                  (LPBYTE)pSvcDescr, nRequired, &nRequired);
    if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
    /* Retrieves the current status of the specified service. */
    bStatus = QueryServiceStatus(hSvc, &sstat);
    if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
    /* Translate the current state. */
    switch( sstat.dwCurrentState ){
      case SERVICE_STOPPED:          zSvcState = zSvcStates[0]; break;
      case SERVICE_START_PENDING:    zSvcState = zSvcStates[1]; break;
      case SERVICE_STOP_PENDING:     zSvcState = zSvcStates[2]; break;
      case SERVICE_RUNNING:          zSvcState = zSvcStates[3]; break;
      case SERVICE_CONTINUE_PENDING: zSvcState = zSvcStates[4]; break;
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "start", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;
    char *zErrFmt = "unable to start service '%s': %s";

    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for start method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_RUNNING ){
      fossil_print("Starting service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
        if( !StartServiceW(hSvc, 0, NULL) ){
          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
        }
      }
      while( sstat.dwCurrentState!=SERVICE_RUNNING ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }
      fossil_print("\nService '%s' started.\n", zSvcName);
    }else{
      fossil_print("Service '%s' is already started.\n", zSvcName);
    }
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "stop", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;
    char *zErrFmt = "unable to stop service '%s': %s";

    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for stop method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
      fossil_print("Stopping service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
        }
      }
      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }







<








|


|





|


















<








|


|





|







927
928
929
930
931
932
933

934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969

970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "start", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;


    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for start method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_RUNNING ){
      fossil_print("Starting service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
        if( !StartServiceW(hSvc, 0, NULL) ){
          winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
        }
      }
      while( sstat.dwCurrentState!=SERVICE_RUNNING ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }
      fossil_print("\nService '%s' started.\n", zSvcName);
    }else{
      fossil_print("Service '%s' is already started.\n", zSvcName);
    }
    CloseServiceHandle(hSvc);
    CloseServiceHandle(hScm);
  }else
  if( strncmp(zMethod, "stop", n)==0 ){
    SC_HANDLE hScm;
    SC_HANDLE hSvc;
    SERVICE_STATUS sstat;


    verify_all_options();
    if( g.argc==4 ){
      zSvcName = g.argv[3];
    }else if( g.argc>4 ){
      fossil_fatal("too many arguments for stop method.");
    }
    hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if( !hScm ) winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
      fossil_print("Stopping service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
        }
      }
      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }
Changes to src/xfer.c.
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  };
  int i;
  Blob src, delta;
  int size = 0;
  int srcId = 0;

  for(i=0; srcId==0 && i<count(azQuery); i++){
    srcId = db_int(0, azQuery[i], rid);
  }
  if( srcId>0
   && (pXfer->syncPrivate || !content_is_private(srcId))
   && content_get(srcId, &src)
  ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
    blob_delta_create(&src, pContent, &delta);







|







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  };
  int i;
  Blob src, delta;
  int size = 0;
  int srcId = 0;

  for(i=0; srcId==0 && i<count(azQuery); i++){
    srcId = db_int(0, azQuery[i] /*works-like:"%d"*/, rid);
  }
  if( srcId>0
   && (pXfer->syncPrivate || !content_is_private(srcId))
   && content_get(srcId, &src)
  ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
    blob_delta_create(&src, pContent, &delta);
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  if( uuid_is_shunned(blob_str(pUuid)) ){
    blob_reset(&uuid);
    return;
  }
  if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
       pXfer->mxSend<=blob_size(pXfer->pOut) ){
    const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
    blob_appendf(pXfer->pOut, zFormat, pUuid);
    pXfer->nIGotSent++;
    blob_reset(&uuid);
    return;
  }
  if( nativeDelta ){
    size = send_delta_native(pXfer, rid, isPriv, pUuid);
    if( size ){







|







420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  if( uuid_is_shunned(blob_str(pUuid)) ){
    blob_reset(&uuid);
    return;
  }
  if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
       pXfer->mxSend<=blob_size(pXfer->pOut) ){
    const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
    blob_appendf(pXfer->pOut, zFormat /*works-like:"%b"*/, pUuid);
    pXfer->nIGotSent++;
    blob_reset(&uuid);
    return;
  }
  if( nativeDelta ){
    size = send_delta_native(pXfer, rid, isPriv, pUuid);
    if( size ){
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
    }
    blob_reset(&content);
  }
  remote_has(rid);
  blob_reset(&uuid);
#if 0
  if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
    blob_appendf(pXfer->pOut, "\n", 1);
  }
#endif
}

/*
** Send the file identified by rid as a compressed artifact.  Basically,
** send the content exactly as it appears in the BLOB table using







|







452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
    }
    blob_reset(&content);
  }
  remote_has(rid);
  blob_reset(&uuid);
#if 0
  if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
    blob_append(pXfer->pOut, "\n", 1);
  }
#endif
}

/*
** Send the file identified by rid as a compressed artifact.  Basically,
** send the content exactly as it appears in the BLOB table using
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
      pXfer->nDeltaSent++;
    }else{
      pXfer->nFileSent++;
    }
    blob_appendf(pXfer->pOut, "%d %d\n", szU, szC);
    blob_append(pXfer->pOut, zContent, szC);
    if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
      blob_appendf(pXfer->pOut, "\n", 1);
    }
    if( !isPrivate && srcIsPrivate ){
      blob_reset(&fullContent);
    }
  }
  db_reset(&q1);
}







|







512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
      pXfer->nDeltaSent++;
    }else{
      pXfer->nFileSent++;
    }
    blob_appendf(pXfer->pOut, "%d %d\n", szU, szC);
    blob_append(pXfer->pOut, zContent, szC);
    if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
      blob_append(pXfer->pOut, "\n", 1);
    }
    if( !isPrivate && srcIsPrivate ){
      blob_reset(&fullContent);
    }
  }
  db_reset(&q1);
}
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
        md5sum_blob(&cluster, &cksum);
        blob_appendf(&cluster, "Z %b\n", &cksum);
        blob_reset(&cksum);
        rid = content_put(&cluster);
        blob_reset(&cluster);
        nUncl -= nRow;
        nRow = 0;
        blob_appendf(&deleteWhere, ",%d", rid);
      }
    }
    db_finalize(&q);
    db_multi_exec(
      "DELETE FROM unclustered WHERE rid NOT IN (0 %s)"
      "   AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=unclustered.rid)",
      blob_str(&deleteWhere)
    );
    blob_reset(&deleteWhere);
    if( nRow>0 ){
      md5sum_blob(&cluster, &cksum);
      blob_appendf(&cluster, "Z %b\n", &cksum);
      blob_reset(&cksum);
      content_put(&cluster);







|






|







709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
        md5sum_blob(&cluster, &cksum);
        blob_appendf(&cluster, "Z %b\n", &cksum);
        blob_reset(&cksum);
        rid = content_put(&cluster);
        blob_reset(&cluster);
        nUncl -= nRow;
        nRow = 0;
        blob_append_sql(&deleteWhere, ",%d", rid);
      }
    }
    db_finalize(&q);
    db_multi_exec(
      "DELETE FROM unclustered WHERE rid NOT IN (0 %s)"
      "   AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=unclustered.rid)",
      blob_sql_text(&deleteWhere)
    );
    blob_reset(&deleteWhere);
    if( nRow>0 ){
      md5sum_blob(&cluster, &cksum);
      blob_appendf(&cluster, "Z %b\n", &cksum);
      blob_reset(&cksum);
      content_put(&cluster);
1494
1495
1496
1497
1498
1499
1500

1501
1502
1503
1504
1505
1506
1507
1508
  if( syncFlags & SYNC_PUSH ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCardSent++;
    if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
    if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff;
  }
  if( syncFlags & SYNC_VERBOSE ){

    fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
  }

  while( go ){
    int newPhantom = 0;
    char *zRandomness;
    db_begin_transaction();
    db_record_repository_filename(0);







>
|







1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
  if( syncFlags & SYNC_PUSH ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCardSent++;
    if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
    if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff;
  }
  if( syncFlags & SYNC_VERBOSE ){
    fossil_print(zLabelFormat /*works-like:"%s%s%s%s%d"*/,
                 "", "Bytes", "Cards", "Artifacts", "Deltas");
  }

  while( go ){
    int newPhantom = 0;
    char *zRandomness;
    db_begin_transaction();
    db_record_repository_filename(0);
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603

1604
1605
1606
1607
1608
1609
1610
1611
      nErr++;
      go = 2;
      break;
    }

    /* Output current stats */
    if( syncFlags & SYNC_VERBOSE ){
      fossil_print(zValueFormat, "Sent:",
                   blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
                   xfer.nFileSent, xfer.nDeltaSent);
    }else{
      nRoundtrip++;
      nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;

      fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
    }
    nCardSent = 0;
    nCardRcvd = 0;
    xfer.nFileSent = 0;
    xfer.nDeltaSent = 0;
    xfer.nGimmeSent = 0;
    xfer.nIGotSent = 0;







|





>
|







1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
      nErr++;
      go = 2;
      break;
    }

    /* Output current stats */
    if( syncFlags & SYNC_VERBOSE ){
      fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:",
                   blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
                   xfer.nFileSent, xfer.nDeltaSent);
    }else{
      nRoundtrip++;
      nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
      fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
                   nRoundtrip, nArtifactSent, nArtifactRcvd);
    }
    nCardSent = 0;
    nCardRcvd = 0;
    xfer.nFileSent = 0;
    xfer.nDeltaSent = 0;
    xfer.nGimmeSent = 0;
    xfer.nIGotSent = 0;
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908

1909
1910
1911
1912
1913
1914
1915
1916
    if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0
     && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
    ){
      configure_finalize_receive();
    }
    origConfigRcvMask = 0;
    if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
      fossil_print(zValueFormat, "Received:",
                   blob_size(&recv), nCardRcvd,
                   xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
    }else{

      fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
    }
    blob_reset(&recv);
    nCycle++;

    /* If we received one or more files on the previous exchange but
    ** there are still phantoms, then go another round.
    */







|



>
|







1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
    if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0
     && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
    ){
      configure_finalize_receive();
    }
    origConfigRcvMask = 0;
    if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
      fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:",
                   blob_size(&recv), nCardRcvd,
                   xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
    }else{
      fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
                   nRoundtrip, nArtifactSent, nArtifactRcvd);
    }
    blob_reset(&recv);
    nCycle++;

    /* If we received one or more files on the previous exchange but
    ** there are still phantoms, then go another round.
    */
Changes to win/Makefile.dmc.
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
RC=$(DMDIR)\bin\rcc
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__

APPNAME = $(OBJDIR)\fossil$(E)

all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E headers  $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR)

	$(DMDIR)\bin\link @link

$(OBJDIR)\fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**

$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
	+echo add allrepo attach bag bisect blob branch browse cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo fusefs glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf rebuild regexp report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@







|

>







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
RC=$(DMDIR)\bin\rcc
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__

APPNAME = $(OBJDIR)\fossil$(E)

all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E codecheck1$E headers  $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR)
	codecheck1$E $(SRC)
	$(DMDIR)\bin\link @link

$(OBJDIR)\fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**

$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
	+echo add allrepo attach bag bisect blob branch browse cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo fusefs glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf rebuild regexp report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
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

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) -o$@ $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) -o$@ $**

version$E: $B\src\mkversion.c



	$(BCC) -o$@ $**

$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
	$(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $**

$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
	$(TCC) -o$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**

$(OBJDIR)\th$O : $(SRCDIR)\th.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
	cp $@ $@

VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
	+$** > $@

page_index.h: mkindex$E $(SRC)
	+$** > $@

clean:
	-del $(OBJDIR)\*.obj
	-del *.obj *_.c *.h *.map

realclean:
	-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E

$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h







|
>
>
>

















|










|







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

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) -o$@ $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) -o$@ $**

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) -o$@ $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) -o$@ $**

$(OBJDIR)\shell$O : $(SRCDIR)\shell.c
	$(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $**

$(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c
	$(TCC) -o$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $**

$(OBJDIR)\th$O : $(SRCDIR)\th.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
	$(TCC) -o$@ -c $**

$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
	cp $@ $@

VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
	+$** > $@

page_index.h: mkindex$E $(SRC)
	+$** > $@

clean:
	-del $(OBJDIR)\*.obj
	-del *.obj *_.c *.h *.map

realclean:
	-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E codecheck1$E

$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h
Changes to win/Makefile.mingw.
683
684
685
686
687
688
689

690
691
692
693
694
695
696
697
698
699
700
701

702
703
704
705
706
707
708
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
VERSION     = $(subst /,\,$(OBJDIR)/version.exe)

CAT         = type
CP          = copy
GREP        = find
MV          = copy
RM          = del /Q
MKDIR       = -mkdir
RMDIR       = rmdir /S /Q
else
TRANSLATE   = $(OBJDIR)/translate.exe
MAKEHEADERS = $(OBJDIR)/makeheaders.exe
MKINDEX     = $(OBJDIR)/mkindex.exe
VERSION     = $(OBJDIR)/version.exe

CAT         = cat
CP          = cp
GREP        = grep
MV          = mv
RM          = rm -f
MKDIR       = -mkdir -p
RMDIR       = rm -rf







>












>







683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
VERSION     = $(subst /,\,$(OBJDIR)/version.exe)
CODECHECK1  = $(subst /,\,$(OBJDIR)/codecheck1.exe)
CAT         = type
CP          = copy
GREP        = find
MV          = copy
RM          = del /Q
MKDIR       = -mkdir
RMDIR       = rmdir /S /Q
else
TRANSLATE   = $(OBJDIR)/translate.exe
MAKEHEADERS = $(OBJDIR)/makeheaders.exe
MKINDEX     = $(OBJDIR)/mkindex.exe
VERSION     = $(OBJDIR)/version.exe
CODECHECK1  = $(OBJDIR)/codecheck1.exe
CAT         = cat
CP          = cp
GREP        = grep
MV          = mv
RM          = rm -f
MKDIR       = -mkdir -p
RMDIR       = rm -rf
748
749
750
751
752
753
754



755
756
757
758
759
760
761

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(BCC) -o $(MKINDEX) $(SRCDIR)/mkindex.c

$(VERSION): $(SRCDIR)/mkversion.c
	$(BCC) -o $(VERSION) $(SRCDIR)/mkversion.c




# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)







>
>
>







750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(BCC) -o $(MKINDEX) $(SRCDIR)/mkindex.c

$(VERSION): $(SRCDIR)/mkversion.c
	$(BCC) -o $(VERSION) $(SRCDIR)/mkversion.c

$(CODECHECK1):	$(SRCDIR)/codecheck1.c
	$(BCC) -o $(CODECHECK1) $(SRCDIR)/codecheck1.c

# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
813
814
815
816
817
818
819
820

821
822
823
824
825
826
827

APPTARGETS += $(LIBTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(APPTARGETS)

	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop







|
>







818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833

APPTARGETS += $(LIBTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(APPTARGETS)
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop
Changes to win/Makefile.mingw.mistachkin.
683
684
685
686
687
688
689

690
691
692
693
694
695
696
697
698
699
700
701

702
703
704
705
706
707
708
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
VERSION     = $(subst /,\,$(OBJDIR)/version.exe)

CAT         = type
CP          = copy
GREP        = find
MV          = copy
RM          = del /Q
MKDIR       = -mkdir
RMDIR       = rmdir /S /Q
else
TRANSLATE   = $(OBJDIR)/translate.exe
MAKEHEADERS = $(OBJDIR)/makeheaders.exe
MKINDEX     = $(OBJDIR)/mkindex.exe
VERSION     = $(OBJDIR)/version.exe

CAT         = cat
CP          = cp
GREP        = grep
MV          = mv
RM          = rm -f
MKDIR       = -mkdir -p
RMDIR       = rm -rf







>












>







683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
#    recognized internally by make.
#
ifdef USE_WINDOWS
TRANSLATE   = $(subst /,\,$(OBJDIR)/translate.exe)
MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe)
MKINDEX     = $(subst /,\,$(OBJDIR)/mkindex.exe)
VERSION     = $(subst /,\,$(OBJDIR)/version.exe)
CODECHECK1  = $(subst /,\,$(OBJDIR)/codecheck1.exe)
CAT         = type
CP          = copy
GREP        = find
MV          = copy
RM          = del /Q
MKDIR       = -mkdir
RMDIR       = rmdir /S /Q
else
TRANSLATE   = $(OBJDIR)/translate.exe
MAKEHEADERS = $(OBJDIR)/makeheaders.exe
MKINDEX     = $(OBJDIR)/mkindex.exe
VERSION     = $(OBJDIR)/version.exe
CODECHECK1  = $(OBJDIR)/codecheck1.exe
CAT         = cat
CP          = cp
GREP        = grep
MV          = mv
RM          = rm -f
MKDIR       = -mkdir -p
RMDIR       = rm -rf
748
749
750
751
752
753
754



755
756
757
758
759
760
761

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(BCC) -o $(MKINDEX) $(SRCDIR)/mkindex.c

$(VERSION): $(SRCDIR)/mkversion.c
	$(BCC) -o $(VERSION) $(SRCDIR)/mkversion.c




# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)







>
>
>







750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766

$(MKINDEX):	$(SRCDIR)/mkindex.c
	$(BCC) -o $(MKINDEX) $(SRCDIR)/mkindex.c

$(VERSION): $(SRCDIR)/mkversion.c
	$(BCC) -o $(VERSION) $(SRCDIR)/mkversion.c

$(CODECHECK1):	$(SRCDIR)/codecheck1.c
	$(BCC) -o $(CODECHECK1) $(SRCDIR)/codecheck1.c

# WARNING. DANGER. Running the test suite modifies the repository the
# build is done from, i.e. the checkout belongs to. Do not sync/push
# the repository after running the tests.
test:	$(OBJDIR) $(APPNAME)
	$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)

$(OBJDIR)/VERSION.h:	$(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
813
814
815
816
817
818
819
820

821
822
823
824
825
826
827

APPTARGETS += $(LIBTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(APPTARGETS)

	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop







|
>







818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833

APPTARGETS += $(LIBTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(APPTARGETS)
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop
Changes to win/Makefile.msc.
467
468
469
470
471
472
473
474
475

476
477
478
479
480
481
482

!ifdef FOSSIL_ENABLE_SSL
!ifdef FOSSIL_BUILD_SSL
APPTARGETS = $(APPTARGETS) openssl
!endif
!endif

$(APPNAME) : $(APPTARGETS) translate$E mkindex$E headers $(OBJ) $(OX)\linkopts
	cd $(OX)

	link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts

$(OX)\linkopts: $B\win\Makefile.msc
	echo $(OX)\add.obj > $@
	echo $(OX)\allrepo.obj >> $@
	echo $(OX)\attach.obj >> $@
	echo $(OX)\bag.obj >> $@







|

>







467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483

!ifdef FOSSIL_ENABLE_SSL
!ifdef FOSSIL_BUILD_SSL
APPTARGETS = $(APPTARGETS) openssl
!endif
!endif

$(APPNAME) : $(APPTARGETS) translate$E mkindex$E codecheck1$E headers $(OBJ) $(OX)\linkopts
	cd $(OX)
	codecheck1$E $(SRC)
	link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts

$(OX)\linkopts: $B\win\Makefile.msc
	echo $(OX)\add.obj > $@
	echo $(OX)\allrepo.obj >> $@
	echo $(OX)\attach.obj >> $@
	echo $(OX)\bag.obj >> $@
606
607
608
609
610
611
612
613



614
615
616
617
618
619
620

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) $**

mkversion$E: $B\src\mkversion.c



	$(BCC) $**

$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
	$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c

$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
	$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c







|
>
>
>







607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624

makeheaders$E: $(SRCDIR)\makeheaders.c
	$(BCC) $**

mkindex$E: $(SRCDIR)\mkindex.c
	$(BCC) $**

mkversion$E: $(SRCDIR)\mkversion.c
	$(BCC) $**

codecheck1$E: $(SRCDIR)\codecheck1.c
	$(BCC) $**

$(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc
	$(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c

$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc
	$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c
658
659
660
661
662
663
664


665
666
667
668
669
670
671
	-del translate$P
	-del mkindex$E
	-del mkindex$P
	-del makeheaders$E
	-del makeheaders$P
	-del mkversion$E
	-del mkversion$P



$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h







>
>







662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
	-del translate$P
	-del mkindex$E
	-del mkindex$P
	-del makeheaders$E
	-del makeheaders$P
	-del mkversion$E
	-del mkversion$P
	-del codecheck1$E
	-del codecheck1$P

$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
$(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h