Fossil SCM

merge trunk

jan.nijtmans 2013-01-21 10:29 improve_commit_warning merge
Commit a68dffbff3c256ffd4a6f241ecd2c013f9fbb84b
+1 -1
--- src/add.c
+++ src/add.c
@@ -140,11 +140,11 @@
140140
const char *zPath, /* Tree-name of file to add. */
141141
int vid, /* Add to this VFILE */
142142
int caseSensitive /* True if filenames are case sensitive */
143143
){
144144
const char *zCollate = caseSensitive ? "binary" : "nocase";
145
- if( !file_is_simple_pathname(zPath) ){
145
+ if( !file_is_simple_pathname(zPath, 1) ){
146146
fossil_warning("filename contains illegal characters: %s", zPath);
147147
return 0;
148148
}
149149
if( db_exists("SELECT 1 FROM vfile"
150150
" WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){
151151
--- src/add.c
+++ src/add.c
@@ -140,11 +140,11 @@
140 const char *zPath, /* Tree-name of file to add. */
141 int vid, /* Add to this VFILE */
142 int caseSensitive /* True if filenames are case sensitive */
143 ){
144 const char *zCollate = caseSensitive ? "binary" : "nocase";
145 if( !file_is_simple_pathname(zPath) ){
146 fossil_warning("filename contains illegal characters: %s", zPath);
147 return 0;
148 }
149 if( db_exists("SELECT 1 FROM vfile"
150 " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){
151
--- src/add.c
+++ src/add.c
@@ -140,11 +140,11 @@
140 const char *zPath, /* Tree-name of file to add. */
141 int vid, /* Add to this VFILE */
142 int caseSensitive /* True if filenames are case sensitive */
143 ){
144 const char *zCollate = caseSensitive ? "binary" : "nocase";
145 if( !file_is_simple_pathname(zPath, 1) ){
146 fossil_warning("filename contains illegal characters: %s", zPath);
147 return 0;
148 }
149 if( db_exists("SELECT 1 FROM vfile"
150 " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){
151
+1
--- src/cgi.c
+++ src/cgi.c
@@ -21,10 +21,11 @@
2121
** formatting function and its cousins, and routines to encode and
2222
** decode strings in HTML or HTTP.
2323
*/
2424
#include "config.h"
2525
#ifdef _WIN32
26
+# include <winsock2.h>
2627
# include <ws2tcpip.h>
2728
#else
2829
# include <sys/socket.h>
2930
# include <netinet/in.h>
3031
# include <arpa/inet.h>
3132
--- src/cgi.c
+++ src/cgi.c
@@ -21,10 +21,11 @@
21 ** formatting function and its cousins, and routines to encode and
22 ** decode strings in HTML or HTTP.
23 */
24 #include "config.h"
25 #ifdef _WIN32
 
26 # include <ws2tcpip.h>
27 #else
28 # include <sys/socket.h>
29 # include <netinet/in.h>
30 # include <arpa/inet.h>
31
--- src/cgi.c
+++ src/cgi.c
@@ -21,10 +21,11 @@
21 ** formatting function and its cousins, and routines to encode and
22 ** decode strings in HTML or HTTP.
23 */
24 #include "config.h"
25 #ifdef _WIN32
26 # include <winsock2.h>
27 # include <ws2tcpip.h>
28 #else
29 # include <sys/socket.h>
30 # include <netinet/in.h>
31 # include <arpa/inet.h>
32
+3 -3
--- src/checkin.c
+++ src/checkin.c
@@ -316,11 +316,11 @@
316316
** unless overridden by the --abs-paths or --rel-paths options.
317317
**
318318
** Options:
319319
** --abs-paths Display absolute pathnames.
320320
** --dotfiles include files beginning with a dot (".")
321
-** --ignore <CSG> ignore files matching patterns from the
321
+** --ignore <CSG> ignore files matching patterns from the argument
322322
** --rel-paths Display pathnames relative to the current working
323323
** directory.
324324
**
325325
** See also: changes, clean, status
326326
*/
@@ -1182,11 +1182,11 @@
11821182
}
11831183
11841184
/* So that older versions of Fossil (that do not understand delta-
11851185
** manifest) can continue to use this repository, do not create a new
11861186
** delta-manifest unless this repository already contains one or more
1187
- ** delta-manifets, or unless the delta-manifest is explicitly requested
1187
+ ** delta-manifests, or unless the delta-manifest is explicitly requested
11881188
** by the --delta option.
11891189
*/
11901190
if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
11911191
forceBaseline = 1;
11921192
}
@@ -1565,11 +1565,11 @@
15651565
exit(1);
15661566
}
15671567
db_end_transaction(0);
15681568
15691569
if( !g.markPrivate ){
1570
- autosync(SYNC_PUSH);
1570
+ autosync(SYNC_PUSH|SYNC_PULL);
15711571
}
15721572
if( count_nonbranch_children(vid)>1 ){
15731573
fossil_print("**** warning: a fork has occurred *****\n");
15741574
}
15751575
}
15761576
--- src/checkin.c
+++ src/checkin.c
@@ -316,11 +316,11 @@
316 ** unless overridden by the --abs-paths or --rel-paths options.
317 **
318 ** Options:
319 ** --abs-paths Display absolute pathnames.
320 ** --dotfiles include files beginning with a dot (".")
321 ** --ignore <CSG> ignore files matching patterns from the
322 ** --rel-paths Display pathnames relative to the current working
323 ** directory.
324 **
325 ** See also: changes, clean, status
326 */
@@ -1182,11 +1182,11 @@
1182 }
1183
1184 /* So that older versions of Fossil (that do not understand delta-
1185 ** manifest) can continue to use this repository, do not create a new
1186 ** delta-manifest unless this repository already contains one or more
1187 ** delta-manifets, or unless the delta-manifest is explicitly requested
1188 ** by the --delta option.
1189 */
1190 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
1191 forceBaseline = 1;
1192 }
@@ -1565,11 +1565,11 @@
1565 exit(1);
1566 }
1567 db_end_transaction(0);
1568
1569 if( !g.markPrivate ){
1570 autosync(SYNC_PUSH);
1571 }
1572 if( count_nonbranch_children(vid)>1 ){
1573 fossil_print("**** warning: a fork has occurred *****\n");
1574 }
1575 }
1576
--- src/checkin.c
+++ src/checkin.c
@@ -316,11 +316,11 @@
316 ** unless overridden by the --abs-paths or --rel-paths options.
317 **
318 ** Options:
319 ** --abs-paths Display absolute pathnames.
320 ** --dotfiles include files beginning with a dot (".")
321 ** --ignore <CSG> ignore files matching patterns from the argument
322 ** --rel-paths Display pathnames relative to the current working
323 ** directory.
324 **
325 ** See also: changes, clean, status
326 */
@@ -1182,11 +1182,11 @@
1182 }
1183
1184 /* So that older versions of Fossil (that do not understand delta-
1185 ** manifest) can continue to use this repository, do not create a new
1186 ** delta-manifest unless this repository already contains one or more
1187 ** delta-manifests, or unless the delta-manifest is explicitly requested
1188 ** by the --delta option.
1189 */
1190 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
1191 forceBaseline = 1;
1192 }
@@ -1565,11 +1565,11 @@
1565 exit(1);
1566 }
1567 db_end_transaction(0);
1568
1569 if( !g.markPrivate ){
1570 autosync(SYNC_PUSH|SYNC_PULL);
1571 }
1572 if( count_nonbranch_children(vid)>1 ){
1573 fossil_print("**** warning: a fork has occurred *****\n");
1574 }
1575 }
1576
+3 -3
--- src/checkin.c
+++ src/checkin.c
@@ -316,11 +316,11 @@
316316
** unless overridden by the --abs-paths or --rel-paths options.
317317
**
318318
** Options:
319319
** --abs-paths Display absolute pathnames.
320320
** --dotfiles include files beginning with a dot (".")
321
-** --ignore <CSG> ignore files matching patterns from the
321
+** --ignore <CSG> ignore files matching patterns from the argument
322322
** --rel-paths Display pathnames relative to the current working
323323
** directory.
324324
**
325325
** See also: changes, clean, status
326326
*/
@@ -1182,11 +1182,11 @@
11821182
}
11831183
11841184
/* So that older versions of Fossil (that do not understand delta-
11851185
** manifest) can continue to use this repository, do not create a new
11861186
** delta-manifest unless this repository already contains one or more
1187
- ** delta-manifets, or unless the delta-manifest is explicitly requested
1187
+ ** delta-manifests, or unless the delta-manifest is explicitly requested
11881188
** by the --delta option.
11891189
*/
11901190
if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
11911191
forceBaseline = 1;
11921192
}
@@ -1565,11 +1565,11 @@
15651565
exit(1);
15661566
}
15671567
db_end_transaction(0);
15681568
15691569
if( !g.markPrivate ){
1570
- autosync(SYNC_PUSH);
1570
+ autosync(SYNC_PUSH|SYNC_PULL);
15711571
}
15721572
if( count_nonbranch_children(vid)>1 ){
15731573
fossil_print("**** warning: a fork has occurred *****\n");
15741574
}
15751575
}
15761576
--- src/checkin.c
+++ src/checkin.c
@@ -316,11 +316,11 @@
316 ** unless overridden by the --abs-paths or --rel-paths options.
317 **
318 ** Options:
319 ** --abs-paths Display absolute pathnames.
320 ** --dotfiles include files beginning with a dot (".")
321 ** --ignore <CSG> ignore files matching patterns from the
322 ** --rel-paths Display pathnames relative to the current working
323 ** directory.
324 **
325 ** See also: changes, clean, status
326 */
@@ -1182,11 +1182,11 @@
1182 }
1183
1184 /* So that older versions of Fossil (that do not understand delta-
1185 ** manifest) can continue to use this repository, do not create a new
1186 ** delta-manifest unless this repository already contains one or more
1187 ** delta-manifets, or unless the delta-manifest is explicitly requested
1188 ** by the --delta option.
1189 */
1190 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
1191 forceBaseline = 1;
1192 }
@@ -1565,11 +1565,11 @@
1565 exit(1);
1566 }
1567 db_end_transaction(0);
1568
1569 if( !g.markPrivate ){
1570 autosync(SYNC_PUSH);
1571 }
1572 if( count_nonbranch_children(vid)>1 ){
1573 fossil_print("**** warning: a fork has occurred *****\n");
1574 }
1575 }
1576
--- src/checkin.c
+++ src/checkin.c
@@ -316,11 +316,11 @@
316 ** unless overridden by the --abs-paths or --rel-paths options.
317 **
318 ** Options:
319 ** --abs-paths Display absolute pathnames.
320 ** --dotfiles include files beginning with a dot (".")
321 ** --ignore <CSG> ignore files matching patterns from the argument
322 ** --rel-paths Display pathnames relative to the current working
323 ** directory.
324 **
325 ** See also: changes, clean, status
326 */
@@ -1182,11 +1182,11 @@
1182 }
1183
1184 /* So that older versions of Fossil (that do not understand delta-
1185 ** manifest) can continue to use this repository, do not create a new
1186 ** delta-manifest unless this repository already contains one or more
1187 ** delta-manifests, or unless the delta-manifest is explicitly requested
1188 ** by the --delta option.
1189 */
1190 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
1191 forceBaseline = 1;
1192 }
@@ -1565,11 +1565,11 @@
1565 exit(1);
1566 }
1567 db_end_transaction(0);
1568
1569 if( !g.markPrivate ){
1570 autosync(SYNC_PUSH|SYNC_PULL);
1571 }
1572 if( count_nonbranch_children(vid)>1 ){
1573 fossil_print("**** warning: a fork has occurred *****\n");
1574 }
1575 }
1576
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -1422,11 +1422,10 @@
14221422
extern "C" {
14231423
#endif
14241424
14251425
14261426
1427
-
14281427
/**
14291428
This type holds the "vtbl" for type-specific operations when
14301429
working with cson_value objects.
14311430
14321431
All cson_values of a given logical type share a pointer to a single
@@ -1524,13 +1523,11 @@
15241523
*/
15251524
#define cson_value_empty_m { &cson_value_api_empty/*api*/, NULL/*value*/, 0/*refcount*/ }
15261525
/**
15271526
Empty-initialized cson_value object.
15281527
*/
1529
-extern const cson_value cson_value_empty;
1530
-
1531
-const cson_value cson_value_empty = cson_value_empty_m;
1528
+static const cson_value cson_value_empty = cson_value_empty_m;
15321529
const cson_parse_opt cson_parse_opt_empty = cson_parse_opt_empty_m;
15331530
const cson_output_opt cson_output_opt_empty = cson_output_opt_empty_m;
15341531
const cson_object_iterator cson_object_iterator_empty = cson_object_iterator_empty_m;
15351532
const cson_buffer cson_buffer_empty = cson_buffer_empty_m;
15361533
const cson_parse_info cson_parse_info_empty = cson_parse_info_empty_m;
@@ -1661,11 +1658,11 @@
16611658
{
16621659
if((m >= (void const *)&CSON_EMPTY_HOLDER)
16631660
&& ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
16641661
return 1;
16651662
else return
1666
- ((m > (void const *)&CSON_SPECIAL_VALUES[0])
1663
+ ((m >= (void const *)&CSON_SPECIAL_VALUES[0])
16671664
&& ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) )
16681665
? 1
16691666
: 0;
16701667
}
16711668
@@ -1711,13 +1708,13 @@
17111708
malloc/free funcs because fossil's lack of header files
17121709
means we would have to #include "main.c" here to
17131710
get the declarations.
17141711
*/
17151712
#if defined(CSON_FOSSIL_MODE)
1716
-void *fossil_malloc(size_t n);
1717
-void fossil_free(void *p);
1718
-void *fossil_realloc(void *p, size_t n);
1713
+extern void *fossil_malloc(size_t n);
1714
+extern void fossil_free(void *p);
1715
+extern void *fossil_realloc(void *p, size_t n);
17191716
# define CSON_MALLOC_IMPL fossil_malloc
17201717
# define CSON_FREE_IMPL fossil_free
17211718
# define CSON_REALLOC_IMPL fossil_realloc
17221719
#endif
17231720
@@ -4380,11 +4377,11 @@
43804377
arg MUST be a (cson_buffer*). This function appends n bytes at
43814378
position arg->used, expanding the buffer as necessary.
43824379
*/
43834380
static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n )
43844381
{
4385
- if( ! arg || (n<0) ) return cson_rc.ArgError;
4382
+ if( !arg ) return cson_rc.ArgError;
43864383
else if( ! n ) return 0;
43874384
else
43884385
{
43894386
cson_buffer * sb = (cson_buffer*)arg;
43904387
char const * data = (char const *)data_;
@@ -4500,11 +4497,10 @@
45004497
cson_value * cv = NULL;
45014498
cson_object const * curObj = obj;
45024499
enum { BufSize = 128 };
45034500
char buf[BufSize];
45044501
memset( buf, 0, BufSize );
4505
- rc = cson_rc.RangeError;
45064502
45074503
while( cson_next_token( &beg, sep, &end ) )
45084504
{
45094505
if( beg == end ) break;
45104506
else
45114507
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -1422,11 +1422,10 @@
1422 extern "C" {
1423 #endif
1424
1425
1426
1427
1428 /**
1429 This type holds the "vtbl" for type-specific operations when
1430 working with cson_value objects.
1431
1432 All cson_values of a given logical type share a pointer to a single
@@ -1524,13 +1523,11 @@
1524 */
1525 #define cson_value_empty_m { &cson_value_api_empty/*api*/, NULL/*value*/, 0/*refcount*/ }
1526 /**
1527 Empty-initialized cson_value object.
1528 */
1529 extern const cson_value cson_value_empty;
1530
1531 const cson_value cson_value_empty = cson_value_empty_m;
1532 const cson_parse_opt cson_parse_opt_empty = cson_parse_opt_empty_m;
1533 const cson_output_opt cson_output_opt_empty = cson_output_opt_empty_m;
1534 const cson_object_iterator cson_object_iterator_empty = cson_object_iterator_empty_m;
1535 const cson_buffer cson_buffer_empty = cson_buffer_empty_m;
1536 const cson_parse_info cson_parse_info_empty = cson_parse_info_empty_m;
@@ -1661,11 +1658,11 @@
1661 {
1662 if((m >= (void const *)&CSON_EMPTY_HOLDER)
1663 && ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
1664 return 1;
1665 else return
1666 ((m > (void const *)&CSON_SPECIAL_VALUES[0])
1667 && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) )
1668 ? 1
1669 : 0;
1670 }
1671
@@ -1711,13 +1708,13 @@
1711 malloc/free funcs because fossil's lack of header files
1712 means we would have to #include "main.c" here to
1713 get the declarations.
1714 */
1715 #if defined(CSON_FOSSIL_MODE)
1716 void *fossil_malloc(size_t n);
1717 void fossil_free(void *p);
1718 void *fossil_realloc(void *p, size_t n);
1719 # define CSON_MALLOC_IMPL fossil_malloc
1720 # define CSON_FREE_IMPL fossil_free
1721 # define CSON_REALLOC_IMPL fossil_realloc
1722 #endif
1723
@@ -4380,11 +4377,11 @@
4380 arg MUST be a (cson_buffer*). This function appends n bytes at
4381 position arg->used, expanding the buffer as necessary.
4382 */
4383 static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n )
4384 {
4385 if( ! arg || (n<0) ) return cson_rc.ArgError;
4386 else if( ! n ) return 0;
4387 else
4388 {
4389 cson_buffer * sb = (cson_buffer*)arg;
4390 char const * data = (char const *)data_;
@@ -4500,11 +4497,10 @@
4500 cson_value * cv = NULL;
4501 cson_object const * curObj = obj;
4502 enum { BufSize = 128 };
4503 char buf[BufSize];
4504 memset( buf, 0, BufSize );
4505 rc = cson_rc.RangeError;
4506
4507 while( cson_next_token( &beg, sep, &end ) )
4508 {
4509 if( beg == end ) break;
4510 else
4511
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -1422,11 +1422,10 @@
1422 extern "C" {
1423 #endif
1424
1425
1426
 
1427 /**
1428 This type holds the "vtbl" for type-specific operations when
1429 working with cson_value objects.
1430
1431 All cson_values of a given logical type share a pointer to a single
@@ -1524,13 +1523,11 @@
1523 */
1524 #define cson_value_empty_m { &cson_value_api_empty/*api*/, NULL/*value*/, 0/*refcount*/ }
1525 /**
1526 Empty-initialized cson_value object.
1527 */
1528 static const cson_value cson_value_empty = cson_value_empty_m;
 
 
1529 const cson_parse_opt cson_parse_opt_empty = cson_parse_opt_empty_m;
1530 const cson_output_opt cson_output_opt_empty = cson_output_opt_empty_m;
1531 const cson_object_iterator cson_object_iterator_empty = cson_object_iterator_empty_m;
1532 const cson_buffer cson_buffer_empty = cson_buffer_empty_m;
1533 const cson_parse_info cson_parse_info_empty = cson_parse_info_empty_m;
@@ -1661,11 +1658,11 @@
1658 {
1659 if((m >= (void const *)&CSON_EMPTY_HOLDER)
1660 && ( m < (void const *)(&CSON_EMPTY_HOLDER+1)))
1661 return 1;
1662 else return
1663 ((m >= (void const *)&CSON_SPECIAL_VALUES[0])
1664 && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) )
1665 ? 1
1666 : 0;
1667 }
1668
@@ -1711,13 +1708,13 @@
1708 malloc/free funcs because fossil's lack of header files
1709 means we would have to #include "main.c" here to
1710 get the declarations.
1711 */
1712 #if defined(CSON_FOSSIL_MODE)
1713 extern void *fossil_malloc(size_t n);
1714 extern void fossil_free(void *p);
1715 extern void *fossil_realloc(void *p, size_t n);
1716 # define CSON_MALLOC_IMPL fossil_malloc
1717 # define CSON_FREE_IMPL fossil_free
1718 # define CSON_REALLOC_IMPL fossil_realloc
1719 #endif
1720
@@ -4380,11 +4377,11 @@
4377 arg MUST be a (cson_buffer*). This function appends n bytes at
4378 position arg->used, expanding the buffer as necessary.
4379 */
4380 static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n )
4381 {
4382 if( !arg ) return cson_rc.ArgError;
4383 else if( ! n ) return 0;
4384 else
4385 {
4386 cson_buffer * sb = (cson_buffer*)arg;
4387 char const * data = (char const *)data_;
@@ -4500,11 +4497,10 @@
4497 cson_value * cv = NULL;
4498 cson_object const * curObj = obj;
4499 enum { BufSize = 128 };
4500 char buf[BufSize];
4501 memset( buf, 0, BufSize );
 
4502
4503 while( cson_next_token( &beg, sep, &end ) )
4504 {
4505 if( beg == end ) break;
4506 else
4507
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -1236,11 +1236,14 @@
12361236
eventually either free the value using cson_value_free() or
12371237
inserting it into a container (array or object), which transfers
12381238
ownership to the container. See the cson_value class documentation
12391239
for more details.
12401240
1241
- Returns NULL on allocation error.
1241
+ Semantically speaking this function Returns NULL on allocation
1242
+ error, but the implementation never actually allocates for this
1243
+ case. Nonetheless, it must be treated as if it were an allocated
1244
+ value.
12421245
*/
12431246
cson_value * cson_value_new_bool( char v );
12441247
12451248
12461249
/**
@@ -1927,11 +1930,12 @@
19271930
modified.
19281931
19291932
buf->mem is owned by buf and must eventually be freed by passing an
19301933
n value of 0 to this function.
19311934
1932
- buf->used is never modified by this function.
1935
+ buf->used is never modified by this function unless n is 0, in which case
1936
+ it is reset.
19331937
*/
19341938
int cson_buffer_reserve( cson_buffer * buf, cson_size_t n );
19351939
19361940
/**
19371941
Fills all bytes of the given buffer with the given character.
19381942
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -1236,11 +1236,14 @@
1236 eventually either free the value using cson_value_free() or
1237 inserting it into a container (array or object), which transfers
1238 ownership to the container. See the cson_value class documentation
1239 for more details.
1240
1241 Returns NULL on allocation error.
 
 
 
1242 */
1243 cson_value * cson_value_new_bool( char v );
1244
1245
1246 /**
@@ -1927,11 +1930,12 @@
1927 modified.
1928
1929 buf->mem is owned by buf and must eventually be freed by passing an
1930 n value of 0 to this function.
1931
1932 buf->used is never modified by this function.
 
1933 */
1934 int cson_buffer_reserve( cson_buffer * buf, cson_size_t n );
1935
1936 /**
1937 Fills all bytes of the given buffer with the given character.
1938
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -1236,11 +1236,14 @@
1236 eventually either free the value using cson_value_free() or
1237 inserting it into a container (array or object), which transfers
1238 ownership to the container. See the cson_value class documentation
1239 for more details.
1240
1241 Semantically speaking this function Returns NULL on allocation
1242 error, but the implementation never actually allocates for this
1243 case. Nonetheless, it must be treated as if it were an allocated
1244 value.
1245 */
1246 cson_value * cson_value_new_bool( char v );
1247
1248
1249 /**
@@ -1927,11 +1930,12 @@
1930 modified.
1931
1932 buf->mem is owned by buf and must eventually be freed by passing an
1933 n value of 0 to this function.
1934
1935 buf->used is never modified by this function unless n is 0, in which case
1936 it is reset.
1937 */
1938 int cson_buffer_reserve( cson_buffer * buf, cson_size_t n );
1939
1940 /**
1941 Fills all bytes of the given buffer with the given character.
1942
+48 -46
--- src/db.c
+++ src/db.c
@@ -22,11 +22,11 @@
2222
**
2323
** (1) The "user" database in ~/.fossil
2424
**
2525
** (2) The "repository" database
2626
**
27
-** (3) A local checkout database named "_FOSSIL_" or ".fos"
27
+** (3) A local checkout database named "_FOSSIL_" or ".fslckout"
2828
** and located at the root of the local copy of the source tree.
2929
**
3030
*/
3131
#include "config.h"
3232
#if ! defined(_WIN32)
@@ -89,11 +89,11 @@
8989
cgi_reply();
9090
}
9191
else if( g.cgiOutput ){
9292
g.cgiOutput = 0;
9393
cgi_printf("<h1>Database Error</h1>\n"
94
- "<pre>%h</pre><p>%s</p>", z, zRebuildMsg);
94
+ "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg);
9595
cgi_reply();
9696
}else{
9797
fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg);
9898
}
9999
free(z);
@@ -484,20 +484,30 @@
484484
/*
485485
** Execute multiple SQL statements.
486486
*/
487487
int db_multi_exec(const char *zSql, ...){
488488
Blob sql;
489
- int rc;
489
+ int rc = SQLITE_OK;
490490
va_list ap;
491
- char *zErr = 0;
491
+ const char *z, *zEnd;
492
+ sqlite3_stmt *pStmt;
492493
blob_init(&sql, 0, 0);
493494
va_start(ap, zSql);
494495
blob_vappendf(&sql, zSql, ap);
495496
va_end(ap);
496
- rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr);
497
- if( rc!=SQLITE_OK ){
498
- db_err("%s\n%s", zErr, blob_buffer(&sql));
497
+ z = blob_str(&sql);
498
+ while( rc==SQLITE_OK && z[0] ){
499
+ pStmt = 0;
500
+ rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
501
+ if( rc!=SQLITE_OK ) break;
502
+ if( pStmt ){
503
+ db.nPrepare++;
504
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){}
505
+ rc = sqlite3_finalize(pStmt);
506
+ if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
507
+ }
508
+ z = zEnd;
499509
}
500510
blob_reset(&sql);
501511
return rc;
502512
}
503513
@@ -642,15 +652,11 @@
642652
sqlite3 *db;
643653
int rc;
644654
const char *zSql;
645655
va_list ap;
646656
647
- rc = sqlite3_open(zFileName, &db);
648
- if( rc!=SQLITE_OK ){
649
- db_err(sqlite3_errmsg(db));
650
- }
651
- sqlite3_busy_timeout(db, 5000);
657
+ db = db_open(zFileName);
652658
sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
653659
rc = sqlite3_exec(db, zSchema, 0, 0, 0);
654660
if( rc!=SQLITE_OK ){
655661
db_err(sqlite3_errmsg(db));
656662
}
@@ -697,29 +703,43 @@
697703
698704
/*
699705
** Open a database file. Return a pointer to the new database
700706
** connection. An error results in process abort.
701707
*/
702
-static sqlite3 *openDatabase(const char *zDbName){
708
+LOCAL sqlite3 *db_open(const char *zDbName){
703709
int rc;
704710
const char *zVfs;
705711
sqlite3 *db;
706712
713
+ if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
707714
zVfs = fossil_getenv("FOSSIL_VFS");
708715
rc = sqlite3_open_v2(
709716
zDbName, &db,
710717
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
711718
zVfs
712719
);
713720
if( rc!=SQLITE_OK ){
714
- db_err(sqlite3_errmsg(db));
721
+ db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
715722
}
716723
sqlite3_busy_timeout(db, 5000);
717724
sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
718725
sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
719726
sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0,
720727
db_checkin_mtime_function, 0, 0);
728
+ sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
729
+ sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
730
+ sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
731
+ sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
732
+ sqlite3_create_function(
733
+ db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
734
+ );
735
+ sqlite3_create_function(
736
+ db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
737
+ );
738
+ if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
739
+ re_add_sql_func(db);
740
+ sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
721741
return db;
722742
}
723743
724744
725745
/*
@@ -747,13 +767,12 @@
747767
const char *zLabel,
748768
int *pWasAttached
749769
){
750770
if( !g.db ){
751771
assert( g.zMainDbType==0 );
752
- g.db = openDatabase(zDbName);
772
+ g.db = db_open(zDbName);
753773
g.zMainDbType = zLabel;
754
- db_connection_init();
755774
if ( pWasAttached ) *pWasAttached = 0;
756775
}else{
757776
assert( g.zMainDbType!=0 );
758777
db_attach(zDbName, zLabel);
759778
if ( pWasAttached ) *pWasAttached = 1;
@@ -820,11 +839,11 @@
820839
db_open_or_attach(zDbName, "configdb", &g.useAttach);
821840
g.dbConfig = 0;
822841
g.zConfigDbType = 0;
823842
}else{
824843
g.useAttach = 0;
825
- g.dbConfig = openDatabase(zDbName);
844
+ g.dbConfig = db_open(zDbName);
826845
g.zConfigDbType = "configdb";
827846
}
828847
g.configOpen = 1;
829848
free(zDbName);
830849
}
@@ -1221,10 +1240,13 @@
12211240
void db_create_default_users(int setupUserOnly, const char *zDefaultUser){
12221241
const char *zUser = zDefaultUser;
12231242
if( zUser==0 ){
12241243
zUser = db_get("default-user", 0);
12251244
}
1245
+ if( zUser==0 ){
1246
+ zUser = fossil_getenv("FOSSIL_USER");
1247
+ }
12261248
if( zUser==0 ){
12271249
#if defined(_WIN32)
12281250
zUser = fossil_getenv("USERNAME");
12291251
#else
12301252
zUser = fossil_getenv("USER");
@@ -1441,11 +1463,11 @@
14411463
** SQL functions for debugging.
14421464
**
14431465
** The print() function writes its arguments on stdout, but only
14441466
** if the -sqlprint command-line option is turned on.
14451467
*/
1446
-static void db_sql_print(
1468
+LOCAL void db_sql_print(
14471469
sqlite3_context *context,
14481470
int argc,
14491471
sqlite3_value **argv
14501472
){
14511473
int i;
@@ -1454,22 +1476,20 @@
14541476
char c = i==argc-1 ? '\n' : ' ';
14551477
fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
14561478
}
14571479
}
14581480
}
1459
-static void db_sql_trace(void *notUsed, const char *zSql){
1481
+LOCAL void db_sql_trace(void *notUsed, const char *zSql){
14601482
int n = strlen(zSql);
1461
- char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
1462
- fossil_puts(zMsg, 1);
1463
- fossil_free(zMsg);
1483
+ fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
14641484
}
14651485
14661486
/*
14671487
** Implement the user() SQL function. user() takes no arguments and
14681488
** returns the user ID of the current user.
14691489
*/
1470
-static void db_sql_user(
1490
+LOCAL void db_sql_user(
14711491
sqlite3_context *context,
14721492
int argc,
14731493
sqlite3_value **argv
14741494
){
14751495
if( g.zLogin!=0 ){
@@ -1481,11 +1501,11 @@
14811501
** Implement the cgi() SQL function. cgi() takes an argument which is
14821502
** a name of CGI query parameter. The value of that parameter is returned,
14831503
** if available. Optional second argument will be returned if the first
14841504
** doesn't exist as a CGI parameter.
14851505
*/
1486
-static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
1506
+LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
14871507
const char* zP;
14881508
if( argc!=1 && argc!=2 ) return;
14891509
zP = P((const char*)sqlite3_value_text(argv[0]));
14901510
if( zP ){
14911511
sqlite3_result_text(context, zP, -1, SQLITE_STATIC);
@@ -1512,11 +1532,11 @@
15121532
** (meaning that id was named on the command-line).
15131533
**
15141534
** In the second form (3 arguments) return argument X if true and Y
15151535
** if false. Except if Y is NULL then always return X.
15161536
*/
1517
-static void file_is_selected(
1537
+LOCAL void file_is_selected(
15181538
sqlite3_context *context,
15191539
int argc,
15201540
sqlite3_value **argv
15211541
){
15221542
int rc = 0;
@@ -1600,31 +1620,10 @@
16001620
zOut = mprintf("%s", zKey);
16011621
}
16021622
return zOut;
16031623
}
16041624
1605
-/*
1606
-** This function registers auxiliary functions when the SQLite
1607
-** database connection is first established.
1608
-*/
1609
-LOCAL void db_connection_init(void){
1610
- sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1611
- sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
1612
- sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1613
- sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1614
- sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
1615
- sqlite3_create_function(
1616
- g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
1617
- );
1618
- sqlite3_create_function(
1619
- g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
1620
- );
1621
- if( g.fSqlTrace ){
1622
- sqlite3_trace(g.db, db_sql_trace, 0);
1623
- }
1624
-}
1625
-
16261625
/*
16271626
** Return true if the string zVal represents "true" (or "false").
16281627
*/
16291628
int is_truth(const char *zVal){
16301629
static const char *const azOn[] = { "on", "yes", "true", "1" };
@@ -2115,10 +2114,13 @@
21152114
** file named .fossil-settings/PROPERTY in the checked out files, if that
21162115
** file exists.
21172116
**
21182117
** The "unset" command clears a property setting.
21192118
**
2119
+**
2120
+** access-log If enabled, record successful and failed login attempts
2121
+** in the "accesslog" table. Default: off
21202122
**
21212123
** allow-symlinks If enabled, don't follow symlinks, and instead treat
21222124
** (versionable) them as symlinks on Unix. Has no effect on Windows
21232125
** (existing links in repository created on Unix become
21242126
** plain-text files with link destination path inside).
21252127
--- src/db.c
+++ src/db.c
@@ -22,11 +22,11 @@
22 **
23 ** (1) The "user" database in ~/.fossil
24 **
25 ** (2) The "repository" database
26 **
27 ** (3) A local checkout database named "_FOSSIL_" or ".fos"
28 ** and located at the root of the local copy of the source tree.
29 **
30 */
31 #include "config.h"
32 #if ! defined(_WIN32)
@@ -89,11 +89,11 @@
89 cgi_reply();
90 }
91 else if( g.cgiOutput ){
92 g.cgiOutput = 0;
93 cgi_printf("<h1>Database Error</h1>\n"
94 "<pre>%h</pre><p>%s</p>", z, zRebuildMsg);
95 cgi_reply();
96 }else{
97 fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg);
98 }
99 free(z);
@@ -484,20 +484,30 @@
484 /*
485 ** Execute multiple SQL statements.
486 */
487 int db_multi_exec(const char *zSql, ...){
488 Blob sql;
489 int rc;
490 va_list ap;
491 char *zErr = 0;
 
492 blob_init(&sql, 0, 0);
493 va_start(ap, zSql);
494 blob_vappendf(&sql, zSql, ap);
495 va_end(ap);
496 rc = sqlite3_exec(g.db, blob_buffer(&sql), 0, 0, &zErr);
497 if( rc!=SQLITE_OK ){
498 db_err("%s\n%s", zErr, blob_buffer(&sql));
 
 
 
 
 
 
 
 
 
499 }
500 blob_reset(&sql);
501 return rc;
502 }
503
@@ -642,15 +652,11 @@
642 sqlite3 *db;
643 int rc;
644 const char *zSql;
645 va_list ap;
646
647 rc = sqlite3_open(zFileName, &db);
648 if( rc!=SQLITE_OK ){
649 db_err(sqlite3_errmsg(db));
650 }
651 sqlite3_busy_timeout(db, 5000);
652 sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
653 rc = sqlite3_exec(db, zSchema, 0, 0, 0);
654 if( rc!=SQLITE_OK ){
655 db_err(sqlite3_errmsg(db));
656 }
@@ -697,29 +703,43 @@
697
698 /*
699 ** Open a database file. Return a pointer to the new database
700 ** connection. An error results in process abort.
701 */
702 static sqlite3 *openDatabase(const char *zDbName){
703 int rc;
704 const char *zVfs;
705 sqlite3 *db;
706
 
707 zVfs = fossil_getenv("FOSSIL_VFS");
708 rc = sqlite3_open_v2(
709 zDbName, &db,
710 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
711 zVfs
712 );
713 if( rc!=SQLITE_OK ){
714 db_err(sqlite3_errmsg(db));
715 }
716 sqlite3_busy_timeout(db, 5000);
717 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
718 sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
719 sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0,
720 db_checkin_mtime_function, 0, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
721 return db;
722 }
723
724
725 /*
@@ -747,13 +767,12 @@
747 const char *zLabel,
748 int *pWasAttached
749 ){
750 if( !g.db ){
751 assert( g.zMainDbType==0 );
752 g.db = openDatabase(zDbName);
753 g.zMainDbType = zLabel;
754 db_connection_init();
755 if ( pWasAttached ) *pWasAttached = 0;
756 }else{
757 assert( g.zMainDbType!=0 );
758 db_attach(zDbName, zLabel);
759 if ( pWasAttached ) *pWasAttached = 1;
@@ -820,11 +839,11 @@
820 db_open_or_attach(zDbName, "configdb", &g.useAttach);
821 g.dbConfig = 0;
822 g.zConfigDbType = 0;
823 }else{
824 g.useAttach = 0;
825 g.dbConfig = openDatabase(zDbName);
826 g.zConfigDbType = "configdb";
827 }
828 g.configOpen = 1;
829 free(zDbName);
830 }
@@ -1221,10 +1240,13 @@
1221 void db_create_default_users(int setupUserOnly, const char *zDefaultUser){
1222 const char *zUser = zDefaultUser;
1223 if( zUser==0 ){
1224 zUser = db_get("default-user", 0);
1225 }
 
 
 
1226 if( zUser==0 ){
1227 #if defined(_WIN32)
1228 zUser = fossil_getenv("USERNAME");
1229 #else
1230 zUser = fossil_getenv("USER");
@@ -1441,11 +1463,11 @@
1441 ** SQL functions for debugging.
1442 **
1443 ** The print() function writes its arguments on stdout, but only
1444 ** if the -sqlprint command-line option is turned on.
1445 */
1446 static void db_sql_print(
1447 sqlite3_context *context,
1448 int argc,
1449 sqlite3_value **argv
1450 ){
1451 int i;
@@ -1454,22 +1476,20 @@
1454 char c = i==argc-1 ? '\n' : ' ';
1455 fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
1456 }
1457 }
1458 }
1459 static void db_sql_trace(void *notUsed, const char *zSql){
1460 int n = strlen(zSql);
1461 char *zMsg = mprintf("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
1462 fossil_puts(zMsg, 1);
1463 fossil_free(zMsg);
1464 }
1465
1466 /*
1467 ** Implement the user() SQL function. user() takes no arguments and
1468 ** returns the user ID of the current user.
1469 */
1470 static void db_sql_user(
1471 sqlite3_context *context,
1472 int argc,
1473 sqlite3_value **argv
1474 ){
1475 if( g.zLogin!=0 ){
@@ -1481,11 +1501,11 @@
1481 ** Implement the cgi() SQL function. cgi() takes an argument which is
1482 ** a name of CGI query parameter. The value of that parameter is returned,
1483 ** if available. Optional second argument will be returned if the first
1484 ** doesn't exist as a CGI parameter.
1485 */
1486 static void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
1487 const char* zP;
1488 if( argc!=1 && argc!=2 ) return;
1489 zP = P((const char*)sqlite3_value_text(argv[0]));
1490 if( zP ){
1491 sqlite3_result_text(context, zP, -1, SQLITE_STATIC);
@@ -1512,11 +1532,11 @@
1512 ** (meaning that id was named on the command-line).
1513 **
1514 ** In the second form (3 arguments) return argument X if true and Y
1515 ** if false. Except if Y is NULL then always return X.
1516 */
1517 static void file_is_selected(
1518 sqlite3_context *context,
1519 int argc,
1520 sqlite3_value **argv
1521 ){
1522 int rc = 0;
@@ -1600,31 +1620,10 @@
1600 zOut = mprintf("%s", zKey);
1601 }
1602 return zOut;
1603 }
1604
1605 /*
1606 ** This function registers auxiliary functions when the SQLite
1607 ** database connection is first established.
1608 */
1609 LOCAL void db_connection_init(void){
1610 sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1611 sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
1612 sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1613 sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1614 sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
1615 sqlite3_create_function(
1616 g.db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
1617 );
1618 sqlite3_create_function(
1619 g.db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
1620 );
1621 if( g.fSqlTrace ){
1622 sqlite3_trace(g.db, db_sql_trace, 0);
1623 }
1624 }
1625
1626 /*
1627 ** Return true if the string zVal represents "true" (or "false").
1628 */
1629 int is_truth(const char *zVal){
1630 static const char *const azOn[] = { "on", "yes", "true", "1" };
@@ -2115,10 +2114,13 @@
2115 ** file named .fossil-settings/PROPERTY in the checked out files, if that
2116 ** file exists.
2117 **
2118 ** The "unset" command clears a property setting.
2119 **
 
 
 
2120 **
2121 ** allow-symlinks If enabled, don't follow symlinks, and instead treat
2122 ** (versionable) them as symlinks on Unix. Has no effect on Windows
2123 ** (existing links in repository created on Unix become
2124 ** plain-text files with link destination path inside).
2125
--- src/db.c
+++ src/db.c
@@ -22,11 +22,11 @@
22 **
23 ** (1) The "user" database in ~/.fossil
24 **
25 ** (2) The "repository" database
26 **
27 ** (3) A local checkout database named "_FOSSIL_" or ".fslckout"
28 ** and located at the root of the local copy of the source tree.
29 **
30 */
31 #include "config.h"
32 #if ! defined(_WIN32)
@@ -89,11 +89,11 @@
89 cgi_reply();
90 }
91 else if( g.cgiOutput ){
92 g.cgiOutput = 0;
93 cgi_printf("<h1>Database Error</h1>\n"
94 "<pre>%h</pre>\n<p>%s</p>\n", z, zRebuildMsg);
95 cgi_reply();
96 }else{
97 fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg);
98 }
99 free(z);
@@ -484,20 +484,30 @@
484 /*
485 ** Execute multiple SQL statements.
486 */
487 int db_multi_exec(const char *zSql, ...){
488 Blob sql;
489 int rc = SQLITE_OK;
490 va_list ap;
491 const char *z, *zEnd;
492 sqlite3_stmt *pStmt;
493 blob_init(&sql, 0, 0);
494 va_start(ap, zSql);
495 blob_vappendf(&sql, zSql, ap);
496 va_end(ap);
497 z = blob_str(&sql);
498 while( rc==SQLITE_OK && z[0] ){
499 pStmt = 0;
500 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
501 if( rc!=SQLITE_OK ) break;
502 if( pStmt ){
503 db.nPrepare++;
504 while( sqlite3_step(pStmt)==SQLITE_ROW ){}
505 rc = sqlite3_finalize(pStmt);
506 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
507 }
508 z = zEnd;
509 }
510 blob_reset(&sql);
511 return rc;
512 }
513
@@ -642,15 +652,11 @@
652 sqlite3 *db;
653 int rc;
654 const char *zSql;
655 va_list ap;
656
657 db = db_open(zFileName);
 
 
 
 
658 sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
659 rc = sqlite3_exec(db, zSchema, 0, 0, 0);
660 if( rc!=SQLITE_OK ){
661 db_err(sqlite3_errmsg(db));
662 }
@@ -697,29 +703,43 @@
703
704 /*
705 ** Open a database file. Return a pointer to the new database
706 ** connection. An error results in process abort.
707 */
708 LOCAL sqlite3 *db_open(const char *zDbName){
709 int rc;
710 const char *zVfs;
711 sqlite3 *db;
712
713 if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
714 zVfs = fossil_getenv("FOSSIL_VFS");
715 rc = sqlite3_open_v2(
716 zDbName, &db,
717 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
718 zVfs
719 );
720 if( rc!=SQLITE_OK ){
721 db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
722 }
723 sqlite3_busy_timeout(db, 5000);
724 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
725 sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
726 sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0,
727 db_checkin_mtime_function, 0, 0);
728 sqlite3_create_function(db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
729 sqlite3_create_function(db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
730 sqlite3_create_function(db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
731 sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
732 sqlite3_create_function(
733 db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
734 );
735 sqlite3_create_function(
736 db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
737 );
738 if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0);
739 re_add_sql_func(db);
740 sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
741 return db;
742 }
743
744
745 /*
@@ -747,13 +767,12 @@
767 const char *zLabel,
768 int *pWasAttached
769 ){
770 if( !g.db ){
771 assert( g.zMainDbType==0 );
772 g.db = db_open(zDbName);
773 g.zMainDbType = zLabel;
 
774 if ( pWasAttached ) *pWasAttached = 0;
775 }else{
776 assert( g.zMainDbType!=0 );
777 db_attach(zDbName, zLabel);
778 if ( pWasAttached ) *pWasAttached = 1;
@@ -820,11 +839,11 @@
839 db_open_or_attach(zDbName, "configdb", &g.useAttach);
840 g.dbConfig = 0;
841 g.zConfigDbType = 0;
842 }else{
843 g.useAttach = 0;
844 g.dbConfig = db_open(zDbName);
845 g.zConfigDbType = "configdb";
846 }
847 g.configOpen = 1;
848 free(zDbName);
849 }
@@ -1221,10 +1240,13 @@
1240 void db_create_default_users(int setupUserOnly, const char *zDefaultUser){
1241 const char *zUser = zDefaultUser;
1242 if( zUser==0 ){
1243 zUser = db_get("default-user", 0);
1244 }
1245 if( zUser==0 ){
1246 zUser = fossil_getenv("FOSSIL_USER");
1247 }
1248 if( zUser==0 ){
1249 #if defined(_WIN32)
1250 zUser = fossil_getenv("USERNAME");
1251 #else
1252 zUser = fossil_getenv("USER");
@@ -1441,11 +1463,11 @@
1463 ** SQL functions for debugging.
1464 **
1465 ** The print() function writes its arguments on stdout, but only
1466 ** if the -sqlprint command-line option is turned on.
1467 */
1468 LOCAL void db_sql_print(
1469 sqlite3_context *context,
1470 int argc,
1471 sqlite3_value **argv
1472 ){
1473 int i;
@@ -1454,22 +1476,20 @@
1476 char c = i==argc-1 ? '\n' : ' ';
1477 fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
1478 }
1479 }
1480 }
1481 LOCAL void db_sql_trace(void *notUsed, const char *zSql){
1482 int n = strlen(zSql);
1483 fossil_trace("%s%s\n", zSql, (n>0 && zSql[n-1]==';') ? "" : ";");
 
 
1484 }
1485
1486 /*
1487 ** Implement the user() SQL function. user() takes no arguments and
1488 ** returns the user ID of the current user.
1489 */
1490 LOCAL void db_sql_user(
1491 sqlite3_context *context,
1492 int argc,
1493 sqlite3_value **argv
1494 ){
1495 if( g.zLogin!=0 ){
@@ -1481,11 +1501,11 @@
1501 ** Implement the cgi() SQL function. cgi() takes an argument which is
1502 ** a name of CGI query parameter. The value of that parameter is returned,
1503 ** if available. Optional second argument will be returned if the first
1504 ** doesn't exist as a CGI parameter.
1505 */
1506 LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
1507 const char* zP;
1508 if( argc!=1 && argc!=2 ) return;
1509 zP = P((const char*)sqlite3_value_text(argv[0]));
1510 if( zP ){
1511 sqlite3_result_text(context, zP, -1, SQLITE_STATIC);
@@ -1512,11 +1532,11 @@
1532 ** (meaning that id was named on the command-line).
1533 **
1534 ** In the second form (3 arguments) return argument X if true and Y
1535 ** if false. Except if Y is NULL then always return X.
1536 */
1537 LOCAL void file_is_selected(
1538 sqlite3_context *context,
1539 int argc,
1540 sqlite3_value **argv
1541 ){
1542 int rc = 0;
@@ -1600,31 +1620,10 @@
1620 zOut = mprintf("%s", zKey);
1621 }
1622 return zOut;
1623 }
1624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1625 /*
1626 ** Return true if the string zVal represents "true" (or "false").
1627 */
1628 int is_truth(const char *zVal){
1629 static const char *const azOn[] = { "on", "yes", "true", "1" };
@@ -2115,10 +2114,13 @@
2114 ** file named .fossil-settings/PROPERTY in the checked out files, if that
2115 ** file exists.
2116 **
2117 ** The "unset" command clears a property setting.
2118 **
2119 **
2120 ** access-log If enabled, record successful and failed login attempts
2121 ** in the "accesslog" table. Default: off
2122 **
2123 ** allow-symlinks If enabled, don't follow symlinks, and instead treat
2124 ** (versionable) them as symlinks on Unix. Has no effect on Windows
2125 ** (existing links in repository created on Unix become
2126 ** plain-text files with link destination path inside).
2127
--- src/descendants.c
+++ src/descendants.c
@@ -348,10 +348,11 @@
348348
** repository database to be recomputed.
349349
**
350350
** Options:
351351
** --all show ALL leaves
352352
** --closed show only closed leaves
353
+** --bybranch order output by branch name
353354
** --recompute recompute the "leaf" table in the repository DB
354355
**
355356
** See also: descendants, finfo, info, branch
356357
*/
357358
void leaves_cmd(void){
@@ -358,10 +359,14 @@
358359
Stmt q;
359360
Blob sql;
360361
int showAll = find_option("all", 0, 0)!=0;
361362
int showClosed = find_option("closed", 0, 0)!=0;
362363
int recomputeFlag = find_option("recompute",0,0)!=0;
364
+ int byBranch = find_option("bybranch",0,0)!=0;
365
+ char *zLastBr = 0;
366
+ int n;
367
+ char zLineNo[10];
363368
364369
db_find_and_open_repository(0,0);
365370
if( recomputeFlag ) leaf_rebuild();
366371
blob_zero(&sql);
367372
blob_append(&sql, timeline_query_for_tty(), -1);
@@ -369,13 +374,39 @@
369374
if( showClosed ){
370375
blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
371376
}else if( !showAll ){
372377
blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
373378
}
374
- db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
379
+ if( byBranch ){
380
+ db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase,"
381
+ " event.mtime DESC",
382
+ blob_str(&sql));
383
+ }else{
384
+ db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
385
+ }
375386
blob_reset(&sql);
376
- print_timeline(&q, 2000, 0);
387
+ n = 0;
388
+ while( db_step(&q)==SQLITE_ROW ){
389
+ const char *zId = db_column_text(&q, 1);
390
+ const char *zDate = db_column_text(&q, 2);
391
+ const char *zCom = db_column_text(&q, 3);
392
+ const char *zBr = db_column_text(&q, 7);
393
+ char *z;
394
+
395
+ if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){
396
+ fossil_print("*** %s ***\n", zBr);
397
+ fossil_free(zLastBr);
398
+ zLastBr = fossil_strdup(zBr);
399
+ }
400
+ n++;
401
+ sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
402
+ fossil_print("%6s ", zLineNo);
403
+ z = mprintf("%s [%.10s] %s", zDate, zId, zCom);
404
+ comment_print(z, 7, 79);
405
+ fossil_free(z);
406
+ }
407
+ fossil_free(zLastBr);
377408
db_finalize(&q);
378409
}
379410
380411
/*
381412
** WEBPAGE: leaves
382413
--- src/descendants.c
+++ src/descendants.c
@@ -348,10 +348,11 @@
348 ** repository database to be recomputed.
349 **
350 ** Options:
351 ** --all show ALL leaves
352 ** --closed show only closed leaves
 
353 ** --recompute recompute the "leaf" table in the repository DB
354 **
355 ** See also: descendants, finfo, info, branch
356 */
357 void leaves_cmd(void){
@@ -358,10 +359,14 @@
358 Stmt q;
359 Blob sql;
360 int showAll = find_option("all", 0, 0)!=0;
361 int showClosed = find_option("closed", 0, 0)!=0;
362 int recomputeFlag = find_option("recompute",0,0)!=0;
 
 
 
 
363
364 db_find_and_open_repository(0,0);
365 if( recomputeFlag ) leaf_rebuild();
366 blob_zero(&sql);
367 blob_append(&sql, timeline_query_for_tty(), -1);
@@ -369,13 +374,39 @@
369 if( showClosed ){
370 blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
371 }else if( !showAll ){
372 blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
373 }
374 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
 
 
 
 
 
 
375 blob_reset(&sql);
376 print_timeline(&q, 2000, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377 db_finalize(&q);
378 }
379
380 /*
381 ** WEBPAGE: leaves
382
--- src/descendants.c
+++ src/descendants.c
@@ -348,10 +348,11 @@
348 ** repository database to be recomputed.
349 **
350 ** Options:
351 ** --all show ALL leaves
352 ** --closed show only closed leaves
353 ** --bybranch order output by branch name
354 ** --recompute recompute the "leaf" table in the repository DB
355 **
356 ** See also: descendants, finfo, info, branch
357 */
358 void leaves_cmd(void){
@@ -358,10 +359,14 @@
359 Stmt q;
360 Blob sql;
361 int showAll = find_option("all", 0, 0)!=0;
362 int showClosed = find_option("closed", 0, 0)!=0;
363 int recomputeFlag = find_option("recompute",0,0)!=0;
364 int byBranch = find_option("bybranch",0,0)!=0;
365 char *zLastBr = 0;
366 int n;
367 char zLineNo[10];
368
369 db_find_and_open_repository(0,0);
370 if( recomputeFlag ) leaf_rebuild();
371 blob_zero(&sql);
372 blob_append(&sql, timeline_query_for_tty(), -1);
@@ -369,13 +374,39 @@
374 if( showClosed ){
375 blob_appendf(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
376 }else if( !showAll ){
377 blob_appendf(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
378 }
379 if( byBranch ){
380 db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase,"
381 " event.mtime DESC",
382 blob_str(&sql));
383 }else{
384 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_str(&sql));
385 }
386 blob_reset(&sql);
387 n = 0;
388 while( db_step(&q)==SQLITE_ROW ){
389 const char *zId = db_column_text(&q, 1);
390 const char *zDate = db_column_text(&q, 2);
391 const char *zCom = db_column_text(&q, 3);
392 const char *zBr = db_column_text(&q, 7);
393 char *z;
394
395 if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){
396 fossil_print("*** %s ***\n", zBr);
397 fossil_free(zLastBr);
398 zLastBr = fossil_strdup(zBr);
399 }
400 n++;
401 sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
402 fossil_print("%6s ", zLineNo);
403 z = mprintf("%s [%.10s] %s", zDate, zId, zCom);
404 comment_print(z, 7, 79);
405 fossil_free(z);
406 }
407 fossil_free(zLastBr);
408 db_finalize(&q);
409 }
410
411 /*
412 ** WEBPAGE: leaves
413
+265 -87
--- src/diff.c
+++ src/diff.c
@@ -23,11 +23,12 @@
2323
#include <assert.h>
2424
2525
2626
#if INTERFACE
2727
/*
28
-** Allowed flag parameters to the text_diff() and html_sbsdiff() functions:
28
+** Flag parameters to the text_diff() routine used to control the formatting
29
+** of the diff output.
2930
*/
3031
#define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
3132
#define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
3233
#define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
3334
#define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
@@ -37,10 +38,11 @@
3738
#define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
3839
#define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
3940
#define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
4041
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
4142
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43
+#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
4244
4345
/*
4446
** These error messages are shared in multiple locations. They are defined
4547
** here for consistency.
4648
*/
@@ -52,11 +54,11 @@
5254
5355
#define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0)
5456
#endif /* INTERFACE */
5557
5658
/*
57
-** Maximum length of a line in a text file, in bytes. (8192)
59
+** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
5860
*/
5961
#define LENGTH_MASK_SZ 13
6062
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
6163
6264
/*
@@ -116,10 +118,13 @@
116118
** more. If trailing whitespace is ignored, the "patch" command gets
117119
** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
118120
**
119121
** Return 0 if the file is binary or contains a line that is
120122
** too long.
123
+**
124
+** Profiling show that in most cases this routine consumes the bulk of
125
+** the CPU time on a diff.
121126
*/
122127
static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
123128
int nLine, i, j, k, x;
124129
unsigned int h, h2;
125130
DLine *a;
@@ -445,34 +450,50 @@
445450
** Return true if two DLine elements are identical.
446451
*/
447452
static int same_dline(DLine *pA, DLine *pB){
448453
return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
449454
}
455
+
456
+/*
457
+** Return true if the regular expression *pRe matches any of the
458
+** N dlines
459
+*/
460
+static int re_dline_match(
461
+ ReCompiled *pRe, /* The regular expression to be matched */
462
+ DLine *aDLine, /* First of N DLines to compare against */
463
+ int N /* Number of DLines to check */
464
+){
465
+ while( N-- ){
466
+ if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){
467
+ return 1;
468
+ }
469
+ aDLine++;
470
+ }
471
+ return 0;
472
+}
450473
451474
/*
452475
** Append a single line of context-diff output to pOut.
453476
*/
454477
static void appendDiffLine(
455478
Blob *pOut, /* Where to write the line of output */
456479
char cPrefix, /* One of " ", "+", or "-" */
457480
DLine *pLine, /* The line to be output */
458
- int html /* True if generating HTML. False for plain text */
481
+ int html, /* True if generating HTML. False for plain text */
482
+ ReCompiled *pRe /* Colorize only if line matches this Regex */
459483
){
460
- int i;
461484
blob_append(pOut, &cPrefix, 1);
462485
if( html ){
463486
char *zHtml;
464
- if( cPrefix=='+' ){
487
+ if( pRe && re_dline_match(pRe, pLine, 1)==0 ){
488
+ cPrefix = ' ';
489
+ }else if( cPrefix=='+' ){
465490
blob_append(pOut, "<span class=\"diffadd\">", -1);
466491
}else if( cPrefix=='-' ){
467492
blob_append(pOut, "<span class=\"diffrm\">", -1);
468493
}
469494
zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
470
- for(i=0; i<strlen(zHtml); i++){
471
- char c = zHtml[i];
472
- if( c=='\t' || c=='\r' || c=='\f' ) zHtml[i] = ' ';
473
- }
474495
blob_append(pOut, zHtml, -1);
475496
fossil_free(zHtml);
476497
if( cPrefix!=' ' ){
477498
blob_append(pOut, "</span>", -1);
478499
}
@@ -482,11 +503,11 @@
482503
blob_append(pOut, "\n", 1);
483504
}
484505
485506
/*
486507
** Add two line numbers to the beginning of an output line for a context
487
-** diff. One or of the other of the two numbers might be zero, which means
508
+** diff. One or the other of the two numbers might be zero, which means
488509
** to leave that number field blank. The "html" parameter means to format
489510
** the output for HTML.
490511
*/
491512
static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
492513
if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
@@ -501,21 +522,19 @@
501522
blob_append(pOut, " ", 8);
502523
}
503524
if( html ) blob_append(pOut, "</span>", -1);
504525
}
505526
506
-
507527
/*
508528
** Given a raw diff p[] in which the p->aEdit[] array has been filled
509529
** in, compute a context diff into pOut.
510530
*/
511531
static void contextDiff(
512532
DContext *p, /* The difference */
513533
Blob *pOut, /* Output a context diff to here */
514
- int nContext, /* Number of lines of context */
515
- int showLn, /* Show line numbers */
516
- int html /* Render as HTML */
534
+ ReCompiled *pRe, /* Only show changes that match this regex */
535
+ u64 diffFlags /* Flags controlling the diff format */
517536
){
518537
DLine *A; /* Left side of the diff */
519538
DLine *B; /* Right side of the diff */
520539
int a = 0; /* Index of next line in A[] */
521540
int b = 0; /* Index of next line in B[] */
@@ -526,11 +545,18 @@
526545
int na, nb; /* Number of lines shown from A and B */
527546
int i, j; /* Loop counters */
528547
int m; /* Number of lines to output */
529548
int skip; /* Number of lines to skip */
530549
int nChunk = 0; /* Number of diff chunks seen so far */
550
+ int nContext; /* Number of lines of context */
551
+ int showLn; /* Show line numbers */
552
+ int html; /* Render as HTML */
553
+ int showDivider = 0; /* True to show the divider between diff blocks */
531554
555
+ nContext = diff_context_lines(diffFlags);
556
+ showLn = (diffFlags & DIFF_LINENO)!=0;
557
+ html = (diffFlags & DIFF_HTML)!=0;
532558
A = p->aFrom;
533559
B = p->aTo;
534560
R = p->aEdit;
535561
mxr = p->nEdit;
536562
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -537,10 +563,35 @@
537563
for(r=0; r<mxr; r += 3*nr){
538564
/* Figure out how many triples to show in a single block */
539565
for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
540566
/* printf("r=%d nr=%d\n", r, nr); */
541567
568
+ /* If there is a regex, skip this block (generate no diff output)
569
+ ** if the regex matches or does not match both insert and delete.
570
+ ** Only display the block if one side matches but the other side does
571
+ ** not.
572
+ */
573
+ if( pRe ){
574
+ int hideBlock = 1;
575
+ int xa = a, xb = b;
576
+ for(i=0; hideBlock && i<nr; i++){
577
+ int c1, c2;
578
+ xa += R[r+i*3];
579
+ xb += R[r+i*3];
580
+ c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
581
+ c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
582
+ hideBlock = c1==c2;
583
+ xa += R[r+i*3+1];
584
+ xb += R[r+i*3+2];
585
+ }
586
+ if( hideBlock ){
587
+ a = xa;
588
+ b = xb;
589
+ continue;
590
+ }
591
+ }
592
+
542593
/* For the current block comprising nr triples, figure out
543594
** how many lines of A and B are to be displayed
544595
*/
545596
if( R[r]>nContext ){
546597
na = nb = nContext;
@@ -564,17 +615,18 @@
564615
na += R[r+i*3];
565616
nb += R[r+i*3];
566617
}
567618
568619
/* Show the header for this block, or if we are doing a modified
569
- ** context diff that contains line numbers, show the separate from
620
+ ** context diff that contains line numbers, show the separator from
570621
** the previous block.
571622
*/
572623
nChunk++;
573624
if( showLn ){
574
- if( r==0 ){
625
+ if( !showDivider ){
575626
/* Do not show a top divider */
627
+ showDivider = 1;
576628
}else if( html ){
577629
blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
578630
blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
579631
}else{
580632
blob_appendf(pOut, "%.80c\n", '.');
@@ -597,34 +649,34 @@
597649
a += skip;
598650
b += skip;
599651
m = R[r] - skip;
600652
for(j=0; j<m; j++){
601653
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
602
- appendDiffLine(pOut, ' ', &A[a+j], html);
654
+ appendDiffLine(pOut, ' ', &A[a+j], html, 0);
603655
}
604656
a += m;
605657
b += m;
606658
607659
/* Show the differences */
608660
for(i=0; i<nr; i++){
609661
m = R[r+i*3+1];
610662
for(j=0; j<m; j++){
611663
if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
612
- appendDiffLine(pOut, '-', &A[a+j], html);
664
+ appendDiffLine(pOut, '-', &A[a+j], html, pRe);
613665
}
614666
a += m;
615667
m = R[r+i*3+2];
616668
for(j=0; j<m; j++){
617669
if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
618
- appendDiffLine(pOut, '+', &B[b+j], html);
670
+ appendDiffLine(pOut, '+', &B[b+j], html, pRe);
619671
}
620672
b += m;
621673
if( i<nr-1 ){
622674
m = R[r+i*3+3];
623675
for(j=0; j<m; j++){
624676
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
625
- appendDiffLine(pOut, ' ', &B[b+j], html);
677
+ appendDiffLine(pOut, ' ', &B[b+j], html, 0);
626678
}
627679
b += m;
628680
a += m;
629681
}
630682
}
@@ -633,11 +685,11 @@
633685
assert( nr==i );
634686
m = R[r+nr*3];
635687
if( m>nContext ) m = nContext;
636688
for(j=0; j<m; j++){
637689
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
638
- appendDiffLine(pOut, ' ', &B[b+j], html);
690
+ appendDiffLine(pOut, ' ', &B[b+j], html, 0);
639691
}
640692
}
641693
}
642694
643695
/*
@@ -653,10 +705,11 @@
653705
const char *zStart; /* A <span> tag */
654706
int iEnd; /* Write </span> prior to character iEnd */
655707
int iStart2; /* Write zStart2 prior to character iStart2 */
656708
const char *zStart2; /* A <span> tag */
657709
int iEnd2; /* Write </span> prior to character iEnd2 */
710
+ ReCompiled *pRe; /* Only colorize matching lines, if not NULL */
658711
};
659712
660713
/*
661714
** Flags for sbsWriteText()
662715
*/
@@ -678,13 +731,17 @@
678731
int k; /* Cursor position */
679732
int needEndSpan = 0;
680733
const char *zIn = pLine->z;
681734
char *z = &p->zLine[p->n];
682735
int w = p->width;
736
+ int colorize = p->escHtml;
737
+ if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
738
+ colorize = 0;
739
+ }
683740
for(i=j=k=0; k<w && i<n; i++, k++){
684741
char c = zIn[i];
685
- if( p->escHtml ){
742
+ if( colorize ){
686743
if( i==p->iStart ){
687744
int x = strlen(p->zStart);
688745
memcpy(z+j, p->zStart, x);
689746
j += x;
690747
needEndSpan = 1;
@@ -835,10 +892,41 @@
835892
}
836893
}
837894
}
838895
return rc;
839896
}
897
+
898
+/*
899
+** Try to shift iStart as far as possible to the left.
900
+*/
901
+static void sbsShiftLeft(SbsLine *p, const char *z){
902
+ int i, j;
903
+ while( (i=p->iStart)>0 && z[i-1]==z[i] ){
904
+ for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){}
905
+ if( j<p->iEnd ) break;
906
+ p->iStart--;
907
+ p->iEnd--;
908
+ }
909
+}
910
+
911
+/*
912
+** Simplify iStart and iStart2:
913
+**
914
+** * If iStart is a null-change then move iStart2 into iStart
915
+** * Make sure any null-changes are in canonoical form.
916
+*/
917
+static void sbsSimplifyLine(SbsLine *p){
918
+ if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
919
+ if( p->iStart==p->iEnd ){
920
+ p->iStart = p->iStart2;
921
+ p->iEnd = p->iEnd2;
922
+ p->zStart = p->zStart2;
923
+ p->iStart2 = 0;
924
+ p->iEnd2 = 0;
925
+ }
926
+ if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
927
+}
840928
841929
/*
842930
** Write out lines that have been edited. Adjust the highlight to cover
843931
** only those parts of the line that actually changed.
844932
*/
@@ -929,41 +1017,35 @@
9291017
&& textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
9301018
){
9311019
sbsWriteLineno(p, lnLeft);
9321020
p->iStart = nPrefix;
9331021
p->iEnd = nPrefix + aLCS[0];
934
- p->zStart = aLCS[2]==0 ? zClassRm : zClassChng;
1022
+ if( aLCS[2]==0 ){
1023
+ sbsShiftLeft(p, pLeft->z);
1024
+ p->zStart = zClassRm;
1025
+ }else{
1026
+ p->zStart = zClassChng;
1027
+ }
9351028
p->iStart2 = nPrefix + aLCS[1];
9361029
p->iEnd2 = nLeft - nSuffix;
9371030
p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
938
- if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
939
- if( p->iStart==p->iEnd ){
940
- p->iStart = p->iStart2;
941
- p->iEnd = p->iEnd2;
942
- p->zStart = p->zStart2;
943
- p->iStart2 = 0;
944
- p->iEnd2 = 0;
945
- }
946
- if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
1031
+ sbsSimplifyLine(p);
9471032
sbsWriteText(p, pLeft, SBS_PAD);
9481033
sbsWrite(p, " | ", 3);
9491034
sbsWriteLineno(p, lnRight);
9501035
p->iStart = nPrefix;
9511036
p->iEnd = nPrefix + aLCS[2];
952
- p->zStart = aLCS[0]==0 ? zClassAdd : zClassChng;
1037
+ if( aLCS[0]==0 ){
1038
+ sbsShiftLeft(p, pRight->z);
1039
+ p->zStart = zClassAdd;
1040
+ }else{
1041
+ p->zStart = zClassChng;
1042
+ }
9531043
p->iStart2 = nPrefix + aLCS[3];
9541044
p->iEnd2 = nRight - nSuffix;
9551045
p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
956
- if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
957
- if( p->iStart==p->iEnd ){
958
- p->iStart = p->iStart2;
959
- p->iEnd = p->iEnd2;
960
- p->zStart = p->zStart2;
961
- p->iStart2 = 0;
962
- p->iEnd2 = 0;
963
- }
964
- if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
1046
+ sbsSimplifyLine(p);
9651047
sbsWriteText(p, pRight, SBS_NEWLINE);
9661048
return;
9671049
}
9681050
9691051
/* If all else fails, show a single big change between left and right */
@@ -1056,13 +1138,16 @@
10561138
**
10571139
** The return value is a buffer of unsigned characters, obtained from
10581140
** fossil_malloc(). (The caller needs to free the return value using
10591141
** fossil_free().) Entries in the returned array have values as follows:
10601142
**
1061
-** 1. Delete the next line of pLeft.
1062
-** 2. The next line of pLeft changes into the next line of pRight.
1063
-** 3. Insert the next line of pRight.
1143
+** 1. Delete the next line of pLeft.
1144
+** 2. Insert the next line of pRight.
1145
+** 3. The next line of pLeft changes into the next line of pRight.
1146
+** 4. Delete one line from pLeft and add one line to pRight.
1147
+**
1148
+** Values larger than three indicate better matches.
10641149
**
10651150
** The length of the returned array will be just large enough to cause
10661151
** all elements of pLeft and pRight to be consumed.
10671152
**
10681153
** Algorithm: Wagner's minimum edit-distance algorithm, modified by
@@ -1077,27 +1162,32 @@
10771162
){
10781163
int i, j, k; /* Loop counters */
10791164
int *a; /* One row of the Wagner matrix */
10801165
int *pToFree; /* Space that needs to be freed */
10811166
unsigned char *aM; /* Wagner result matrix */
1167
+ int nMatch, iMatch; /* Number of matching lines and match score */
1168
+ int mnLen; /* MIN(nLeft, nRight) */
1169
+ int mxLen; /* MAX(nLeft, nRight) */
10821170
int aBuf[100]; /* Stack space for a[] if nRight not to big */
10831171
10841172
aM = fossil_malloc( (nLeft+1)*(nRight+1) );
10851173
if( nLeft==0 ){
1086
- memset(aM, 3, nRight);
1174
+ memset(aM, 2, nRight);
10871175
return aM;
10881176
}
10891177
if( nRight==0 ){
10901178
memset(aM, 1, nLeft);
10911179
return aM;
10921180
}
10931181
10941182
/* This algorithm is O(N**2). So if N is too big, bail out with a
10951183
** simple (but stupid and ugly) result that doesn't take too long. */
1184
+ mnLen = nLeft<nRight ? nLeft : nRight;
10961185
if( nLeft*nRight>100000 ){
1097
- memset(aM, 3, nRight);
1098
- memset(aM+nRight, 1, nLeft);
1186
+ memset(aM, 4, mnLen);
1187
+ if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1188
+ if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
10991189
return aM;
11001190
}
11011191
11021192
if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){
11031193
pToFree = 0;
@@ -1106,30 +1196,30 @@
11061196
a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
11071197
}
11081198
11091199
/* Compute the best alignment */
11101200
for(i=0; i<=nRight; i++){
1111
- aM[i] = 3;
1201
+ aM[i] = 2;
11121202
a[i] = i*50;
11131203
}
11141204
aM[0] = 0;
11151205
for(j=1; j<=nLeft; j++){
11161206
int p = a[0];
11171207
a[0] = p+50;
11181208
aM[j*(nRight+1)] = 1;
11191209
for(i=1; i<=nRight; i++){
11201210
int m = a[i-1]+50;
1121
- int d = 3;
1211
+ int d = 2;
11221212
if( m>a[i]+50 ){
11231213
m = a[i]+50;
11241214
d = 1;
11251215
}
11261216
if( m>p ){
11271217
int score = match_dline(&aLeft[j-1], &aRight[i-1]);
1128
- if( (score<66 || (i<j+1 && i>j-1)) && m>p+score ){
1218
+ if( (score<=63 || (i<j+1 && i>j-1)) && m>p+score ){
11291219
m = p+score;
1130
- d = 2;
1220
+ d = 3 | score*4;
11311221
}
11321222
}
11331223
p = a[i];
11341224
a[i] = m;
11351225
aM[j*(nRight+1)+i] = d;
@@ -1138,28 +1228,50 @@
11381228
11391229
/* Compute the lowest-cost path back through the matrix */
11401230
i = nRight;
11411231
j = nLeft;
11421232
k = (nRight+1)*(nLeft+1)-1;
1233
+ nMatch = iMatch = 0;
11431234
while( i+j>0 ){
1144
- unsigned char c = aM[k--];
1145
- if( c==2 ){
1235
+ unsigned char c = aM[k];
1236
+ if( c>=3 ){
11461237
assert( i>0 && j>0 );
11471238
i--;
11481239
j--;
1149
- }else if( c==3 ){
1240
+ nMatch++;
1241
+ iMatch += (c>>2);
1242
+ aM[k] = 3;
1243
+ }else if( c==2 ){
11501244
assert( i>0 );
11511245
i--;
11521246
}else{
11531247
assert( j>0 );
11541248
j--;
11551249
}
1250
+ k--;
11561251
aM[k] = aM[j*(nRight+1)+i];
11571252
}
11581253
k++;
11591254
i = (nRight+1)*(nLeft+1) - k;
11601255
memmove(aM, &aM[k], i);
1256
+
1257
+ /* If:
1258
+ ** (1) the alignment is more than 25% longer than the longest side, and
1259
+ ** (2) the average match cost exceeds 15
1260
+ ** Then this is probably an alignment that will be difficult for humans
1261
+ ** to read. So instead, just show all of the right side inserted followed
1262
+ ** by all of the left side deleted.
1263
+ **
1264
+ ** The coefficients for conditions (1) and (2) above are determined by
1265
+ ** experimentation.
1266
+ */
1267
+ mxLen = nLeft>nRight ? nLeft : nRight;
1268
+ if( i*4>mxLen*5 && (nMatch==0 || iMatch/nMatch>15) ){
1269
+ memset(aM, 4, mnLen);
1270
+ if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1271
+ if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
1272
+ }
11611273
11621274
/* Return the result */
11631275
fossil_free(pToFree);
11641276
return aM;
11651277
}
@@ -1179,13 +1291,12 @@
11791291
** in, compute a side-by-side diff into pOut.
11801292
*/
11811293
static void sbsDiff(
11821294
DContext *p, /* The computed diff */
11831295
Blob *pOut, /* Write the results here */
1184
- int nContext, /* Number of lines of context around each change */
1185
- int width, /* Width of each column of output */
1186
- int escHtml /* True to generate HTML output */
1296
+ ReCompiled *pRe, /* Only show changes that match this regex */
1297
+ u64 diffFlags /* Flags controlling the diff */
11871298
){
11881299
DLine *A; /* Left side of the diff */
11891300
DLine *B; /* Right side of the diff */
11901301
int a = 0; /* Index of next line in A[] */
11911302
int b = 0; /* Index of next line in B[] */
@@ -1197,16 +1308,20 @@
11971308
int i, j; /* Loop counters */
11981309
int m, ma, mb;/* Number of lines to output */
11991310
int skip; /* Number of lines to skip */
12001311
int nChunk = 0; /* Number of chunks of diff output seen so far */
12011312
SbsLine s; /* Output line buffer */
1313
+ int nContext; /* Lines of context above and below each change */
1314
+ int showDivider = 0; /* True to show the divider */
12021315
12031316
memset(&s, 0, sizeof(s));
1204
- s.zLine = fossil_malloc( 15*width + 200 );
1317
+ s.width = diff_width(diffFlags);
1318
+ s.zLine = fossil_malloc( 15*s.width + 200 );
12051319
if( s.zLine==0 ) return;
1206
- s.width = width;
1207
- s.escHtml = escHtml;
1320
+ nContext = diff_context_lines(diffFlags);
1321
+ s.escHtml = (diffFlags & DIFF_HTML)!=0;
1322
+ s.pRe = pRe;
12081323
s.iStart = -1;
12091324
s.iStart2 = 0;
12101325
s.iEnd = -1;
12111326
A = p->aFrom;
12121327
B = p->aTo;
@@ -1215,10 +1330,35 @@
12151330
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
12161331
for(r=0; r<mxr; r += 3*nr){
12171332
/* Figure out how many triples to show in a single block */
12181333
for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
12191334
/* printf("r=%d nr=%d\n", r, nr); */
1335
+
1336
+ /* If there is a regex, skip this block (generate no diff output)
1337
+ ** if the regex matches or does not match both insert and delete.
1338
+ ** Only display the block if one side matches but the other side does
1339
+ ** not.
1340
+ */
1341
+ if( pRe ){
1342
+ int hideBlock = 1;
1343
+ int xa = a, xb = b;
1344
+ for(i=0; hideBlock && i<nr; i++){
1345
+ int c1, c2;
1346
+ xa += R[r+i*3];
1347
+ xb += R[r+i*3];
1348
+ c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
1349
+ c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
1350
+ hideBlock = c1==c2;
1351
+ xa += R[r+i*3+1];
1352
+ xb += R[r+i*3+2];
1353
+ }
1354
+ if( hideBlock ){
1355
+ a = xa;
1356
+ b = xb;
1357
+ continue;
1358
+ }
1359
+ }
12201360
12211361
/* For the current block comprising nr triples, figure out
12221362
** how many lines of A and B are to be displayed
12231363
*/
12241364
if( R[r]>nContext ){
@@ -1243,20 +1383,21 @@
12431383
na += R[r+i*3];
12441384
nb += R[r+i*3];
12451385
}
12461386
12471387
/* Draw the separator between blocks */
1248
- if( r>0 ){
1249
- if( escHtml ){
1388
+ if( showDivider ){
1389
+ if( s.escHtml ){
12501390
blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
1251
- width*2+16, '.');
1391
+ s.width*2+16, '.');
12521392
}else{
1253
- blob_appendf(pOut, "%.*c\n", width*2+16, '.');
1393
+ blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
12541394
}
12551395
}
1396
+ showDivider = 1;
12561397
nChunk++;
1257
- if( escHtml ){
1398
+ if( s.escHtml ){
12581399
blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
12591400
}
12601401
12611402
/* Show the initial common area */
12621403
a += skip;
@@ -1292,38 +1433,41 @@
12921433
}
12931434
12941435
alignment = sbsAlignment(&A[a], ma, &B[b], mb);
12951436
for(j=0; ma+mb>0; j++){
12961437
if( alignment[j]==1 ){
1438
+ /* Delete one line from the left */
12971439
s.n = 0;
12981440
sbsWriteLineno(&s, a);
12991441
s.iStart = 0;
13001442
s.zStart = "<span class=\"diffrm\">";
13011443
s.iEnd = s.width;
13021444
sbsWriteText(&s, &A[a], SBS_PAD);
1303
- if( escHtml ){
1445
+ if( s.escHtml ){
13041446
sbsWrite(&s, " &lt;\n", 6);
13051447
}else{
13061448
sbsWrite(&s, " <\n", 3);
13071449
}
13081450
blob_append(pOut, s.zLine, s.n);
13091451
assert( ma>0 );
13101452
ma--;
13111453
a++;
1312
- }else if( alignment[j]==2 ){
1454
+ }else if( alignment[j]==3 ){
1455
+ /* The left line is changed into the right line */
13131456
s.n = 0;
13141457
sbsWriteLineChange(&s, &A[a], a, &B[b], b);
13151458
blob_append(pOut, s.zLine, s.n);
13161459
assert( ma>0 && mb>0 );
13171460
ma--;
13181461
mb--;
13191462
a++;
13201463
b++;
1321
- }else{
1464
+ }else if( alignment[j]==2 ){
1465
+ /* Insert one line on the right */
13221466
s.n = 0;
1323
- sbsWriteSpace(&s, width + 7);
1324
- if( escHtml ){
1467
+ sbsWriteSpace(&s, s.width + 7);
1468
+ if( s.escHtml ){
13251469
sbsWrite(&s, " &gt; ", 6);
13261470
}else{
13271471
sbsWrite(&s, " > ", 3);
13281472
}
13291473
sbsWriteLineno(&s, b);
@@ -1333,11 +1477,31 @@
13331477
sbsWriteText(&s, &B[b], SBS_NEWLINE);
13341478
blob_append(pOut, s.zLine, s.n);
13351479
assert( mb>0 );
13361480
mb--;
13371481
b++;
1482
+ }else{
1483
+ /* Delete from the left and insert on the right */
1484
+ s.n = 0;
1485
+ sbsWriteLineno(&s, a);
1486
+ s.iStart = 0;
1487
+ s.zStart = "<span class=\"diffrm\">";
1488
+ s.iEnd = s.width;
1489
+ sbsWriteText(&s, &A[a], SBS_PAD);
1490
+ sbsWrite(&s, " | ", 3);
1491
+ sbsWriteLineno(&s, b);
1492
+ s.iStart = 0;
1493
+ s.zStart = "<span class=\"diffadd\">";
1494
+ s.iEnd = s.width;
1495
+ sbsWriteText(&s, &B[b], SBS_NEWLINE);
1496
+ blob_append(pOut, s.zLine, s.n);
1497
+ ma--;
1498
+ mb--;
1499
+ a++;
1500
+ b++;
13381501
}
1502
+
13391503
}
13401504
fossil_free(alignment);
13411505
if( i<nr-1 ){
13421506
m = R[r+i*3+3];
13431507
for(j=0; j<m; j++){
@@ -1761,11 +1925,11 @@
17611925
** Extract the number of lines of context from diffFlags. Supply an
17621926
** appropriate default if no context width is specified.
17631927
*/
17641928
int diff_context_lines(u64 diffFlags){
17651929
int n = diffFlags & DIFF_CONTEXT_MASK;
1766
- if( n==0 ) n = 5;
1930
+ if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5;
17671931
return n;
17681932
}
17691933
17701934
/*
17711935
** Extract the width of columns for side-by-side diff. Supply an
@@ -1793,22 +1957,21 @@
17931957
*/
17941958
int *text_diff(
17951959
Blob *pA_Blob, /* FROM file */
17961960
Blob *pB_Blob, /* TO file */
17971961
Blob *pOut, /* Write diff here if not NULL */
1962
+ ReCompiled *pRe, /* Only output changes where this Regexp matches */
17981963
u64 diffFlags /* DIFF_* flags defined above */
17991964
){
18001965
int ignoreEolWs; /* Ignore whitespace at the end of lines */
1801
- int nContext; /* Amount of context to display */
18021966
DContext c;
18031967
18041968
if( diffFlags & DIFF_INVERT ){
18051969
Blob *pTemp = pA_Blob;
18061970
pA_Blob = pB_Blob;
18071971
pB_Blob = pTemp;
18081972
}
1809
- nContext = diff_context_lines(diffFlags);
18101973
ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
18111974
18121975
/* Prepare the input files */
18131976
memset(&c, 0, sizeof(c));
18141977
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
@@ -1828,17 +1991,14 @@
18281991
diff_all(&c);
18291992
if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
18301993
18311994
if( pOut ){
18321995
/* Compute a context or side-by-side diff into pOut */
1833
- int escHtml = (diffFlags & DIFF_HTML)!=0;
18341996
if( diffFlags & DIFF_SIDEBYSIDE ){
1835
- int width = diff_width(diffFlags);
1836
- sbsDiff(&c, pOut, nContext, width, escHtml);
1997
+ sbsDiff(&c, pOut, pRe, diffFlags);
18371998
}else{
1838
- int showLn = (diffFlags & DIFF_LINENO)!=0;
1839
- contextDiff(&c, pOut, nContext, showLn, escHtml);
1999
+ contextDiff(&c, pOut, pRe, diffFlags);
18402000
}
18412001
fossil_free(c.aFrom);
18422002
fossil_free(c.aTo);
18432003
fossil_free(c.aEdit);
18442004
return 0;
@@ -1864,19 +2024,19 @@
18642024
** --noopt Disable optimization DIFF_NOOPT
18652025
** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
18662026
** --unified Unified diff. ~DIFF_SIDEBYSIDE
18672027
** --width|-W N N character lines. DIFF_WIDTH_MASK
18682028
*/
1869
-int diff_options(void){
2029
+u64 diff_options(void){
18702030
u64 diffFlags = 0;
18712031
const char *z;
18722032
int f;
18732033
if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
18742034
if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
1875
- if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
2035
+ if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
18762036
if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1877
- diffFlags |= f;
2037
+ diffFlags |= f + DIFF_CONTEXT_EX;
18782038
}
18792039
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
18802040
f *= DIFF_CONTEXT_MASK+1;
18812041
if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
18822042
diffFlags |= f;
@@ -1901,34 +2061,52 @@
19012061
if( g.argc<4 ) usage("FILE1 FILE2 ...");
19022062
blob_read_from_file(&a, g.argv[2]);
19032063
for(i=3; i<g.argc; i++){
19042064
if( i>3 ) fossil_print("-------------------------------\n");
19052065
blob_read_from_file(&b, g.argv[i]);
1906
- R = text_diff(&a, &b, 0, diffFlags);
2066
+ R = text_diff(&a, &b, 0, 0, diffFlags);
19072067
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
19082068
fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
19092069
}
19102070
/* free(R); */
19112071
blob_reset(&b);
19122072
}
19132073
}
19142074
19152075
/*
1916
-** COMMAND: test-udiff
2076
+** COMMAND: test-diff
2077
+**
2078
+** Usage: %fossil [options] FILE1 FILE2
19172079
**
19182080
** Print the difference between two files. The usual diff options apply.
19192081
*/
1920
-void test_udiff_cmd(void){
2082
+void test_diff_cmd(void){
19212083
Blob a, b, out;
1922
- u64 diffFlag = diff_options();
2084
+ u64 diffFlag;
2085
+ const char *zRe; /* Regex filter for diff output */
2086
+ ReCompiled *pRe = 0; /* Regex filter for diff output */
19232087
2088
+ if( find_option("tk",0,0)!=0 ){
2089
+ diff_tk("test-diff", 2);
2090
+ return;
2091
+ }
2092
+ find_option("i",0,0);
2093
+ zRe = find_option("regexp","e",1);
2094
+ if( zRe ){
2095
+ const char *zErr = re_compile(&pRe, zRe, 0);
2096
+ if( zErr ) fossil_fatal("regex error: %s", zErr);
2097
+ }
2098
+ diffFlag = diff_options();
2099
+ verify_all_options();
19242100
if( g.argc!=4 ) usage("FILE1 FILE2");
2101
+ diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
19252102
blob_read_from_file(&a, g.argv[2]);
19262103
blob_read_from_file(&b, g.argv[3]);
19272104
blob_zero(&out);
1928
- text_diff(&a, &b, &out, diffFlag);
2105
+ text_diff(&a, &b, &out, pRe, diffFlag);
19292106
blob_write_to_file(&out, "-");
2107
+ re_free(pRe);
19302108
}
19312109
19322110
/**************************************************************************
19332111
** The basic difference engine is above. What follows is the annotation
19342112
** engine. Both are in the same file since they share many components.
19352113
--- src/diff.c
+++ src/diff.c
@@ -23,11 +23,12 @@
23 #include <assert.h>
24
25
26 #if INTERFACE
27 /*
28 ** Allowed flag parameters to the text_diff() and html_sbsdiff() functions:
 
29 */
30 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
31 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
32 #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
33 #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
@@ -37,10 +38,11 @@
37 #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
38 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
39 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
40 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
41 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
 
42
43 /*
44 ** These error messages are shared in multiple locations. They are defined
45 ** here for consistency.
46 */
@@ -52,11 +54,11 @@
52
53 #define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0)
54 #endif /* INTERFACE */
55
56 /*
57 ** Maximum length of a line in a text file, in bytes. (8192)
58 */
59 #define LENGTH_MASK_SZ 13
60 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
61
62 /*
@@ -116,10 +118,13 @@
116 ** more. If trailing whitespace is ignored, the "patch" command gets
117 ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
118 **
119 ** Return 0 if the file is binary or contains a line that is
120 ** too long.
 
 
 
121 */
122 static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
123 int nLine, i, j, k, x;
124 unsigned int h, h2;
125 DLine *a;
@@ -445,34 +450,50 @@
445 ** Return true if two DLine elements are identical.
446 */
447 static int same_dline(DLine *pA, DLine *pB){
448 return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
449 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
451 /*
452 ** Append a single line of context-diff output to pOut.
453 */
454 static void appendDiffLine(
455 Blob *pOut, /* Where to write the line of output */
456 char cPrefix, /* One of " ", "+", or "-" */
457 DLine *pLine, /* The line to be output */
458 int html /* True if generating HTML. False for plain text */
 
459 ){
460 int i;
461 blob_append(pOut, &cPrefix, 1);
462 if( html ){
463 char *zHtml;
464 if( cPrefix=='+' ){
 
 
465 blob_append(pOut, "<span class=\"diffadd\">", -1);
466 }else if( cPrefix=='-' ){
467 blob_append(pOut, "<span class=\"diffrm\">", -1);
468 }
469 zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
470 for(i=0; i<strlen(zHtml); i++){
471 char c = zHtml[i];
472 if( c=='\t' || c=='\r' || c=='\f' ) zHtml[i] = ' ';
473 }
474 blob_append(pOut, zHtml, -1);
475 fossil_free(zHtml);
476 if( cPrefix!=' ' ){
477 blob_append(pOut, "</span>", -1);
478 }
@@ -482,11 +503,11 @@
482 blob_append(pOut, "\n", 1);
483 }
484
485 /*
486 ** Add two line numbers to the beginning of an output line for a context
487 ** diff. One or of the other of the two numbers might be zero, which means
488 ** to leave that number field blank. The "html" parameter means to format
489 ** the output for HTML.
490 */
491 static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
492 if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
@@ -501,21 +522,19 @@
501 blob_append(pOut, " ", 8);
502 }
503 if( html ) blob_append(pOut, "</span>", -1);
504 }
505
506
507 /*
508 ** Given a raw diff p[] in which the p->aEdit[] array has been filled
509 ** in, compute a context diff into pOut.
510 */
511 static void contextDiff(
512 DContext *p, /* The difference */
513 Blob *pOut, /* Output a context diff to here */
514 int nContext, /* Number of lines of context */
515 int showLn, /* Show line numbers */
516 int html /* Render as HTML */
517 ){
518 DLine *A; /* Left side of the diff */
519 DLine *B; /* Right side of the diff */
520 int a = 0; /* Index of next line in A[] */
521 int b = 0; /* Index of next line in B[] */
@@ -526,11 +545,18 @@
526 int na, nb; /* Number of lines shown from A and B */
527 int i, j; /* Loop counters */
528 int m; /* Number of lines to output */
529 int skip; /* Number of lines to skip */
530 int nChunk = 0; /* Number of diff chunks seen so far */
 
 
 
 
531
 
 
 
532 A = p->aFrom;
533 B = p->aTo;
534 R = p->aEdit;
535 mxr = p->nEdit;
536 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -537,10 +563,35 @@
537 for(r=0; r<mxr; r += 3*nr){
538 /* Figure out how many triples to show in a single block */
539 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
540 /* printf("r=%d nr=%d\n", r, nr); */
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542 /* For the current block comprising nr triples, figure out
543 ** how many lines of A and B are to be displayed
544 */
545 if( R[r]>nContext ){
546 na = nb = nContext;
@@ -564,17 +615,18 @@
564 na += R[r+i*3];
565 nb += R[r+i*3];
566 }
567
568 /* Show the header for this block, or if we are doing a modified
569 ** context diff that contains line numbers, show the separate from
570 ** the previous block.
571 */
572 nChunk++;
573 if( showLn ){
574 if( r==0 ){
575 /* Do not show a top divider */
 
576 }else if( html ){
577 blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
578 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
579 }else{
580 blob_appendf(pOut, "%.80c\n", '.');
@@ -597,34 +649,34 @@
597 a += skip;
598 b += skip;
599 m = R[r] - skip;
600 for(j=0; j<m; j++){
601 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
602 appendDiffLine(pOut, ' ', &A[a+j], html);
603 }
604 a += m;
605 b += m;
606
607 /* Show the differences */
608 for(i=0; i<nr; i++){
609 m = R[r+i*3+1];
610 for(j=0; j<m; j++){
611 if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
612 appendDiffLine(pOut, '-', &A[a+j], html);
613 }
614 a += m;
615 m = R[r+i*3+2];
616 for(j=0; j<m; j++){
617 if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
618 appendDiffLine(pOut, '+', &B[b+j], html);
619 }
620 b += m;
621 if( i<nr-1 ){
622 m = R[r+i*3+3];
623 for(j=0; j<m; j++){
624 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
625 appendDiffLine(pOut, ' ', &B[b+j], html);
626 }
627 b += m;
628 a += m;
629 }
630 }
@@ -633,11 +685,11 @@
633 assert( nr==i );
634 m = R[r+nr*3];
635 if( m>nContext ) m = nContext;
636 for(j=0; j<m; j++){
637 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
638 appendDiffLine(pOut, ' ', &B[b+j], html);
639 }
640 }
641 }
642
643 /*
@@ -653,10 +705,11 @@
653 const char *zStart; /* A <span> tag */
654 int iEnd; /* Write </span> prior to character iEnd */
655 int iStart2; /* Write zStart2 prior to character iStart2 */
656 const char *zStart2; /* A <span> tag */
657 int iEnd2; /* Write </span> prior to character iEnd2 */
 
658 };
659
660 /*
661 ** Flags for sbsWriteText()
662 */
@@ -678,13 +731,17 @@
678 int k; /* Cursor position */
679 int needEndSpan = 0;
680 const char *zIn = pLine->z;
681 char *z = &p->zLine[p->n];
682 int w = p->width;
 
 
 
 
683 for(i=j=k=0; k<w && i<n; i++, k++){
684 char c = zIn[i];
685 if( p->escHtml ){
686 if( i==p->iStart ){
687 int x = strlen(p->zStart);
688 memcpy(z+j, p->zStart, x);
689 j += x;
690 needEndSpan = 1;
@@ -835,10 +892,41 @@
835 }
836 }
837 }
838 return rc;
839 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
841 /*
842 ** Write out lines that have been edited. Adjust the highlight to cover
843 ** only those parts of the line that actually changed.
844 */
@@ -929,41 +1017,35 @@
929 && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
930 ){
931 sbsWriteLineno(p, lnLeft);
932 p->iStart = nPrefix;
933 p->iEnd = nPrefix + aLCS[0];
934 p->zStart = aLCS[2]==0 ? zClassRm : zClassChng;
 
 
 
 
 
935 p->iStart2 = nPrefix + aLCS[1];
936 p->iEnd2 = nLeft - nSuffix;
937 p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
938 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
939 if( p->iStart==p->iEnd ){
940 p->iStart = p->iStart2;
941 p->iEnd = p->iEnd2;
942 p->zStart = p->zStart2;
943 p->iStart2 = 0;
944 p->iEnd2 = 0;
945 }
946 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
947 sbsWriteText(p, pLeft, SBS_PAD);
948 sbsWrite(p, " | ", 3);
949 sbsWriteLineno(p, lnRight);
950 p->iStart = nPrefix;
951 p->iEnd = nPrefix + aLCS[2];
952 p->zStart = aLCS[0]==0 ? zClassAdd : zClassChng;
 
 
 
 
 
953 p->iStart2 = nPrefix + aLCS[3];
954 p->iEnd2 = nRight - nSuffix;
955 p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
956 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
957 if( p->iStart==p->iEnd ){
958 p->iStart = p->iStart2;
959 p->iEnd = p->iEnd2;
960 p->zStart = p->zStart2;
961 p->iStart2 = 0;
962 p->iEnd2 = 0;
963 }
964 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
965 sbsWriteText(p, pRight, SBS_NEWLINE);
966 return;
967 }
968
969 /* If all else fails, show a single big change between left and right */
@@ -1056,13 +1138,16 @@
1056 **
1057 ** The return value is a buffer of unsigned characters, obtained from
1058 ** fossil_malloc(). (The caller needs to free the return value using
1059 ** fossil_free().) Entries in the returned array have values as follows:
1060 **
1061 ** 1. Delete the next line of pLeft.
1062 ** 2. The next line of pLeft changes into the next line of pRight.
1063 ** 3. Insert the next line of pRight.
 
 
 
1064 **
1065 ** The length of the returned array will be just large enough to cause
1066 ** all elements of pLeft and pRight to be consumed.
1067 **
1068 ** Algorithm: Wagner's minimum edit-distance algorithm, modified by
@@ -1077,27 +1162,32 @@
1077 ){
1078 int i, j, k; /* Loop counters */
1079 int *a; /* One row of the Wagner matrix */
1080 int *pToFree; /* Space that needs to be freed */
1081 unsigned char *aM; /* Wagner result matrix */
 
 
 
1082 int aBuf[100]; /* Stack space for a[] if nRight not to big */
1083
1084 aM = fossil_malloc( (nLeft+1)*(nRight+1) );
1085 if( nLeft==0 ){
1086 memset(aM, 3, nRight);
1087 return aM;
1088 }
1089 if( nRight==0 ){
1090 memset(aM, 1, nLeft);
1091 return aM;
1092 }
1093
1094 /* This algorithm is O(N**2). So if N is too big, bail out with a
1095 ** simple (but stupid and ugly) result that doesn't take too long. */
 
1096 if( nLeft*nRight>100000 ){
1097 memset(aM, 3, nRight);
1098 memset(aM+nRight, 1, nLeft);
 
1099 return aM;
1100 }
1101
1102 if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){
1103 pToFree = 0;
@@ -1106,30 +1196,30 @@
1106 a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
1107 }
1108
1109 /* Compute the best alignment */
1110 for(i=0; i<=nRight; i++){
1111 aM[i] = 3;
1112 a[i] = i*50;
1113 }
1114 aM[0] = 0;
1115 for(j=1; j<=nLeft; j++){
1116 int p = a[0];
1117 a[0] = p+50;
1118 aM[j*(nRight+1)] = 1;
1119 for(i=1; i<=nRight; i++){
1120 int m = a[i-1]+50;
1121 int d = 3;
1122 if( m>a[i]+50 ){
1123 m = a[i]+50;
1124 d = 1;
1125 }
1126 if( m>p ){
1127 int score = match_dline(&aLeft[j-1], &aRight[i-1]);
1128 if( (score<66 || (i<j+1 && i>j-1)) && m>p+score ){
1129 m = p+score;
1130 d = 2;
1131 }
1132 }
1133 p = a[i];
1134 a[i] = m;
1135 aM[j*(nRight+1)+i] = d;
@@ -1138,28 +1228,50 @@
1138
1139 /* Compute the lowest-cost path back through the matrix */
1140 i = nRight;
1141 j = nLeft;
1142 k = (nRight+1)*(nLeft+1)-1;
 
1143 while( i+j>0 ){
1144 unsigned char c = aM[k--];
1145 if( c==2 ){
1146 assert( i>0 && j>0 );
1147 i--;
1148 j--;
1149 }else if( c==3 ){
 
 
 
1150 assert( i>0 );
1151 i--;
1152 }else{
1153 assert( j>0 );
1154 j--;
1155 }
 
1156 aM[k] = aM[j*(nRight+1)+i];
1157 }
1158 k++;
1159 i = (nRight+1)*(nLeft+1) - k;
1160 memmove(aM, &aM[k], i);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1161
1162 /* Return the result */
1163 fossil_free(pToFree);
1164 return aM;
1165 }
@@ -1179,13 +1291,12 @@
1179 ** in, compute a side-by-side diff into pOut.
1180 */
1181 static void sbsDiff(
1182 DContext *p, /* The computed diff */
1183 Blob *pOut, /* Write the results here */
1184 int nContext, /* Number of lines of context around each change */
1185 int width, /* Width of each column of output */
1186 int escHtml /* True to generate HTML output */
1187 ){
1188 DLine *A; /* Left side of the diff */
1189 DLine *B; /* Right side of the diff */
1190 int a = 0; /* Index of next line in A[] */
1191 int b = 0; /* Index of next line in B[] */
@@ -1197,16 +1308,20 @@
1197 int i, j; /* Loop counters */
1198 int m, ma, mb;/* Number of lines to output */
1199 int skip; /* Number of lines to skip */
1200 int nChunk = 0; /* Number of chunks of diff output seen so far */
1201 SbsLine s; /* Output line buffer */
 
 
1202
1203 memset(&s, 0, sizeof(s));
1204 s.zLine = fossil_malloc( 15*width + 200 );
 
1205 if( s.zLine==0 ) return;
1206 s.width = width;
1207 s.escHtml = escHtml;
 
1208 s.iStart = -1;
1209 s.iStart2 = 0;
1210 s.iEnd = -1;
1211 A = p->aFrom;
1212 B = p->aTo;
@@ -1215,10 +1330,35 @@
1215 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
1216 for(r=0; r<mxr; r += 3*nr){
1217 /* Figure out how many triples to show in a single block */
1218 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
1219 /* printf("r=%d nr=%d\n", r, nr); */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1220
1221 /* For the current block comprising nr triples, figure out
1222 ** how many lines of A and B are to be displayed
1223 */
1224 if( R[r]>nContext ){
@@ -1243,20 +1383,21 @@
1243 na += R[r+i*3];
1244 nb += R[r+i*3];
1245 }
1246
1247 /* Draw the separator between blocks */
1248 if( r>0 ){
1249 if( escHtml ){
1250 blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
1251 width*2+16, '.');
1252 }else{
1253 blob_appendf(pOut, "%.*c\n", width*2+16, '.');
1254 }
1255 }
 
1256 nChunk++;
1257 if( escHtml ){
1258 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
1259 }
1260
1261 /* Show the initial common area */
1262 a += skip;
@@ -1292,38 +1433,41 @@
1292 }
1293
1294 alignment = sbsAlignment(&A[a], ma, &B[b], mb);
1295 for(j=0; ma+mb>0; j++){
1296 if( alignment[j]==1 ){
 
1297 s.n = 0;
1298 sbsWriteLineno(&s, a);
1299 s.iStart = 0;
1300 s.zStart = "<span class=\"diffrm\">";
1301 s.iEnd = s.width;
1302 sbsWriteText(&s, &A[a], SBS_PAD);
1303 if( escHtml ){
1304 sbsWrite(&s, " &lt;\n", 6);
1305 }else{
1306 sbsWrite(&s, " <\n", 3);
1307 }
1308 blob_append(pOut, s.zLine, s.n);
1309 assert( ma>0 );
1310 ma--;
1311 a++;
1312 }else if( alignment[j]==2 ){
 
1313 s.n = 0;
1314 sbsWriteLineChange(&s, &A[a], a, &B[b], b);
1315 blob_append(pOut, s.zLine, s.n);
1316 assert( ma>0 && mb>0 );
1317 ma--;
1318 mb--;
1319 a++;
1320 b++;
1321 }else{
 
1322 s.n = 0;
1323 sbsWriteSpace(&s, width + 7);
1324 if( escHtml ){
1325 sbsWrite(&s, " &gt; ", 6);
1326 }else{
1327 sbsWrite(&s, " > ", 3);
1328 }
1329 sbsWriteLineno(&s, b);
@@ -1333,11 +1477,31 @@
1333 sbsWriteText(&s, &B[b], SBS_NEWLINE);
1334 blob_append(pOut, s.zLine, s.n);
1335 assert( mb>0 );
1336 mb--;
1337 b++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1338 }
 
1339 }
1340 fossil_free(alignment);
1341 if( i<nr-1 ){
1342 m = R[r+i*3+3];
1343 for(j=0; j<m; j++){
@@ -1761,11 +1925,11 @@
1761 ** Extract the number of lines of context from diffFlags. Supply an
1762 ** appropriate default if no context width is specified.
1763 */
1764 int diff_context_lines(u64 diffFlags){
1765 int n = diffFlags & DIFF_CONTEXT_MASK;
1766 if( n==0 ) n = 5;
1767 return n;
1768 }
1769
1770 /*
1771 ** Extract the width of columns for side-by-side diff. Supply an
@@ -1793,22 +1957,21 @@
1793 */
1794 int *text_diff(
1795 Blob *pA_Blob, /* FROM file */
1796 Blob *pB_Blob, /* TO file */
1797 Blob *pOut, /* Write diff here if not NULL */
 
1798 u64 diffFlags /* DIFF_* flags defined above */
1799 ){
1800 int ignoreEolWs; /* Ignore whitespace at the end of lines */
1801 int nContext; /* Amount of context to display */
1802 DContext c;
1803
1804 if( diffFlags & DIFF_INVERT ){
1805 Blob *pTemp = pA_Blob;
1806 pA_Blob = pB_Blob;
1807 pB_Blob = pTemp;
1808 }
1809 nContext = diff_context_lines(diffFlags);
1810 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1811
1812 /* Prepare the input files */
1813 memset(&c, 0, sizeof(c));
1814 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
@@ -1828,17 +1991,14 @@
1828 diff_all(&c);
1829 if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
1830
1831 if( pOut ){
1832 /* Compute a context or side-by-side diff into pOut */
1833 int escHtml = (diffFlags & DIFF_HTML)!=0;
1834 if( diffFlags & DIFF_SIDEBYSIDE ){
1835 int width = diff_width(diffFlags);
1836 sbsDiff(&c, pOut, nContext, width, escHtml);
1837 }else{
1838 int showLn = (diffFlags & DIFF_LINENO)!=0;
1839 contextDiff(&c, pOut, nContext, showLn, escHtml);
1840 }
1841 fossil_free(c.aFrom);
1842 fossil_free(c.aTo);
1843 fossil_free(c.aEdit);
1844 return 0;
@@ -1864,19 +2024,19 @@
1864 ** --noopt Disable optimization DIFF_NOOPT
1865 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1866 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
1867 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1868 */
1869 int diff_options(void){
1870 u64 diffFlags = 0;
1871 const char *z;
1872 int f;
1873 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
1874 if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
1875 if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
1876 if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1877 diffFlags |= f;
1878 }
1879 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
1880 f *= DIFF_CONTEXT_MASK+1;
1881 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1882 diffFlags |= f;
@@ -1901,34 +2061,52 @@
1901 if( g.argc<4 ) usage("FILE1 FILE2 ...");
1902 blob_read_from_file(&a, g.argv[2]);
1903 for(i=3; i<g.argc; i++){
1904 if( i>3 ) fossil_print("-------------------------------\n");
1905 blob_read_from_file(&b, g.argv[i]);
1906 R = text_diff(&a, &b, 0, diffFlags);
1907 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
1908 fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
1909 }
1910 /* free(R); */
1911 blob_reset(&b);
1912 }
1913 }
1914
1915 /*
1916 ** COMMAND: test-udiff
 
 
1917 **
1918 ** Print the difference between two files. The usual diff options apply.
1919 */
1920 void test_udiff_cmd(void){
1921 Blob a, b, out;
1922 u64 diffFlag = diff_options();
 
 
1923
 
 
 
 
 
 
 
 
 
 
 
 
1924 if( g.argc!=4 ) usage("FILE1 FILE2");
 
1925 blob_read_from_file(&a, g.argv[2]);
1926 blob_read_from_file(&b, g.argv[3]);
1927 blob_zero(&out);
1928 text_diff(&a, &b, &out, diffFlag);
1929 blob_write_to_file(&out, "-");
 
1930 }
1931
1932 /**************************************************************************
1933 ** The basic difference engine is above. What follows is the annotation
1934 ** engine. Both are in the same file since they share many components.
1935
--- src/diff.c
+++ src/diff.c
@@ -23,11 +23,12 @@
23 #include <assert.h>
24
25
26 #if INTERFACE
27 /*
28 ** Flag parameters to the text_diff() routine used to control the formatting
29 ** of the diff output.
30 */
31 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
32 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33 #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
34 #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
@@ -37,10 +38,11 @@
38 #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
39 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
40 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
41 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
42 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43 #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
44
45 /*
46 ** These error messages are shared in multiple locations. They are defined
47 ** here for consistency.
48 */
@@ -52,11 +54,11 @@
54
55 #define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0)
56 #endif /* INTERFACE */
57
58 /*
59 ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
60 */
61 #define LENGTH_MASK_SZ 13
62 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
63
64 /*
@@ -116,10 +118,13 @@
118 ** more. If trailing whitespace is ignored, the "patch" command gets
119 ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
120 **
121 ** Return 0 if the file is binary or contains a line that is
122 ** too long.
123 **
124 ** Profiling show that in most cases this routine consumes the bulk of
125 ** the CPU time on a diff.
126 */
127 static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
128 int nLine, i, j, k, x;
129 unsigned int h, h2;
130 DLine *a;
@@ -445,34 +450,50 @@
450 ** Return true if two DLine elements are identical.
451 */
452 static int same_dline(DLine *pA, DLine *pB){
453 return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
454 }
455
456 /*
457 ** Return true if the regular expression *pRe matches any of the
458 ** N dlines
459 */
460 static int re_dline_match(
461 ReCompiled *pRe, /* The regular expression to be matched */
462 DLine *aDLine, /* First of N DLines to compare against */
463 int N /* Number of DLines to check */
464 ){
465 while( N-- ){
466 if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){
467 return 1;
468 }
469 aDLine++;
470 }
471 return 0;
472 }
473
474 /*
475 ** Append a single line of context-diff output to pOut.
476 */
477 static void appendDiffLine(
478 Blob *pOut, /* Where to write the line of output */
479 char cPrefix, /* One of " ", "+", or "-" */
480 DLine *pLine, /* The line to be output */
481 int html, /* True if generating HTML. False for plain text */
482 ReCompiled *pRe /* Colorize only if line matches this Regex */
483 ){
 
484 blob_append(pOut, &cPrefix, 1);
485 if( html ){
486 char *zHtml;
487 if( pRe && re_dline_match(pRe, pLine, 1)==0 ){
488 cPrefix = ' ';
489 }else if( cPrefix=='+' ){
490 blob_append(pOut, "<span class=\"diffadd\">", -1);
491 }else if( cPrefix=='-' ){
492 blob_append(pOut, "<span class=\"diffrm\">", -1);
493 }
494 zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
 
 
 
 
495 blob_append(pOut, zHtml, -1);
496 fossil_free(zHtml);
497 if( cPrefix!=' ' ){
498 blob_append(pOut, "</span>", -1);
499 }
@@ -482,11 +503,11 @@
503 blob_append(pOut, "\n", 1);
504 }
505
506 /*
507 ** Add two line numbers to the beginning of an output line for a context
508 ** diff. One or the other of the two numbers might be zero, which means
509 ** to leave that number field blank. The "html" parameter means to format
510 ** the output for HTML.
511 */
512 static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
513 if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
@@ -501,21 +522,19 @@
522 blob_append(pOut, " ", 8);
523 }
524 if( html ) blob_append(pOut, "</span>", -1);
525 }
526
 
527 /*
528 ** Given a raw diff p[] in which the p->aEdit[] array has been filled
529 ** in, compute a context diff into pOut.
530 */
531 static void contextDiff(
532 DContext *p, /* The difference */
533 Blob *pOut, /* Output a context diff to here */
534 ReCompiled *pRe, /* Only show changes that match this regex */
535 u64 diffFlags /* Flags controlling the diff format */
 
536 ){
537 DLine *A; /* Left side of the diff */
538 DLine *B; /* Right side of the diff */
539 int a = 0; /* Index of next line in A[] */
540 int b = 0; /* Index of next line in B[] */
@@ -526,11 +545,18 @@
545 int na, nb; /* Number of lines shown from A and B */
546 int i, j; /* Loop counters */
547 int m; /* Number of lines to output */
548 int skip; /* Number of lines to skip */
549 int nChunk = 0; /* Number of diff chunks seen so far */
550 int nContext; /* Number of lines of context */
551 int showLn; /* Show line numbers */
552 int html; /* Render as HTML */
553 int showDivider = 0; /* True to show the divider between diff blocks */
554
555 nContext = diff_context_lines(diffFlags);
556 showLn = (diffFlags & DIFF_LINENO)!=0;
557 html = (diffFlags & DIFF_HTML)!=0;
558 A = p->aFrom;
559 B = p->aTo;
560 R = p->aEdit;
561 mxr = p->nEdit;
562 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -537,10 +563,35 @@
563 for(r=0; r<mxr; r += 3*nr){
564 /* Figure out how many triples to show in a single block */
565 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
566 /* printf("r=%d nr=%d\n", r, nr); */
567
568 /* If there is a regex, skip this block (generate no diff output)
569 ** if the regex matches or does not match both insert and delete.
570 ** Only display the block if one side matches but the other side does
571 ** not.
572 */
573 if( pRe ){
574 int hideBlock = 1;
575 int xa = a, xb = b;
576 for(i=0; hideBlock && i<nr; i++){
577 int c1, c2;
578 xa += R[r+i*3];
579 xb += R[r+i*3];
580 c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
581 c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
582 hideBlock = c1==c2;
583 xa += R[r+i*3+1];
584 xb += R[r+i*3+2];
585 }
586 if( hideBlock ){
587 a = xa;
588 b = xb;
589 continue;
590 }
591 }
592
593 /* For the current block comprising nr triples, figure out
594 ** how many lines of A and B are to be displayed
595 */
596 if( R[r]>nContext ){
597 na = nb = nContext;
@@ -564,17 +615,18 @@
615 na += R[r+i*3];
616 nb += R[r+i*3];
617 }
618
619 /* Show the header for this block, or if we are doing a modified
620 ** context diff that contains line numbers, show the separator from
621 ** the previous block.
622 */
623 nChunk++;
624 if( showLn ){
625 if( !showDivider ){
626 /* Do not show a top divider */
627 showDivider = 1;
628 }else if( html ){
629 blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
630 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
631 }else{
632 blob_appendf(pOut, "%.80c\n", '.');
@@ -597,34 +649,34 @@
649 a += skip;
650 b += skip;
651 m = R[r] - skip;
652 for(j=0; j<m; j++){
653 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
654 appendDiffLine(pOut, ' ', &A[a+j], html, 0);
655 }
656 a += m;
657 b += m;
658
659 /* Show the differences */
660 for(i=0; i<nr; i++){
661 m = R[r+i*3+1];
662 for(j=0; j<m; j++){
663 if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
664 appendDiffLine(pOut, '-', &A[a+j], html, pRe);
665 }
666 a += m;
667 m = R[r+i*3+2];
668 for(j=0; j<m; j++){
669 if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
670 appendDiffLine(pOut, '+', &B[b+j], html, pRe);
671 }
672 b += m;
673 if( i<nr-1 ){
674 m = R[r+i*3+3];
675 for(j=0; j<m; j++){
676 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
677 appendDiffLine(pOut, ' ', &B[b+j], html, 0);
678 }
679 b += m;
680 a += m;
681 }
682 }
@@ -633,11 +685,11 @@
685 assert( nr==i );
686 m = R[r+nr*3];
687 if( m>nContext ) m = nContext;
688 for(j=0; j<m; j++){
689 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
690 appendDiffLine(pOut, ' ', &B[b+j], html, 0);
691 }
692 }
693 }
694
695 /*
@@ -653,10 +705,11 @@
705 const char *zStart; /* A <span> tag */
706 int iEnd; /* Write </span> prior to character iEnd */
707 int iStart2; /* Write zStart2 prior to character iStart2 */
708 const char *zStart2; /* A <span> tag */
709 int iEnd2; /* Write </span> prior to character iEnd2 */
710 ReCompiled *pRe; /* Only colorize matching lines, if not NULL */
711 };
712
713 /*
714 ** Flags for sbsWriteText()
715 */
@@ -678,13 +731,17 @@
731 int k; /* Cursor position */
732 int needEndSpan = 0;
733 const char *zIn = pLine->z;
734 char *z = &p->zLine[p->n];
735 int w = p->width;
736 int colorize = p->escHtml;
737 if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
738 colorize = 0;
739 }
740 for(i=j=k=0; k<w && i<n; i++, k++){
741 char c = zIn[i];
742 if( colorize ){
743 if( i==p->iStart ){
744 int x = strlen(p->zStart);
745 memcpy(z+j, p->zStart, x);
746 j += x;
747 needEndSpan = 1;
@@ -835,10 +892,41 @@
892 }
893 }
894 }
895 return rc;
896 }
897
898 /*
899 ** Try to shift iStart as far as possible to the left.
900 */
901 static void sbsShiftLeft(SbsLine *p, const char *z){
902 int i, j;
903 while( (i=p->iStart)>0 && z[i-1]==z[i] ){
904 for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){}
905 if( j<p->iEnd ) break;
906 p->iStart--;
907 p->iEnd--;
908 }
909 }
910
911 /*
912 ** Simplify iStart and iStart2:
913 **
914 ** * If iStart is a null-change then move iStart2 into iStart
915 ** * Make sure any null-changes are in canonoical form.
916 */
917 static void sbsSimplifyLine(SbsLine *p){
918 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
919 if( p->iStart==p->iEnd ){
920 p->iStart = p->iStart2;
921 p->iEnd = p->iEnd2;
922 p->zStart = p->zStart2;
923 p->iStart2 = 0;
924 p->iEnd2 = 0;
925 }
926 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
927 }
928
929 /*
930 ** Write out lines that have been edited. Adjust the highlight to cover
931 ** only those parts of the line that actually changed.
932 */
@@ -929,41 +1017,35 @@
1017 && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
1018 ){
1019 sbsWriteLineno(p, lnLeft);
1020 p->iStart = nPrefix;
1021 p->iEnd = nPrefix + aLCS[0];
1022 if( aLCS[2]==0 ){
1023 sbsShiftLeft(p, pLeft->z);
1024 p->zStart = zClassRm;
1025 }else{
1026 p->zStart = zClassChng;
1027 }
1028 p->iStart2 = nPrefix + aLCS[1];
1029 p->iEnd2 = nLeft - nSuffix;
1030 p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
1031 sbsSimplifyLine(p);
 
 
 
 
 
 
 
 
1032 sbsWriteText(p, pLeft, SBS_PAD);
1033 sbsWrite(p, " | ", 3);
1034 sbsWriteLineno(p, lnRight);
1035 p->iStart = nPrefix;
1036 p->iEnd = nPrefix + aLCS[2];
1037 if( aLCS[0]==0 ){
1038 sbsShiftLeft(p, pRight->z);
1039 p->zStart = zClassAdd;
1040 }else{
1041 p->zStart = zClassChng;
1042 }
1043 p->iStart2 = nPrefix + aLCS[3];
1044 p->iEnd2 = nRight - nSuffix;
1045 p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
1046 sbsSimplifyLine(p);
 
 
 
 
 
 
 
 
1047 sbsWriteText(p, pRight, SBS_NEWLINE);
1048 return;
1049 }
1050
1051 /* If all else fails, show a single big change between left and right */
@@ -1056,13 +1138,16 @@
1138 **
1139 ** The return value is a buffer of unsigned characters, obtained from
1140 ** fossil_malloc(). (The caller needs to free the return value using
1141 ** fossil_free().) Entries in the returned array have values as follows:
1142 **
1143 ** 1. Delete the next line of pLeft.
1144 ** 2. Insert the next line of pRight.
1145 ** 3. The next line of pLeft changes into the next line of pRight.
1146 ** 4. Delete one line from pLeft and add one line to pRight.
1147 **
1148 ** Values larger than three indicate better matches.
1149 **
1150 ** The length of the returned array will be just large enough to cause
1151 ** all elements of pLeft and pRight to be consumed.
1152 **
1153 ** Algorithm: Wagner's minimum edit-distance algorithm, modified by
@@ -1077,27 +1162,32 @@
1162 ){
1163 int i, j, k; /* Loop counters */
1164 int *a; /* One row of the Wagner matrix */
1165 int *pToFree; /* Space that needs to be freed */
1166 unsigned char *aM; /* Wagner result matrix */
1167 int nMatch, iMatch; /* Number of matching lines and match score */
1168 int mnLen; /* MIN(nLeft, nRight) */
1169 int mxLen; /* MAX(nLeft, nRight) */
1170 int aBuf[100]; /* Stack space for a[] if nRight not to big */
1171
1172 aM = fossil_malloc( (nLeft+1)*(nRight+1) );
1173 if( nLeft==0 ){
1174 memset(aM, 2, nRight);
1175 return aM;
1176 }
1177 if( nRight==0 ){
1178 memset(aM, 1, nLeft);
1179 return aM;
1180 }
1181
1182 /* This algorithm is O(N**2). So if N is too big, bail out with a
1183 ** simple (but stupid and ugly) result that doesn't take too long. */
1184 mnLen = nLeft<nRight ? nLeft : nRight;
1185 if( nLeft*nRight>100000 ){
1186 memset(aM, 4, mnLen);
1187 if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1188 if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
1189 return aM;
1190 }
1191
1192 if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){
1193 pToFree = 0;
@@ -1106,30 +1196,30 @@
1196 a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
1197 }
1198
1199 /* Compute the best alignment */
1200 for(i=0; i<=nRight; i++){
1201 aM[i] = 2;
1202 a[i] = i*50;
1203 }
1204 aM[0] = 0;
1205 for(j=1; j<=nLeft; j++){
1206 int p = a[0];
1207 a[0] = p+50;
1208 aM[j*(nRight+1)] = 1;
1209 for(i=1; i<=nRight; i++){
1210 int m = a[i-1]+50;
1211 int d = 2;
1212 if( m>a[i]+50 ){
1213 m = a[i]+50;
1214 d = 1;
1215 }
1216 if( m>p ){
1217 int score = match_dline(&aLeft[j-1], &aRight[i-1]);
1218 if( (score<=63 || (i<j+1 && i>j-1)) && m>p+score ){
1219 m = p+score;
1220 d = 3 | score*4;
1221 }
1222 }
1223 p = a[i];
1224 a[i] = m;
1225 aM[j*(nRight+1)+i] = d;
@@ -1138,28 +1228,50 @@
1228
1229 /* Compute the lowest-cost path back through the matrix */
1230 i = nRight;
1231 j = nLeft;
1232 k = (nRight+1)*(nLeft+1)-1;
1233 nMatch = iMatch = 0;
1234 while( i+j>0 ){
1235 unsigned char c = aM[k];
1236 if( c>=3 ){
1237 assert( i>0 && j>0 );
1238 i--;
1239 j--;
1240 nMatch++;
1241 iMatch += (c>>2);
1242 aM[k] = 3;
1243 }else if( c==2 ){
1244 assert( i>0 );
1245 i--;
1246 }else{
1247 assert( j>0 );
1248 j--;
1249 }
1250 k--;
1251 aM[k] = aM[j*(nRight+1)+i];
1252 }
1253 k++;
1254 i = (nRight+1)*(nLeft+1) - k;
1255 memmove(aM, &aM[k], i);
1256
1257 /* If:
1258 ** (1) the alignment is more than 25% longer than the longest side, and
1259 ** (2) the average match cost exceeds 15
1260 ** Then this is probably an alignment that will be difficult for humans
1261 ** to read. So instead, just show all of the right side inserted followed
1262 ** by all of the left side deleted.
1263 **
1264 ** The coefficients for conditions (1) and (2) above are determined by
1265 ** experimentation.
1266 */
1267 mxLen = nLeft>nRight ? nLeft : nRight;
1268 if( i*4>mxLen*5 && (nMatch==0 || iMatch/nMatch>15) ){
1269 memset(aM, 4, mnLen);
1270 if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1271 if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
1272 }
1273
1274 /* Return the result */
1275 fossil_free(pToFree);
1276 return aM;
1277 }
@@ -1179,13 +1291,12 @@
1291 ** in, compute a side-by-side diff into pOut.
1292 */
1293 static void sbsDiff(
1294 DContext *p, /* The computed diff */
1295 Blob *pOut, /* Write the results here */
1296 ReCompiled *pRe, /* Only show changes that match this regex */
1297 u64 diffFlags /* Flags controlling the diff */
 
1298 ){
1299 DLine *A; /* Left side of the diff */
1300 DLine *B; /* Right side of the diff */
1301 int a = 0; /* Index of next line in A[] */
1302 int b = 0; /* Index of next line in B[] */
@@ -1197,16 +1308,20 @@
1308 int i, j; /* Loop counters */
1309 int m, ma, mb;/* Number of lines to output */
1310 int skip; /* Number of lines to skip */
1311 int nChunk = 0; /* Number of chunks of diff output seen so far */
1312 SbsLine s; /* Output line buffer */
1313 int nContext; /* Lines of context above and below each change */
1314 int showDivider = 0; /* True to show the divider */
1315
1316 memset(&s, 0, sizeof(s));
1317 s.width = diff_width(diffFlags);
1318 s.zLine = fossil_malloc( 15*s.width + 200 );
1319 if( s.zLine==0 ) return;
1320 nContext = diff_context_lines(diffFlags);
1321 s.escHtml = (diffFlags & DIFF_HTML)!=0;
1322 s.pRe = pRe;
1323 s.iStart = -1;
1324 s.iStart2 = 0;
1325 s.iEnd = -1;
1326 A = p->aFrom;
1327 B = p->aTo;
@@ -1215,10 +1330,35 @@
1330 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
1331 for(r=0; r<mxr; r += 3*nr){
1332 /* Figure out how many triples to show in a single block */
1333 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
1334 /* printf("r=%d nr=%d\n", r, nr); */
1335
1336 /* If there is a regex, skip this block (generate no diff output)
1337 ** if the regex matches or does not match both insert and delete.
1338 ** Only display the block if one side matches but the other side does
1339 ** not.
1340 */
1341 if( pRe ){
1342 int hideBlock = 1;
1343 int xa = a, xb = b;
1344 for(i=0; hideBlock && i<nr; i++){
1345 int c1, c2;
1346 xa += R[r+i*3];
1347 xb += R[r+i*3];
1348 c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
1349 c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
1350 hideBlock = c1==c2;
1351 xa += R[r+i*3+1];
1352 xb += R[r+i*3+2];
1353 }
1354 if( hideBlock ){
1355 a = xa;
1356 b = xb;
1357 continue;
1358 }
1359 }
1360
1361 /* For the current block comprising nr triples, figure out
1362 ** how many lines of A and B are to be displayed
1363 */
1364 if( R[r]>nContext ){
@@ -1243,20 +1383,21 @@
1383 na += R[r+i*3];
1384 nb += R[r+i*3];
1385 }
1386
1387 /* Draw the separator between blocks */
1388 if( showDivider ){
1389 if( s.escHtml ){
1390 blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
1391 s.width*2+16, '.');
1392 }else{
1393 blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
1394 }
1395 }
1396 showDivider = 1;
1397 nChunk++;
1398 if( s.escHtml ){
1399 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
1400 }
1401
1402 /* Show the initial common area */
1403 a += skip;
@@ -1292,38 +1433,41 @@
1433 }
1434
1435 alignment = sbsAlignment(&A[a], ma, &B[b], mb);
1436 for(j=0; ma+mb>0; j++){
1437 if( alignment[j]==1 ){
1438 /* Delete one line from the left */
1439 s.n = 0;
1440 sbsWriteLineno(&s, a);
1441 s.iStart = 0;
1442 s.zStart = "<span class=\"diffrm\">";
1443 s.iEnd = s.width;
1444 sbsWriteText(&s, &A[a], SBS_PAD);
1445 if( s.escHtml ){
1446 sbsWrite(&s, " &lt;\n", 6);
1447 }else{
1448 sbsWrite(&s, " <\n", 3);
1449 }
1450 blob_append(pOut, s.zLine, s.n);
1451 assert( ma>0 );
1452 ma--;
1453 a++;
1454 }else if( alignment[j]==3 ){
1455 /* The left line is changed into the right line */
1456 s.n = 0;
1457 sbsWriteLineChange(&s, &A[a], a, &B[b], b);
1458 blob_append(pOut, s.zLine, s.n);
1459 assert( ma>0 && mb>0 );
1460 ma--;
1461 mb--;
1462 a++;
1463 b++;
1464 }else if( alignment[j]==2 ){
1465 /* Insert one line on the right */
1466 s.n = 0;
1467 sbsWriteSpace(&s, s.width + 7);
1468 if( s.escHtml ){
1469 sbsWrite(&s, " &gt; ", 6);
1470 }else{
1471 sbsWrite(&s, " > ", 3);
1472 }
1473 sbsWriteLineno(&s, b);
@@ -1333,11 +1477,31 @@
1477 sbsWriteText(&s, &B[b], SBS_NEWLINE);
1478 blob_append(pOut, s.zLine, s.n);
1479 assert( mb>0 );
1480 mb--;
1481 b++;
1482 }else{
1483 /* Delete from the left and insert on the right */
1484 s.n = 0;
1485 sbsWriteLineno(&s, a);
1486 s.iStart = 0;
1487 s.zStart = "<span class=\"diffrm\">";
1488 s.iEnd = s.width;
1489 sbsWriteText(&s, &A[a], SBS_PAD);
1490 sbsWrite(&s, " | ", 3);
1491 sbsWriteLineno(&s, b);
1492 s.iStart = 0;
1493 s.zStart = "<span class=\"diffadd\">";
1494 s.iEnd = s.width;
1495 sbsWriteText(&s, &B[b], SBS_NEWLINE);
1496 blob_append(pOut, s.zLine, s.n);
1497 ma--;
1498 mb--;
1499 a++;
1500 b++;
1501 }
1502
1503 }
1504 fossil_free(alignment);
1505 if( i<nr-1 ){
1506 m = R[r+i*3+3];
1507 for(j=0; j<m; j++){
@@ -1761,11 +1925,11 @@
1925 ** Extract the number of lines of context from diffFlags. Supply an
1926 ** appropriate default if no context width is specified.
1927 */
1928 int diff_context_lines(u64 diffFlags){
1929 int n = diffFlags & DIFF_CONTEXT_MASK;
1930 if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5;
1931 return n;
1932 }
1933
1934 /*
1935 ** Extract the width of columns for side-by-side diff. Supply an
@@ -1793,22 +1957,21 @@
1957 */
1958 int *text_diff(
1959 Blob *pA_Blob, /* FROM file */
1960 Blob *pB_Blob, /* TO file */
1961 Blob *pOut, /* Write diff here if not NULL */
1962 ReCompiled *pRe, /* Only output changes where this Regexp matches */
1963 u64 diffFlags /* DIFF_* flags defined above */
1964 ){
1965 int ignoreEolWs; /* Ignore whitespace at the end of lines */
 
1966 DContext c;
1967
1968 if( diffFlags & DIFF_INVERT ){
1969 Blob *pTemp = pA_Blob;
1970 pA_Blob = pB_Blob;
1971 pB_Blob = pTemp;
1972 }
 
1973 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1974
1975 /* Prepare the input files */
1976 memset(&c, 0, sizeof(c));
1977 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
@@ -1828,17 +1991,14 @@
1991 diff_all(&c);
1992 if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
1993
1994 if( pOut ){
1995 /* Compute a context or side-by-side diff into pOut */
 
1996 if( diffFlags & DIFF_SIDEBYSIDE ){
1997 sbsDiff(&c, pOut, pRe, diffFlags);
 
1998 }else{
1999 contextDiff(&c, pOut, pRe, diffFlags);
 
2000 }
2001 fossil_free(c.aFrom);
2002 fossil_free(c.aTo);
2003 fossil_free(c.aEdit);
2004 return 0;
@@ -1864,19 +2024,19 @@
2024 ** --noopt Disable optimization DIFF_NOOPT
2025 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
2026 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
2027 ** --width|-W N N character lines. DIFF_WIDTH_MASK
2028 */
2029 u64 diff_options(void){
2030 u64 diffFlags = 0;
2031 const char *z;
2032 int f;
2033 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
2034 if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
2035 if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
2036 if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
2037 diffFlags |= f + DIFF_CONTEXT_EX;
2038 }
2039 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
2040 f *= DIFF_CONTEXT_MASK+1;
2041 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
2042 diffFlags |= f;
@@ -1901,34 +2061,52 @@
2061 if( g.argc<4 ) usage("FILE1 FILE2 ...");
2062 blob_read_from_file(&a, g.argv[2]);
2063 for(i=3; i<g.argc; i++){
2064 if( i>3 ) fossil_print("-------------------------------\n");
2065 blob_read_from_file(&b, g.argv[i]);
2066 R = text_diff(&a, &b, 0, 0, diffFlags);
2067 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
2068 fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
2069 }
2070 /* free(R); */
2071 blob_reset(&b);
2072 }
2073 }
2074
2075 /*
2076 ** COMMAND: test-diff
2077 **
2078 ** Usage: %fossil [options] FILE1 FILE2
2079 **
2080 ** Print the difference between two files. The usual diff options apply.
2081 */
2082 void test_diff_cmd(void){
2083 Blob a, b, out;
2084 u64 diffFlag;
2085 const char *zRe; /* Regex filter for diff output */
2086 ReCompiled *pRe = 0; /* Regex filter for diff output */
2087
2088 if( find_option("tk",0,0)!=0 ){
2089 diff_tk("test-diff", 2);
2090 return;
2091 }
2092 find_option("i",0,0);
2093 zRe = find_option("regexp","e",1);
2094 if( zRe ){
2095 const char *zErr = re_compile(&pRe, zRe, 0);
2096 if( zErr ) fossil_fatal("regex error: %s", zErr);
2097 }
2098 diffFlag = diff_options();
2099 verify_all_options();
2100 if( g.argc!=4 ) usage("FILE1 FILE2");
2101 diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
2102 blob_read_from_file(&a, g.argv[2]);
2103 blob_read_from_file(&b, g.argv[3]);
2104 blob_zero(&out);
2105 text_diff(&a, &b, &out, pRe, diffFlag);
2106 blob_write_to_file(&out, "-");
2107 re_free(pRe);
2108 }
2109
2110 /**************************************************************************
2111 ** The basic difference engine is above. What follows is the annotation
2112 ** engine. Both are in the same file since they share many components.
2113
+265 -87
--- src/diff.c
+++ src/diff.c
@@ -23,11 +23,12 @@
2323
#include <assert.h>
2424
2525
2626
#if INTERFACE
2727
/*
28
-** Allowed flag parameters to the text_diff() and html_sbsdiff() functions:
28
+** Flag parameters to the text_diff() routine used to control the formatting
29
+** of the diff output.
2930
*/
3031
#define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
3132
#define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
3233
#define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
3334
#define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
@@ -37,10 +38,11 @@
3738
#define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
3839
#define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
3940
#define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
4041
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
4142
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43
+#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
4244
4345
/*
4446
** These error messages are shared in multiple locations. They are defined
4547
** here for consistency.
4648
*/
@@ -52,11 +54,11 @@
5254
5355
#define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0)
5456
#endif /* INTERFACE */
5557
5658
/*
57
-** Maximum length of a line in a text file, in bytes. (8192)
59
+** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
5860
*/
5961
#define LENGTH_MASK_SZ 13
6062
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
6163
6264
/*
@@ -116,10 +118,13 @@
116118
** more. If trailing whitespace is ignored, the "patch" command gets
117119
** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
118120
**
119121
** Return 0 if the file is binary or contains a line that is
120122
** too long.
123
+**
124
+** Profiling show that in most cases this routine consumes the bulk of
125
+** the CPU time on a diff.
121126
*/
122127
static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
123128
int nLine, i, j, k, x;
124129
unsigned int h, h2;
125130
DLine *a;
@@ -445,34 +450,50 @@
445450
** Return true if two DLine elements are identical.
446451
*/
447452
static int same_dline(DLine *pA, DLine *pB){
448453
return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
449454
}
455
+
456
+/*
457
+** Return true if the regular expression *pRe matches any of the
458
+** N dlines
459
+*/
460
+static int re_dline_match(
461
+ ReCompiled *pRe, /* The regular expression to be matched */
462
+ DLine *aDLine, /* First of N DLines to compare against */
463
+ int N /* Number of DLines to check */
464
+){
465
+ while( N-- ){
466
+ if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){
467
+ return 1;
468
+ }
469
+ aDLine++;
470
+ }
471
+ return 0;
472
+}
450473
451474
/*
452475
** Append a single line of context-diff output to pOut.
453476
*/
454477
static void appendDiffLine(
455478
Blob *pOut, /* Where to write the line of output */
456479
char cPrefix, /* One of " ", "+", or "-" */
457480
DLine *pLine, /* The line to be output */
458
- int html /* True if generating HTML. False for plain text */
481
+ int html, /* True if generating HTML. False for plain text */
482
+ ReCompiled *pRe /* Colorize only if line matches this Regex */
459483
){
460
- int i;
461484
blob_append(pOut, &cPrefix, 1);
462485
if( html ){
463486
char *zHtml;
464
- if( cPrefix=='+' ){
487
+ if( pRe && re_dline_match(pRe, pLine, 1)==0 ){
488
+ cPrefix = ' ';
489
+ }else if( cPrefix=='+' ){
465490
blob_append(pOut, "<span class=\"diffadd\">", -1);
466491
}else if( cPrefix=='-' ){
467492
blob_append(pOut, "<span class=\"diffrm\">", -1);
468493
}
469494
zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
470
- for(i=0; i<strlen(zHtml); i++){
471
- char c = zHtml[i];
472
- if( c=='\t' || c=='\r' || c=='\f' ) zHtml[i] = ' ';
473
- }
474495
blob_append(pOut, zHtml, -1);
475496
fossil_free(zHtml);
476497
if( cPrefix!=' ' ){
477498
blob_append(pOut, "</span>", -1);
478499
}
@@ -482,11 +503,11 @@
482503
blob_append(pOut, "\n", 1);
483504
}
484505
485506
/*
486507
** Add two line numbers to the beginning of an output line for a context
487
-** diff. One or of the other of the two numbers might be zero, which means
508
+** diff. One or the other of the two numbers might be zero, which means
488509
** to leave that number field blank. The "html" parameter means to format
489510
** the output for HTML.
490511
*/
491512
static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
492513
if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
@@ -501,21 +522,19 @@
501522
blob_append(pOut, " ", 8);
502523
}
503524
if( html ) blob_append(pOut, "</span>", -1);
504525
}
505526
506
-
507527
/*
508528
** Given a raw diff p[] in which the p->aEdit[] array has been filled
509529
** in, compute a context diff into pOut.
510530
*/
511531
static void contextDiff(
512532
DContext *p, /* The difference */
513533
Blob *pOut, /* Output a context diff to here */
514
- int nContext, /* Number of lines of context */
515
- int showLn, /* Show line numbers */
516
- int html /* Render as HTML */
534
+ ReCompiled *pRe, /* Only show changes that match this regex */
535
+ u64 diffFlags /* Flags controlling the diff format */
517536
){
518537
DLine *A; /* Left side of the diff */
519538
DLine *B; /* Right side of the diff */
520539
int a = 0; /* Index of next line in A[] */
521540
int b = 0; /* Index of next line in B[] */
@@ -526,11 +545,18 @@
526545
int na, nb; /* Number of lines shown from A and B */
527546
int i, j; /* Loop counters */
528547
int m; /* Number of lines to output */
529548
int skip; /* Number of lines to skip */
530549
int nChunk = 0; /* Number of diff chunks seen so far */
550
+ int nContext; /* Number of lines of context */
551
+ int showLn; /* Show line numbers */
552
+ int html; /* Render as HTML */
553
+ int showDivider = 0; /* True to show the divider between diff blocks */
531554
555
+ nContext = diff_context_lines(diffFlags);
556
+ showLn = (diffFlags & DIFF_LINENO)!=0;
557
+ html = (diffFlags & DIFF_HTML)!=0;
532558
A = p->aFrom;
533559
B = p->aTo;
534560
R = p->aEdit;
535561
mxr = p->nEdit;
536562
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -537,10 +563,35 @@
537563
for(r=0; r<mxr; r += 3*nr){
538564
/* Figure out how many triples to show in a single block */
539565
for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
540566
/* printf("r=%d nr=%d\n", r, nr); */
541567
568
+ /* If there is a regex, skip this block (generate no diff output)
569
+ ** if the regex matches or does not match both insert and delete.
570
+ ** Only display the block if one side matches but the other side does
571
+ ** not.
572
+ */
573
+ if( pRe ){
574
+ int hideBlock = 1;
575
+ int xa = a, xb = b;
576
+ for(i=0; hideBlock && i<nr; i++){
577
+ int c1, c2;
578
+ xa += R[r+i*3];
579
+ xb += R[r+i*3];
580
+ c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
581
+ c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
582
+ hideBlock = c1==c2;
583
+ xa += R[r+i*3+1];
584
+ xb += R[r+i*3+2];
585
+ }
586
+ if( hideBlock ){
587
+ a = xa;
588
+ b = xb;
589
+ continue;
590
+ }
591
+ }
592
+
542593
/* For the current block comprising nr triples, figure out
543594
** how many lines of A and B are to be displayed
544595
*/
545596
if( R[r]>nContext ){
546597
na = nb = nContext;
@@ -564,17 +615,18 @@
564615
na += R[r+i*3];
565616
nb += R[r+i*3];
566617
}
567618
568619
/* Show the header for this block, or if we are doing a modified
569
- ** context diff that contains line numbers, show the separate from
620
+ ** context diff that contains line numbers, show the separator from
570621
** the previous block.
571622
*/
572623
nChunk++;
573624
if( showLn ){
574
- if( r==0 ){
625
+ if( !showDivider ){
575626
/* Do not show a top divider */
627
+ showDivider = 1;
576628
}else if( html ){
577629
blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
578630
blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
579631
}else{
580632
blob_appendf(pOut, "%.80c\n", '.');
@@ -597,34 +649,34 @@
597649
a += skip;
598650
b += skip;
599651
m = R[r] - skip;
600652
for(j=0; j<m; j++){
601653
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
602
- appendDiffLine(pOut, ' ', &A[a+j], html);
654
+ appendDiffLine(pOut, ' ', &A[a+j], html, 0);
603655
}
604656
a += m;
605657
b += m;
606658
607659
/* Show the differences */
608660
for(i=0; i<nr; i++){
609661
m = R[r+i*3+1];
610662
for(j=0; j<m; j++){
611663
if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
612
- appendDiffLine(pOut, '-', &A[a+j], html);
664
+ appendDiffLine(pOut, '-', &A[a+j], html, pRe);
613665
}
614666
a += m;
615667
m = R[r+i*3+2];
616668
for(j=0; j<m; j++){
617669
if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
618
- appendDiffLine(pOut, '+', &B[b+j], html);
670
+ appendDiffLine(pOut, '+', &B[b+j], html, pRe);
619671
}
620672
b += m;
621673
if( i<nr-1 ){
622674
m = R[r+i*3+3];
623675
for(j=0; j<m; j++){
624676
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
625
- appendDiffLine(pOut, ' ', &B[b+j], html);
677
+ appendDiffLine(pOut, ' ', &B[b+j], html, 0);
626678
}
627679
b += m;
628680
a += m;
629681
}
630682
}
@@ -633,11 +685,11 @@
633685
assert( nr==i );
634686
m = R[r+nr*3];
635687
if( m>nContext ) m = nContext;
636688
for(j=0; j<m; j++){
637689
if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
638
- appendDiffLine(pOut, ' ', &B[b+j], html);
690
+ appendDiffLine(pOut, ' ', &B[b+j], html, 0);
639691
}
640692
}
641693
}
642694
643695
/*
@@ -653,10 +705,11 @@
653705
const char *zStart; /* A <span> tag */
654706
int iEnd; /* Write </span> prior to character iEnd */
655707
int iStart2; /* Write zStart2 prior to character iStart2 */
656708
const char *zStart2; /* A <span> tag */
657709
int iEnd2; /* Write </span> prior to character iEnd2 */
710
+ ReCompiled *pRe; /* Only colorize matching lines, if not NULL */
658711
};
659712
660713
/*
661714
** Flags for sbsWriteText()
662715
*/
@@ -678,13 +731,17 @@
678731
int k; /* Cursor position */
679732
int needEndSpan = 0;
680733
const char *zIn = pLine->z;
681734
char *z = &p->zLine[p->n];
682735
int w = p->width;
736
+ int colorize = p->escHtml;
737
+ if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
738
+ colorize = 0;
739
+ }
683740
for(i=j=k=0; k<w && i<n; i++, k++){
684741
char c = zIn[i];
685
- if( p->escHtml ){
742
+ if( colorize ){
686743
if( i==p->iStart ){
687744
int x = strlen(p->zStart);
688745
memcpy(z+j, p->zStart, x);
689746
j += x;
690747
needEndSpan = 1;
@@ -835,10 +892,41 @@
835892
}
836893
}
837894
}
838895
return rc;
839896
}
897
+
898
+/*
899
+** Try to shift iStart as far as possible to the left.
900
+*/
901
+static void sbsShiftLeft(SbsLine *p, const char *z){
902
+ int i, j;
903
+ while( (i=p->iStart)>0 && z[i-1]==z[i] ){
904
+ for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){}
905
+ if( j<p->iEnd ) break;
906
+ p->iStart--;
907
+ p->iEnd--;
908
+ }
909
+}
910
+
911
+/*
912
+** Simplify iStart and iStart2:
913
+**
914
+** * If iStart is a null-change then move iStart2 into iStart
915
+** * Make sure any null-changes are in canonoical form.
916
+*/
917
+static void sbsSimplifyLine(SbsLine *p){
918
+ if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
919
+ if( p->iStart==p->iEnd ){
920
+ p->iStart = p->iStart2;
921
+ p->iEnd = p->iEnd2;
922
+ p->zStart = p->zStart2;
923
+ p->iStart2 = 0;
924
+ p->iEnd2 = 0;
925
+ }
926
+ if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
927
+}
840928
841929
/*
842930
** Write out lines that have been edited. Adjust the highlight to cover
843931
** only those parts of the line that actually changed.
844932
*/
@@ -929,41 +1017,35 @@
9291017
&& textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
9301018
){
9311019
sbsWriteLineno(p, lnLeft);
9321020
p->iStart = nPrefix;
9331021
p->iEnd = nPrefix + aLCS[0];
934
- p->zStart = aLCS[2]==0 ? zClassRm : zClassChng;
1022
+ if( aLCS[2]==0 ){
1023
+ sbsShiftLeft(p, pLeft->z);
1024
+ p->zStart = zClassRm;
1025
+ }else{
1026
+ p->zStart = zClassChng;
1027
+ }
9351028
p->iStart2 = nPrefix + aLCS[1];
9361029
p->iEnd2 = nLeft - nSuffix;
9371030
p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
938
- if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
939
- if( p->iStart==p->iEnd ){
940
- p->iStart = p->iStart2;
941
- p->iEnd = p->iEnd2;
942
- p->zStart = p->zStart2;
943
- p->iStart2 = 0;
944
- p->iEnd2 = 0;
945
- }
946
- if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
1031
+ sbsSimplifyLine(p);
9471032
sbsWriteText(p, pLeft, SBS_PAD);
9481033
sbsWrite(p, " | ", 3);
9491034
sbsWriteLineno(p, lnRight);
9501035
p->iStart = nPrefix;
9511036
p->iEnd = nPrefix + aLCS[2];
952
- p->zStart = aLCS[0]==0 ? zClassAdd : zClassChng;
1037
+ if( aLCS[0]==0 ){
1038
+ sbsShiftLeft(p, pRight->z);
1039
+ p->zStart = zClassAdd;
1040
+ }else{
1041
+ p->zStart = zClassChng;
1042
+ }
9531043
p->iStart2 = nPrefix + aLCS[3];
9541044
p->iEnd2 = nRight - nSuffix;
9551045
p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
956
- if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
957
- if( p->iStart==p->iEnd ){
958
- p->iStart = p->iStart2;
959
- p->iEnd = p->iEnd2;
960
- p->zStart = p->zStart2;
961
- p->iStart2 = 0;
962
- p->iEnd2 = 0;
963
- }
964
- if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
1046
+ sbsSimplifyLine(p);
9651047
sbsWriteText(p, pRight, SBS_NEWLINE);
9661048
return;
9671049
}
9681050
9691051
/* If all else fails, show a single big change between left and right */
@@ -1056,13 +1138,16 @@
10561138
**
10571139
** The return value is a buffer of unsigned characters, obtained from
10581140
** fossil_malloc(). (The caller needs to free the return value using
10591141
** fossil_free().) Entries in the returned array have values as follows:
10601142
**
1061
-** 1. Delete the next line of pLeft.
1062
-** 2. The next line of pLeft changes into the next line of pRight.
1063
-** 3. Insert the next line of pRight.
1143
+** 1. Delete the next line of pLeft.
1144
+** 2. Insert the next line of pRight.
1145
+** 3. The next line of pLeft changes into the next line of pRight.
1146
+** 4. Delete one line from pLeft and add one line to pRight.
1147
+**
1148
+** Values larger than three indicate better matches.
10641149
**
10651150
** The length of the returned array will be just large enough to cause
10661151
** all elements of pLeft and pRight to be consumed.
10671152
**
10681153
** Algorithm: Wagner's minimum edit-distance algorithm, modified by
@@ -1077,27 +1162,32 @@
10771162
){
10781163
int i, j, k; /* Loop counters */
10791164
int *a; /* One row of the Wagner matrix */
10801165
int *pToFree; /* Space that needs to be freed */
10811166
unsigned char *aM; /* Wagner result matrix */
1167
+ int nMatch, iMatch; /* Number of matching lines and match score */
1168
+ int mnLen; /* MIN(nLeft, nRight) */
1169
+ int mxLen; /* MAX(nLeft, nRight) */
10821170
int aBuf[100]; /* Stack space for a[] if nRight not to big */
10831171
10841172
aM = fossil_malloc( (nLeft+1)*(nRight+1) );
10851173
if( nLeft==0 ){
1086
- memset(aM, 3, nRight);
1174
+ memset(aM, 2, nRight);
10871175
return aM;
10881176
}
10891177
if( nRight==0 ){
10901178
memset(aM, 1, nLeft);
10911179
return aM;
10921180
}
10931181
10941182
/* This algorithm is O(N**2). So if N is too big, bail out with a
10951183
** simple (but stupid and ugly) result that doesn't take too long. */
1184
+ mnLen = nLeft<nRight ? nLeft : nRight;
10961185
if( nLeft*nRight>100000 ){
1097
- memset(aM, 3, nRight);
1098
- memset(aM+nRight, 1, nLeft);
1186
+ memset(aM, 4, mnLen);
1187
+ if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1188
+ if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
10991189
return aM;
11001190
}
11011191
11021192
if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){
11031193
pToFree = 0;
@@ -1106,30 +1196,30 @@
11061196
a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
11071197
}
11081198
11091199
/* Compute the best alignment */
11101200
for(i=0; i<=nRight; i++){
1111
- aM[i] = 3;
1201
+ aM[i] = 2;
11121202
a[i] = i*50;
11131203
}
11141204
aM[0] = 0;
11151205
for(j=1; j<=nLeft; j++){
11161206
int p = a[0];
11171207
a[0] = p+50;
11181208
aM[j*(nRight+1)] = 1;
11191209
for(i=1; i<=nRight; i++){
11201210
int m = a[i-1]+50;
1121
- int d = 3;
1211
+ int d = 2;
11221212
if( m>a[i]+50 ){
11231213
m = a[i]+50;
11241214
d = 1;
11251215
}
11261216
if( m>p ){
11271217
int score = match_dline(&aLeft[j-1], &aRight[i-1]);
1128
- if( (score<66 || (i<j+1 && i>j-1)) && m>p+score ){
1218
+ if( (score<=63 || (i<j+1 && i>j-1)) && m>p+score ){
11291219
m = p+score;
1130
- d = 2;
1220
+ d = 3 | score*4;
11311221
}
11321222
}
11331223
p = a[i];
11341224
a[i] = m;
11351225
aM[j*(nRight+1)+i] = d;
@@ -1138,28 +1228,50 @@
11381228
11391229
/* Compute the lowest-cost path back through the matrix */
11401230
i = nRight;
11411231
j = nLeft;
11421232
k = (nRight+1)*(nLeft+1)-1;
1233
+ nMatch = iMatch = 0;
11431234
while( i+j>0 ){
1144
- unsigned char c = aM[k--];
1145
- if( c==2 ){
1235
+ unsigned char c = aM[k];
1236
+ if( c>=3 ){
11461237
assert( i>0 && j>0 );
11471238
i--;
11481239
j--;
1149
- }else if( c==3 ){
1240
+ nMatch++;
1241
+ iMatch += (c>>2);
1242
+ aM[k] = 3;
1243
+ }else if( c==2 ){
11501244
assert( i>0 );
11511245
i--;
11521246
}else{
11531247
assert( j>0 );
11541248
j--;
11551249
}
1250
+ k--;
11561251
aM[k] = aM[j*(nRight+1)+i];
11571252
}
11581253
k++;
11591254
i = (nRight+1)*(nLeft+1) - k;
11601255
memmove(aM, &aM[k], i);
1256
+
1257
+ /* If:
1258
+ ** (1) the alignment is more than 25% longer than the longest side, and
1259
+ ** (2) the average match cost exceeds 15
1260
+ ** Then this is probably an alignment that will be difficult for humans
1261
+ ** to read. So instead, just show all of the right side inserted followed
1262
+ ** by all of the left side deleted.
1263
+ **
1264
+ ** The coefficients for conditions (1) and (2) above are determined by
1265
+ ** experimentation.
1266
+ */
1267
+ mxLen = nLeft>nRight ? nLeft : nRight;
1268
+ if( i*4>mxLen*5 && (nMatch==0 || iMatch/nMatch>15) ){
1269
+ memset(aM, 4, mnLen);
1270
+ if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1271
+ if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
1272
+ }
11611273
11621274
/* Return the result */
11631275
fossil_free(pToFree);
11641276
return aM;
11651277
}
@@ -1179,13 +1291,12 @@
11791291
** in, compute a side-by-side diff into pOut.
11801292
*/
11811293
static void sbsDiff(
11821294
DContext *p, /* The computed diff */
11831295
Blob *pOut, /* Write the results here */
1184
- int nContext, /* Number of lines of context around each change */
1185
- int width, /* Width of each column of output */
1186
- int escHtml /* True to generate HTML output */
1296
+ ReCompiled *pRe, /* Only show changes that match this regex */
1297
+ u64 diffFlags /* Flags controlling the diff */
11871298
){
11881299
DLine *A; /* Left side of the diff */
11891300
DLine *B; /* Right side of the diff */
11901301
int a = 0; /* Index of next line in A[] */
11911302
int b = 0; /* Index of next line in B[] */
@@ -1197,16 +1308,20 @@
11971308
int i, j; /* Loop counters */
11981309
int m, ma, mb;/* Number of lines to output */
11991310
int skip; /* Number of lines to skip */
12001311
int nChunk = 0; /* Number of chunks of diff output seen so far */
12011312
SbsLine s; /* Output line buffer */
1313
+ int nContext; /* Lines of context above and below each change */
1314
+ int showDivider = 0; /* True to show the divider */
12021315
12031316
memset(&s, 0, sizeof(s));
1204
- s.zLine = fossil_malloc( 15*width + 200 );
1317
+ s.width = diff_width(diffFlags);
1318
+ s.zLine = fossil_malloc( 15*s.width + 200 );
12051319
if( s.zLine==0 ) return;
1206
- s.width = width;
1207
- s.escHtml = escHtml;
1320
+ nContext = diff_context_lines(diffFlags);
1321
+ s.escHtml = (diffFlags & DIFF_HTML)!=0;
1322
+ s.pRe = pRe;
12081323
s.iStart = -1;
12091324
s.iStart2 = 0;
12101325
s.iEnd = -1;
12111326
A = p->aFrom;
12121327
B = p->aTo;
@@ -1215,10 +1330,35 @@
12151330
while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
12161331
for(r=0; r<mxr; r += 3*nr){
12171332
/* Figure out how many triples to show in a single block */
12181333
for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
12191334
/* printf("r=%d nr=%d\n", r, nr); */
1335
+
1336
+ /* If there is a regex, skip this block (generate no diff output)
1337
+ ** if the regex matches or does not match both insert and delete.
1338
+ ** Only display the block if one side matches but the other side does
1339
+ ** not.
1340
+ */
1341
+ if( pRe ){
1342
+ int hideBlock = 1;
1343
+ int xa = a, xb = b;
1344
+ for(i=0; hideBlock && i<nr; i++){
1345
+ int c1, c2;
1346
+ xa += R[r+i*3];
1347
+ xb += R[r+i*3];
1348
+ c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
1349
+ c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
1350
+ hideBlock = c1==c2;
1351
+ xa += R[r+i*3+1];
1352
+ xb += R[r+i*3+2];
1353
+ }
1354
+ if( hideBlock ){
1355
+ a = xa;
1356
+ b = xb;
1357
+ continue;
1358
+ }
1359
+ }
12201360
12211361
/* For the current block comprising nr triples, figure out
12221362
** how many lines of A and B are to be displayed
12231363
*/
12241364
if( R[r]>nContext ){
@@ -1243,20 +1383,21 @@
12431383
na += R[r+i*3];
12441384
nb += R[r+i*3];
12451385
}
12461386
12471387
/* Draw the separator between blocks */
1248
- if( r>0 ){
1249
- if( escHtml ){
1388
+ if( showDivider ){
1389
+ if( s.escHtml ){
12501390
blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
1251
- width*2+16, '.');
1391
+ s.width*2+16, '.');
12521392
}else{
1253
- blob_appendf(pOut, "%.*c\n", width*2+16, '.');
1393
+ blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
12541394
}
12551395
}
1396
+ showDivider = 1;
12561397
nChunk++;
1257
- if( escHtml ){
1398
+ if( s.escHtml ){
12581399
blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
12591400
}
12601401
12611402
/* Show the initial common area */
12621403
a += skip;
@@ -1292,38 +1433,41 @@
12921433
}
12931434
12941435
alignment = sbsAlignment(&A[a], ma, &B[b], mb);
12951436
for(j=0; ma+mb>0; j++){
12961437
if( alignment[j]==1 ){
1438
+ /* Delete one line from the left */
12971439
s.n = 0;
12981440
sbsWriteLineno(&s, a);
12991441
s.iStart = 0;
13001442
s.zStart = "<span class=\"diffrm\">";
13011443
s.iEnd = s.width;
13021444
sbsWriteText(&s, &A[a], SBS_PAD);
1303
- if( escHtml ){
1445
+ if( s.escHtml ){
13041446
sbsWrite(&s, " &lt;\n", 6);
13051447
}else{
13061448
sbsWrite(&s, " <\n", 3);
13071449
}
13081450
blob_append(pOut, s.zLine, s.n);
13091451
assert( ma>0 );
13101452
ma--;
13111453
a++;
1312
- }else if( alignment[j]==2 ){
1454
+ }else if( alignment[j]==3 ){
1455
+ /* The left line is changed into the right line */
13131456
s.n = 0;
13141457
sbsWriteLineChange(&s, &A[a], a, &B[b], b);
13151458
blob_append(pOut, s.zLine, s.n);
13161459
assert( ma>0 && mb>0 );
13171460
ma--;
13181461
mb--;
13191462
a++;
13201463
b++;
1321
- }else{
1464
+ }else if( alignment[j]==2 ){
1465
+ /* Insert one line on the right */
13221466
s.n = 0;
1323
- sbsWriteSpace(&s, width + 7);
1324
- if( escHtml ){
1467
+ sbsWriteSpace(&s, s.width + 7);
1468
+ if( s.escHtml ){
13251469
sbsWrite(&s, " &gt; ", 6);
13261470
}else{
13271471
sbsWrite(&s, " > ", 3);
13281472
}
13291473
sbsWriteLineno(&s, b);
@@ -1333,11 +1477,31 @@
13331477
sbsWriteText(&s, &B[b], SBS_NEWLINE);
13341478
blob_append(pOut, s.zLine, s.n);
13351479
assert( mb>0 );
13361480
mb--;
13371481
b++;
1482
+ }else{
1483
+ /* Delete from the left and insert on the right */
1484
+ s.n = 0;
1485
+ sbsWriteLineno(&s, a);
1486
+ s.iStart = 0;
1487
+ s.zStart = "<span class=\"diffrm\">";
1488
+ s.iEnd = s.width;
1489
+ sbsWriteText(&s, &A[a], SBS_PAD);
1490
+ sbsWrite(&s, " | ", 3);
1491
+ sbsWriteLineno(&s, b);
1492
+ s.iStart = 0;
1493
+ s.zStart = "<span class=\"diffadd\">";
1494
+ s.iEnd = s.width;
1495
+ sbsWriteText(&s, &B[b], SBS_NEWLINE);
1496
+ blob_append(pOut, s.zLine, s.n);
1497
+ ma--;
1498
+ mb--;
1499
+ a++;
1500
+ b++;
13381501
}
1502
+
13391503
}
13401504
fossil_free(alignment);
13411505
if( i<nr-1 ){
13421506
m = R[r+i*3+3];
13431507
for(j=0; j<m; j++){
@@ -1761,11 +1925,11 @@
17611925
** Extract the number of lines of context from diffFlags. Supply an
17621926
** appropriate default if no context width is specified.
17631927
*/
17641928
int diff_context_lines(u64 diffFlags){
17651929
int n = diffFlags & DIFF_CONTEXT_MASK;
1766
- if( n==0 ) n = 5;
1930
+ if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5;
17671931
return n;
17681932
}
17691933
17701934
/*
17711935
** Extract the width of columns for side-by-side diff. Supply an
@@ -1793,22 +1957,21 @@
17931957
*/
17941958
int *text_diff(
17951959
Blob *pA_Blob, /* FROM file */
17961960
Blob *pB_Blob, /* TO file */
17971961
Blob *pOut, /* Write diff here if not NULL */
1962
+ ReCompiled *pRe, /* Only output changes where this Regexp matches */
17981963
u64 diffFlags /* DIFF_* flags defined above */
17991964
){
18001965
int ignoreEolWs; /* Ignore whitespace at the end of lines */
1801
- int nContext; /* Amount of context to display */
18021966
DContext c;
18031967
18041968
if( diffFlags & DIFF_INVERT ){
18051969
Blob *pTemp = pA_Blob;
18061970
pA_Blob = pB_Blob;
18071971
pB_Blob = pTemp;
18081972
}
1809
- nContext = diff_context_lines(diffFlags);
18101973
ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
18111974
18121975
/* Prepare the input files */
18131976
memset(&c, 0, sizeof(c));
18141977
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
@@ -1828,17 +1991,14 @@
18281991
diff_all(&c);
18291992
if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
18301993
18311994
if( pOut ){
18321995
/* Compute a context or side-by-side diff into pOut */
1833
- int escHtml = (diffFlags & DIFF_HTML)!=0;
18341996
if( diffFlags & DIFF_SIDEBYSIDE ){
1835
- int width = diff_width(diffFlags);
1836
- sbsDiff(&c, pOut, nContext, width, escHtml);
1997
+ sbsDiff(&c, pOut, pRe, diffFlags);
18371998
}else{
1838
- int showLn = (diffFlags & DIFF_LINENO)!=0;
1839
- contextDiff(&c, pOut, nContext, showLn, escHtml);
1999
+ contextDiff(&c, pOut, pRe, diffFlags);
18402000
}
18412001
fossil_free(c.aFrom);
18422002
fossil_free(c.aTo);
18432003
fossil_free(c.aEdit);
18442004
return 0;
@@ -1864,19 +2024,19 @@
18642024
** --noopt Disable optimization DIFF_NOOPT
18652025
** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
18662026
** --unified Unified diff. ~DIFF_SIDEBYSIDE
18672027
** --width|-W N N character lines. DIFF_WIDTH_MASK
18682028
*/
1869
-int diff_options(void){
2029
+u64 diff_options(void){
18702030
u64 diffFlags = 0;
18712031
const char *z;
18722032
int f;
18732033
if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
18742034
if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
1875
- if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
2035
+ if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
18762036
if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1877
- diffFlags |= f;
2037
+ diffFlags |= f + DIFF_CONTEXT_EX;
18782038
}
18792039
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
18802040
f *= DIFF_CONTEXT_MASK+1;
18812041
if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
18822042
diffFlags |= f;
@@ -1901,34 +2061,52 @@
19012061
if( g.argc<4 ) usage("FILE1 FILE2 ...");
19022062
blob_read_from_file(&a, g.argv[2]);
19032063
for(i=3; i<g.argc; i++){
19042064
if( i>3 ) fossil_print("-------------------------------\n");
19052065
blob_read_from_file(&b, g.argv[i]);
1906
- R = text_diff(&a, &b, 0, diffFlags);
2066
+ R = text_diff(&a, &b, 0, 0, diffFlags);
19072067
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
19082068
fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
19092069
}
19102070
/* free(R); */
19112071
blob_reset(&b);
19122072
}
19132073
}
19142074
19152075
/*
1916
-** COMMAND: test-udiff
2076
+** COMMAND: test-diff
2077
+**
2078
+** Usage: %fossil [options] FILE1 FILE2
19172079
**
19182080
** Print the difference between two files. The usual diff options apply.
19192081
*/
1920
-void test_udiff_cmd(void){
2082
+void test_diff_cmd(void){
19212083
Blob a, b, out;
1922
- u64 diffFlag = diff_options();
2084
+ u64 diffFlag;
2085
+ const char *zRe; /* Regex filter for diff output */
2086
+ ReCompiled *pRe = 0; /* Regex filter for diff output */
19232087
2088
+ if( find_option("tk",0,0)!=0 ){
2089
+ diff_tk("test-diff", 2);
2090
+ return;
2091
+ }
2092
+ find_option("i",0,0);
2093
+ zRe = find_option("regexp","e",1);
2094
+ if( zRe ){
2095
+ const char *zErr = re_compile(&pRe, zRe, 0);
2096
+ if( zErr ) fossil_fatal("regex error: %s", zErr);
2097
+ }
2098
+ diffFlag = diff_options();
2099
+ verify_all_options();
19242100
if( g.argc!=4 ) usage("FILE1 FILE2");
2101
+ diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
19252102
blob_read_from_file(&a, g.argv[2]);
19262103
blob_read_from_file(&b, g.argv[3]);
19272104
blob_zero(&out);
1928
- text_diff(&a, &b, &out, diffFlag);
2105
+ text_diff(&a, &b, &out, pRe, diffFlag);
19292106
blob_write_to_file(&out, "-");
2107
+ re_free(pRe);
19302108
}
19312109
19322110
/**************************************************************************
19332111
** The basic difference engine is above. What follows is the annotation
19342112
** engine. Both are in the same file since they share many components.
19352113
--- src/diff.c
+++ src/diff.c
@@ -23,11 +23,12 @@
23 #include <assert.h>
24
25
26 #if INTERFACE
27 /*
28 ** Allowed flag parameters to the text_diff() and html_sbsdiff() functions:
 
29 */
30 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
31 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
32 #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
33 #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
@@ -37,10 +38,11 @@
37 #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
38 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
39 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
40 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
41 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
 
42
43 /*
44 ** These error messages are shared in multiple locations. They are defined
45 ** here for consistency.
46 */
@@ -52,11 +54,11 @@
52
53 #define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0)
54 #endif /* INTERFACE */
55
56 /*
57 ** Maximum length of a line in a text file, in bytes. (8192)
58 */
59 #define LENGTH_MASK_SZ 13
60 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
61
62 /*
@@ -116,10 +118,13 @@
116 ** more. If trailing whitespace is ignored, the "patch" command gets
117 ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
118 **
119 ** Return 0 if the file is binary or contains a line that is
120 ** too long.
 
 
 
121 */
122 static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
123 int nLine, i, j, k, x;
124 unsigned int h, h2;
125 DLine *a;
@@ -445,34 +450,50 @@
445 ** Return true if two DLine elements are identical.
446 */
447 static int same_dline(DLine *pA, DLine *pB){
448 return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
449 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
451 /*
452 ** Append a single line of context-diff output to pOut.
453 */
454 static void appendDiffLine(
455 Blob *pOut, /* Where to write the line of output */
456 char cPrefix, /* One of " ", "+", or "-" */
457 DLine *pLine, /* The line to be output */
458 int html /* True if generating HTML. False for plain text */
 
459 ){
460 int i;
461 blob_append(pOut, &cPrefix, 1);
462 if( html ){
463 char *zHtml;
464 if( cPrefix=='+' ){
 
 
465 blob_append(pOut, "<span class=\"diffadd\">", -1);
466 }else if( cPrefix=='-' ){
467 blob_append(pOut, "<span class=\"diffrm\">", -1);
468 }
469 zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
470 for(i=0; i<strlen(zHtml); i++){
471 char c = zHtml[i];
472 if( c=='\t' || c=='\r' || c=='\f' ) zHtml[i] = ' ';
473 }
474 blob_append(pOut, zHtml, -1);
475 fossil_free(zHtml);
476 if( cPrefix!=' ' ){
477 blob_append(pOut, "</span>", -1);
478 }
@@ -482,11 +503,11 @@
482 blob_append(pOut, "\n", 1);
483 }
484
485 /*
486 ** Add two line numbers to the beginning of an output line for a context
487 ** diff. One or of the other of the two numbers might be zero, which means
488 ** to leave that number field blank. The "html" parameter means to format
489 ** the output for HTML.
490 */
491 static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
492 if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
@@ -501,21 +522,19 @@
501 blob_append(pOut, " ", 8);
502 }
503 if( html ) blob_append(pOut, "</span>", -1);
504 }
505
506
507 /*
508 ** Given a raw diff p[] in which the p->aEdit[] array has been filled
509 ** in, compute a context diff into pOut.
510 */
511 static void contextDiff(
512 DContext *p, /* The difference */
513 Blob *pOut, /* Output a context diff to here */
514 int nContext, /* Number of lines of context */
515 int showLn, /* Show line numbers */
516 int html /* Render as HTML */
517 ){
518 DLine *A; /* Left side of the diff */
519 DLine *B; /* Right side of the diff */
520 int a = 0; /* Index of next line in A[] */
521 int b = 0; /* Index of next line in B[] */
@@ -526,11 +545,18 @@
526 int na, nb; /* Number of lines shown from A and B */
527 int i, j; /* Loop counters */
528 int m; /* Number of lines to output */
529 int skip; /* Number of lines to skip */
530 int nChunk = 0; /* Number of diff chunks seen so far */
 
 
 
 
531
 
 
 
532 A = p->aFrom;
533 B = p->aTo;
534 R = p->aEdit;
535 mxr = p->nEdit;
536 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -537,10 +563,35 @@
537 for(r=0; r<mxr; r += 3*nr){
538 /* Figure out how many triples to show in a single block */
539 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
540 /* printf("r=%d nr=%d\n", r, nr); */
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542 /* For the current block comprising nr triples, figure out
543 ** how many lines of A and B are to be displayed
544 */
545 if( R[r]>nContext ){
546 na = nb = nContext;
@@ -564,17 +615,18 @@
564 na += R[r+i*3];
565 nb += R[r+i*3];
566 }
567
568 /* Show the header for this block, or if we are doing a modified
569 ** context diff that contains line numbers, show the separate from
570 ** the previous block.
571 */
572 nChunk++;
573 if( showLn ){
574 if( r==0 ){
575 /* Do not show a top divider */
 
576 }else if( html ){
577 blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
578 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
579 }else{
580 blob_appendf(pOut, "%.80c\n", '.');
@@ -597,34 +649,34 @@
597 a += skip;
598 b += skip;
599 m = R[r] - skip;
600 for(j=0; j<m; j++){
601 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
602 appendDiffLine(pOut, ' ', &A[a+j], html);
603 }
604 a += m;
605 b += m;
606
607 /* Show the differences */
608 for(i=0; i<nr; i++){
609 m = R[r+i*3+1];
610 for(j=0; j<m; j++){
611 if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
612 appendDiffLine(pOut, '-', &A[a+j], html);
613 }
614 a += m;
615 m = R[r+i*3+2];
616 for(j=0; j<m; j++){
617 if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
618 appendDiffLine(pOut, '+', &B[b+j], html);
619 }
620 b += m;
621 if( i<nr-1 ){
622 m = R[r+i*3+3];
623 for(j=0; j<m; j++){
624 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
625 appendDiffLine(pOut, ' ', &B[b+j], html);
626 }
627 b += m;
628 a += m;
629 }
630 }
@@ -633,11 +685,11 @@
633 assert( nr==i );
634 m = R[r+nr*3];
635 if( m>nContext ) m = nContext;
636 for(j=0; j<m; j++){
637 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
638 appendDiffLine(pOut, ' ', &B[b+j], html);
639 }
640 }
641 }
642
643 /*
@@ -653,10 +705,11 @@
653 const char *zStart; /* A <span> tag */
654 int iEnd; /* Write </span> prior to character iEnd */
655 int iStart2; /* Write zStart2 prior to character iStart2 */
656 const char *zStart2; /* A <span> tag */
657 int iEnd2; /* Write </span> prior to character iEnd2 */
 
658 };
659
660 /*
661 ** Flags for sbsWriteText()
662 */
@@ -678,13 +731,17 @@
678 int k; /* Cursor position */
679 int needEndSpan = 0;
680 const char *zIn = pLine->z;
681 char *z = &p->zLine[p->n];
682 int w = p->width;
 
 
 
 
683 for(i=j=k=0; k<w && i<n; i++, k++){
684 char c = zIn[i];
685 if( p->escHtml ){
686 if( i==p->iStart ){
687 int x = strlen(p->zStart);
688 memcpy(z+j, p->zStart, x);
689 j += x;
690 needEndSpan = 1;
@@ -835,10 +892,41 @@
835 }
836 }
837 }
838 return rc;
839 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
841 /*
842 ** Write out lines that have been edited. Adjust the highlight to cover
843 ** only those parts of the line that actually changed.
844 */
@@ -929,41 +1017,35 @@
929 && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
930 ){
931 sbsWriteLineno(p, lnLeft);
932 p->iStart = nPrefix;
933 p->iEnd = nPrefix + aLCS[0];
934 p->zStart = aLCS[2]==0 ? zClassRm : zClassChng;
 
 
 
 
 
935 p->iStart2 = nPrefix + aLCS[1];
936 p->iEnd2 = nLeft - nSuffix;
937 p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
938 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
939 if( p->iStart==p->iEnd ){
940 p->iStart = p->iStart2;
941 p->iEnd = p->iEnd2;
942 p->zStart = p->zStart2;
943 p->iStart2 = 0;
944 p->iEnd2 = 0;
945 }
946 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
947 sbsWriteText(p, pLeft, SBS_PAD);
948 sbsWrite(p, " | ", 3);
949 sbsWriteLineno(p, lnRight);
950 p->iStart = nPrefix;
951 p->iEnd = nPrefix + aLCS[2];
952 p->zStart = aLCS[0]==0 ? zClassAdd : zClassChng;
 
 
 
 
 
953 p->iStart2 = nPrefix + aLCS[3];
954 p->iEnd2 = nRight - nSuffix;
955 p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
956 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
957 if( p->iStart==p->iEnd ){
958 p->iStart = p->iStart2;
959 p->iEnd = p->iEnd2;
960 p->zStart = p->zStart2;
961 p->iStart2 = 0;
962 p->iEnd2 = 0;
963 }
964 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
965 sbsWriteText(p, pRight, SBS_NEWLINE);
966 return;
967 }
968
969 /* If all else fails, show a single big change between left and right */
@@ -1056,13 +1138,16 @@
1056 **
1057 ** The return value is a buffer of unsigned characters, obtained from
1058 ** fossil_malloc(). (The caller needs to free the return value using
1059 ** fossil_free().) Entries in the returned array have values as follows:
1060 **
1061 ** 1. Delete the next line of pLeft.
1062 ** 2. The next line of pLeft changes into the next line of pRight.
1063 ** 3. Insert the next line of pRight.
 
 
 
1064 **
1065 ** The length of the returned array will be just large enough to cause
1066 ** all elements of pLeft and pRight to be consumed.
1067 **
1068 ** Algorithm: Wagner's minimum edit-distance algorithm, modified by
@@ -1077,27 +1162,32 @@
1077 ){
1078 int i, j, k; /* Loop counters */
1079 int *a; /* One row of the Wagner matrix */
1080 int *pToFree; /* Space that needs to be freed */
1081 unsigned char *aM; /* Wagner result matrix */
 
 
 
1082 int aBuf[100]; /* Stack space for a[] if nRight not to big */
1083
1084 aM = fossil_malloc( (nLeft+1)*(nRight+1) );
1085 if( nLeft==0 ){
1086 memset(aM, 3, nRight);
1087 return aM;
1088 }
1089 if( nRight==0 ){
1090 memset(aM, 1, nLeft);
1091 return aM;
1092 }
1093
1094 /* This algorithm is O(N**2). So if N is too big, bail out with a
1095 ** simple (but stupid and ugly) result that doesn't take too long. */
 
1096 if( nLeft*nRight>100000 ){
1097 memset(aM, 3, nRight);
1098 memset(aM+nRight, 1, nLeft);
 
1099 return aM;
1100 }
1101
1102 if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){
1103 pToFree = 0;
@@ -1106,30 +1196,30 @@
1106 a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
1107 }
1108
1109 /* Compute the best alignment */
1110 for(i=0; i<=nRight; i++){
1111 aM[i] = 3;
1112 a[i] = i*50;
1113 }
1114 aM[0] = 0;
1115 for(j=1; j<=nLeft; j++){
1116 int p = a[0];
1117 a[0] = p+50;
1118 aM[j*(nRight+1)] = 1;
1119 for(i=1; i<=nRight; i++){
1120 int m = a[i-1]+50;
1121 int d = 3;
1122 if( m>a[i]+50 ){
1123 m = a[i]+50;
1124 d = 1;
1125 }
1126 if( m>p ){
1127 int score = match_dline(&aLeft[j-1], &aRight[i-1]);
1128 if( (score<66 || (i<j+1 && i>j-1)) && m>p+score ){
1129 m = p+score;
1130 d = 2;
1131 }
1132 }
1133 p = a[i];
1134 a[i] = m;
1135 aM[j*(nRight+1)+i] = d;
@@ -1138,28 +1228,50 @@
1138
1139 /* Compute the lowest-cost path back through the matrix */
1140 i = nRight;
1141 j = nLeft;
1142 k = (nRight+1)*(nLeft+1)-1;
 
1143 while( i+j>0 ){
1144 unsigned char c = aM[k--];
1145 if( c==2 ){
1146 assert( i>0 && j>0 );
1147 i--;
1148 j--;
1149 }else if( c==3 ){
 
 
 
1150 assert( i>0 );
1151 i--;
1152 }else{
1153 assert( j>0 );
1154 j--;
1155 }
 
1156 aM[k] = aM[j*(nRight+1)+i];
1157 }
1158 k++;
1159 i = (nRight+1)*(nLeft+1) - k;
1160 memmove(aM, &aM[k], i);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1161
1162 /* Return the result */
1163 fossil_free(pToFree);
1164 return aM;
1165 }
@@ -1179,13 +1291,12 @@
1179 ** in, compute a side-by-side diff into pOut.
1180 */
1181 static void sbsDiff(
1182 DContext *p, /* The computed diff */
1183 Blob *pOut, /* Write the results here */
1184 int nContext, /* Number of lines of context around each change */
1185 int width, /* Width of each column of output */
1186 int escHtml /* True to generate HTML output */
1187 ){
1188 DLine *A; /* Left side of the diff */
1189 DLine *B; /* Right side of the diff */
1190 int a = 0; /* Index of next line in A[] */
1191 int b = 0; /* Index of next line in B[] */
@@ -1197,16 +1308,20 @@
1197 int i, j; /* Loop counters */
1198 int m, ma, mb;/* Number of lines to output */
1199 int skip; /* Number of lines to skip */
1200 int nChunk = 0; /* Number of chunks of diff output seen so far */
1201 SbsLine s; /* Output line buffer */
 
 
1202
1203 memset(&s, 0, sizeof(s));
1204 s.zLine = fossil_malloc( 15*width + 200 );
 
1205 if( s.zLine==0 ) return;
1206 s.width = width;
1207 s.escHtml = escHtml;
 
1208 s.iStart = -1;
1209 s.iStart2 = 0;
1210 s.iEnd = -1;
1211 A = p->aFrom;
1212 B = p->aTo;
@@ -1215,10 +1330,35 @@
1215 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
1216 for(r=0; r<mxr; r += 3*nr){
1217 /* Figure out how many triples to show in a single block */
1218 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
1219 /* printf("r=%d nr=%d\n", r, nr); */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1220
1221 /* For the current block comprising nr triples, figure out
1222 ** how many lines of A and B are to be displayed
1223 */
1224 if( R[r]>nContext ){
@@ -1243,20 +1383,21 @@
1243 na += R[r+i*3];
1244 nb += R[r+i*3];
1245 }
1246
1247 /* Draw the separator between blocks */
1248 if( r>0 ){
1249 if( escHtml ){
1250 blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
1251 width*2+16, '.');
1252 }else{
1253 blob_appendf(pOut, "%.*c\n", width*2+16, '.');
1254 }
1255 }
 
1256 nChunk++;
1257 if( escHtml ){
1258 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
1259 }
1260
1261 /* Show the initial common area */
1262 a += skip;
@@ -1292,38 +1433,41 @@
1292 }
1293
1294 alignment = sbsAlignment(&A[a], ma, &B[b], mb);
1295 for(j=0; ma+mb>0; j++){
1296 if( alignment[j]==1 ){
 
1297 s.n = 0;
1298 sbsWriteLineno(&s, a);
1299 s.iStart = 0;
1300 s.zStart = "<span class=\"diffrm\">";
1301 s.iEnd = s.width;
1302 sbsWriteText(&s, &A[a], SBS_PAD);
1303 if( escHtml ){
1304 sbsWrite(&s, " &lt;\n", 6);
1305 }else{
1306 sbsWrite(&s, " <\n", 3);
1307 }
1308 blob_append(pOut, s.zLine, s.n);
1309 assert( ma>0 );
1310 ma--;
1311 a++;
1312 }else if( alignment[j]==2 ){
 
1313 s.n = 0;
1314 sbsWriteLineChange(&s, &A[a], a, &B[b], b);
1315 blob_append(pOut, s.zLine, s.n);
1316 assert( ma>0 && mb>0 );
1317 ma--;
1318 mb--;
1319 a++;
1320 b++;
1321 }else{
 
1322 s.n = 0;
1323 sbsWriteSpace(&s, width + 7);
1324 if( escHtml ){
1325 sbsWrite(&s, " &gt; ", 6);
1326 }else{
1327 sbsWrite(&s, " > ", 3);
1328 }
1329 sbsWriteLineno(&s, b);
@@ -1333,11 +1477,31 @@
1333 sbsWriteText(&s, &B[b], SBS_NEWLINE);
1334 blob_append(pOut, s.zLine, s.n);
1335 assert( mb>0 );
1336 mb--;
1337 b++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1338 }
 
1339 }
1340 fossil_free(alignment);
1341 if( i<nr-1 ){
1342 m = R[r+i*3+3];
1343 for(j=0; j<m; j++){
@@ -1761,11 +1925,11 @@
1761 ** Extract the number of lines of context from diffFlags. Supply an
1762 ** appropriate default if no context width is specified.
1763 */
1764 int diff_context_lines(u64 diffFlags){
1765 int n = diffFlags & DIFF_CONTEXT_MASK;
1766 if( n==0 ) n = 5;
1767 return n;
1768 }
1769
1770 /*
1771 ** Extract the width of columns for side-by-side diff. Supply an
@@ -1793,22 +1957,21 @@
1793 */
1794 int *text_diff(
1795 Blob *pA_Blob, /* FROM file */
1796 Blob *pB_Blob, /* TO file */
1797 Blob *pOut, /* Write diff here if not NULL */
 
1798 u64 diffFlags /* DIFF_* flags defined above */
1799 ){
1800 int ignoreEolWs; /* Ignore whitespace at the end of lines */
1801 int nContext; /* Amount of context to display */
1802 DContext c;
1803
1804 if( diffFlags & DIFF_INVERT ){
1805 Blob *pTemp = pA_Blob;
1806 pA_Blob = pB_Blob;
1807 pB_Blob = pTemp;
1808 }
1809 nContext = diff_context_lines(diffFlags);
1810 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1811
1812 /* Prepare the input files */
1813 memset(&c, 0, sizeof(c));
1814 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
@@ -1828,17 +1991,14 @@
1828 diff_all(&c);
1829 if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
1830
1831 if( pOut ){
1832 /* Compute a context or side-by-side diff into pOut */
1833 int escHtml = (diffFlags & DIFF_HTML)!=0;
1834 if( diffFlags & DIFF_SIDEBYSIDE ){
1835 int width = diff_width(diffFlags);
1836 sbsDiff(&c, pOut, nContext, width, escHtml);
1837 }else{
1838 int showLn = (diffFlags & DIFF_LINENO)!=0;
1839 contextDiff(&c, pOut, nContext, showLn, escHtml);
1840 }
1841 fossil_free(c.aFrom);
1842 fossil_free(c.aTo);
1843 fossil_free(c.aEdit);
1844 return 0;
@@ -1864,19 +2024,19 @@
1864 ** --noopt Disable optimization DIFF_NOOPT
1865 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1866 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
1867 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1868 */
1869 int diff_options(void){
1870 u64 diffFlags = 0;
1871 const char *z;
1872 int f;
1873 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
1874 if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
1875 if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
1876 if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1877 diffFlags |= f;
1878 }
1879 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
1880 f *= DIFF_CONTEXT_MASK+1;
1881 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1882 diffFlags |= f;
@@ -1901,34 +2061,52 @@
1901 if( g.argc<4 ) usage("FILE1 FILE2 ...");
1902 blob_read_from_file(&a, g.argv[2]);
1903 for(i=3; i<g.argc; i++){
1904 if( i>3 ) fossil_print("-------------------------------\n");
1905 blob_read_from_file(&b, g.argv[i]);
1906 R = text_diff(&a, &b, 0, diffFlags);
1907 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
1908 fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
1909 }
1910 /* free(R); */
1911 blob_reset(&b);
1912 }
1913 }
1914
1915 /*
1916 ** COMMAND: test-udiff
 
 
1917 **
1918 ** Print the difference between two files. The usual diff options apply.
1919 */
1920 void test_udiff_cmd(void){
1921 Blob a, b, out;
1922 u64 diffFlag = diff_options();
 
 
1923
 
 
 
 
 
 
 
 
 
 
 
 
1924 if( g.argc!=4 ) usage("FILE1 FILE2");
 
1925 blob_read_from_file(&a, g.argv[2]);
1926 blob_read_from_file(&b, g.argv[3]);
1927 blob_zero(&out);
1928 text_diff(&a, &b, &out, diffFlag);
1929 blob_write_to_file(&out, "-");
 
1930 }
1931
1932 /**************************************************************************
1933 ** The basic difference engine is above. What follows is the annotation
1934 ** engine. Both are in the same file since they share many components.
1935
--- src/diff.c
+++ src/diff.c
@@ -23,11 +23,12 @@
23 #include <assert.h>
24
25
26 #if INTERFACE
27 /*
28 ** Flag parameters to the text_diff() routine used to control the formatting
29 ** of the diff output.
30 */
31 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
32 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33 #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
34 #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
@@ -37,10 +38,11 @@
38 #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
39 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
40 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
41 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
42 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43 #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
44
45 /*
46 ** These error messages are shared in multiple locations. They are defined
47 ** here for consistency.
48 */
@@ -52,11 +54,11 @@
54
55 #define looks_like_binary(blob) ((looks_like_utf8((blob))&3) == 0)
56 #endif /* INTERFACE */
57
58 /*
59 ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
60 */
61 #define LENGTH_MASK_SZ 13
62 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
63
64 /*
@@ -116,10 +118,13 @@
118 ** more. If trailing whitespace is ignored, the "patch" command gets
119 ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b]
120 **
121 ** Return 0 if the file is binary or contains a line that is
122 ** too long.
123 **
124 ** Profiling show that in most cases this routine consumes the bulk of
125 ** the CPU time on a diff.
126 */
127 static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
128 int nLine, i, j, k, x;
129 unsigned int h, h2;
130 DLine *a;
@@ -445,34 +450,50 @@
450 ** Return true if two DLine elements are identical.
451 */
452 static int same_dline(DLine *pA, DLine *pB){
453 return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0;
454 }
455
456 /*
457 ** Return true if the regular expression *pRe matches any of the
458 ** N dlines
459 */
460 static int re_dline_match(
461 ReCompiled *pRe, /* The regular expression to be matched */
462 DLine *aDLine, /* First of N DLines to compare against */
463 int N /* Number of DLines to check */
464 ){
465 while( N-- ){
466 if( re_match(pRe, (const unsigned char *)aDLine->z, LENGTH(aDLine)) ){
467 return 1;
468 }
469 aDLine++;
470 }
471 return 0;
472 }
473
474 /*
475 ** Append a single line of context-diff output to pOut.
476 */
477 static void appendDiffLine(
478 Blob *pOut, /* Where to write the line of output */
479 char cPrefix, /* One of " ", "+", or "-" */
480 DLine *pLine, /* The line to be output */
481 int html, /* True if generating HTML. False for plain text */
482 ReCompiled *pRe /* Colorize only if line matches this Regex */
483 ){
 
484 blob_append(pOut, &cPrefix, 1);
485 if( html ){
486 char *zHtml;
487 if( pRe && re_dline_match(pRe, pLine, 1)==0 ){
488 cPrefix = ' ';
489 }else if( cPrefix=='+' ){
490 blob_append(pOut, "<span class=\"diffadd\">", -1);
491 }else if( cPrefix=='-' ){
492 blob_append(pOut, "<span class=\"diffrm\">", -1);
493 }
494 zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK));
 
 
 
 
495 blob_append(pOut, zHtml, -1);
496 fossil_free(zHtml);
497 if( cPrefix!=' ' ){
498 blob_append(pOut, "</span>", -1);
499 }
@@ -482,11 +503,11 @@
503 blob_append(pOut, "\n", 1);
504 }
505
506 /*
507 ** Add two line numbers to the beginning of an output line for a context
508 ** diff. One or the other of the two numbers might be zero, which means
509 ** to leave that number field blank. The "html" parameter means to format
510 ** the output for HTML.
511 */
512 static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){
513 if( html ) blob_append(pOut, "<span class=\"diffln\">", -1);
@@ -501,21 +522,19 @@
522 blob_append(pOut, " ", 8);
523 }
524 if( html ) blob_append(pOut, "</span>", -1);
525 }
526
 
527 /*
528 ** Given a raw diff p[] in which the p->aEdit[] array has been filled
529 ** in, compute a context diff into pOut.
530 */
531 static void contextDiff(
532 DContext *p, /* The difference */
533 Blob *pOut, /* Output a context diff to here */
534 ReCompiled *pRe, /* Only show changes that match this regex */
535 u64 diffFlags /* Flags controlling the diff format */
 
536 ){
537 DLine *A; /* Left side of the diff */
538 DLine *B; /* Right side of the diff */
539 int a = 0; /* Index of next line in A[] */
540 int b = 0; /* Index of next line in B[] */
@@ -526,11 +545,18 @@
545 int na, nb; /* Number of lines shown from A and B */
546 int i, j; /* Loop counters */
547 int m; /* Number of lines to output */
548 int skip; /* Number of lines to skip */
549 int nChunk = 0; /* Number of diff chunks seen so far */
550 int nContext; /* Number of lines of context */
551 int showLn; /* Show line numbers */
552 int html; /* Render as HTML */
553 int showDivider = 0; /* True to show the divider between diff blocks */
554
555 nContext = diff_context_lines(diffFlags);
556 showLn = (diffFlags & DIFF_LINENO)!=0;
557 html = (diffFlags & DIFF_HTML)!=0;
558 A = p->aFrom;
559 B = p->aTo;
560 R = p->aEdit;
561 mxr = p->nEdit;
562 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
@@ -537,10 +563,35 @@
563 for(r=0; r<mxr; r += 3*nr){
564 /* Figure out how many triples to show in a single block */
565 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
566 /* printf("r=%d nr=%d\n", r, nr); */
567
568 /* If there is a regex, skip this block (generate no diff output)
569 ** if the regex matches or does not match both insert and delete.
570 ** Only display the block if one side matches but the other side does
571 ** not.
572 */
573 if( pRe ){
574 int hideBlock = 1;
575 int xa = a, xb = b;
576 for(i=0; hideBlock && i<nr; i++){
577 int c1, c2;
578 xa += R[r+i*3];
579 xb += R[r+i*3];
580 c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
581 c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
582 hideBlock = c1==c2;
583 xa += R[r+i*3+1];
584 xb += R[r+i*3+2];
585 }
586 if( hideBlock ){
587 a = xa;
588 b = xb;
589 continue;
590 }
591 }
592
593 /* For the current block comprising nr triples, figure out
594 ** how many lines of A and B are to be displayed
595 */
596 if( R[r]>nContext ){
597 na = nb = nContext;
@@ -564,17 +615,18 @@
615 na += R[r+i*3];
616 nb += R[r+i*3];
617 }
618
619 /* Show the header for this block, or if we are doing a modified
620 ** context diff that contains line numbers, show the separator from
621 ** the previous block.
622 */
623 nChunk++;
624 if( showLn ){
625 if( !showDivider ){
626 /* Do not show a top divider */
627 showDivider = 1;
628 }else if( html ){
629 blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.');
630 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
631 }else{
632 blob_appendf(pOut, "%.80c\n", '.');
@@ -597,34 +649,34 @@
649 a += skip;
650 b += skip;
651 m = R[r] - skip;
652 for(j=0; j<m; j++){
653 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
654 appendDiffLine(pOut, ' ', &A[a+j], html, 0);
655 }
656 a += m;
657 b += m;
658
659 /* Show the differences */
660 for(i=0; i<nr; i++){
661 m = R[r+i*3+1];
662 for(j=0; j<m; j++){
663 if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html);
664 appendDiffLine(pOut, '-', &A[a+j], html, pRe);
665 }
666 a += m;
667 m = R[r+i*3+2];
668 for(j=0; j<m; j++){
669 if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html);
670 appendDiffLine(pOut, '+', &B[b+j], html, pRe);
671 }
672 b += m;
673 if( i<nr-1 ){
674 m = R[r+i*3+3];
675 for(j=0; j<m; j++){
676 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
677 appendDiffLine(pOut, ' ', &B[b+j], html, 0);
678 }
679 b += m;
680 a += m;
681 }
682 }
@@ -633,11 +685,11 @@
685 assert( nr==i );
686 m = R[r+nr*3];
687 if( m>nContext ) m = nContext;
688 for(j=0; j<m; j++){
689 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
690 appendDiffLine(pOut, ' ', &B[b+j], html, 0);
691 }
692 }
693 }
694
695 /*
@@ -653,10 +705,11 @@
705 const char *zStart; /* A <span> tag */
706 int iEnd; /* Write </span> prior to character iEnd */
707 int iStart2; /* Write zStart2 prior to character iStart2 */
708 const char *zStart2; /* A <span> tag */
709 int iEnd2; /* Write </span> prior to character iEnd2 */
710 ReCompiled *pRe; /* Only colorize matching lines, if not NULL */
711 };
712
713 /*
714 ** Flags for sbsWriteText()
715 */
@@ -678,13 +731,17 @@
731 int k; /* Cursor position */
732 int needEndSpan = 0;
733 const char *zIn = pLine->z;
734 char *z = &p->zLine[p->n];
735 int w = p->width;
736 int colorize = p->escHtml;
737 if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
738 colorize = 0;
739 }
740 for(i=j=k=0; k<w && i<n; i++, k++){
741 char c = zIn[i];
742 if( colorize ){
743 if( i==p->iStart ){
744 int x = strlen(p->zStart);
745 memcpy(z+j, p->zStart, x);
746 j += x;
747 needEndSpan = 1;
@@ -835,10 +892,41 @@
892 }
893 }
894 }
895 return rc;
896 }
897
898 /*
899 ** Try to shift iStart as far as possible to the left.
900 */
901 static void sbsShiftLeft(SbsLine *p, const char *z){
902 int i, j;
903 while( (i=p->iStart)>0 && z[i-1]==z[i] ){
904 for(j=i+1; j<p->iEnd && z[j-1]==z[j]; j++){}
905 if( j<p->iEnd ) break;
906 p->iStart--;
907 p->iEnd--;
908 }
909 }
910
911 /*
912 ** Simplify iStart and iStart2:
913 **
914 ** * If iStart is a null-change then move iStart2 into iStart
915 ** * Make sure any null-changes are in canonoical form.
916 */
917 static void sbsSimplifyLine(SbsLine *p){
918 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0;
919 if( p->iStart==p->iEnd ){
920 p->iStart = p->iStart2;
921 p->iEnd = p->iEnd2;
922 p->zStart = p->zStart2;
923 p->iStart2 = 0;
924 p->iEnd2 = 0;
925 }
926 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1;
927 }
928
929 /*
930 ** Write out lines that have been edited. Adjust the highlight to cover
931 ** only those parts of the line that actually changed.
932 */
@@ -929,41 +1017,35 @@
1017 && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS)
1018 ){
1019 sbsWriteLineno(p, lnLeft);
1020 p->iStart = nPrefix;
1021 p->iEnd = nPrefix + aLCS[0];
1022 if( aLCS[2]==0 ){
1023 sbsShiftLeft(p, pLeft->z);
1024 p->zStart = zClassRm;
1025 }else{
1026 p->zStart = zClassChng;
1027 }
1028 p->iStart2 = nPrefix + aLCS[1];
1029 p->iEnd2 = nLeft - nSuffix;
1030 p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng;
1031 sbsSimplifyLine(p);
 
 
 
 
 
 
 
 
1032 sbsWriteText(p, pLeft, SBS_PAD);
1033 sbsWrite(p, " | ", 3);
1034 sbsWriteLineno(p, lnRight);
1035 p->iStart = nPrefix;
1036 p->iEnd = nPrefix + aLCS[2];
1037 if( aLCS[0]==0 ){
1038 sbsShiftLeft(p, pRight->z);
1039 p->zStart = zClassAdd;
1040 }else{
1041 p->zStart = zClassChng;
1042 }
1043 p->iStart2 = nPrefix + aLCS[3];
1044 p->iEnd2 = nRight - nSuffix;
1045 p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng;
1046 sbsSimplifyLine(p);
 
 
 
 
 
 
 
 
1047 sbsWriteText(p, pRight, SBS_NEWLINE);
1048 return;
1049 }
1050
1051 /* If all else fails, show a single big change between left and right */
@@ -1056,13 +1138,16 @@
1138 **
1139 ** The return value is a buffer of unsigned characters, obtained from
1140 ** fossil_malloc(). (The caller needs to free the return value using
1141 ** fossil_free().) Entries in the returned array have values as follows:
1142 **
1143 ** 1. Delete the next line of pLeft.
1144 ** 2. Insert the next line of pRight.
1145 ** 3. The next line of pLeft changes into the next line of pRight.
1146 ** 4. Delete one line from pLeft and add one line to pRight.
1147 **
1148 ** Values larger than three indicate better matches.
1149 **
1150 ** The length of the returned array will be just large enough to cause
1151 ** all elements of pLeft and pRight to be consumed.
1152 **
1153 ** Algorithm: Wagner's minimum edit-distance algorithm, modified by
@@ -1077,27 +1162,32 @@
1162 ){
1163 int i, j, k; /* Loop counters */
1164 int *a; /* One row of the Wagner matrix */
1165 int *pToFree; /* Space that needs to be freed */
1166 unsigned char *aM; /* Wagner result matrix */
1167 int nMatch, iMatch; /* Number of matching lines and match score */
1168 int mnLen; /* MIN(nLeft, nRight) */
1169 int mxLen; /* MAX(nLeft, nRight) */
1170 int aBuf[100]; /* Stack space for a[] if nRight not to big */
1171
1172 aM = fossil_malloc( (nLeft+1)*(nRight+1) );
1173 if( nLeft==0 ){
1174 memset(aM, 2, nRight);
1175 return aM;
1176 }
1177 if( nRight==0 ){
1178 memset(aM, 1, nLeft);
1179 return aM;
1180 }
1181
1182 /* This algorithm is O(N**2). So if N is too big, bail out with a
1183 ** simple (but stupid and ugly) result that doesn't take too long. */
1184 mnLen = nLeft<nRight ? nLeft : nRight;
1185 if( nLeft*nRight>100000 ){
1186 memset(aM, 4, mnLen);
1187 if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1188 if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
1189 return aM;
1190 }
1191
1192 if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){
1193 pToFree = 0;
@@ -1106,30 +1196,30 @@
1196 a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) );
1197 }
1198
1199 /* Compute the best alignment */
1200 for(i=0; i<=nRight; i++){
1201 aM[i] = 2;
1202 a[i] = i*50;
1203 }
1204 aM[0] = 0;
1205 for(j=1; j<=nLeft; j++){
1206 int p = a[0];
1207 a[0] = p+50;
1208 aM[j*(nRight+1)] = 1;
1209 for(i=1; i<=nRight; i++){
1210 int m = a[i-1]+50;
1211 int d = 2;
1212 if( m>a[i]+50 ){
1213 m = a[i]+50;
1214 d = 1;
1215 }
1216 if( m>p ){
1217 int score = match_dline(&aLeft[j-1], &aRight[i-1]);
1218 if( (score<=63 || (i<j+1 && i>j-1)) && m>p+score ){
1219 m = p+score;
1220 d = 3 | score*4;
1221 }
1222 }
1223 p = a[i];
1224 a[i] = m;
1225 aM[j*(nRight+1)+i] = d;
@@ -1138,28 +1228,50 @@
1228
1229 /* Compute the lowest-cost path back through the matrix */
1230 i = nRight;
1231 j = nLeft;
1232 k = (nRight+1)*(nLeft+1)-1;
1233 nMatch = iMatch = 0;
1234 while( i+j>0 ){
1235 unsigned char c = aM[k];
1236 if( c>=3 ){
1237 assert( i>0 && j>0 );
1238 i--;
1239 j--;
1240 nMatch++;
1241 iMatch += (c>>2);
1242 aM[k] = 3;
1243 }else if( c==2 ){
1244 assert( i>0 );
1245 i--;
1246 }else{
1247 assert( j>0 );
1248 j--;
1249 }
1250 k--;
1251 aM[k] = aM[j*(nRight+1)+i];
1252 }
1253 k++;
1254 i = (nRight+1)*(nLeft+1) - k;
1255 memmove(aM, &aM[k], i);
1256
1257 /* If:
1258 ** (1) the alignment is more than 25% longer than the longest side, and
1259 ** (2) the average match cost exceeds 15
1260 ** Then this is probably an alignment that will be difficult for humans
1261 ** to read. So instead, just show all of the right side inserted followed
1262 ** by all of the left side deleted.
1263 **
1264 ** The coefficients for conditions (1) and (2) above are determined by
1265 ** experimentation.
1266 */
1267 mxLen = nLeft>nRight ? nLeft : nRight;
1268 if( i*4>mxLen*5 && (nMatch==0 || iMatch/nMatch>15) ){
1269 memset(aM, 4, mnLen);
1270 if( nLeft>mnLen ) memset(aM+mnLen, 1, nLeft-mnLen);
1271 if( nRight>mnLen ) memset(aM+mnLen, 2, nRight-mnLen);
1272 }
1273
1274 /* Return the result */
1275 fossil_free(pToFree);
1276 return aM;
1277 }
@@ -1179,13 +1291,12 @@
1291 ** in, compute a side-by-side diff into pOut.
1292 */
1293 static void sbsDiff(
1294 DContext *p, /* The computed diff */
1295 Blob *pOut, /* Write the results here */
1296 ReCompiled *pRe, /* Only show changes that match this regex */
1297 u64 diffFlags /* Flags controlling the diff */
 
1298 ){
1299 DLine *A; /* Left side of the diff */
1300 DLine *B; /* Right side of the diff */
1301 int a = 0; /* Index of next line in A[] */
1302 int b = 0; /* Index of next line in B[] */
@@ -1197,16 +1308,20 @@
1308 int i, j; /* Loop counters */
1309 int m, ma, mb;/* Number of lines to output */
1310 int skip; /* Number of lines to skip */
1311 int nChunk = 0; /* Number of chunks of diff output seen so far */
1312 SbsLine s; /* Output line buffer */
1313 int nContext; /* Lines of context above and below each change */
1314 int showDivider = 0; /* True to show the divider */
1315
1316 memset(&s, 0, sizeof(s));
1317 s.width = diff_width(diffFlags);
1318 s.zLine = fossil_malloc( 15*s.width + 200 );
1319 if( s.zLine==0 ) return;
1320 nContext = diff_context_lines(diffFlags);
1321 s.escHtml = (diffFlags & DIFF_HTML)!=0;
1322 s.pRe = pRe;
1323 s.iStart = -1;
1324 s.iStart2 = 0;
1325 s.iEnd = -1;
1326 A = p->aFrom;
1327 B = p->aTo;
@@ -1215,10 +1330,35 @@
1330 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
1331 for(r=0; r<mxr; r += 3*nr){
1332 /* Figure out how many triples to show in a single block */
1333 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
1334 /* printf("r=%d nr=%d\n", r, nr); */
1335
1336 /* If there is a regex, skip this block (generate no diff output)
1337 ** if the regex matches or does not match both insert and delete.
1338 ** Only display the block if one side matches but the other side does
1339 ** not.
1340 */
1341 if( pRe ){
1342 int hideBlock = 1;
1343 int xa = a, xb = b;
1344 for(i=0; hideBlock && i<nr; i++){
1345 int c1, c2;
1346 xa += R[r+i*3];
1347 xb += R[r+i*3];
1348 c1 = re_dline_match(pRe, &A[xa], R[r+i*3+1]);
1349 c2 = re_dline_match(pRe, &B[xb], R[r+i*3+2]);
1350 hideBlock = c1==c2;
1351 xa += R[r+i*3+1];
1352 xb += R[r+i*3+2];
1353 }
1354 if( hideBlock ){
1355 a = xa;
1356 b = xb;
1357 continue;
1358 }
1359 }
1360
1361 /* For the current block comprising nr triples, figure out
1362 ** how many lines of A and B are to be displayed
1363 */
1364 if( R[r]>nContext ){
@@ -1243,20 +1383,21 @@
1383 na += R[r+i*3];
1384 nb += R[r+i*3];
1385 }
1386
1387 /* Draw the separator between blocks */
1388 if( showDivider ){
1389 if( s.escHtml ){
1390 blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n",
1391 s.width*2+16, '.');
1392 }else{
1393 blob_appendf(pOut, "%.*c\n", s.width*2+16, '.');
1394 }
1395 }
1396 showDivider = 1;
1397 nChunk++;
1398 if( s.escHtml ){
1399 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk);
1400 }
1401
1402 /* Show the initial common area */
1403 a += skip;
@@ -1292,38 +1433,41 @@
1433 }
1434
1435 alignment = sbsAlignment(&A[a], ma, &B[b], mb);
1436 for(j=0; ma+mb>0; j++){
1437 if( alignment[j]==1 ){
1438 /* Delete one line from the left */
1439 s.n = 0;
1440 sbsWriteLineno(&s, a);
1441 s.iStart = 0;
1442 s.zStart = "<span class=\"diffrm\">";
1443 s.iEnd = s.width;
1444 sbsWriteText(&s, &A[a], SBS_PAD);
1445 if( s.escHtml ){
1446 sbsWrite(&s, " &lt;\n", 6);
1447 }else{
1448 sbsWrite(&s, " <\n", 3);
1449 }
1450 blob_append(pOut, s.zLine, s.n);
1451 assert( ma>0 );
1452 ma--;
1453 a++;
1454 }else if( alignment[j]==3 ){
1455 /* The left line is changed into the right line */
1456 s.n = 0;
1457 sbsWriteLineChange(&s, &A[a], a, &B[b], b);
1458 blob_append(pOut, s.zLine, s.n);
1459 assert( ma>0 && mb>0 );
1460 ma--;
1461 mb--;
1462 a++;
1463 b++;
1464 }else if( alignment[j]==2 ){
1465 /* Insert one line on the right */
1466 s.n = 0;
1467 sbsWriteSpace(&s, s.width + 7);
1468 if( s.escHtml ){
1469 sbsWrite(&s, " &gt; ", 6);
1470 }else{
1471 sbsWrite(&s, " > ", 3);
1472 }
1473 sbsWriteLineno(&s, b);
@@ -1333,11 +1477,31 @@
1477 sbsWriteText(&s, &B[b], SBS_NEWLINE);
1478 blob_append(pOut, s.zLine, s.n);
1479 assert( mb>0 );
1480 mb--;
1481 b++;
1482 }else{
1483 /* Delete from the left and insert on the right */
1484 s.n = 0;
1485 sbsWriteLineno(&s, a);
1486 s.iStart = 0;
1487 s.zStart = "<span class=\"diffrm\">";
1488 s.iEnd = s.width;
1489 sbsWriteText(&s, &A[a], SBS_PAD);
1490 sbsWrite(&s, " | ", 3);
1491 sbsWriteLineno(&s, b);
1492 s.iStart = 0;
1493 s.zStart = "<span class=\"diffadd\">";
1494 s.iEnd = s.width;
1495 sbsWriteText(&s, &B[b], SBS_NEWLINE);
1496 blob_append(pOut, s.zLine, s.n);
1497 ma--;
1498 mb--;
1499 a++;
1500 b++;
1501 }
1502
1503 }
1504 fossil_free(alignment);
1505 if( i<nr-1 ){
1506 m = R[r+i*3+3];
1507 for(j=0; j<m; j++){
@@ -1761,11 +1925,11 @@
1925 ** Extract the number of lines of context from diffFlags. Supply an
1926 ** appropriate default if no context width is specified.
1927 */
1928 int diff_context_lines(u64 diffFlags){
1929 int n = diffFlags & DIFF_CONTEXT_MASK;
1930 if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5;
1931 return n;
1932 }
1933
1934 /*
1935 ** Extract the width of columns for side-by-side diff. Supply an
@@ -1793,22 +1957,21 @@
1957 */
1958 int *text_diff(
1959 Blob *pA_Blob, /* FROM file */
1960 Blob *pB_Blob, /* TO file */
1961 Blob *pOut, /* Write diff here if not NULL */
1962 ReCompiled *pRe, /* Only output changes where this Regexp matches */
1963 u64 diffFlags /* DIFF_* flags defined above */
1964 ){
1965 int ignoreEolWs; /* Ignore whitespace at the end of lines */
 
1966 DContext c;
1967
1968 if( diffFlags & DIFF_INVERT ){
1969 Blob *pTemp = pA_Blob;
1970 pA_Blob = pB_Blob;
1971 pB_Blob = pTemp;
1972 }
 
1973 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1974
1975 /* Prepare the input files */
1976 memset(&c, 0, sizeof(c));
1977 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
@@ -1828,17 +1991,14 @@
1991 diff_all(&c);
1992 if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c);
1993
1994 if( pOut ){
1995 /* Compute a context or side-by-side diff into pOut */
 
1996 if( diffFlags & DIFF_SIDEBYSIDE ){
1997 sbsDiff(&c, pOut, pRe, diffFlags);
 
1998 }else{
1999 contextDiff(&c, pOut, pRe, diffFlags);
 
2000 }
2001 fossil_free(c.aFrom);
2002 fossil_free(c.aTo);
2003 fossil_free(c.aEdit);
2004 return 0;
@@ -1864,19 +2024,19 @@
2024 ** --noopt Disable optimization DIFF_NOOPT
2025 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
2026 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
2027 ** --width|-W N N character lines. DIFF_WIDTH_MASK
2028 */
2029 u64 diff_options(void){
2030 u64 diffFlags = 0;
2031 const char *z;
2032 int f;
2033 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
2034 if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
2035 if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
2036 if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
2037 diffFlags |= f + DIFF_CONTEXT_EX;
2038 }
2039 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
2040 f *= DIFF_CONTEXT_MASK+1;
2041 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
2042 diffFlags |= f;
@@ -1901,34 +2061,52 @@
2061 if( g.argc<4 ) usage("FILE1 FILE2 ...");
2062 blob_read_from_file(&a, g.argv[2]);
2063 for(i=3; i<g.argc; i++){
2064 if( i>3 ) fossil_print("-------------------------------\n");
2065 blob_read_from_file(&b, g.argv[i]);
2066 R = text_diff(&a, &b, 0, 0, diffFlags);
2067 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
2068 fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
2069 }
2070 /* free(R); */
2071 blob_reset(&b);
2072 }
2073 }
2074
2075 /*
2076 ** COMMAND: test-diff
2077 **
2078 ** Usage: %fossil [options] FILE1 FILE2
2079 **
2080 ** Print the difference between two files. The usual diff options apply.
2081 */
2082 void test_diff_cmd(void){
2083 Blob a, b, out;
2084 u64 diffFlag;
2085 const char *zRe; /* Regex filter for diff output */
2086 ReCompiled *pRe = 0; /* Regex filter for diff output */
2087
2088 if( find_option("tk",0,0)!=0 ){
2089 diff_tk("test-diff", 2);
2090 return;
2091 }
2092 find_option("i",0,0);
2093 zRe = find_option("regexp","e",1);
2094 if( zRe ){
2095 const char *zErr = re_compile(&pRe, zRe, 0);
2096 if( zErr ) fossil_fatal("regex error: %s", zErr);
2097 }
2098 diffFlag = diff_options();
2099 verify_all_options();
2100 if( g.argc!=4 ) usage("FILE1 FILE2");
2101 diff_print_filenames(g.argv[2], g.argv[3], diffFlag);
2102 blob_read_from_file(&a, g.argv[2]);
2103 blob_read_from_file(&b, g.argv[3]);
2104 blob_zero(&out);
2105 text_diff(&a, &b, &out, pRe, diffFlag);
2106 blob_write_to_file(&out, "-");
2107 re_free(pRe);
2108 }
2109
2110 /**************************************************************************
2111 ** The basic difference engine is above. What follows is the annotation
2112 ** engine. Both are in the same file since they share many components.
2113
+9 -3
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -109,11 +109,11 @@
109109
if( blob_compare(pFile1, &file2) ){
110110
fossil_print("CHANGED %s\n", zName);
111111
}
112112
}else{
113113
blob_zero(&out);
114
- text_diff(pFile1, &file2, &out, diffFlags);
114
+ text_diff(pFile1, &file2, &out, 0, diffFlags);
115115
if( blob_size(&out) ){
116116
diff_print_filenames(zName, zName2, diffFlags);
117117
fossil_print("%s\n", blob_str(&out));
118118
}
119119
blob_reset(&out);
@@ -210,11 +210,11 @@
210210
if( diffFlags & DIFF_BRIEF ) return;
211211
if( zDiffCmd==0 ){
212212
Blob out; /* Diff output text */
213213
214214
blob_zero(&out);
215
- text_diff(pFile1, pFile2, &out, diffFlags);
215
+ text_diff(pFile1, pFile2, &out, 0, diffFlags);
216216
diff_print_filenames(zName, zName, diffFlags);
217217
fossil_print("%s\n", blob_str(&out));
218218
219219
/* Release memory resources */
220220
blob_reset(&out);
@@ -659,12 +659,18 @@
659659
char *zCmd;
660660
blob_zero(&script);
661661
blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i",
662662
g.nameOfExe, zSubCmd);
663663
for(i=firstArg; i<g.argc; i++){
664
+ const char *z = g.argv[i];
665
+ if( z[0]=='-' ){
666
+ if( strglob("*-html",z) ) continue;
667
+ if( strglob("*-y",z) ) continue;
668
+ if( strglob("*-i",z) ) continue;
669
+ }
664670
blob_append(&script, " ", 1);
665
- shell_escape(&script, g.argv[i]);
671
+ shell_escape(&script, z);
666672
}
667673
blob_appendf(&script, "}\n%s", zDiffScript);
668674
zTempFile = write_blob_to_temp_file(&script);
669675
zCmd = mprintf("tclsh \"%s\"", zTempFile);
670676
fossil_system(zCmd);
671677
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -109,11 +109,11 @@
109 if( blob_compare(pFile1, &file2) ){
110 fossil_print("CHANGED %s\n", zName);
111 }
112 }else{
113 blob_zero(&out);
114 text_diff(pFile1, &file2, &out, diffFlags);
115 if( blob_size(&out) ){
116 diff_print_filenames(zName, zName2, diffFlags);
117 fossil_print("%s\n", blob_str(&out));
118 }
119 blob_reset(&out);
@@ -210,11 +210,11 @@
210 if( diffFlags & DIFF_BRIEF ) return;
211 if( zDiffCmd==0 ){
212 Blob out; /* Diff output text */
213
214 blob_zero(&out);
215 text_diff(pFile1, pFile2, &out, diffFlags);
216 diff_print_filenames(zName, zName, diffFlags);
217 fossil_print("%s\n", blob_str(&out));
218
219 /* Release memory resources */
220 blob_reset(&out);
@@ -659,12 +659,18 @@
659 char *zCmd;
660 blob_zero(&script);
661 blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i",
662 g.nameOfExe, zSubCmd);
663 for(i=firstArg; i<g.argc; i++){
 
 
 
 
 
 
664 blob_append(&script, " ", 1);
665 shell_escape(&script, g.argv[i]);
666 }
667 blob_appendf(&script, "}\n%s", zDiffScript);
668 zTempFile = write_blob_to_temp_file(&script);
669 zCmd = mprintf("tclsh \"%s\"", zTempFile);
670 fossil_system(zCmd);
671
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -109,11 +109,11 @@
109 if( blob_compare(pFile1, &file2) ){
110 fossil_print("CHANGED %s\n", zName);
111 }
112 }else{
113 blob_zero(&out);
114 text_diff(pFile1, &file2, &out, 0, diffFlags);
115 if( blob_size(&out) ){
116 diff_print_filenames(zName, zName2, diffFlags);
117 fossil_print("%s\n", blob_str(&out));
118 }
119 blob_reset(&out);
@@ -210,11 +210,11 @@
210 if( diffFlags & DIFF_BRIEF ) return;
211 if( zDiffCmd==0 ){
212 Blob out; /* Diff output text */
213
214 blob_zero(&out);
215 text_diff(pFile1, pFile2, &out, 0, diffFlags);
216 diff_print_filenames(zName, zName, diffFlags);
217 fossil_print("%s\n", blob_str(&out));
218
219 /* Release memory resources */
220 blob_reset(&out);
@@ -659,12 +659,18 @@
659 char *zCmd;
660 blob_zero(&script);
661 blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i",
662 g.nameOfExe, zSubCmd);
663 for(i=firstArg; i<g.argc; i++){
664 const char *z = g.argv[i];
665 if( z[0]=='-' ){
666 if( strglob("*-html",z) ) continue;
667 if( strglob("*-y",z) ) continue;
668 if( strglob("*-i",z) ) continue;
669 }
670 blob_append(&script, " ", 1);
671 shell_escape(&script, z);
672 }
673 blob_appendf(&script, "}\n%s", zDiffScript);
674 zTempFile = write_blob_to_temp_file(&script);
675 zCmd = mprintf("tclsh \"%s\"", zTempFile);
676 fossil_system(zCmd);
677
+2 -2
--- src/doc.c
+++ src/doc.c
@@ -380,15 +380,15 @@
380380
g.zPath = mprintf("%s/%s", g.zPath, zName);
381381
memcpy(zBaseline, zName, i);
382382
zBaseline[i] = 0;
383383
zName += i;
384384
while( zName[0]=='/' ){ zName++; }
385
- if( !file_is_simple_pathname(zName) ){
385
+ if( !file_is_simple_pathname(zName, 1) ){
386386
int n = strlen(zName);
387387
if( n>0 && zName[n-1]=='/' ){
388388
zName = mprintf("%sindex.html", zName);
389
- if( !file_is_simple_pathname(zName) ){
389
+ if( !file_is_simple_pathname(zName, 1) ){
390390
goto doc_not_found;
391391
}
392392
}else{
393393
goto doc_not_found;
394394
}
395395
--- src/doc.c
+++ src/doc.c
@@ -380,15 +380,15 @@
380 g.zPath = mprintf("%s/%s", g.zPath, zName);
381 memcpy(zBaseline, zName, i);
382 zBaseline[i] = 0;
383 zName += i;
384 while( zName[0]=='/' ){ zName++; }
385 if( !file_is_simple_pathname(zName) ){
386 int n = strlen(zName);
387 if( n>0 && zName[n-1]=='/' ){
388 zName = mprintf("%sindex.html", zName);
389 if( !file_is_simple_pathname(zName) ){
390 goto doc_not_found;
391 }
392 }else{
393 goto doc_not_found;
394 }
395
--- src/doc.c
+++ src/doc.c
@@ -380,15 +380,15 @@
380 g.zPath = mprintf("%s/%s", g.zPath, zName);
381 memcpy(zBaseline, zName, i);
382 zBaseline[i] = 0;
383 zName += i;
384 while( zName[0]=='/' ){ zName++; }
385 if( !file_is_simple_pathname(zName, 1) ){
386 int n = strlen(zName);
387 if( n>0 && zName[n-1]=='/' ){
388 zName = mprintf("%sindex.html", zName);
389 if( !file_is_simple_pathname(zName, 1) ){
390 goto doc_not_found;
391 }
392 }else{
393 goto doc_not_found;
394 }
395
+2 -3
--- src/encode.c
+++ src/encode.c
@@ -131,18 +131,17 @@
131131
static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
132132
int c;
133133
int i = 0;
134134
int count = 0;
135135
char *zOut;
136
- int other;
137136
# define IsSafeChar(X) \
138137
(fossil_isalnum(X) || (X)=='.' || (X)=='$' \
139
- || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other)
138
+ || (X)=='~' || (X)=='-' || (X)=='_' \
139
+ || (!encodeSlash && ((X)=='/' || (X)==':')))
140140
141141
if( zIn==0 ) return 0;
142142
if( n<0 ) n = strlen(zIn);
143
- other = encodeSlash ? 'a' : '/';
144143
while( i<n && (c = zIn[i])!=0 ){
145144
if( IsSafeChar(c) || c==' ' ){
146145
count++;
147146
}else{
148147
count += 3;
149148
--- src/encode.c
+++ src/encode.c
@@ -131,18 +131,17 @@
131 static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
132 int c;
133 int i = 0;
134 int count = 0;
135 char *zOut;
136 int other;
137 # define IsSafeChar(X) \
138 (fossil_isalnum(X) || (X)=='.' || (X)=='$' \
139 || (X)=='~' || (X)=='-' || (X)=='_' || (X)==other)
 
140
141 if( zIn==0 ) return 0;
142 if( n<0 ) n = strlen(zIn);
143 other = encodeSlash ? 'a' : '/';
144 while( i<n && (c = zIn[i])!=0 ){
145 if( IsSafeChar(c) || c==' ' ){
146 count++;
147 }else{
148 count += 3;
149
--- src/encode.c
+++ src/encode.c
@@ -131,18 +131,17 @@
131 static char *EncodeHttp(const char *zIn, int n, int encodeSlash){
132 int c;
133 int i = 0;
134 int count = 0;
135 char *zOut;
 
136 # define IsSafeChar(X) \
137 (fossil_isalnum(X) || (X)=='.' || (X)=='$' \
138 || (X)=='~' || (X)=='-' || (X)=='_' \
139 || (!encodeSlash && ((X)=='/' || (X)==':')))
140
141 if( zIn==0 ) return 0;
142 if( n<0 ) n = strlen(zIn);
 
143 while( i<n && (c = zIn[i])!=0 ){
144 if( IsSafeChar(c) || c==' ' ){
145 count++;
146 }else{
147 count += 3;
148
+29 -27
--- src/file.c
+++ src/file.c
@@ -487,46 +487,48 @@
487487
** * Does not contain any path element named "." or ".."
488488
** * Does not contain any of these characters in the path: "\"
489489
** * Does not end with "/".
490490
** * Does not contain two or more "/" characters in a row.
491491
** * Contains at least one character
492
+**
493
+** Invalid UTF8 characters result in a false return if bStrictUtf8 is
494
+** true. If bStrictUtf8 is false, invalid UTF8 characters are silently
495
+** ignored.
492496
*/
493
-int file_is_simple_pathname(const char *z){
497
+int file_is_simple_pathname(const char *z, int bStrictUtf8){
494498
int i;
495499
char c = z[0];
500
+ char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00;
496501
if( c=='/' || c==0 ) return 0;
497502
if( c=='.' ){
498503
if( z[1]=='/' || z[1]==0 ) return 0;
499504
if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
500505
}
501506
for(i=0; (c=z[i])!=0; i++){
502
- if( (c & 0xf0) == 0xf0 ) {
503
- /* Unicode characters > U+FFFF are not supported.
504
- * Windows XP and earlier cannot handle them.
505
- */
506
- return 0;
507
- }
508
- if( (c & 0xf0) == 0xe0 ) {
509
- /* This is a 3-byte UTF-8 character */
510
- if ( (c & 0xfe) == 0xee ){
511
- /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */
512
- if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){
513
- /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4),
514
- * which contain valid characters. */
515
- continue;
516
- }
517
- /* Unicode character in the range U+E000 - U+F8FF are for
518
- * private use, they shouldn't occur in filenames. */
519
- return 0;
520
- }
521
- if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){
522
- /* Unicode character in the range U+D800 - U+DFFF are for
523
- * surrogate pairs, they shouldn't occur in filenames. */
524
- return 0;
525
- }
526
- }
527
- if( c=='\\' ){
507
+ if( c & maskNonAscii ){
508
+ if( (c & 0xf0) == 0xf0 ) {
509
+ /* Unicode characters > U+FFFF are not supported.
510
+ * Windows XP and earlier cannot handle them.
511
+ */
512
+ return 0;
513
+ }
514
+ if( (c & 0xf0) == 0xe0 ) {
515
+ /* This is a 3-byte UTF-8 character */
516
+ if ( (c & 0xfe) == 0xee ){
517
+ /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */
518
+ if ( !(c & 1) || ((z[i+1] & 0xff) < 0xa4) ){
519
+ /* Unicode character in the range U+E000 - U+F8FF are for
520
+ * private use, they shouldn't occur in filenames. */
521
+ return 0;
522
+ }
523
+ }else if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){
524
+ /* Unicode character in the range U+D800 - U+DFFF are for
525
+ * surrogate pairs, they shouldn't occur in filenames. */
526
+ return 0;
527
+ }
528
+ }
529
+ }else if( c=='\\' ){
528530
return 0;
529531
}
530532
if( c=='/' ){
531533
if( z[i+1]=='/' ) return 0;
532534
if( z[i+1]=='.' ){
533535
--- src/file.c
+++ src/file.c
@@ -487,46 +487,48 @@
487 ** * Does not contain any path element named "." or ".."
488 ** * Does not contain any of these characters in the path: "\"
489 ** * Does not end with "/".
490 ** * Does not contain two or more "/" characters in a row.
491 ** * Contains at least one character
 
 
 
 
492 */
493 int file_is_simple_pathname(const char *z){
494 int i;
495 char c = z[0];
 
496 if( c=='/' || c==0 ) return 0;
497 if( c=='.' ){
498 if( z[1]=='/' || z[1]==0 ) return 0;
499 if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
500 }
501 for(i=0; (c=z[i])!=0; i++){
502 if( (c & 0xf0) == 0xf0 ) {
503 /* Unicode characters > U+FFFF are not supported.
504 * Windows XP and earlier cannot handle them.
505 */
506 return 0;
507 }
508 if( (c & 0xf0) == 0xe0 ) {
509 /* This is a 3-byte UTF-8 character */
510 if ( (c & 0xfe) == 0xee ){
511 /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */
512 if ( (c & 1) && ((z[i+1] & 0xff) >= 0xa4) ){
513 /* But exclude U+F900 - U+FFFF (0xef followed by byte >= 0xa4),
514 * which contain valid characters. */
515 continue;
516 }
517 /* Unicode character in the range U+E000 - U+F8FF are for
518 * private use, they shouldn't occur in filenames. */
519 return 0;
520 }
521 if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){
522 /* Unicode character in the range U+D800 - U+DFFF are for
523 * surrogate pairs, they shouldn't occur in filenames. */
524 return 0;
525 }
526 }
527 if( c=='\\' ){
528 return 0;
529 }
530 if( c=='/' ){
531 if( z[i+1]=='/' ) return 0;
532 if( z[i+1]=='.' ){
533
--- src/file.c
+++ src/file.c
@@ -487,46 +487,48 @@
487 ** * Does not contain any path element named "." or ".."
488 ** * Does not contain any of these characters in the path: "\"
489 ** * Does not end with "/".
490 ** * Does not contain two or more "/" characters in a row.
491 ** * Contains at least one character
492 **
493 ** Invalid UTF8 characters result in a false return if bStrictUtf8 is
494 ** true. If bStrictUtf8 is false, invalid UTF8 characters are silently
495 ** ignored.
496 */
497 int file_is_simple_pathname(const char *z, int bStrictUtf8){
498 int i;
499 char c = z[0];
500 char maskNonAscii = bStrictUtf8 ? 0x80 : 0x00;
501 if( c=='/' || c==0 ) return 0;
502 if( c=='.' ){
503 if( z[1]=='/' || z[1]==0 ) return 0;
504 if( z[1]=='.' && (z[2]=='/' || z[2]==0) ) return 0;
505 }
506 for(i=0; (c=z[i])!=0; i++){
507 if( c & maskNonAscii ){
508 if( (c & 0xf0) == 0xf0 ) {
509 /* Unicode characters > U+FFFF are not supported.
510 * Windows XP and earlier cannot handle them.
511 */
512 return 0;
513 }
514 if( (c & 0xf0) == 0xe0 ) {
515 /* This is a 3-byte UTF-8 character */
516 if ( (c & 0xfe) == 0xee ){
517 /* Range U+E000 - U+FFFF (Starting with 0xee or 0xef in UTF-8 ) */
518 if ( !(c & 1) || ((z[i+1] & 0xff) < 0xa4) ){
519 /* Unicode character in the range U+E000 - U+F8FF are for
520 * private use, they shouldn't occur in filenames. */
521 return 0;
522 }
523 }else if( ((c & 0xff) == 0xed) && ((z[i+1] & 0xe0) == 0xa0) ){
524 /* Unicode character in the range U+D800 - U+DFFF are for
525 * surrogate pairs, they shouldn't occur in filenames. */
526 return 0;
527 }
528 }
529 }else if( c=='\\' ){
 
 
 
530 return 0;
531 }
532 if( c=='/' ){
533 if( z[i+1]=='/' ) return 0;
534 if( z[i+1]=='.' ){
535
--- src/http_socket.c
+++ src/http_socket.c
@@ -27,14 +27,12 @@
2727
*/
2828
2929
#include "config.h"
3030
#include "http_socket.h"
3131
#if defined(_WIN32)
32
-# if defined(__MINGW32__)
33
-# include <ws2tcpip.h>
34
-# endif
35
-# include <windows.h>
32
+# include <winsock2.h>
33
+# include <ws2tcpip.h>
3634
#else
3735
# include <netinet/in.h>
3836
# include <arpa/inet.h>
3937
# include <sys/socket.h>
4038
# include <netdb.h>
4139
--- src/http_socket.c
+++ src/http_socket.c
@@ -27,14 +27,12 @@
27 */
28
29 #include "config.h"
30 #include "http_socket.h"
31 #if defined(_WIN32)
32 # if defined(__MINGW32__)
33 # include <ws2tcpip.h>
34 # endif
35 # include <windows.h>
36 #else
37 # include <netinet/in.h>
38 # include <arpa/inet.h>
39 # include <sys/socket.h>
40 # include <netdb.h>
41
--- src/http_socket.c
+++ src/http_socket.c
@@ -27,14 +27,12 @@
27 */
28
29 #include "config.h"
30 #include "http_socket.h"
31 #if defined(_WIN32)
32 # include <winsock2.h>
33 # include <ws2tcpip.h>
 
 
34 #else
35 # include <netinet/in.h>
36 # include <arpa/inet.h>
37 # include <sys/socket.h>
38 # include <netdb.h>
39
+4 -10
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -1,21 +1,15 @@
11
/*
22
** Copyright (c) 2009 D. Richard Hipp
33
**
44
** This program is free software; you can redistribute it and/or
5
-** modify it under the terms of the GNU General Public
6
-** License version 2 as published by the Free Software Foundation.
5
+** modify it under the terms of the Simplified BSD License (also
6
+** known as the "2-Clause License" or "FreeBSD License".)
77
**
88
** This program is distributed in the hope that it will be useful,
9
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
10
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
-** General Public License for more details.
12
-**
13
-** You should have received a copy of the GNU General Public
14
-** License along with this library; if not, write to the
15
-** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16
-** Boston, MA 02111-1307, USA.
9
+** but without any warranty; without even the implied warranty of
10
+** merchantability or fitness for a particular purpose.
1711
**
1812
** Author contact information:
1913
** [email protected]
2014
** http://www.hwaci.com/drh/
2115
**
2216
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -1,21 +1,15 @@
1 /*
2 ** Copyright (c) 2009 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU General Public
6 ** License version 2 as published by the Free Software Foundation.
7 **
8 ** This program is distributed in the hope that it will be useful,
9 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
10 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 ** General Public License for more details.
12 **
13 ** You should have received a copy of the GNU General Public
14 ** License along with this library; if not, write to the
15 ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 ** Boston, MA 02111-1307, USA.
17 **
18 ** Author contact information:
19 ** [email protected]
20 ** http://www.hwaci.com/drh/
21 **
22
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -1,21 +1,15 @@
1 /*
2 ** Copyright (c) 2009 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 **
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
 
 
 
 
 
 
11 **
12 ** Author contact information:
13 ** [email protected]
14 ** http://www.hwaci.com/drh/
15 **
16
+50 -15
--- src/info.c
+++ src/info.c
@@ -208,10 +208,12 @@
208208
fossil_print("project-code: %s\n", db_get("project-code", ""));
209209
vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
210210
if( vid ){
211211
show_common_info(vid, "checkout:", 1, 1);
212212
}
213
+ fossil_print("checkins: %d\n",
214
+ db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/"));
213215
}else{
214216
int rid;
215217
rid = name_to_rid(g.argv[2]);
216218
if( rid==0 ){
217219
fossil_panic("no such object: %s\n", g.argv[2]);
@@ -287,13 +289,18 @@
287289
}
288290
}
289291
290292
291293
/*
292
-** Append the difference between two RIDs to the output
294
+** Append the difference between artifacts to the output
293295
*/
294
-static void append_diff(const char *zFrom, const char *zTo, u64 diffFlags){
296
+static void append_diff(
297
+ const char *zFrom, /* Diff from this artifact */
298
+ const char *zTo, /* ... to this artifact */
299
+ u64 diffFlags, /* Diff formatting flags */
300
+ ReCompiled *pRe /* Only show change matching this regex */
301
+){
295302
int fromid;
296303
int toid;
297304
Blob from, to, out;
298305
if( zFrom ){
299306
fromid = uuid_to_rid(zFrom, 0);
@@ -307,16 +314,16 @@
307314
}else{
308315
blob_zero(&to);
309316
}
310317
blob_zero(&out);
311318
if( diffFlags & DIFF_SIDEBYSIDE ){
312
- text_diff(&from, &to, &out, diffFlags | DIFF_HTML);
319
+ text_diff(&from, &to, &out, pRe, diffFlags | DIFF_HTML);
313320
@ <div class="sbsdiff">
314321
@ %s(blob_str(&out))
315322
@ </div>
316323
}else{
317
- text_diff(&from, &to, &out, diffFlags | DIFF_LINENO | DIFF_HTML);
324
+ text_diff(&from, &to, &out, pRe, diffFlags | DIFF_LINENO | DIFF_HTML);
318325
@ <div class="udiff">
319326
@ %s(blob_str(&out))
320327
@ </div>
321328
}
322329
blob_reset(&from);
@@ -333,10 +340,11 @@
333340
const char *zName, /* Name of the file that has changed */
334341
const char *zOld, /* blob.uuid before change. NULL for added files */
335342
const char *zNew, /* blob.uuid after change. NULL for deletes */
336343
const char *zOldName, /* Prior name. NULL if no name change. */
337344
u64 diffFlags, /* Flags for text_diff(). Zero to omit diffs */
345
+ ReCompiled *pRe, /* Only show diffs that match this regex, if not NULL */
338346
int mperm /* executable or symlink permission for zNew */
339347
){
340348
if( !g.perm.Hyperlink ){
341349
if( zNew==0 ){
342350
@ <p>Deleted %h(zName)</p>
@@ -350,21 +358,21 @@
350358
}else{
351359
@ <p>Changes to %h(zName)</p>
352360
}
353361
if( diffFlags ){
354362
@ <pre style="white-space:pre;">
355
- append_diff(zOld, zNew, diffFlags);
363
+ append_diff(zOld, zNew, diffFlags, pRe);
356364
@ </pre>
357365
}
358366
}else{
359367
if( zOld && zNew ){
360368
if( fossil_strcmp(zOld, zNew)!=0 ){
361369
@ <p>Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
362370
@ from %z(href("%R/artifact/%s",zOld))[%S(zOld)]</a>
363371
@ to %z(href("%R/artifact/%s",zNew))[%S(zNew)].</a>
364372
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
365
- @ <p>Name change from
373
+ @ <p>Name change
366374
@ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a>
367375
@ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>.
368376
}else{
369377
@ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for
370378
@ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
@@ -376,11 +384,11 @@
376384
@ <p>Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
377385
@ version %z(href("%R/artifact/%s",zNew))[%S(zNew)]</a>
378386
}
379387
if( diffFlags ){
380388
@ <pre style="white-space:pre;">
381
- append_diff(zOld, zNew, diffFlags);
389
+ append_diff(zOld, zNew, diffFlags, pRe);
382390
@ </pre>
383391
}else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
384392
@ &nbsp;&nbsp;
385393
@ %z(href("%R/fdiff?v1=%S&v2=%S",zOld,zNew))[diff]</a>
386394
}
@@ -444,10 +452,12 @@
444452
int sideBySide; /* True for side-by-side diffs */
445453
u64 diffFlags; /* Flag parameter for text_diff() */
446454
const char *zName; /* Name of the checkin to be displayed */
447455
const char *zUuid; /* UUID of zName */
448456
const char *zParent; /* UUID of the parent checkin (if any) */
457
+ const char *zRe; /* regex parameter */
458
+ ReCompiled *pRe = 0; /* regex */
449459
450460
login_check_credentials();
451461
if( !g.perm.Read ){ login_needed(); return; }
452462
zName = P("name");
453463
rid = name_to_rid_www("name");
@@ -455,10 +465,12 @@
455465
style_header("Check-in Information Error");
456466
@ No such object: %h(g.argv[2])
457467
style_footer();
458468
return;
459469
}
470
+ zRe = P("regex");
471
+ if( zRe ) re_compile(&pRe, zRe, 0);
460472
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
461473
zParent = db_text(0,
462474
"SELECT uuid FROM plink, blob"
463475
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
464476
rid
@@ -692,29 +704,35 @@
692704
@ show&nbsp;side-by-side&nbsp;diffs</a>
693705
}
694706
}
695707
@ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
696708
@ patch</a></div>
709
+ if( pRe ){
710
+ @ <p><b>Only differences that match regular expression "%h(zRe)"
711
+ @ are shown.</b></p>
712
+ }
697713
db_prepare(&q,
698714
"SELECT name,"
699715
" mperm,"
700716
" (SELECT uuid FROM blob WHERE rid=mlink.pid),"
701717
" (SELECT uuid FROM blob WHERE rid=mlink.fid),"
702718
" (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)"
703719
" FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
704720
" WHERE mlink.mid=%d"
721
+ " AND (mlink.fid>0"
722
+ " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))"
705723
" ORDER BY name /*sort*/",
706
- rid
724
+ rid, rid
707725
);
708726
diffFlags = construct_diff_flags(showDiff, sideBySide);
709727
while( db_step(&q)==SQLITE_ROW ){
710728
const char *zName = db_column_text(&q,0);
711729
int mperm = db_column_int(&q, 1);
712730
const char *zOld = db_column_text(&q,2);
713731
const char *zNew = db_column_text(&q,3);
714732
const char *zOldName = db_column_text(&q, 4);
715
- append_file_change_line(zName, zOld, zNew, zOldName, diffFlags, mperm);
733
+ append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
716734
}
717735
db_finalize(&q);
718736
}
719737
style_footer();
720738
}
@@ -933,15 +951,19 @@
933951
Manifest *pFrom, *pTo;
934952
ManifestFile *pFileFrom, *pFileTo;
935953
const char *zBranch;
936954
const char *zFrom;
937955
const char *zTo;
956
+ const char *zRe;
957
+ ReCompiled *pRe = 0;
938958
939959
login_check_credentials();
940960
if( !g.perm.Read ){ login_needed(); return; }
941961
login_anonymous_available();
942962
963
+ zRe = P("regex");
964
+ if( zRe ) re_compile(&pRe, zRe, 0);
943965
zBranch = P("branch");
944966
if( zBranch && zBranch[0] ){
945967
cgi_replace_parameter("from", mprintf("root:%s", zBranch));
946968
cgi_replace_parameter("to", zBranch);
947969
}
@@ -969,11 +991,16 @@
969991
style_header("Check-in Differences");
970992
@ <h2>Difference From:</h2><blockquote>
971993
checkin_description(ridFrom);
972994
@ </blockquote><h2>To:</h2><blockquote>
973995
checkin_description(ridTo);
974
- @ </blockquote><hr /><p>
996
+ @ </blockquote>
997
+ if( pRe ){
998
+ @ <p><b>Only differences that match regular expression "%h(zRe)"
999
+ @ are shown.</b></p>
1000
+ }
1001
+ @<hr /><p>
9751002
9761003
manifest_file_rewind(pFrom);
9771004
pFileFrom = manifest_file_next(pFrom, 0);
9781005
manifest_file_rewind(pTo);
9791006
pFileTo = manifest_file_next(pTo, 0);
@@ -987,25 +1014,25 @@
9871014
}else{
9881015
cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
9891016
}
9901017
if( cmp<0 ){
9911018
append_file_change_line(pFileFrom->zName,
992
- pFileFrom->zUuid, 0, 0, diffFlags, 0);
1019
+ pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0);
9931020
pFileFrom = manifest_file_next(pFrom, 0);
9941021
}else if( cmp>0 ){
9951022
append_file_change_line(pFileTo->zName,
996
- 0, pFileTo->zUuid, 0, diffFlags,
1023
+ 0, pFileTo->zUuid, 0, diffFlags, pRe,
9971024
manifest_file_mperm(pFileTo));
9981025
pFileTo = manifest_file_next(pTo, 0);
9991026
}else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
10001027
/* No changes */
10011028
pFileFrom = manifest_file_next(pFrom, 0);
10021029
pFileTo = manifest_file_next(pTo, 0);
10031030
}else{
10041031
append_file_change_line(pFileFrom->zName,
10051032
pFileFrom->zUuid,
1006
- pFileTo->zUuid, 0, diffFlags,
1033
+ pFileTo->zUuid, 0, diffFlags, pRe,
10071034
manifest_file_mperm(pFileTo));
10081035
pFileFrom = manifest_file_next(pFrom, 0);
10091036
pFileTo = manifest_file_next(pTo, 0);
10101037
}
10111038
}
@@ -1254,11 +1281,11 @@
12541281
}
12551282
12561283
12571284
/*
12581285
** WEBPAGE: fdiff
1259
-** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN
1286
+** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN&regex=REGEX
12601287
**
12611288
** Two arguments, v1 and v2, identify the files to be diffed. Show the
12621289
** difference between the two artifacts. Show diff side by side unless sbs
12631290
** is 0. Generate plaintext if "patch" is present.
12641291
*/
@@ -1267,10 +1294,12 @@
12671294
int isPatch;
12681295
int sideBySide;
12691296
Blob c1, c2, diff, *pOut;
12701297
char *zV1;
12711298
char *zV2;
1299
+ const char *zRe;
1300
+ ReCompiled *pRe = 0;
12721301
u64 diffFlags;
12731302
const char *zStyle = "sbsdiff";
12741303
12751304
login_check_credentials();
12761305
if( !g.perm.Read ){ login_needed(); return; }
@@ -1294,13 +1323,15 @@
12941323
}else{
12951324
diffFlags |= DIFF_LINENO;
12961325
zStyle = "udiff";
12971326
}
12981327
}
1328
+ zRe = P("regex");
1329
+ if( zRe ) re_compile(&pRe, zRe, 0);
12991330
content_get(v1, &c1);
13001331
content_get(v2, &c2);
1301
- text_diff(&c1, &c2, pOut, diffFlags);
1332
+ text_diff(&c1, &c2, pOut, pRe, diffFlags);
13021333
blob_reset(&c1);
13031334
blob_reset(&c2);
13041335
if( !isPatch ){
13051336
style_header("Diff");
13061337
style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
@@ -1324,10 +1355,14 @@
13241355
@ Artifact %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
13251356
object_description(v1, 0, 0);
13261357
@ <h2>To Artifact %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
13271358
object_description(v2, 0, 0);
13281359
}
1360
+ if( pRe ){
1361
+ @ <b>Only differences that match regular expression "%h(zRe)"
1362
+ @ are shown.</b>
1363
+ }
13291364
@ <hr />
13301365
@ <div class="%s(zStyle)">
13311366
@ %s(blob_str(&diff))
13321367
@ </div>
13331368
blob_reset(&diff);
13341369
--- src/info.c
+++ src/info.c
@@ -208,10 +208,12 @@
208 fossil_print("project-code: %s\n", db_get("project-code", ""));
209 vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
210 if( vid ){
211 show_common_info(vid, "checkout:", 1, 1);
212 }
 
 
213 }else{
214 int rid;
215 rid = name_to_rid(g.argv[2]);
216 if( rid==0 ){
217 fossil_panic("no such object: %s\n", g.argv[2]);
@@ -287,13 +289,18 @@
287 }
288 }
289
290
291 /*
292 ** Append the difference between two RIDs to the output
293 */
294 static void append_diff(const char *zFrom, const char *zTo, u64 diffFlags){
 
 
 
 
 
295 int fromid;
296 int toid;
297 Blob from, to, out;
298 if( zFrom ){
299 fromid = uuid_to_rid(zFrom, 0);
@@ -307,16 +314,16 @@
307 }else{
308 blob_zero(&to);
309 }
310 blob_zero(&out);
311 if( diffFlags & DIFF_SIDEBYSIDE ){
312 text_diff(&from, &to, &out, diffFlags | DIFF_HTML);
313 @ <div class="sbsdiff">
314 @ %s(blob_str(&out))
315 @ </div>
316 }else{
317 text_diff(&from, &to, &out, diffFlags | DIFF_LINENO | DIFF_HTML);
318 @ <div class="udiff">
319 @ %s(blob_str(&out))
320 @ </div>
321 }
322 blob_reset(&from);
@@ -333,10 +340,11 @@
333 const char *zName, /* Name of the file that has changed */
334 const char *zOld, /* blob.uuid before change. NULL for added files */
335 const char *zNew, /* blob.uuid after change. NULL for deletes */
336 const char *zOldName, /* Prior name. NULL if no name change. */
337 u64 diffFlags, /* Flags for text_diff(). Zero to omit diffs */
 
338 int mperm /* executable or symlink permission for zNew */
339 ){
340 if( !g.perm.Hyperlink ){
341 if( zNew==0 ){
342 @ <p>Deleted %h(zName)</p>
@@ -350,21 +358,21 @@
350 }else{
351 @ <p>Changes to %h(zName)</p>
352 }
353 if( diffFlags ){
354 @ <pre style="white-space:pre;">
355 append_diff(zOld, zNew, diffFlags);
356 @ </pre>
357 }
358 }else{
359 if( zOld && zNew ){
360 if( fossil_strcmp(zOld, zNew)!=0 ){
361 @ <p>Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
362 @ from %z(href("%R/artifact/%s",zOld))[%S(zOld)]</a>
363 @ to %z(href("%R/artifact/%s",zNew))[%S(zNew)].</a>
364 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
365 @ <p>Name change from
366 @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a>
367 @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>.
368 }else{
369 @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for
370 @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
@@ -376,11 +384,11 @@
376 @ <p>Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
377 @ version %z(href("%R/artifact/%s",zNew))[%S(zNew)]</a>
378 }
379 if( diffFlags ){
380 @ <pre style="white-space:pre;">
381 append_diff(zOld, zNew, diffFlags);
382 @ </pre>
383 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
384 @ &nbsp;&nbsp;
385 @ %z(href("%R/fdiff?v1=%S&v2=%S",zOld,zNew))[diff]</a>
386 }
@@ -444,10 +452,12 @@
444 int sideBySide; /* True for side-by-side diffs */
445 u64 diffFlags; /* Flag parameter for text_diff() */
446 const char *zName; /* Name of the checkin to be displayed */
447 const char *zUuid; /* UUID of zName */
448 const char *zParent; /* UUID of the parent checkin (if any) */
 
 
449
450 login_check_credentials();
451 if( !g.perm.Read ){ login_needed(); return; }
452 zName = P("name");
453 rid = name_to_rid_www("name");
@@ -455,10 +465,12 @@
455 style_header("Check-in Information Error");
456 @ No such object: %h(g.argv[2])
457 style_footer();
458 return;
459 }
 
 
460 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
461 zParent = db_text(0,
462 "SELECT uuid FROM plink, blob"
463 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
464 rid
@@ -692,29 +704,35 @@
692 @ show&nbsp;side-by-side&nbsp;diffs</a>
693 }
694 }
695 @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
696 @ patch</a></div>
 
 
 
 
697 db_prepare(&q,
698 "SELECT name,"
699 " mperm,"
700 " (SELECT uuid FROM blob WHERE rid=mlink.pid),"
701 " (SELECT uuid FROM blob WHERE rid=mlink.fid),"
702 " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)"
703 " FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
704 " WHERE mlink.mid=%d"
 
 
705 " ORDER BY name /*sort*/",
706 rid
707 );
708 diffFlags = construct_diff_flags(showDiff, sideBySide);
709 while( db_step(&q)==SQLITE_ROW ){
710 const char *zName = db_column_text(&q,0);
711 int mperm = db_column_int(&q, 1);
712 const char *zOld = db_column_text(&q,2);
713 const char *zNew = db_column_text(&q,3);
714 const char *zOldName = db_column_text(&q, 4);
715 append_file_change_line(zName, zOld, zNew, zOldName, diffFlags, mperm);
716 }
717 db_finalize(&q);
718 }
719 style_footer();
720 }
@@ -933,15 +951,19 @@
933 Manifest *pFrom, *pTo;
934 ManifestFile *pFileFrom, *pFileTo;
935 const char *zBranch;
936 const char *zFrom;
937 const char *zTo;
 
 
938
939 login_check_credentials();
940 if( !g.perm.Read ){ login_needed(); return; }
941 login_anonymous_available();
942
 
 
943 zBranch = P("branch");
944 if( zBranch && zBranch[0] ){
945 cgi_replace_parameter("from", mprintf("root:%s", zBranch));
946 cgi_replace_parameter("to", zBranch);
947 }
@@ -969,11 +991,16 @@
969 style_header("Check-in Differences");
970 @ <h2>Difference From:</h2><blockquote>
971 checkin_description(ridFrom);
972 @ </blockquote><h2>To:</h2><blockquote>
973 checkin_description(ridTo);
974 @ </blockquote><hr /><p>
 
 
 
 
 
975
976 manifest_file_rewind(pFrom);
977 pFileFrom = manifest_file_next(pFrom, 0);
978 manifest_file_rewind(pTo);
979 pFileTo = manifest_file_next(pTo, 0);
@@ -987,25 +1014,25 @@
987 }else{
988 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
989 }
990 if( cmp<0 ){
991 append_file_change_line(pFileFrom->zName,
992 pFileFrom->zUuid, 0, 0, diffFlags, 0);
993 pFileFrom = manifest_file_next(pFrom, 0);
994 }else if( cmp>0 ){
995 append_file_change_line(pFileTo->zName,
996 0, pFileTo->zUuid, 0, diffFlags,
997 manifest_file_mperm(pFileTo));
998 pFileTo = manifest_file_next(pTo, 0);
999 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
1000 /* No changes */
1001 pFileFrom = manifest_file_next(pFrom, 0);
1002 pFileTo = manifest_file_next(pTo, 0);
1003 }else{
1004 append_file_change_line(pFileFrom->zName,
1005 pFileFrom->zUuid,
1006 pFileTo->zUuid, 0, diffFlags,
1007 manifest_file_mperm(pFileTo));
1008 pFileFrom = manifest_file_next(pFrom, 0);
1009 pFileTo = manifest_file_next(pTo, 0);
1010 }
1011 }
@@ -1254,11 +1281,11 @@
1254 }
1255
1256
1257 /*
1258 ** WEBPAGE: fdiff
1259 ** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN
1260 **
1261 ** Two arguments, v1 and v2, identify the files to be diffed. Show the
1262 ** difference between the two artifacts. Show diff side by side unless sbs
1263 ** is 0. Generate plaintext if "patch" is present.
1264 */
@@ -1267,10 +1294,12 @@
1267 int isPatch;
1268 int sideBySide;
1269 Blob c1, c2, diff, *pOut;
1270 char *zV1;
1271 char *zV2;
 
 
1272 u64 diffFlags;
1273 const char *zStyle = "sbsdiff";
1274
1275 login_check_credentials();
1276 if( !g.perm.Read ){ login_needed(); return; }
@@ -1294,13 +1323,15 @@
1294 }else{
1295 diffFlags |= DIFF_LINENO;
1296 zStyle = "udiff";
1297 }
1298 }
 
 
1299 content_get(v1, &c1);
1300 content_get(v2, &c2);
1301 text_diff(&c1, &c2, pOut, diffFlags);
1302 blob_reset(&c1);
1303 blob_reset(&c2);
1304 if( !isPatch ){
1305 style_header("Diff");
1306 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
@@ -1324,10 +1355,14 @@
1324 @ Artifact %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
1325 object_description(v1, 0, 0);
1326 @ <h2>To Artifact %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
1327 object_description(v2, 0, 0);
1328 }
 
 
 
 
1329 @ <hr />
1330 @ <div class="%s(zStyle)">
1331 @ %s(blob_str(&diff))
1332 @ </div>
1333 blob_reset(&diff);
1334
--- src/info.c
+++ src/info.c
@@ -208,10 +208,12 @@
208 fossil_print("project-code: %s\n", db_get("project-code", ""));
209 vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
210 if( vid ){
211 show_common_info(vid, "checkout:", 1, 1);
212 }
213 fossil_print("checkins: %d\n",
214 db_int(-1, "SELECT count(distinct mid) FROM mlink /*scan*/"));
215 }else{
216 int rid;
217 rid = name_to_rid(g.argv[2]);
218 if( rid==0 ){
219 fossil_panic("no such object: %s\n", g.argv[2]);
@@ -287,13 +289,18 @@
289 }
290 }
291
292
293 /*
294 ** Append the difference between artifacts to the output
295 */
296 static void append_diff(
297 const char *zFrom, /* Diff from this artifact */
298 const char *zTo, /* ... to this artifact */
299 u64 diffFlags, /* Diff formatting flags */
300 ReCompiled *pRe /* Only show change matching this regex */
301 ){
302 int fromid;
303 int toid;
304 Blob from, to, out;
305 if( zFrom ){
306 fromid = uuid_to_rid(zFrom, 0);
@@ -307,16 +314,16 @@
314 }else{
315 blob_zero(&to);
316 }
317 blob_zero(&out);
318 if( diffFlags & DIFF_SIDEBYSIDE ){
319 text_diff(&from, &to, &out, pRe, diffFlags | DIFF_HTML);
320 @ <div class="sbsdiff">
321 @ %s(blob_str(&out))
322 @ </div>
323 }else{
324 text_diff(&from, &to, &out, pRe, diffFlags | DIFF_LINENO | DIFF_HTML);
325 @ <div class="udiff">
326 @ %s(blob_str(&out))
327 @ </div>
328 }
329 blob_reset(&from);
@@ -333,10 +340,11 @@
340 const char *zName, /* Name of the file that has changed */
341 const char *zOld, /* blob.uuid before change. NULL for added files */
342 const char *zNew, /* blob.uuid after change. NULL for deletes */
343 const char *zOldName, /* Prior name. NULL if no name change. */
344 u64 diffFlags, /* Flags for text_diff(). Zero to omit diffs */
345 ReCompiled *pRe, /* Only show diffs that match this regex, if not NULL */
346 int mperm /* executable or symlink permission for zNew */
347 ){
348 if( !g.perm.Hyperlink ){
349 if( zNew==0 ){
350 @ <p>Deleted %h(zName)</p>
@@ -350,21 +358,21 @@
358 }else{
359 @ <p>Changes to %h(zName)</p>
360 }
361 if( diffFlags ){
362 @ <pre style="white-space:pre;">
363 append_diff(zOld, zNew, diffFlags, pRe);
364 @ </pre>
365 }
366 }else{
367 if( zOld && zNew ){
368 if( fossil_strcmp(zOld, zNew)!=0 ){
369 @ <p>Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
370 @ from %z(href("%R/artifact/%s",zOld))[%S(zOld)]</a>
371 @ to %z(href("%R/artifact/%s",zNew))[%S(zNew)].</a>
372 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
373 @ <p>Name change
374 @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a>
375 @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>.
376 }else{
377 @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for
378 @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
@@ -376,11 +384,11 @@
384 @ <p>Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
385 @ version %z(href("%R/artifact/%s",zNew))[%S(zNew)]</a>
386 }
387 if( diffFlags ){
388 @ <pre style="white-space:pre;">
389 append_diff(zOld, zNew, diffFlags, pRe);
390 @ </pre>
391 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
392 @ &nbsp;&nbsp;
393 @ %z(href("%R/fdiff?v1=%S&v2=%S",zOld,zNew))[diff]</a>
394 }
@@ -444,10 +452,12 @@
452 int sideBySide; /* True for side-by-side diffs */
453 u64 diffFlags; /* Flag parameter for text_diff() */
454 const char *zName; /* Name of the checkin to be displayed */
455 const char *zUuid; /* UUID of zName */
456 const char *zParent; /* UUID of the parent checkin (if any) */
457 const char *zRe; /* regex parameter */
458 ReCompiled *pRe = 0; /* regex */
459
460 login_check_credentials();
461 if( !g.perm.Read ){ login_needed(); return; }
462 zName = P("name");
463 rid = name_to_rid_www("name");
@@ -455,10 +465,12 @@
465 style_header("Check-in Information Error");
466 @ No such object: %h(g.argv[2])
467 style_footer();
468 return;
469 }
470 zRe = P("regex");
471 if( zRe ) re_compile(&pRe, zRe, 0);
472 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
473 zParent = db_text(0,
474 "SELECT uuid FROM plink, blob"
475 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
476 rid
@@ -692,29 +704,35 @@
704 @ show&nbsp;side-by-side&nbsp;diffs</a>
705 }
706 }
707 @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
708 @ patch</a></div>
709 if( pRe ){
710 @ <p><b>Only differences that match regular expression "%h(zRe)"
711 @ are shown.</b></p>
712 }
713 db_prepare(&q,
714 "SELECT name,"
715 " mperm,"
716 " (SELECT uuid FROM blob WHERE rid=mlink.pid),"
717 " (SELECT uuid FROM blob WHERE rid=mlink.fid),"
718 " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)"
719 " FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
720 " WHERE mlink.mid=%d"
721 " AND (mlink.fid>0"
722 " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))"
723 " ORDER BY name /*sort*/",
724 rid, rid
725 );
726 diffFlags = construct_diff_flags(showDiff, sideBySide);
727 while( db_step(&q)==SQLITE_ROW ){
728 const char *zName = db_column_text(&q,0);
729 int mperm = db_column_int(&q, 1);
730 const char *zOld = db_column_text(&q,2);
731 const char *zNew = db_column_text(&q,3);
732 const char *zOldName = db_column_text(&q, 4);
733 append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm);
734 }
735 db_finalize(&q);
736 }
737 style_footer();
738 }
@@ -933,15 +951,19 @@
951 Manifest *pFrom, *pTo;
952 ManifestFile *pFileFrom, *pFileTo;
953 const char *zBranch;
954 const char *zFrom;
955 const char *zTo;
956 const char *zRe;
957 ReCompiled *pRe = 0;
958
959 login_check_credentials();
960 if( !g.perm.Read ){ login_needed(); return; }
961 login_anonymous_available();
962
963 zRe = P("regex");
964 if( zRe ) re_compile(&pRe, zRe, 0);
965 zBranch = P("branch");
966 if( zBranch && zBranch[0] ){
967 cgi_replace_parameter("from", mprintf("root:%s", zBranch));
968 cgi_replace_parameter("to", zBranch);
969 }
@@ -969,11 +991,16 @@
991 style_header("Check-in Differences");
992 @ <h2>Difference From:</h2><blockquote>
993 checkin_description(ridFrom);
994 @ </blockquote><h2>To:</h2><blockquote>
995 checkin_description(ridTo);
996 @ </blockquote>
997 if( pRe ){
998 @ <p><b>Only differences that match regular expression "%h(zRe)"
999 @ are shown.</b></p>
1000 }
1001 @<hr /><p>
1002
1003 manifest_file_rewind(pFrom);
1004 pFileFrom = manifest_file_next(pFrom, 0);
1005 manifest_file_rewind(pTo);
1006 pFileTo = manifest_file_next(pTo, 0);
@@ -987,25 +1014,25 @@
1014 }else{
1015 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
1016 }
1017 if( cmp<0 ){
1018 append_file_change_line(pFileFrom->zName,
1019 pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0);
1020 pFileFrom = manifest_file_next(pFrom, 0);
1021 }else if( cmp>0 ){
1022 append_file_change_line(pFileTo->zName,
1023 0, pFileTo->zUuid, 0, diffFlags, pRe,
1024 manifest_file_mperm(pFileTo));
1025 pFileTo = manifest_file_next(pTo, 0);
1026 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
1027 /* No changes */
1028 pFileFrom = manifest_file_next(pFrom, 0);
1029 pFileTo = manifest_file_next(pTo, 0);
1030 }else{
1031 append_file_change_line(pFileFrom->zName,
1032 pFileFrom->zUuid,
1033 pFileTo->zUuid, 0, diffFlags, pRe,
1034 manifest_file_mperm(pFileTo));
1035 pFileFrom = manifest_file_next(pFrom, 0);
1036 pFileTo = manifest_file_next(pTo, 0);
1037 }
1038 }
@@ -1254,11 +1281,11 @@
1281 }
1282
1283
1284 /*
1285 ** WEBPAGE: fdiff
1286 ** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN&regex=REGEX
1287 **
1288 ** Two arguments, v1 and v2, identify the files to be diffed. Show the
1289 ** difference between the two artifacts. Show diff side by side unless sbs
1290 ** is 0. Generate plaintext if "patch" is present.
1291 */
@@ -1267,10 +1294,12 @@
1294 int isPatch;
1295 int sideBySide;
1296 Blob c1, c2, diff, *pOut;
1297 char *zV1;
1298 char *zV2;
1299 const char *zRe;
1300 ReCompiled *pRe = 0;
1301 u64 diffFlags;
1302 const char *zStyle = "sbsdiff";
1303
1304 login_check_credentials();
1305 if( !g.perm.Read ){ login_needed(); return; }
@@ -1294,13 +1323,15 @@
1323 }else{
1324 diffFlags |= DIFF_LINENO;
1325 zStyle = "udiff";
1326 }
1327 }
1328 zRe = P("regex");
1329 if( zRe ) re_compile(&pRe, zRe, 0);
1330 content_get(v1, &c1);
1331 content_get(v2, &c2);
1332 text_diff(&c1, &c2, pOut, pRe, diffFlags);
1333 blob_reset(&c1);
1334 blob_reset(&c2);
1335 if( !isPatch ){
1336 style_header("Diff");
1337 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
@@ -1324,10 +1355,14 @@
1355 @ Artifact %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a>:</h2>
1356 object_description(v1, 0, 0);
1357 @ <h2>To Artifact %z(href("%R/artifact/%S",zV2))[%S(zV2)]</a>:</h2>
1358 object_description(v2, 0, 0);
1359 }
1360 if( pRe ){
1361 @ <b>Only differences that match regular expression "%h(zRe)"
1362 @ are shown.</b>
1363 }
1364 @ <hr />
1365 @ <div class="%s(zStyle)">
1366 @ %s(blob_str(&diff))
1367 @ </div>
1368 blob_reset(&diff);
1369
+1 -3
--- src/json.c
+++ src/json.c
@@ -2085,15 +2085,13 @@
20852085
cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n));
20862086
}/*full*/
20872087
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
20882088
" + 0.99");
20892089
cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
2090
- cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24));
2090
+ cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
20912091
sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
20922092
SETBUF(jo, "projectCode");
2093
- sqlite3_snprintf(BufLen, zBuf, db_get("server-code",""));
2094
- SETBUF(jo, "serverCode");
20952093
cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
20962094
20972095
jv2 = cson_value_new_object();
20982096
jo2 = cson_value_get_object(jv2);
20992097
cson_object_set(jo, "sqlite", jv2);
21002098
--- src/json.c
+++ src/json.c
@@ -2085,15 +2085,13 @@
2085 cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n));
2086 }/*full*/
2087 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
2088 " + 0.99");
2089 cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
2090 cson_object_set(jo, "ageYears", cson_value_new_double(n/365.24));
2091 sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
2092 SETBUF(jo, "projectCode");
2093 sqlite3_snprintf(BufLen, zBuf, db_get("server-code",""));
2094 SETBUF(jo, "serverCode");
2095 cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
2096
2097 jv2 = cson_value_new_object();
2098 jo2 = cson_value_get_object(jv2);
2099 cson_object_set(jo, "sqlite", jv2);
2100
--- src/json.c
+++ src/json.c
@@ -2085,15 +2085,13 @@
2085 cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n));
2086 }/*full*/
2087 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
2088 " + 0.99");
2089 cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
2090 cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
2091 sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
2092 SETBUF(jo, "projectCode");
 
 
2093 cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
2094
2095 jv2 = cson_value_new_object();
2096 jo2 = cson_value_get_object(jv2);
2097 cson_object_set(jo, "sqlite", jv2);
2098
+1 -1
--- src/json_diff.c
+++ src/json_diff.c
@@ -58,11 +58,11 @@
5858
return NULL;
5959
}
6060
content_get(fromid, &from);
6161
content_get(toid, &to);
6262
blob_zero(&out);
63
- text_diff(&from, &to, &out, flags);
63
+ text_diff(&from, &to, &out, 0, flags);
6464
blob_reset(&from);
6565
blob_reset(&to);
6666
outLen = blob_size(&out);
6767
if(outLen>=0){
6868
rc = cson_value_new_string(blob_buffer(&out),
6969
--- src/json_diff.c
+++ src/json_diff.c
@@ -58,11 +58,11 @@
58 return NULL;
59 }
60 content_get(fromid, &from);
61 content_get(toid, &to);
62 blob_zero(&out);
63 text_diff(&from, &to, &out, flags);
64 blob_reset(&from);
65 blob_reset(&to);
66 outLen = blob_size(&out);
67 if(outLen>=0){
68 rc = cson_value_new_string(blob_buffer(&out),
69
--- src/json_diff.c
+++ src/json_diff.c
@@ -58,11 +58,11 @@
58 return NULL;
59 }
60 content_get(fromid, &from);
61 content_get(toid, &to);
62 blob_zero(&out);
63 text_diff(&from, &to, &out, 0, flags);
64 blob_reset(&from);
65 blob_reset(&to);
66 outLen = blob_size(&out);
67 if(outLen>=0){
68 rc = cson_value_new_string(blob_buffer(&out),
69
--- src/json_timeline.c
+++ src/json_timeline.c
@@ -90,11 +90,11 @@
9090
9191
/*
9292
** Return a pointer to a constant string that forms the basis
9393
** for a timeline query for the JSON interface.
9494
*/
95
-const char const * json_timeline_query(void){
95
+char const * json_timeline_query(void){
9696
/* Field order MUST match that from json_timeline_temp_table()!!! */
9797
static const char zBaseSql[] =
9898
@ SELECT
9999
@ NULL,
100100
@ blob.rid,
101101
--- src/json_timeline.c
+++ src/json_timeline.c
@@ -90,11 +90,11 @@
90
91 /*
92 ** Return a pointer to a constant string that forms the basis
93 ** for a timeline query for the JSON interface.
94 */
95 const char const * json_timeline_query(void){
96 /* Field order MUST match that from json_timeline_temp_table()!!! */
97 static const char zBaseSql[] =
98 @ SELECT
99 @ NULL,
100 @ blob.rid,
101
--- src/json_timeline.c
+++ src/json_timeline.c
@@ -90,11 +90,11 @@
90
91 /*
92 ** Return a pointer to a constant string that forms the basis
93 ** for a timeline query for the JSON interface.
94 */
95 char const * json_timeline_query(void){
96 /* Field order MUST match that from json_timeline_temp_table()!!! */
97 static const char zBaseSql[] =
98 @ SELECT
99 @ NULL,
100 @ blob.rid,
101
+1 -1
--- src/json_wiki.c
+++ src/json_wiki.c
@@ -543,11 +543,11 @@
543543
blob_init(&w1, pW1->zWiki, -1);
544544
blob_zero(&w2);
545545
blob_init(&w2, pW2->zWiki, -1);
546546
blob_zero(&d);
547547
diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE;
548
- text_diff(&w2, &w1, &d, diffFlags);
548
+ text_diff(&w2, &w1, &d, 0, diffFlags);
549549
blob_reset(&w1);
550550
blob_reset(&w2);
551551
552552
pay = cson_new_object();
553553
554554
--- src/json_wiki.c
+++ src/json_wiki.c
@@ -543,11 +543,11 @@
543 blob_init(&w1, pW1->zWiki, -1);
544 blob_zero(&w2);
545 blob_init(&w2, pW2->zWiki, -1);
546 blob_zero(&d);
547 diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE;
548 text_diff(&w2, &w1, &d, diffFlags);
549 blob_reset(&w1);
550 blob_reset(&w2);
551
552 pay = cson_new_object();
553
554
--- src/json_wiki.c
+++ src/json_wiki.c
@@ -543,11 +543,11 @@
543 blob_init(&w1, pW1->zWiki, -1);
544 blob_zero(&w2);
545 blob_init(&w2, pW2->zWiki, -1);
546 blob_zero(&d);
547 diffFlags = DIFF_IGNORE_EOLWS | DIFF_INLINE;
548 text_diff(&w2, &w1, &d, 0, diffFlags);
549 blob_reset(&w1);
550 blob_reset(&w2);
551
552 pay = cson_new_object();
553
554
+2 -2
--- src/login.c
+++ src/login.c
@@ -397,11 +397,11 @@
397397
}
398398
if( memcmp(zAgent, "Mozilla/", 8)==0 ){
399399
if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */
400400
if( strglob("*Firefox/[1-9]*", zAgent) ) return 1;
401401
if( strglob("*Chrome/[1-9]*", zAgent) ) return 1;
402
- if( strglob("*(compatible;?MSIE?[1-9]*", zAgent) ) return 1;
402
+ if( strglob("*(compatible;?MSIE?[1789]*", zAgent) ) return 1;
403403
if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1;
404404
return 0;
405405
}
406406
if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
407407
if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
@@ -625,11 +625,11 @@
625625
@ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
626626
}
627627
if( zAnonPw ){
628628
unsigned int uSeed = captcha_seed();
629629
char const *zDecoded = captcha_decode(uSeed);
630
- int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
630
+ int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
631631
char *zCaptcha = captcha_render(zDecoded);
632632
633633
@ <p><input type="hidden" name="cs" value="%u(uSeed)" />
634634
@ Visitors may enter <b>anonymous</b> as the user-ID with
635635
@ the 8-character hexadecimal password shown below:</p>
636636
--- src/login.c
+++ src/login.c
@@ -397,11 +397,11 @@
397 }
398 if( memcmp(zAgent, "Mozilla/", 8)==0 ){
399 if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */
400 if( strglob("*Firefox/[1-9]*", zAgent) ) return 1;
401 if( strglob("*Chrome/[1-9]*", zAgent) ) return 1;
402 if( strglob("*(compatible;?MSIE?[1-9]*", zAgent) ) return 1;
403 if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1;
404 return 0;
405 }
406 if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
407 if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
@@ -625,11 +625,11 @@
625 @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
626 }
627 if( zAnonPw ){
628 unsigned int uSeed = captcha_seed();
629 char const *zDecoded = captcha_decode(uSeed);
630 int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
631 char *zCaptcha = captcha_render(zDecoded);
632
633 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
634 @ Visitors may enter <b>anonymous</b> as the user-ID with
635 @ the 8-character hexadecimal password shown below:</p>
636
--- src/login.c
+++ src/login.c
@@ -397,11 +397,11 @@
397 }
398 if( memcmp(zAgent, "Mozilla/", 8)==0 ){
399 if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */
400 if( strglob("*Firefox/[1-9]*", zAgent) ) return 1;
401 if( strglob("*Chrome/[1-9]*", zAgent) ) return 1;
402 if( strglob("*(compatible;?MSIE?[1789]*", zAgent) ) return 1;
403 if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1;
404 return 0;
405 }
406 if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
407 if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
@@ -625,11 +625,11 @@
625 @ <a href="%s(g.zTop)/register?g=%T(P("G"))">create one</a>.
626 }
627 if( zAnonPw ){
628 unsigned int uSeed = captcha_seed();
629 char const *zDecoded = captcha_decode(uSeed);
630 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
631 char *zCaptcha = captcha_render(zDecoded);
632
633 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
634 @ Visitors may enter <b>anonymous</b> as the user-ID with
635 @ the 8-character hexadecimal password shown below:</p>
636
+17 -27
--- src/main.c
+++ src/main.c
@@ -605,14 +605,11 @@
605605
if( g.cgiOutput && once ){
606606
once = 0;
607607
cgi_printf("<p class=\"generalError\">%h</p>", z);
608608
cgi_reply();
609609
}else if( !g.fQuiet ){
610
- char *zOut = mprintf("%s: %s\n", g.argv[0], z);
611
- fossil_force_newline();
612
- fossil_puts(zOut, 1);
613
- fossil_free(zOut);
610
+ fossil_trace("%s: %s\n", g.argv[0], z);
614611
}
615612
}
616613
free(z);
617614
db_force_rollback();
618615
fossil_exit(rc);
@@ -636,17 +633,14 @@
636633
else
637634
#endif
638635
{
639636
if( g.cgiOutput ){
640637
g.cgiOutput = 0;
641
- cgi_printf("<p class=\"generalError\">%h</p>", z);
638
+ cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
642639
cgi_reply();
643640
}else if( !g.fQuiet ){
644
- char *zOut = mprintf("\r%s: %s\n", g.argv[0], z);
645
- fossil_force_newline();
646
- fossil_puts(zOut, 1);
647
- fossil_free(zOut);
641
+ fossil_trace("%s: %s\n", g.argv[0], z);
648642
}
649643
}
650644
free(z);
651645
db_force_rollback();
652646
fossil_exit(rc);
@@ -679,17 +673,14 @@
679673
} else
680674
#endif
681675
{
682676
if( g.cgiOutput ){
683677
g.cgiOutput = 0;
684
- cgi_printf("<p class=\"generalError\">%h</p>", z);
678
+ cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
685679
cgi_reply();
686680
}else{
687
- char *zOut = mprintf("\r%s: %s\n", g.argv[0], z);
688
- fossil_force_newline();
689
- fossil_puts(zOut, 1);
690
- fossil_free(zOut);
681
+ fossil_trace("%s: %s\n", g.argv[0], z);
691682
}
692683
}
693684
db_force_rollback();
694685
fossil_exit(rc);
695686
}
@@ -707,16 +698,13 @@
707698
json_warn( FSL_JSON_W_UNKNOWN, z );
708699
}else
709700
#endif
710701
{
711702
if( g.cgiOutput ){
712
- cgi_printf("<p class=\"generalError\">%h</p>", z);
703
+ cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
713704
}else{
714
- char *zOut = mprintf("\r%s: %s\n", g.argv[0], z);
715
- fossil_force_newline();
716
- fossil_puts(zOut, 1);
717
- fossil_free(zOut);
705
+ fossil_trace("%s: %s\n", g.argv[0], z);
718706
}
719707
}
720708
free(z);
721709
}
722710
@@ -747,13 +735,11 @@
747735
** Who knows why - this is just the way windows works.
748736
*/
749737
char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
750738
WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd);
751739
if( g.fSystemTrace ) {
752
- char *zOut = mprintf("SYSTEM: %s\n", zNewCmd);
753
- fossil_puts(zOut, 1);
754
- fossil_free(zOut);
740
+ fossil_trace("SYSTEM: %s\n", zNewCmd);
755741
}
756742
rc = _wsystem(zUnicode);
757743
fossil_unicode_free(zUnicode);
758744
free(zNewCmd);
759745
#else
@@ -892,11 +878,13 @@
892878
*/
893879
void verify_all_options(void){
894880
int i;
895881
for(i=1; i<g.argc; i++){
896882
if( g.argv[i][0]=='-' ){
897
- fossil_fatal("unrecognized command-line option, or missing argument: %s", g.argv[i]);
883
+ fossil_fatal(
884
+ "unrecognized command-line option, or missing argument: %s",
885
+ g.argv[i]);
898886
}
899887
}
900888
}
901889
902890
/*
@@ -1223,15 +1211,17 @@
12231211
}
12241212
zRepo = "/";
12251213
}else{
12261214
for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
12271215
if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo);
1228
- zDir[i] = 0;
1229
- if( chdir(zDir) || chroot(zDir) || chdir("/") ){
1230
- fossil_fatal("unable to chroot into %s", zDir);
1216
+ if( i>0 ){
1217
+ zDir[i] = 0;
1218
+ if( chdir(zDir) || chroot(zDir) || chdir("/") ){
1219
+ fossil_fatal("unable to chroot into %s", zDir);
1220
+ }
1221
+ zDir[i] = '/';
12311222
}
1232
- zDir[i] = '/';
12331223
zRepo = &zDir[i];
12341224
}
12351225
if( stat(zRepo, &sStat)!=0 ){
12361226
fossil_fatal("cannot stat() repository: %s", zRepo);
12371227
}
12381228
--- src/main.c
+++ src/main.c
@@ -605,14 +605,11 @@
605 if( g.cgiOutput && once ){
606 once = 0;
607 cgi_printf("<p class=\"generalError\">%h</p>", z);
608 cgi_reply();
609 }else if( !g.fQuiet ){
610 char *zOut = mprintf("%s: %s\n", g.argv[0], z);
611 fossil_force_newline();
612 fossil_puts(zOut, 1);
613 fossil_free(zOut);
614 }
615 }
616 free(z);
617 db_force_rollback();
618 fossil_exit(rc);
@@ -636,17 +633,14 @@
636 else
637 #endif
638 {
639 if( g.cgiOutput ){
640 g.cgiOutput = 0;
641 cgi_printf("<p class=\"generalError\">%h</p>", z);
642 cgi_reply();
643 }else if( !g.fQuiet ){
644 char *zOut = mprintf("\r%s: %s\n", g.argv[0], z);
645 fossil_force_newline();
646 fossil_puts(zOut, 1);
647 fossil_free(zOut);
648 }
649 }
650 free(z);
651 db_force_rollback();
652 fossil_exit(rc);
@@ -679,17 +673,14 @@
679 } else
680 #endif
681 {
682 if( g.cgiOutput ){
683 g.cgiOutput = 0;
684 cgi_printf("<p class=\"generalError\">%h</p>", z);
685 cgi_reply();
686 }else{
687 char *zOut = mprintf("\r%s: %s\n", g.argv[0], z);
688 fossil_force_newline();
689 fossil_puts(zOut, 1);
690 fossil_free(zOut);
691 }
692 }
693 db_force_rollback();
694 fossil_exit(rc);
695 }
@@ -707,16 +698,13 @@
707 json_warn( FSL_JSON_W_UNKNOWN, z );
708 }else
709 #endif
710 {
711 if( g.cgiOutput ){
712 cgi_printf("<p class=\"generalError\">%h</p>", z);
713 }else{
714 char *zOut = mprintf("\r%s: %s\n", g.argv[0], z);
715 fossil_force_newline();
716 fossil_puts(zOut, 1);
717 fossil_free(zOut);
718 }
719 }
720 free(z);
721 }
722
@@ -747,13 +735,11 @@
747 ** Who knows why - this is just the way windows works.
748 */
749 char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
750 WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd);
751 if( g.fSystemTrace ) {
752 char *zOut = mprintf("SYSTEM: %s\n", zNewCmd);
753 fossil_puts(zOut, 1);
754 fossil_free(zOut);
755 }
756 rc = _wsystem(zUnicode);
757 fossil_unicode_free(zUnicode);
758 free(zNewCmd);
759 #else
@@ -892,11 +878,13 @@
892 */
893 void verify_all_options(void){
894 int i;
895 for(i=1; i<g.argc; i++){
896 if( g.argv[i][0]=='-' ){
897 fossil_fatal("unrecognized command-line option, or missing argument: %s", g.argv[i]);
 
 
898 }
899 }
900 }
901
902 /*
@@ -1223,15 +1211,17 @@
1223 }
1224 zRepo = "/";
1225 }else{
1226 for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
1227 if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo);
1228 zDir[i] = 0;
1229 if( chdir(zDir) || chroot(zDir) || chdir("/") ){
1230 fossil_fatal("unable to chroot into %s", zDir);
 
 
 
1231 }
1232 zDir[i] = '/';
1233 zRepo = &zDir[i];
1234 }
1235 if( stat(zRepo, &sStat)!=0 ){
1236 fossil_fatal("cannot stat() repository: %s", zRepo);
1237 }
1238
--- src/main.c
+++ src/main.c
@@ -605,14 +605,11 @@
605 if( g.cgiOutput && once ){
606 once = 0;
607 cgi_printf("<p class=\"generalError\">%h</p>", z);
608 cgi_reply();
609 }else if( !g.fQuiet ){
610 fossil_trace("%s: %s\n", g.argv[0], z);
 
 
 
611 }
612 }
613 free(z);
614 db_force_rollback();
615 fossil_exit(rc);
@@ -636,17 +633,14 @@
633 else
634 #endif
635 {
636 if( g.cgiOutput ){
637 g.cgiOutput = 0;
638 cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
639 cgi_reply();
640 }else if( !g.fQuiet ){
641 fossil_trace("%s: %s\n", g.argv[0], z);
 
 
 
642 }
643 }
644 free(z);
645 db_force_rollback();
646 fossil_exit(rc);
@@ -679,17 +673,14 @@
673 } else
674 #endif
675 {
676 if( g.cgiOutput ){
677 g.cgiOutput = 0;
678 cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
679 cgi_reply();
680 }else{
681 fossil_trace("%s: %s\n", g.argv[0], z);
 
 
 
682 }
683 }
684 db_force_rollback();
685 fossil_exit(rc);
686 }
@@ -707,16 +698,13 @@
698 json_warn( FSL_JSON_W_UNKNOWN, z );
699 }else
700 #endif
701 {
702 if( g.cgiOutput ){
703 cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
704 }else{
705 fossil_trace("%s: %s\n", g.argv[0], z);
 
 
 
706 }
707 }
708 free(z);
709 }
710
@@ -747,13 +735,11 @@
735 ** Who knows why - this is just the way windows works.
736 */
737 char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
738 WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd);
739 if( g.fSystemTrace ) {
740 fossil_trace("SYSTEM: %s\n", zNewCmd);
 
 
741 }
742 rc = _wsystem(zUnicode);
743 fossil_unicode_free(zUnicode);
744 free(zNewCmd);
745 #else
@@ -892,11 +878,13 @@
878 */
879 void verify_all_options(void){
880 int i;
881 for(i=1; i<g.argc; i++){
882 if( g.argv[i][0]=='-' ){
883 fossil_fatal(
884 "unrecognized command-line option, or missing argument: %s",
885 g.argv[i]);
886 }
887 }
888 }
889
890 /*
@@ -1223,15 +1211,17 @@
1211 }
1212 zRepo = "/";
1213 }else{
1214 for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
1215 if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo);
1216 if( i>0 ){
1217 zDir[i] = 0;
1218 if( chdir(zDir) || chroot(zDir) || chdir("/") ){
1219 fossil_fatal("unable to chroot into %s", zDir);
1220 }
1221 zDir[i] = '/';
1222 }
 
1223 zRepo = &zDir[i];
1224 }
1225 if( stat(zRepo, &sStat)!=0 ){
1226 fossil_fatal("cannot stat() repository: %s", zRepo);
1227 }
1228
+21 -1
--- src/main.mk
+++ src/main.mk
@@ -81,10 +81,11 @@
8181
$(SRCDIR)/pivot.c \
8282
$(SRCDIR)/popen.c \
8383
$(SRCDIR)/pqueue.c \
8484
$(SRCDIR)/printf.c \
8585
$(SRCDIR)/rebuild.c \
86
+ $(SRCDIR)/regexp.c \
8687
$(SRCDIR)/report.c \
8788
$(SRCDIR)/rss.c \
8889
$(SRCDIR)/schema.c \
8990
$(SRCDIR)/search.c \
9091
$(SRCDIR)/setup.c \
@@ -101,10 +102,11 @@
101102
$(SRCDIR)/th_main.c \
102103
$(SRCDIR)/timeline.c \
103104
$(SRCDIR)/tkt.c \
104105
$(SRCDIR)/tktsetup.c \
105106
$(SRCDIR)/undo.c \
107
+ $(SRCDIR)/unicode.c \
106108
$(SRCDIR)/update.c \
107109
$(SRCDIR)/url.c \
108110
$(SRCDIR)/user.c \
109111
$(SRCDIR)/utf8.c \
110112
$(SRCDIR)/verify.c \
@@ -185,10 +187,11 @@
185187
$(OBJDIR)/pivot_.c \
186188
$(OBJDIR)/popen_.c \
187189
$(OBJDIR)/pqueue_.c \
188190
$(OBJDIR)/printf_.c \
189191
$(OBJDIR)/rebuild_.c \
192
+ $(OBJDIR)/regexp_.c \
190193
$(OBJDIR)/report_.c \
191194
$(OBJDIR)/rss_.c \
192195
$(OBJDIR)/schema_.c \
193196
$(OBJDIR)/search_.c \
194197
$(OBJDIR)/setup_.c \
@@ -205,10 +208,11 @@
205208
$(OBJDIR)/th_main_.c \
206209
$(OBJDIR)/timeline_.c \
207210
$(OBJDIR)/tkt_.c \
208211
$(OBJDIR)/tktsetup_.c \
209212
$(OBJDIR)/undo_.c \
213
+ $(OBJDIR)/unicode_.c \
210214
$(OBJDIR)/update_.c \
211215
$(OBJDIR)/url_.c \
212216
$(OBJDIR)/user_.c \
213217
$(OBJDIR)/utf8_.c \
214218
$(OBJDIR)/verify_.c \
@@ -289,10 +293,11 @@
289293
$(OBJDIR)/pivot.o \
290294
$(OBJDIR)/popen.o \
291295
$(OBJDIR)/pqueue.o \
292296
$(OBJDIR)/printf.o \
293297
$(OBJDIR)/rebuild.o \
298
+ $(OBJDIR)/regexp.o \
294299
$(OBJDIR)/report.o \
295300
$(OBJDIR)/rss.o \
296301
$(OBJDIR)/schema.o \
297302
$(OBJDIR)/search.o \
298303
$(OBJDIR)/setup.o \
@@ -309,10 +314,11 @@
309314
$(OBJDIR)/th_main.o \
310315
$(OBJDIR)/timeline.o \
311316
$(OBJDIR)/tkt.o \
312317
$(OBJDIR)/tktsetup.o \
313318
$(OBJDIR)/undo.o \
319
+ $(OBJDIR)/unicode.o \
314320
$(OBJDIR)/update.o \
315321
$(OBJDIR)/url.o \
316322
$(OBJDIR)/user.o \
317323
$(OBJDIR)/utf8.o \
318324
$(OBJDIR)/verify.o \
@@ -390,11 +396,11 @@
390396
391397
392398
$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
393399
$(OBJDIR)/mkindex $(TRANS_SRC) >$@
394400
$(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
395
- $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
401
+ $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
396402
touch $(OBJDIR)/headers
397403
$(OBJDIR)/headers: Makefile
398404
$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/json_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
399405
Makefile:
400406
$(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
@@ -878,10 +884,17 @@
878884
879885
$(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
880886
$(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
881887
882888
$(OBJDIR)/rebuild.h: $(OBJDIR)/headers
889
+$(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate
890
+ $(OBJDIR)/translate $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
891
+
892
+$(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h
893
+ $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
894
+
895
+$(OBJDIR)/regexp.h: $(OBJDIR)/headers
883896
$(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
884897
$(OBJDIR)/translate $(SRCDIR)/report.c >$(OBJDIR)/report_.c
885898
886899
$(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
887900
$(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
@@ -1018,10 +1031,17 @@
10181031
10191032
$(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
10201033
$(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
10211034
10221035
$(OBJDIR)/undo.h: $(OBJDIR)/headers
1036
+$(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate
1037
+ $(OBJDIR)/translate $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
1038
+
1039
+$(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1040
+ $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1041
+
1042
+$(OBJDIR)/unicode.h: $(OBJDIR)/headers
10231043
$(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
10241044
$(OBJDIR)/translate $(SRCDIR)/update.c >$(OBJDIR)/update_.c
10251045
10261046
$(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
10271047
$(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c
10281048
--- src/main.mk
+++ src/main.mk
@@ -81,10 +81,11 @@
81 $(SRCDIR)/pivot.c \
82 $(SRCDIR)/popen.c \
83 $(SRCDIR)/pqueue.c \
84 $(SRCDIR)/printf.c \
85 $(SRCDIR)/rebuild.c \
 
86 $(SRCDIR)/report.c \
87 $(SRCDIR)/rss.c \
88 $(SRCDIR)/schema.c \
89 $(SRCDIR)/search.c \
90 $(SRCDIR)/setup.c \
@@ -101,10 +102,11 @@
101 $(SRCDIR)/th_main.c \
102 $(SRCDIR)/timeline.c \
103 $(SRCDIR)/tkt.c \
104 $(SRCDIR)/tktsetup.c \
105 $(SRCDIR)/undo.c \
 
106 $(SRCDIR)/update.c \
107 $(SRCDIR)/url.c \
108 $(SRCDIR)/user.c \
109 $(SRCDIR)/utf8.c \
110 $(SRCDIR)/verify.c \
@@ -185,10 +187,11 @@
185 $(OBJDIR)/pivot_.c \
186 $(OBJDIR)/popen_.c \
187 $(OBJDIR)/pqueue_.c \
188 $(OBJDIR)/printf_.c \
189 $(OBJDIR)/rebuild_.c \
 
190 $(OBJDIR)/report_.c \
191 $(OBJDIR)/rss_.c \
192 $(OBJDIR)/schema_.c \
193 $(OBJDIR)/search_.c \
194 $(OBJDIR)/setup_.c \
@@ -205,10 +208,11 @@
205 $(OBJDIR)/th_main_.c \
206 $(OBJDIR)/timeline_.c \
207 $(OBJDIR)/tkt_.c \
208 $(OBJDIR)/tktsetup_.c \
209 $(OBJDIR)/undo_.c \
 
210 $(OBJDIR)/update_.c \
211 $(OBJDIR)/url_.c \
212 $(OBJDIR)/user_.c \
213 $(OBJDIR)/utf8_.c \
214 $(OBJDIR)/verify_.c \
@@ -289,10 +293,11 @@
289 $(OBJDIR)/pivot.o \
290 $(OBJDIR)/popen.o \
291 $(OBJDIR)/pqueue.o \
292 $(OBJDIR)/printf.o \
293 $(OBJDIR)/rebuild.o \
 
294 $(OBJDIR)/report.o \
295 $(OBJDIR)/rss.o \
296 $(OBJDIR)/schema.o \
297 $(OBJDIR)/search.o \
298 $(OBJDIR)/setup.o \
@@ -309,10 +314,11 @@
309 $(OBJDIR)/th_main.o \
310 $(OBJDIR)/timeline.o \
311 $(OBJDIR)/tkt.o \
312 $(OBJDIR)/tktsetup.o \
313 $(OBJDIR)/undo.o \
 
314 $(OBJDIR)/update.o \
315 $(OBJDIR)/url.o \
316 $(OBJDIR)/user.o \
317 $(OBJDIR)/utf8.o \
318 $(OBJDIR)/verify.o \
@@ -390,11 +396,11 @@
390
391
392 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
393 $(OBJDIR)/mkindex $(TRANS_SRC) >$@
394 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
395 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
396 touch $(OBJDIR)/headers
397 $(OBJDIR)/headers: Makefile
398 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/json_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
399 Makefile:
400 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
@@ -878,10 +884,17 @@
878
879 $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
880 $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
881
882 $(OBJDIR)/rebuild.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
883 $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
884 $(OBJDIR)/translate $(SRCDIR)/report.c >$(OBJDIR)/report_.c
885
886 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
887 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
@@ -1018,10 +1031,17 @@
1018
1019 $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
1020 $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
1021
1022 $(OBJDIR)/undo.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
1023 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1024 $(OBJDIR)/translate $(SRCDIR)/update.c >$(OBJDIR)/update_.c
1025
1026 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1027 $(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c
1028
--- src/main.mk
+++ src/main.mk
@@ -81,10 +81,11 @@
81 $(SRCDIR)/pivot.c \
82 $(SRCDIR)/popen.c \
83 $(SRCDIR)/pqueue.c \
84 $(SRCDIR)/printf.c \
85 $(SRCDIR)/rebuild.c \
86 $(SRCDIR)/regexp.c \
87 $(SRCDIR)/report.c \
88 $(SRCDIR)/rss.c \
89 $(SRCDIR)/schema.c \
90 $(SRCDIR)/search.c \
91 $(SRCDIR)/setup.c \
@@ -101,10 +102,11 @@
102 $(SRCDIR)/th_main.c \
103 $(SRCDIR)/timeline.c \
104 $(SRCDIR)/tkt.c \
105 $(SRCDIR)/tktsetup.c \
106 $(SRCDIR)/undo.c \
107 $(SRCDIR)/unicode.c \
108 $(SRCDIR)/update.c \
109 $(SRCDIR)/url.c \
110 $(SRCDIR)/user.c \
111 $(SRCDIR)/utf8.c \
112 $(SRCDIR)/verify.c \
@@ -185,10 +187,11 @@
187 $(OBJDIR)/pivot_.c \
188 $(OBJDIR)/popen_.c \
189 $(OBJDIR)/pqueue_.c \
190 $(OBJDIR)/printf_.c \
191 $(OBJDIR)/rebuild_.c \
192 $(OBJDIR)/regexp_.c \
193 $(OBJDIR)/report_.c \
194 $(OBJDIR)/rss_.c \
195 $(OBJDIR)/schema_.c \
196 $(OBJDIR)/search_.c \
197 $(OBJDIR)/setup_.c \
@@ -205,10 +208,11 @@
208 $(OBJDIR)/th_main_.c \
209 $(OBJDIR)/timeline_.c \
210 $(OBJDIR)/tkt_.c \
211 $(OBJDIR)/tktsetup_.c \
212 $(OBJDIR)/undo_.c \
213 $(OBJDIR)/unicode_.c \
214 $(OBJDIR)/update_.c \
215 $(OBJDIR)/url_.c \
216 $(OBJDIR)/user_.c \
217 $(OBJDIR)/utf8_.c \
218 $(OBJDIR)/verify_.c \
@@ -289,10 +293,11 @@
293 $(OBJDIR)/pivot.o \
294 $(OBJDIR)/popen.o \
295 $(OBJDIR)/pqueue.o \
296 $(OBJDIR)/printf.o \
297 $(OBJDIR)/rebuild.o \
298 $(OBJDIR)/regexp.o \
299 $(OBJDIR)/report.o \
300 $(OBJDIR)/rss.o \
301 $(OBJDIR)/schema.o \
302 $(OBJDIR)/search.o \
303 $(OBJDIR)/setup.o \
@@ -309,10 +314,11 @@
314 $(OBJDIR)/th_main.o \
315 $(OBJDIR)/timeline.o \
316 $(OBJDIR)/tkt.o \
317 $(OBJDIR)/tktsetup.o \
318 $(OBJDIR)/undo.o \
319 $(OBJDIR)/unicode.o \
320 $(OBJDIR)/update.o \
321 $(OBJDIR)/url.o \
322 $(OBJDIR)/user.o \
323 $(OBJDIR)/utf8.o \
324 $(OBJDIR)/verify.o \
@@ -390,11 +396,11 @@
396
397
398 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
399 $(OBJDIR)/mkindex $(TRANS_SRC) >$@
400 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
401 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
402 touch $(OBJDIR)/headers
403 $(OBJDIR)/headers: Makefile
404 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/json_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
405 Makefile:
406 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
@@ -878,10 +884,17 @@
884
885 $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
886 $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
887
888 $(OBJDIR)/rebuild.h: $(OBJDIR)/headers
889 $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate
890 $(OBJDIR)/translate $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
891
892 $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h
893 $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
894
895 $(OBJDIR)/regexp.h: $(OBJDIR)/headers
896 $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
897 $(OBJDIR)/translate $(SRCDIR)/report.c >$(OBJDIR)/report_.c
898
899 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
900 $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c
@@ -1018,10 +1031,17 @@
1031
1032 $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
1033 $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
1034
1035 $(OBJDIR)/undo.h: $(OBJDIR)/headers
1036 $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate
1037 $(OBJDIR)/translate $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
1038
1039 $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1040 $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1041
1042 $(OBJDIR)/unicode.h: $(OBJDIR)/headers
1043 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1044 $(OBJDIR)/translate $(SRCDIR)/update.c >$(OBJDIR)/update_.c
1045
1046 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1047 $(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c
1048
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -84,10 +84,11 @@
8484
pivot
8585
popen
8686
pqueue
8787
printf
8888
rebuild
89
+ regexp
8990
report
9091
rss
9192
schema
9293
search
9394
setup
@@ -104,10 +105,11 @@
104105
th_main
105106
timeline
106107
tkt
107108
tktsetup
108109
undo
110
+ unicode
109111
update
110112
url
111113
user
112114
utf8
113115
verify
114116
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -84,10 +84,11 @@
84 pivot
85 popen
86 pqueue
87 printf
88 rebuild
 
89 report
90 rss
91 schema
92 search
93 setup
@@ -104,10 +105,11 @@
104 th_main
105 timeline
106 tkt
107 tktsetup
108 undo
 
109 update
110 url
111 user
112 utf8
113 verify
114
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -84,10 +84,11 @@
84 pivot
85 popen
86 pqueue
87 printf
88 rebuild
89 regexp
90 report
91 rss
92 schema
93 search
94 setup
@@ -104,10 +105,11 @@
105 th_main
106 timeline
107 tkt
108 tktsetup
109 undo
110 unicode
111 update
112 url
113 user
114 utf8
115 verify
116
+21 -3
--- src/manifest.c
+++ src/manifest.c
@@ -430,11 +430,11 @@
430430
zTarget = next_token(&x, &nTarget);
431431
zSrc = next_token(&x, &nSrc);
432432
if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
433433
if( p->zAttachName!=0 ) goto manifest_syntax_error;
434434
defossilize(zName);
435
- if( !file_is_simple_pathname(zName) ){
435
+ if( !file_is_simple_pathname(zName, 0) ){
436436
SYNTAX("invalid filename on A-card");
437437
}
438438
defossilize(zTarget);
439439
if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
440440
&& !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
@@ -524,11 +524,11 @@
524524
case 'F': {
525525
char *zName, *zPerm, *zPriorName;
526526
zName = next_token(&x,0);
527527
if( zName==0 ) SYNTAX("missing filename on F-card");
528528
defossilize(zName);
529
- if( !file_is_simple_pathname(zName) ){
529
+ if( !file_is_simple_pathname(zName, 0) ){
530530
SYNTAX("F-card filename is not a simple path");
531531
}
532532
zUuid = next_token(&x, &sz);
533533
if( p->zBaseline==0 || zUuid!=0 ){
534534
if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size");
@@ -536,11 +536,11 @@
536536
}
537537
zPerm = next_token(&x,0);
538538
zPriorName = next_token(&x,0);
539539
if( zPriorName ){
540540
defossilize(zPriorName);
541
- if( !file_is_simple_pathname(zPriorName) ){
541
+ if( !file_is_simple_pathname(zPriorName, 0) ){
542542
SYNTAX("F-card old filename is not a simple path");
543543
}
544544
}
545545
if( p->nFile>=p->nFileAlloc ){
546546
p->nFileAlloc = p->nFileAlloc*2 + 10;
@@ -1983,5 +1983,23 @@
19831983
manifest_destroy(p);
19841984
}
19851985
assert( blob_is_reset(pContent) );
19861986
return 1;
19871987
}
1988
+
1989
+/*
1990
+** COMMAND: test-crosslink
1991
+**
1992
+** Usage: %fossil test-crosslink RECORDID
1993
+**
1994
+** Run the manifest_crosslink() routine on the artifact with the given
1995
+** record ID. This is typically done in the debugger.
1996
+*/
1997
+void test_crosslink_cmd(void){
1998
+ int rid;
1999
+ Blob content;
2000
+ db_find_and_open_repository(0, 0);
2001
+ if( g.argc!=3 ) usage("RECORDID");
2002
+ rid = name_to_rid(g.argv[2]);
2003
+ content_get(rid, &content);
2004
+ manifest_crosslink(rid, &content);
2005
+}
19882006
--- src/manifest.c
+++ src/manifest.c
@@ -430,11 +430,11 @@
430 zTarget = next_token(&x, &nTarget);
431 zSrc = next_token(&x, &nSrc);
432 if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
433 if( p->zAttachName!=0 ) goto manifest_syntax_error;
434 defossilize(zName);
435 if( !file_is_simple_pathname(zName) ){
436 SYNTAX("invalid filename on A-card");
437 }
438 defossilize(zTarget);
439 if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
440 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
@@ -524,11 +524,11 @@
524 case 'F': {
525 char *zName, *zPerm, *zPriorName;
526 zName = next_token(&x,0);
527 if( zName==0 ) SYNTAX("missing filename on F-card");
528 defossilize(zName);
529 if( !file_is_simple_pathname(zName) ){
530 SYNTAX("F-card filename is not a simple path");
531 }
532 zUuid = next_token(&x, &sz);
533 if( p->zBaseline==0 || zUuid!=0 ){
534 if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size");
@@ -536,11 +536,11 @@
536 }
537 zPerm = next_token(&x,0);
538 zPriorName = next_token(&x,0);
539 if( zPriorName ){
540 defossilize(zPriorName);
541 if( !file_is_simple_pathname(zPriorName) ){
542 SYNTAX("F-card old filename is not a simple path");
543 }
544 }
545 if( p->nFile>=p->nFileAlloc ){
546 p->nFileAlloc = p->nFileAlloc*2 + 10;
@@ -1983,5 +1983,23 @@
1983 manifest_destroy(p);
1984 }
1985 assert( blob_is_reset(pContent) );
1986 return 1;
1987 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1988
--- src/manifest.c
+++ src/manifest.c
@@ -430,11 +430,11 @@
430 zTarget = next_token(&x, &nTarget);
431 zSrc = next_token(&x, &nSrc);
432 if( zName==0 || zTarget==0 ) goto manifest_syntax_error;
433 if( p->zAttachName!=0 ) goto manifest_syntax_error;
434 defossilize(zName);
435 if( !file_is_simple_pathname(zName, 0) ){
436 SYNTAX("invalid filename on A-card");
437 }
438 defossilize(zTarget);
439 if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE))
440 && !wiki_name_is_wellformed((const unsigned char *)zTarget) ){
@@ -524,11 +524,11 @@
524 case 'F': {
525 char *zName, *zPerm, *zPriorName;
526 zName = next_token(&x,0);
527 if( zName==0 ) SYNTAX("missing filename on F-card");
528 defossilize(zName);
529 if( !file_is_simple_pathname(zName, 0) ){
530 SYNTAX("F-card filename is not a simple path");
531 }
532 zUuid = next_token(&x, &sz);
533 if( p->zBaseline==0 || zUuid!=0 ){
534 if( sz!=UUID_SIZE ) SYNTAX("F-card UUID is the wrong size");
@@ -536,11 +536,11 @@
536 }
537 zPerm = next_token(&x,0);
538 zPriorName = next_token(&x,0);
539 if( zPriorName ){
540 defossilize(zPriorName);
541 if( !file_is_simple_pathname(zPriorName, 0) ){
542 SYNTAX("F-card old filename is not a simple path");
543 }
544 }
545 if( p->nFile>=p->nFileAlloc ){
546 p->nFileAlloc = p->nFileAlloc*2 + 10;
@@ -1983,5 +1983,23 @@
1983 manifest_destroy(p);
1984 }
1985 assert( blob_is_reset(pContent) );
1986 return 1;
1987 }
1988
1989 /*
1990 ** COMMAND: test-crosslink
1991 **
1992 ** Usage: %fossil test-crosslink RECORDID
1993 **
1994 ** Run the manifest_crosslink() routine on the artifact with the given
1995 ** record ID. This is typically done in the debugger.
1996 */
1997 void test_crosslink_cmd(void){
1998 int rid;
1999 Blob content;
2000 db_find_and_open_repository(0, 0);
2001 if( g.argc!=3 ) usage("RECORDID");
2002 rid = name_to_rid(g.argv[2]);
2003 content_get(rid, &content);
2004 manifest_crosslink(rid, &content);
2005 }
2006
+72 -9
--- src/merge.c
+++ src/merge.c
@@ -57,19 +57,22 @@
5757
5858
5959
/*
6060
** COMMAND: merge
6161
**
62
-** Usage: %fossil merge ?OPTIONS? VERSION
62
+** Usage: %fossil merge ?OPTIONS? ?VERSION?
6363
**
6464
** The argument VERSION is a version that should be merged into the
6565
** current checkout. All changes from VERSION back to the nearest
6666
** common ancestor are merged. Except, if either of the --cherrypick or
6767
** --backout options are used only the changes associated with the
6868
** single check-in VERSION are merged. The --backout option causes
6969
** the changes associated with VERSION to be removed from the current
7070
** checkout rather than added.
71
+**
72
+** If the VERSION argument is omitted, then Fossil attempts to find
73
+** a recent fork on the current branch to merge.
7174
**
7275
** Only file content is merged. The result continues to use the
7376
** file and directory names from the current checkout even if those
7477
** names might have been changed in the branch being merged in.
7578
**
@@ -87,11 +90,11 @@
8790
** option overrides the "binary-glob" setting.
8891
**
8992
** --nochange | -n Dryrun: do not actually make any changes; just
9093
** show what would have happened.
9194
**
92
-** --case-sensitive BOOL Overwrite the case-sensitive setting. If false,
95
+** --case-sensitive BOOL Override the case-sensitive setting. If false,
9396
** files whose names differ only in case are taken
9497
** to be the same file.
9598
**
9699
** --force | -f Force the merge even if it would be a no-op.
97100
*/
@@ -131,24 +134,84 @@
131134
zBinGlob = find_option("binary",0,1);
132135
nochangeFlag = find_option("nochange","n",0)!=0;
133136
forceFlag = find_option("force","f",0)!=0;
134137
zPivot = find_option("baseline",0,1);
135138
capture_case_sensitive_option();
136
- if( g.argc!=3 ){
137
- usage("VERSION");
138
- }
139
+ verify_all_options();
139140
db_must_be_within_tree();
140141
caseSensitive = filenames_are_case_sensitive();
141142
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
142143
vid = db_lget_int("checkout", 0);
143144
if( vid==0 ){
144145
fossil_fatal("nothing is checked out");
145146
}
146
- mid = name_to_typed_rid(g.argv[2], "ci");
147
- if( mid==0 || !is_a_version(mid) ){
148
- fossil_fatal("not a version: %s", g.argv[2]);
147
+
148
+ /* Find mid, the artifactID of the version to be merged into the current
149
+ ** check-out */
150
+ if( g.argc==3 ){
151
+ /* Mid is specified as an argument on the command-line */
152
+ mid = name_to_typed_rid(g.argv[2], "ci");
153
+ if( mid==0 || !is_a_version(mid) ){
154
+ fossil_fatal("not a version: %s", g.argv[2]);
155
+ }
156
+ }else if( g.argc==2 ){
157
+ /* No version specified on the command-line so pick the most recent
158
+ ** leaf that is (1) not the version currently checked out and (2)
159
+ ** has not already been merged into the current checkout and (3)
160
+ ** the leaf is not closed and (4) the leaf is in the same branch
161
+ ** as the current checkout.
162
+ */
163
+ Stmt q;
164
+ if( pickFlag || backoutFlag ){
165
+ fossil_fatal("cannot use --cherrypick or --backout with a fork merge");
166
+ }
167
+ mid = db_int(0,
168
+ "SELECT leaf.rid"
169
+ " FROM leaf, event"
170
+ " WHERE leaf.rid=event.objid"
171
+ " AND leaf.rid!=%d" /* Constraint (1) */
172
+ " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */
173
+ " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */
174
+ " WHERE rid=leaf.rid"
175
+ " AND tagid=%d"
176
+ " AND tagtype>0)"
177
+ " AND (SELECT value FROM tagxref" /* Constraint (4) */
178
+ " WHERE tagid=%d AND rid=%d AND tagtype>0) ="
179
+ " (SELECT value FROM tagxref"
180
+ " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)"
181
+ " ORDER BY event.mtime DESC LIMIT 1",
182
+ vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH
183
+ );
184
+ if( mid==0 ){
185
+ fossil_fatal("no unmerged forks of branch \"%s\"",
186
+ db_text(0, "SELECT value FROM tagxref"
187
+ " WHERE tagid=%d AND rid=%d AND tagtype>0",
188
+ TAG_BRANCH, vid)
189
+ );
190
+ }
191
+ db_prepare(&q,
192
+ "SELECT blob.uuid,"
193
+ " datetime(event.mtime,'localtime'),"
194
+ " coalesce(ecomment, comment),"
195
+ " coalesce(euser, user)"
196
+ " FROM event, blob"
197
+ " WHERE event.objid=%d AND blob.rid=%d",
198
+ mid, mid
199
+ );
200
+ if( db_step(&q)==SQLITE_ROW ){
201
+ char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
202
+ db_column_text(&q, 0), db_column_text(&q, 1),
203
+ db_column_text(&q, 3), db_column_text(&q, 2));
204
+ comment_print(zCom, 0, 79);
205
+ fossil_free(zCom);
206
+ }
207
+ db_finalize(&q);
208
+ }else{
209
+ usage("?OPTIONS? ?VERSION?");
210
+ return;
149211
}
212
+
150213
if( zPivot ){
151214
pid = name_to_typed_rid(zPivot, "ci");
152215
if( pid==0 || !is_a_version(pid) ){
153216
fossil_fatal("not a version: %s", zPivot);
154217
}
@@ -347,11 +410,11 @@
347410
"SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
348411
);
349412
while( db_step(&q)==SQLITE_ROW ){
350413
int idm = db_column_int(&q, 0);
351414
char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
352
- fossil_warning("WARNING - no common ancestor: %s\n", zName);
415
+ fossil_warning("WARNING - no common ancestor: %s", zName);
353416
free(zName);
354417
db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
355418
}
356419
db_finalize(&q);
357420
358421
--- src/merge.c
+++ src/merge.c
@@ -57,19 +57,22 @@
57
58
59 /*
60 ** COMMAND: merge
61 **
62 ** Usage: %fossil merge ?OPTIONS? VERSION
63 **
64 ** The argument VERSION is a version that should be merged into the
65 ** current checkout. All changes from VERSION back to the nearest
66 ** common ancestor are merged. Except, if either of the --cherrypick or
67 ** --backout options are used only the changes associated with the
68 ** single check-in VERSION are merged. The --backout option causes
69 ** the changes associated with VERSION to be removed from the current
70 ** checkout rather than added.
 
 
 
71 **
72 ** Only file content is merged. The result continues to use the
73 ** file and directory names from the current checkout even if those
74 ** names might have been changed in the branch being merged in.
75 **
@@ -87,11 +90,11 @@
87 ** option overrides the "binary-glob" setting.
88 **
89 ** --nochange | -n Dryrun: do not actually make any changes; just
90 ** show what would have happened.
91 **
92 ** --case-sensitive BOOL Overwrite the case-sensitive setting. If false,
93 ** files whose names differ only in case are taken
94 ** to be the same file.
95 **
96 ** --force | -f Force the merge even if it would be a no-op.
97 */
@@ -131,24 +134,84 @@
131 zBinGlob = find_option("binary",0,1);
132 nochangeFlag = find_option("nochange","n",0)!=0;
133 forceFlag = find_option("force","f",0)!=0;
134 zPivot = find_option("baseline",0,1);
135 capture_case_sensitive_option();
136 if( g.argc!=3 ){
137 usage("VERSION");
138 }
139 db_must_be_within_tree();
140 caseSensitive = filenames_are_case_sensitive();
141 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
142 vid = db_lget_int("checkout", 0);
143 if( vid==0 ){
144 fossil_fatal("nothing is checked out");
145 }
146 mid = name_to_typed_rid(g.argv[2], "ci");
147 if( mid==0 || !is_a_version(mid) ){
148 fossil_fatal("not a version: %s", g.argv[2]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149 }
 
150 if( zPivot ){
151 pid = name_to_typed_rid(zPivot, "ci");
152 if( pid==0 || !is_a_version(pid) ){
153 fossil_fatal("not a version: %s", zPivot);
154 }
@@ -347,11 +410,11 @@
347 "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
348 );
349 while( db_step(&q)==SQLITE_ROW ){
350 int idm = db_column_int(&q, 0);
351 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
352 fossil_warning("WARNING - no common ancestor: %s\n", zName);
353 free(zName);
354 db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
355 }
356 db_finalize(&q);
357
358
--- src/merge.c
+++ src/merge.c
@@ -57,19 +57,22 @@
57
58
59 /*
60 ** COMMAND: merge
61 **
62 ** Usage: %fossil merge ?OPTIONS? ?VERSION?
63 **
64 ** The argument VERSION is a version that should be merged into the
65 ** current checkout. All changes from VERSION back to the nearest
66 ** common ancestor are merged. Except, if either of the --cherrypick or
67 ** --backout options are used only the changes associated with the
68 ** single check-in VERSION are merged. The --backout option causes
69 ** the changes associated with VERSION to be removed from the current
70 ** checkout rather than added.
71 **
72 ** If the VERSION argument is omitted, then Fossil attempts to find
73 ** a recent fork on the current branch to merge.
74 **
75 ** Only file content is merged. The result continues to use the
76 ** file and directory names from the current checkout even if those
77 ** names might have been changed in the branch being merged in.
78 **
@@ -87,11 +90,11 @@
90 ** option overrides the "binary-glob" setting.
91 **
92 ** --nochange | -n Dryrun: do not actually make any changes; just
93 ** show what would have happened.
94 **
95 ** --case-sensitive BOOL Override the case-sensitive setting. If false,
96 ** files whose names differ only in case are taken
97 ** to be the same file.
98 **
99 ** --force | -f Force the merge even if it would be a no-op.
100 */
@@ -131,24 +134,84 @@
134 zBinGlob = find_option("binary",0,1);
135 nochangeFlag = find_option("nochange","n",0)!=0;
136 forceFlag = find_option("force","f",0)!=0;
137 zPivot = find_option("baseline",0,1);
138 capture_case_sensitive_option();
139 verify_all_options();
 
 
140 db_must_be_within_tree();
141 caseSensitive = filenames_are_case_sensitive();
142 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
143 vid = db_lget_int("checkout", 0);
144 if( vid==0 ){
145 fossil_fatal("nothing is checked out");
146 }
147
148 /* Find mid, the artifactID of the version to be merged into the current
149 ** check-out */
150 if( g.argc==3 ){
151 /* Mid is specified as an argument on the command-line */
152 mid = name_to_typed_rid(g.argv[2], "ci");
153 if( mid==0 || !is_a_version(mid) ){
154 fossil_fatal("not a version: %s", g.argv[2]);
155 }
156 }else if( g.argc==2 ){
157 /* No version specified on the command-line so pick the most recent
158 ** leaf that is (1) not the version currently checked out and (2)
159 ** has not already been merged into the current checkout and (3)
160 ** the leaf is not closed and (4) the leaf is in the same branch
161 ** as the current checkout.
162 */
163 Stmt q;
164 if( pickFlag || backoutFlag ){
165 fossil_fatal("cannot use --cherrypick or --backout with a fork merge");
166 }
167 mid = db_int(0,
168 "SELECT leaf.rid"
169 " FROM leaf, event"
170 " WHERE leaf.rid=event.objid"
171 " AND leaf.rid!=%d" /* Constraint (1) */
172 " AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */
173 " AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */
174 " WHERE rid=leaf.rid"
175 " AND tagid=%d"
176 " AND tagtype>0)"
177 " AND (SELECT value FROM tagxref" /* Constraint (4) */
178 " WHERE tagid=%d AND rid=%d AND tagtype>0) ="
179 " (SELECT value FROM tagxref"
180 " WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)"
181 " ORDER BY event.mtime DESC LIMIT 1",
182 vid, TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH
183 );
184 if( mid==0 ){
185 fossil_fatal("no unmerged forks of branch \"%s\"",
186 db_text(0, "SELECT value FROM tagxref"
187 " WHERE tagid=%d AND rid=%d AND tagtype>0",
188 TAG_BRANCH, vid)
189 );
190 }
191 db_prepare(&q,
192 "SELECT blob.uuid,"
193 " datetime(event.mtime,'localtime'),"
194 " coalesce(ecomment, comment),"
195 " coalesce(euser, user)"
196 " FROM event, blob"
197 " WHERE event.objid=%d AND blob.rid=%d",
198 mid, mid
199 );
200 if( db_step(&q)==SQLITE_ROW ){
201 char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
202 db_column_text(&q, 0), db_column_text(&q, 1),
203 db_column_text(&q, 3), db_column_text(&q, 2));
204 comment_print(zCom, 0, 79);
205 fossil_free(zCom);
206 }
207 db_finalize(&q);
208 }else{
209 usage("?OPTIONS? ?VERSION?");
210 return;
211 }
212
213 if( zPivot ){
214 pid = name_to_typed_rid(zPivot, "ci");
215 if( pid==0 || !is_a_version(pid) ){
216 fossil_fatal("not a version: %s", zPivot);
217 }
@@ -347,11 +410,11 @@
410 "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
411 );
412 while( db_step(&q)==SQLITE_ROW ){
413 int idm = db_column_int(&q, 0);
414 char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
415 fossil_warning("WARNING - no common ancestor: %s", zName);
416 free(zName);
417 db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
418 }
419 db_finalize(&q);
420
421
+2 -2
--- src/merge3.c
+++ src/merge3.c
@@ -175,12 +175,12 @@
175175
** is the number of lines of text to copy directly from the pivot,
176176
** the second integer is the number of lines of text to omit from the
177177
** pivot, and the third integer is the number of lines of text that are
178178
** inserted. The edit array ends with a triple of 0,0,0.
179179
*/
180
- aC1 = text_diff(pPivot, pV1, 0, 0);
181
- aC2 = text_diff(pPivot, pV2, 0, 0);
180
+ aC1 = text_diff(pPivot, pV1, 0, 0, 0);
181
+ aC2 = text_diff(pPivot, pV2, 0, 0, 0);
182182
if( aC1==0 || aC2==0 ){
183183
free(aC1);
184184
free(aC2);
185185
return -1;
186186
}
187187
--- src/merge3.c
+++ src/merge3.c
@@ -175,12 +175,12 @@
175 ** is the number of lines of text to copy directly from the pivot,
176 ** the second integer is the number of lines of text to omit from the
177 ** pivot, and the third integer is the number of lines of text that are
178 ** inserted. The edit array ends with a triple of 0,0,0.
179 */
180 aC1 = text_diff(pPivot, pV1, 0, 0);
181 aC2 = text_diff(pPivot, pV2, 0, 0);
182 if( aC1==0 || aC2==0 ){
183 free(aC1);
184 free(aC2);
185 return -1;
186 }
187
--- src/merge3.c
+++ src/merge3.c
@@ -175,12 +175,12 @@
175 ** is the number of lines of text to copy directly from the pivot,
176 ** the second integer is the number of lines of text to omit from the
177 ** pivot, and the third integer is the number of lines of text that are
178 ** inserted. The edit array ends with a triple of 0,0,0.
179 */
180 aC1 = text_diff(pPivot, pV1, 0, 0, 0);
181 aC2 = text_diff(pPivot, pV2, 0, 0, 0);
182 if( aC1==0 || aC2==0 ){
183 free(aC1);
184 free(aC2);
185 return -1;
186 }
187
+15
--- src/printf.c
+++ src/printf.c
@@ -871,10 +871,25 @@
871871
Blob b = empty_blob;
872872
vxprintf(&b, zFormat, ap);
873873
fossil_puts(blob_str(&b), 0);
874874
blob_reset(&b);
875875
}
876
+ va_end(ap);
877
+}
878
+
879
+/*
880
+** Print a trace message on standard error.
881
+*/
882
+void fossil_trace(const char *zFormat, ...){
883
+ va_list ap;
884
+ Blob b;
885
+ va_start(ap, zFormat);
886
+ b = empty_blob;
887
+ vxprintf(&b, zFormat, ap);
888
+ fossil_puts(blob_str(&b), 1);
889
+ blob_reset(&b);
890
+ va_end(ap);
876891
}
877892
878893
/*
879894
** Like strcmp() except that it accepts NULL pointers. NULL sorts before
880895
** all non-NULL string pointers. Also, this strcmp() is a binary comparison
881896
--- src/printf.c
+++ src/printf.c
@@ -871,10 +871,25 @@
871 Blob b = empty_blob;
872 vxprintf(&b, zFormat, ap);
873 fossil_puts(blob_str(&b), 0);
874 blob_reset(&b);
875 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876 }
877
878 /*
879 ** Like strcmp() except that it accepts NULL pointers. NULL sorts before
880 ** all non-NULL string pointers. Also, this strcmp() is a binary comparison
881
--- src/printf.c
+++ src/printf.c
@@ -871,10 +871,25 @@
871 Blob b = empty_blob;
872 vxprintf(&b, zFormat, ap);
873 fossil_puts(blob_str(&b), 0);
874 blob_reset(&b);
875 }
876 va_end(ap);
877 }
878
879 /*
880 ** Print a trace message on standard error.
881 */
882 void fossil_trace(const char *zFormat, ...){
883 va_list ap;
884 Blob b;
885 va_start(ap, zFormat);
886 b = empty_blob;
887 vxprintf(&b, zFormat, ap);
888 fossil_puts(blob_str(&b), 1);
889 blob_reset(&b);
890 va_end(ap);
891 }
892
893 /*
894 ** Like strcmp() except that it accepts NULL pointers. NULL sorts before
895 ** all non-NULL string pointers. Also, this strcmp() is a binary comparison
896
--- src/rebuild.c
+++ src/rebuild.c
@@ -614,10 +614,13 @@
614614
}
615615
if( activateWal ){
616616
db_multi_exec("PRAGMA journal_mode=WAL;");
617617
}
618618
}
619
+ fossil_print("Analyzing the database... "); fflush(stdout);
620
+ db_multi_exec("analyze");
621
+ fossil_print("done\n");
619622
if( showStats ){
620623
static struct { int idx; const char *zLabel; } aStat[] = {
621624
{ CFTYPE_ANY, "Artifacts:" },
622625
{ CFTYPE_MANIFEST, "Manifests:" },
623626
{ CFTYPE_CLUSTER, "Clusters:" },
624627
625628
ADDED src/regexp.c
--- src/rebuild.c
+++ src/rebuild.c
@@ -614,10 +614,13 @@
614 }
615 if( activateWal ){
616 db_multi_exec("PRAGMA journal_mode=WAL;");
617 }
618 }
 
 
 
619 if( showStats ){
620 static struct { int idx; const char *zLabel; } aStat[] = {
621 { CFTYPE_ANY, "Artifacts:" },
622 { CFTYPE_MANIFEST, "Manifests:" },
623 { CFTYPE_CLUSTER, "Clusters:" },
624
625 DDED src/regexp.c
--- src/rebuild.c
+++ src/rebuild.c
@@ -614,10 +614,13 @@
614 }
615 if( activateWal ){
616 db_multi_exec("PRAGMA journal_mode=WAL;");
617 }
618 }
619 fossil_print("Analyzing the database... "); fflush(stdout);
620 db_multi_exec("analyze");
621 fossil_print("done\n");
622 if( showStats ){
623 static struct { int idx; const char *zLabel; } aStat[] = {
624 { CFTYPE_ANY, "Artifacts:" },
625 { CFTYPE_MANIFEST, "Manifests:" },
626 { CFTYPE_CLUSTER, "Clusters:" },
627
628 DDED src/regexp.c
+50
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -0,0 +1,50 @@
1
+/*
2
+** Co#ifndef SQL(sizeof(aSp*
3
+** Co#ifndef SQLITE_MAX_REGEXP_REPEAT
4
+# define SQLITE_MAX_REGEXP_REPEAT 999
5
+#endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
6
+*/
7
+int re_maxlen(void){
8
+ return 1000) : 1000;
9
+}re_maxlen(), noCase) Co0);
10
+ /, 0 <= p,q <= 999*
11
+** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
12
+** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
13
+*/
14
+static void grepprintfREPEAT
15
+# define SQLITE_MAX_REGEXP_REPEAT 999
16
+#endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
17
+*/
18
+int re_maxlen(void){
19
+ return 1000) : 1000;
20
+}re_maxlen(), noCase) Co0);
21
+ /, 0 <= p,q <= 999*
22
+** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
23
+** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
24
+*/
25
+static void grepprintf har const
26
+ int argc, resqlite3char zInit[12];
27
+** Co#ifnde** Co#ifndef SQLITE_MAX_REGEXP_REPEAT
28
+# define SQLITE_MAX_REGEXP_REPEAT 999
29
+#endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
30
+*/
31
+int re_maxlen(void){
32
+ return 1000) : 1000;
33
+}re_maxlen(), noCase) Co0);
34
+ /, 0 <= p,q <= 999*
35
+** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
36
+** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
37
+*/
38
+static void grepprintfREPEAT
39
+# define SQLITE_MAX_REGEXP_REPEAT 999
40
+#endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
41
+*/
42
+int re_maxlen(void){
43
+ return 1000) : 1000;
44
+}re_maxlen(), noCase) Co0);
45
+ /, 0 <= p,q <= 999*
46
+** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
47
+** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
48
+*/
49
+static void grepprintf har const
50
+ int argc, resqlite3 strncmp(zIn+in.i, mem
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -0,0 +1,50 @@
1 /*
2 ** Co#ifndef SQL(sizeof(aSp*
3 ** Co#ifndef SQLITE_MAX_REGEXP_REPEAT
4 # define SQLITE_MAX_REGEXP_REPEAT 999
5 #endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
6 */
7 int re_maxlen(void){
8 return 1000) : 1000;
9 }re_maxlen(), noCase) Co0);
10 /, 0 <= p,q <= 999*
11 ** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
12 ** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
13 */
14 static void grepprintfREPEAT
15 # define SQLITE_MAX_REGEXP_REPEAT 999
16 #endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
17 */
18 int re_maxlen(void){
19 return 1000) : 1000;
20 }re_maxlen(), noCase) Co0);
21 /, 0 <= p,q <= 999*
22 ** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
23 ** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
24 */
25 static void grepprintf har const
26 int argc, resqlite3char zInit[12];
27 ** Co#ifnde** Co#ifndef SQLITE_MAX_REGEXP_REPEAT
28 # define SQLITE_MAX_REGEXP_REPEAT 999
29 #endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
30 */
31 int re_maxlen(void){
32 return 1000) : 1000;
33 }re_maxlen(), noCase) Co0);
34 /, 0 <= p,q <= 999*
35 ** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
36 ** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
37 */
38 static void grepprintfREPEAT
39 # define SQLITE_MAX_REGEXP_REPEAT 999
40 #endifcharacter3CC_INC>0 &&ute a reasonable limit on the length of the REGEXP NFA.
41 */
42 int re_maxlen(void){
43 return 1000) : 1000;
44 }re_maxlen(), noCase) Co0);
45 /, 0 <= p,q <= 999*
46 ** To help prevent DoS attacks, the values of p and q in the "{p,q}" syntax
47 ** are limited to SQLITE_MAX_REGEXP_REPEAT, default 999 return 1; return 1
48 */
49 static void grepprintf har const
50 int argc, resqlite3 strncmp(zIn+in.i, mem
--- src/schema.c
+++ src/schema.c
@@ -385,10 +385,11 @@
385385
@ CREATE TABLE ticket(
386386
@ -- Do not change any column that begins with tkt_
387387
@ tkt_id INTEGER PRIMARY KEY,
388388
@ tkt_uuid TEXT UNIQUE,
389389
@ tkt_mtime DATE,
390
+@ tkt_ctime DATE,
390391
@ -- Add as many field as required below this line
391392
@ type TEXT,
392393
@ status TEXT,
393394
@ subsystem TEXT,
394395
@ priority TEXT,
@@ -400,10 +401,11 @@
400401
@ comment TEXT
401402
@ );
402403
@ CREATE TABLE ticketchng(
403404
@ -- Do not change any column that begins with tkt_
404405
@ tkt_id INTEGER REFERENCES ticket,
406
+@ tkt_rid INTEGER REFERENCES blob,
405407
@ tkt_mtime DATE,
406408
@ -- Add as many fields as required below this line
407409
@ login TEXT,
408410
@ username TEXT,
409411
@ mimetype TEXT,
410412
--- src/schema.c
+++ src/schema.c
@@ -385,10 +385,11 @@
385 @ CREATE TABLE ticket(
386 @ -- Do not change any column that begins with tkt_
387 @ tkt_id INTEGER PRIMARY KEY,
388 @ tkt_uuid TEXT UNIQUE,
389 @ tkt_mtime DATE,
 
390 @ -- Add as many field as required below this line
391 @ type TEXT,
392 @ status TEXT,
393 @ subsystem TEXT,
394 @ priority TEXT,
@@ -400,10 +401,11 @@
400 @ comment TEXT
401 @ );
402 @ CREATE TABLE ticketchng(
403 @ -- Do not change any column that begins with tkt_
404 @ tkt_id INTEGER REFERENCES ticket,
 
405 @ tkt_mtime DATE,
406 @ -- Add as many fields as required below this line
407 @ login TEXT,
408 @ username TEXT,
409 @ mimetype TEXT,
410
--- src/schema.c
+++ src/schema.c
@@ -385,10 +385,11 @@
385 @ CREATE TABLE ticket(
386 @ -- Do not change any column that begins with tkt_
387 @ tkt_id INTEGER PRIMARY KEY,
388 @ tkt_uuid TEXT UNIQUE,
389 @ tkt_mtime DATE,
390 @ tkt_ctime DATE,
391 @ -- Add as many field as required below this line
392 @ type TEXT,
393 @ status TEXT,
394 @ subsystem TEXT,
395 @ priority TEXT,
@@ -400,10 +401,11 @@
401 @ comment TEXT
402 @ );
403 @ CREATE TABLE ticketchng(
404 @ -- Do not change any column that begins with tkt_
405 @ tkt_id INTEGER REFERENCES ticket,
406 @ tkt_rid INTEGER REFERENCES blob,
407 @ tkt_mtime DATE,
408 @ -- Add as many fields as required below this line
409 @ login TEXT,
410 @ username TEXT,
411 @ mimetype TEXT,
412
+47 -1
--- src/setup.c
+++ src/setup.c
@@ -94,11 +94,11 @@
9494
"Edit HTML text inserted at the top of every page");
9595
setup_menu_entry("Footer", "setup_footer",
9696
"Edit HTML text inserted at the bottom of every page");
9797
setup_menu_entry("Moderation", "setup_modreq",
9898
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
99
- "edits and attachments.");
99
+ " changes and attachments.");
100100
setup_menu_entry("Ad-Unit", "setup_adunit",
101101
"Edit HTML text for an ad unit inserted after the menu bar");
102102
setup_menu_entry("Logo", "setup_logo",
103103
"Change the logo and background images for the server");
104104
setup_menu_entry("Shunned", "shun",
@@ -109,10 +109,12 @@
109109
"A record of login attempts");
110110
setup_menu_entry("Stats", "stat",
111111
"Display repository statistics");
112112
setup_menu_entry("SQL", "admin_sql",
113113
"Enter raw SQL commands");
114
+ setup_menu_entry("TH1", "admin_th1",
115
+ "Enter raw TH1 commands");
114116
@ </table>
115117
116118
style_footer();
117119
}
118120
@@ -1759,8 +1761,52 @@
17591761
@ </tr>
17601762
}
17611763
sqlite3_finalize(pStmt);
17621764
@ </table>
17631765
}
1766
+ }
1767
+ style_footer();
1768
+}
1769
+
1770
+
1771
+/*
1772
+** WEBPAGE: admin_th1
1773
+**
1774
+** Run raw TH1 commands using the web interface. If Tcl integration was
1775
+** enabled at compile-time and the "tcl" setting is enabled, Tcl commands
1776
+** may be run as well.
1777
+*/
1778
+void th1_page(void){
1779
+ const char *zQ = P("q");
1780
+ int go = P("go")!=0;
1781
+ login_check_credentials();
1782
+ if( !g.perm.Setup ){
1783
+ login_needed();
1784
+ }
1785
+ db_begin_transaction();
1786
+ style_header("Raw TH1 Commands");
1787
+ @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
1788
+ @ run by this page. If Tcl integration was enabled at compile-time and
1789
+ @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
1790
+ @
1791
+ @ <form method="post" action="%s(g.zTop)/admin_th1">
1792
+ login_insert_csrf_secret();
1793
+ @ TH1:<br />
1794
+ @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br />
1795
+ @ <input type="submit" name="go" value="Run TH1">
1796
+ @ </form>
1797
+ if( go ){
1798
+ const char *zR;
1799
+ int rc;
1800
+ int n;
1801
+ @ <hr />
1802
+ login_verify_csrf_secret();
1803
+ rc = Th_Eval(g.interp, 0, zQ, -1);
1804
+ zR = Th_GetResult(g.interp, &n);
1805
+ if( rc==TH_OK ){
1806
+ @ <pre class="th1result">%h(zR)</pre>
1807
+ }else{
1808
+ @ <pre class="th1error">%h(zR)</pre>
1809
+ }
17641810
}
17651811
style_footer();
17661812
}
17671813
--- src/setup.c
+++ src/setup.c
@@ -94,11 +94,11 @@
94 "Edit HTML text inserted at the top of every page");
95 setup_menu_entry("Footer", "setup_footer",
96 "Edit HTML text inserted at the bottom of every page");
97 setup_menu_entry("Moderation", "setup_modreq",
98 "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
99 "edits and attachments.");
100 setup_menu_entry("Ad-Unit", "setup_adunit",
101 "Edit HTML text for an ad unit inserted after the menu bar");
102 setup_menu_entry("Logo", "setup_logo",
103 "Change the logo and background images for the server");
104 setup_menu_entry("Shunned", "shun",
@@ -109,10 +109,12 @@
109 "A record of login attempts");
110 setup_menu_entry("Stats", "stat",
111 "Display repository statistics");
112 setup_menu_entry("SQL", "admin_sql",
113 "Enter raw SQL commands");
 
 
114 @ </table>
115
116 style_footer();
117 }
118
@@ -1759,8 +1761,52 @@
1759 @ </tr>
1760 }
1761 sqlite3_finalize(pStmt);
1762 @ </table>
1763 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1764 }
1765 style_footer();
1766 }
1767
--- src/setup.c
+++ src/setup.c
@@ -94,11 +94,11 @@
94 "Edit HTML text inserted at the top of every page");
95 setup_menu_entry("Footer", "setup_footer",
96 "Edit HTML text inserted at the bottom of every page");
97 setup_menu_entry("Moderation", "setup_modreq",
98 "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
99 " changes and attachments.");
100 setup_menu_entry("Ad-Unit", "setup_adunit",
101 "Edit HTML text for an ad unit inserted after the menu bar");
102 setup_menu_entry("Logo", "setup_logo",
103 "Change the logo and background images for the server");
104 setup_menu_entry("Shunned", "shun",
@@ -109,10 +109,12 @@
109 "A record of login attempts");
110 setup_menu_entry("Stats", "stat",
111 "Display repository statistics");
112 setup_menu_entry("SQL", "admin_sql",
113 "Enter raw SQL commands");
114 setup_menu_entry("TH1", "admin_th1",
115 "Enter raw TH1 commands");
116 @ </table>
117
118 style_footer();
119 }
120
@@ -1759,8 +1761,52 @@
1761 @ </tr>
1762 }
1763 sqlite3_finalize(pStmt);
1764 @ </table>
1765 }
1766 }
1767 style_footer();
1768 }
1769
1770
1771 /*
1772 ** WEBPAGE: admin_th1
1773 **
1774 ** Run raw TH1 commands using the web interface. If Tcl integration was
1775 ** enabled at compile-time and the "tcl" setting is enabled, Tcl commands
1776 ** may be run as well.
1777 */
1778 void th1_page(void){
1779 const char *zQ = P("q");
1780 int go = P("go")!=0;
1781 login_check_credentials();
1782 if( !g.perm.Setup ){
1783 login_needed();
1784 }
1785 db_begin_transaction();
1786 style_header("Raw TH1 Commands");
1787 @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
1788 @ run by this page. If Tcl integration was enabled at compile-time and
1789 @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
1790 @
1791 @ <form method="post" action="%s(g.zTop)/admin_th1">
1792 login_insert_csrf_secret();
1793 @ TH1:<br />
1794 @ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br />
1795 @ <input type="submit" name="go" value="Run TH1">
1796 @ </form>
1797 if( go ){
1798 const char *zR;
1799 int rc;
1800 int n;
1801 @ <hr />
1802 login_verify_csrf_secret();
1803 rc = Th_Eval(g.interp, 0, zQ, -1);
1804 zR = Th_GetResult(g.interp, &n);
1805 if( rc==TH_OK ){
1806 @ <pre class="th1result">%h(zR)</pre>
1807 }else{
1808 @ <pre class="th1error">%h(zR)</pre>
1809 }
1810 }
1811 style_footer();
1812 }
1813
--- src/shell.c
+++ src/shell.c
@@ -1477,10 +1477,16 @@
14771477
p->zDbFilename, sqlite3_errmsg(db));
14781478
exit(1);
14791479
}
14801480
#ifndef SQLITE_OMIT_LOAD_EXTENSION
14811481
sqlite3_enable_load_extension(p->db, 1);
1482
+#endif
1483
+#ifdef SQLITE_ENABLE_REGEXP
1484
+ {
1485
+ extern int sqlite3_add_regexp_func(sqlite3*);
1486
+ sqlite3_add_regexp_func(db);
1487
+ }
14821488
#endif
14831489
}
14841490
}
14851491
14861492
/*
14871493
--- src/shell.c
+++ src/shell.c
@@ -1477,10 +1477,16 @@
1477 p->zDbFilename, sqlite3_errmsg(db));
1478 exit(1);
1479 }
1480 #ifndef SQLITE_OMIT_LOAD_EXTENSION
1481 sqlite3_enable_load_extension(p->db, 1);
 
 
 
 
 
 
1482 #endif
1483 }
1484 }
1485
1486 /*
1487
--- src/shell.c
+++ src/shell.c
@@ -1477,10 +1477,16 @@
1477 p->zDbFilename, sqlite3_errmsg(db));
1478 exit(1);
1479 }
1480 #ifndef SQLITE_OMIT_LOAD_EXTENSION
1481 sqlite3_enable_load_extension(p->db, 1);
1482 #endif
1483 #ifdef SQLITE_ENABLE_REGEXP
1484 {
1485 extern int sqlite3_add_regexp_func(sqlite3*);
1486 sqlite3_add_regexp_func(db);
1487 }
1488 #endif
1489 }
1490 }
1491
1492 /*
1493
+12 -7
--- src/skins.c
+++ src/skins.c
@@ -355,12 +355,13 @@
355355
@ div.footer a:visited { color: white; }
356356
@ div.footer a:hover { background-color: white; color: #558195; }
357357
@
358358
@ /* <verbatim> blocks */
359359
@ pre.verbatim {
360
-@ background-color: #f5f5f5;
361
-@ padding: 0.5em;
360
+@ background-color: #f5f5f5;
361
+@ padding: 0.5em;
362
+@ white-space: pre-wrap;
362363
@ }
363364
@
364365
@ /* The label/value pairs on (for example) the ci page */
365366
@ table.label-value th {
366367
@ vertical-align: top;
@@ -595,12 +596,13 @@
595596
@ color: #555;
596597
@ }
597598
@
598599
@ /* <verbatim> blocks */
599600
@ pre.verbatim {
600
-@ background-color: #f5f5f5;
601
-@ padding: 0.5em;
601
+@ background-color: #f5f5f5;
602
+@ padding: 0.5em;
603
+@ white-space: pre-wrap;
602604
@ }
603605
@
604606
@ /* The label/value pairs on (for example) the ci page */
605607
@ table.label-value th {
606608
@ vertical-align: top;
@@ -1083,13 +1085,14 @@
10831085
@ div.footer a:visited { color: white; }
10841086
@ div.footer a:hover { background-color: white; color: #558195; }
10851087
@
10861088
@ /* verbatim blocks */
10871089
@ pre.verbatim {
1088
-@ background-color: #f5f5f5;
1089
-@ padding: 0.5em;
1090
-@}
1090
+@ background-color: #f5f5f5;
1091
+@ padding: 0.5em;
1092
+@ white-space: pre-wrap;
1093
+@ }
10911094
@
10921095
@ /* The label/value pairs on (for example) the ci page */
10931096
@ table.label-value th {
10941097
@ vertical-align: top;
10951098
@ text-align: right;
@@ -1226,10 +1229,12 @@
12261229
@ }
12271230
@ set version [getVersion $manifest_version]
12281231
@ set tclVersion [getTclVersion]
12291232
@ set fossilUrl http://www.fossil-scm.org
12301233
@ </th1>
1234
+@ This page was generated in about
1235
+@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
12311236
@ <a href="$fossilUrl/">Fossil</a>
12321237
@ version $release_version $tclVersion
12331238
@ <a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
12341239
@ <a href="$fossilUrl/fossil/timeline?c=$manifest_date&amp;y=ci">$manifest_date</a>
12351240
@ </div>
12361241
--- src/skins.c
+++ src/skins.c
@@ -355,12 +355,13 @@
355 @ div.footer a:visited { color: white; }
356 @ div.footer a:hover { background-color: white; color: #558195; }
357 @
358 @ /* <verbatim> blocks */
359 @ pre.verbatim {
360 @ background-color: #f5f5f5;
361 @ padding: 0.5em;
 
362 @ }
363 @
364 @ /* The label/value pairs on (for example) the ci page */
365 @ table.label-value th {
366 @ vertical-align: top;
@@ -595,12 +596,13 @@
595 @ color: #555;
596 @ }
597 @
598 @ /* <verbatim> blocks */
599 @ pre.verbatim {
600 @ background-color: #f5f5f5;
601 @ padding: 0.5em;
 
602 @ }
603 @
604 @ /* The label/value pairs on (for example) the ci page */
605 @ table.label-value th {
606 @ vertical-align: top;
@@ -1083,13 +1085,14 @@
1083 @ div.footer a:visited { color: white; }
1084 @ div.footer a:hover { background-color: white; color: #558195; }
1085 @
1086 @ /* verbatim blocks */
1087 @ pre.verbatim {
1088 @ background-color: #f5f5f5;
1089 @ padding: 0.5em;
1090 @}
 
1091 @
1092 @ /* The label/value pairs on (for example) the ci page */
1093 @ table.label-value th {
1094 @ vertical-align: top;
1095 @ text-align: right;
@@ -1226,10 +1229,12 @@
1226 @ }
1227 @ set version [getVersion $manifest_version]
1228 @ set tclVersion [getTclVersion]
1229 @ set fossilUrl http://www.fossil-scm.org
1230 @ </th1>
 
 
1231 @ <a href="$fossilUrl/">Fossil</a>
1232 @ version $release_version $tclVersion
1233 @ <a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
1234 @ <a href="$fossilUrl/fossil/timeline?c=$manifest_date&amp;y=ci">$manifest_date</a>
1235 @ </div>
1236
--- src/skins.c
+++ src/skins.c
@@ -355,12 +355,13 @@
355 @ div.footer a:visited { color: white; }
356 @ div.footer a:hover { background-color: white; color: #558195; }
357 @
358 @ /* <verbatim> blocks */
359 @ pre.verbatim {
360 @ background-color: #f5f5f5;
361 @ padding: 0.5em;
362 @ white-space: pre-wrap;
363 @ }
364 @
365 @ /* The label/value pairs on (for example) the ci page */
366 @ table.label-value th {
367 @ vertical-align: top;
@@ -595,12 +596,13 @@
596 @ color: #555;
597 @ }
598 @
599 @ /* <verbatim> blocks */
600 @ pre.verbatim {
601 @ background-color: #f5f5f5;
602 @ padding: 0.5em;
603 @ white-space: pre-wrap;
604 @ }
605 @
606 @ /* The label/value pairs on (for example) the ci page */
607 @ table.label-value th {
608 @ vertical-align: top;
@@ -1083,13 +1085,14 @@
1085 @ div.footer a:visited { color: white; }
1086 @ div.footer a:hover { background-color: white; color: #558195; }
1087 @
1088 @ /* verbatim blocks */
1089 @ pre.verbatim {
1090 @ background-color: #f5f5f5;
1091 @ padding: 0.5em;
1092 @ white-space: pre-wrap;
1093 @ }
1094 @
1095 @ /* The label/value pairs on (for example) the ci page */
1096 @ table.label-value th {
1097 @ vertical-align: top;
1098 @ text-align: right;
@@ -1226,10 +1229,12 @@
1229 @ }
1230 @ set version [getVersion $manifest_version]
1231 @ set tclVersion [getTclVersion]
1232 @ set fossilUrl http://www.fossil-scm.org
1233 @ </th1>
1234 @ This page was generated in about
1235 @ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
1236 @ <a href="$fossilUrl/">Fossil</a>
1237 @ version $release_version $tclVersion
1238 @ <a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
1239 @ <a href="$fossilUrl/fossil/timeline?c=$manifest_date&amp;y=ci">$manifest_date</a>
1240 @ </div>
1241
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -118,10 +118,11 @@
118118
sqlcmd_content, 0, 0);
119119
sqlite3_create_function(db, "compress", 1, SQLITE_ANY, 0,
120120
sqlcmd_compress, 0, 0);
121121
sqlite3_create_function(db, "decompress", 1, SQLITE_ANY, 0,
122122
sqlcmd_decompress, 0, 0);
123
+ re_add_sql_func(db);
123124
g.repositoryOpen = 1;
124125
g.db = db;
125126
return SQLITE_OK;
126127
}
127128
128129
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -118,10 +118,11 @@
118 sqlcmd_content, 0, 0);
119 sqlite3_create_function(db, "compress", 1, SQLITE_ANY, 0,
120 sqlcmd_compress, 0, 0);
121 sqlite3_create_function(db, "decompress", 1, SQLITE_ANY, 0,
122 sqlcmd_decompress, 0, 0);
 
123 g.repositoryOpen = 1;
124 g.db = db;
125 return SQLITE_OK;
126 }
127
128
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -118,10 +118,11 @@
118 sqlcmd_content, 0, 0);
119 sqlite3_create_function(db, "compress", 1, SQLITE_ANY, 0,
120 sqlcmd_compress, 0, 0);
121 sqlite3_create_function(db, "decompress", 1, SQLITE_ANY, 0,
122 sqlcmd_decompress, 0, 0);
123 re_add_sql_func(db);
124 g.repositoryOpen = 1;
125 g.db = db;
126 return SQLITE_OK;
127 }
128
129
+720 -305
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
11
/******************************************************************************
22
** This file is an amalgamation of many separate C source files from SQLite
3
-** version 3.7.15. By combining all the individual C code files into this
3
+** version 3.7.16. By combining all the individual C code files into this
44
** single large file, the entire code can be compiled as a single translation
55
** unit. This allows many compilers to do optimizations that would not be
66
** possible if the files were compiled separately. Performance improvements
77
** of 5% or more are commonly seen when SQLite is compiled as a single
88
** translation unit.
@@ -671,13 +671,13 @@
671671
**
672672
** See also: [sqlite3_libversion()],
673673
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
674674
** [sqlite_version()] and [sqlite_source_id()].
675675
*/
676
-#define SQLITE_VERSION "3.7.15"
677
-#define SQLITE_VERSION_NUMBER 3007015
678
-#define SQLITE_SOURCE_ID "2012-12-10 22:19:14 bd7aeeb691fee69dd6a562138a7aba8e8e192272"
676
+#define SQLITE_VERSION "3.7.16"
677
+#define SQLITE_VERSION_NUMBER 3007016
678
+#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3"
679679
680680
/*
681681
** CAPI3REF: Run-Time Library Version Numbers
682682
** KEYWORDS: sqlite3_version, sqlite3_sourceid
683683
**
@@ -2156,11 +2156,11 @@
21562156
** database connection is opened. By default, URI handling is globally
21572157
** disabled. The default value may be changed by compiling with the
21582158
** [SQLITE_USE_URI] symbol defined.
21592159
**
21602160
** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
2161
-** <dd> This option taks a single integer argument which is interpreted as
2161
+** <dd> This option takes a single integer argument which is interpreted as
21622162
** a boolean in order to enable or disable the use of covering indices for
21632163
** full table scans in the query optimizer. The default setting is determined
21642164
** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
21652165
** if that compile-time option is omitted.
21662166
** The ability to disable the use of covering indices for full table scans
@@ -8238,10 +8238,15 @@
82388238
** A convenience macro that returns the number of elements in
82398239
** an array.
82408240
*/
82418241
#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0])))
82428242
8243
+/*
8244
+** Determine if the argument is a power of two
8245
+*/
8246
+#define IsPowerOfTwo(X) (((X)&((X)-1))==0)
8247
+
82438248
/*
82448249
** The following value as a destructor means to use sqlite3DbFree().
82458250
** The sqlite3DbFree() routine requires two parameters instead of the
82468251
** one parameter that destructors normally want. So we have to introduce
82478252
** this magic value that the code knows to handle differently. Any
@@ -10042,10 +10047,11 @@
1004210047
#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */
1004310048
#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
1004410049
#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
1004510050
#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
1004610051
#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
10052
+#define SQLITE_Transitive 0x0200 /* Transitive constraints */
1004710053
#define SQLITE_AllOpts 0xffff /* All optimizations */
1004810054
1004910055
/*
1005010056
** Macros for testing whether or not optimizations are enabled or disabled.
1005110057
*/
@@ -10553,24 +10559,24 @@
1055310559
** and the value of Index.onError indicate the which conflict resolution
1055410560
** algorithm to employ whenever an attempt is made to insert a non-unique
1055510561
** element.
1055610562
*/
1055710563
struct Index {
10558
- char *zName; /* Name of this index */
10559
- int *aiColumn; /* Which columns are used by this index. 1st is 0 */
10560
- tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
10561
- Table *pTable; /* The SQL table being indexed */
10562
- char *zColAff; /* String defining the affinity of each column */
10563
- Index *pNext; /* The next index associated with the same table */
10564
- Schema *pSchema; /* Schema containing this index */
10565
- u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
10566
- char **azColl; /* Array of collation sequence names for index */
10567
- int nColumn; /* Number of columns in the table used by this index */
10568
- int tnum; /* Page containing root of this index in database file */
10569
- u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
10570
- u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
10571
- u8 bUnordered; /* Use this index for == or IN queries only */
10564
+ char *zName; /* Name of this index */
10565
+ int *aiColumn; /* Which columns are used by this index. 1st is 0 */
10566
+ tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */
10567
+ Table *pTable; /* The SQL table being indexed */
10568
+ char *zColAff; /* String defining the affinity of each column */
10569
+ Index *pNext; /* The next index associated with the same table */
10570
+ Schema *pSchema; /* Schema containing this index */
10571
+ u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
10572
+ char **azColl; /* Array of collation sequence names for index */
10573
+ int tnum; /* DB Page containing root of this index */
10574
+ u16 nColumn; /* Number of columns in table used by this index */
10575
+ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
10576
+ unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
10577
+ unsigned bUnordered:1; /* Use this index for == or IN queries only */
1057210578
#ifdef SQLITE_ENABLE_STAT3
1057310579
int nSample; /* Number of elements in aSample[] */
1057410580
tRowcnt avgEq; /* Average nEq value for key values not in aSample */
1057510581
IndexSample *aSample; /* Samples of the left-most key */
1057610582
#endif
@@ -10840,22 +10846,31 @@
1084010846
** name. An expr/name combination can be used in several ways, such
1084110847
** as the list of "expr AS ID" fields following a "SELECT" or in the
1084210848
** list of "ID = expr" items in an UPDATE. A list of expressions can
1084310849
** also be used as the argument to a function, in which case the a.zName
1084410850
** field is not used.
10851
+**
10852
+** By default the Expr.zSpan field holds a human-readable description of
10853
+** the expression that is used in the generation of error messages and
10854
+** column labels. In this case, Expr.zSpan is typically the text of a
10855
+** column expression as it exists in a SELECT statement. However, if
10856
+** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name
10857
+** of the result column in the form: DATABASE.TABLE.COLUMN. This later
10858
+** form is used for name resolution with nested FROM clauses.
1084510859
*/
1084610860
struct ExprList {
1084710861
int nExpr; /* Number of expressions on the list */
1084810862
int iECursor; /* VDBE Cursor associated with this ExprList */
1084910863
struct ExprList_item { /* For each expression in the list */
10850
- Expr *pExpr; /* The list of expressions */
10851
- char *zName; /* Token associated with this expression */
10852
- char *zSpan; /* Original text of the expression */
10853
- u8 sortOrder; /* 1 for DESC or 0 for ASC */
10854
- u8 done; /* A flag to indicate when processing is finished */
10855
- u16 iOrderByCol; /* For ORDER BY, column number in result set */
10856
- u16 iAlias; /* Index into Parse.aAlias[] for zName */
10864
+ Expr *pExpr; /* The list of expressions */
10865
+ char *zName; /* Token associated with this expression */
10866
+ char *zSpan; /* Original text of the expression */
10867
+ u8 sortOrder; /* 1 for DESC or 0 for ASC */
10868
+ unsigned done :1; /* A flag to indicate when processing is finished */
10869
+ unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
10870
+ u16 iOrderByCol; /* For ORDER BY, column number in result set */
10871
+ u16 iAlias; /* Index into Parse.aAlias[] for zName */
1085710872
} *a; /* Alloc a power of two greater or equal to nExpr */
1085810873
};
1085910874
1086010875
/*
1086110876
** An instance of this structure is used by the parser to record both
@@ -11171,10 +11186,11 @@
1117111186
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
1117211187
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
1117311188
#define SF_UseSorter 0x0040 /* Sort using a sorter */
1117411189
#define SF_Values 0x0080 /* Synthesized from VALUES clause */
1117511190
#define SF_Materialize 0x0100 /* Force materialization of views */
11191
+#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
1117611192
1117711193
1117811194
/*
1117911195
** The results of a select can be distributed in several ways. The
1118011196
** "SRT" prefix means "SELECT Result Type".
@@ -11883,11 +11899,11 @@
1188311899
SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
1188411900
Token*, int, int);
1188511901
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
1188611902
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
1188711903
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
11888
- Expr*,ExprList*,int,Expr*,Expr*);
11904
+ Expr*,ExprList*,u16,Expr*,Expr*);
1188911905
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
1189011906
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
1189111907
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
1189211908
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
1189311909
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
@@ -12140,10 +12156,11 @@
1214012156
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
1214112157
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
1214212158
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
1214312159
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int);
1214412160
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
12161
+SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
1214512162
SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
1214612163
SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
1214712164
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
1214812165
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
1214912166
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
@@ -12278,12 +12295,14 @@
1227812295
#define sqlite3FkOldmask(a,b) 0
1227912296
#define sqlite3FkRequired(a,b,c,d) 0
1228012297
#endif
1228112298
#ifndef SQLITE_OMIT_FOREIGN_KEY
1228212299
SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*);
12300
+SQLITE_PRIVATE int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**);
1228312301
#else
1228412302
#define sqlite3FkDelete(a,b)
12303
+ #define sqlite3FkLocateIndex(a,b,c,d,e)
1228512304
#endif
1228612305
1228712306
1228812307
/*
1228912308
** Available fault injectors. Should be numbered beginning with 0.
@@ -23286,15 +23305,11 @@
2328623305
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
2328723306
#endif
2328823307
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
2328923308
aSyscall[13].pCurrent)
2329023309
23291
-#if SQLITE_ENABLE_LOCKING_STYLE
2329223310
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
23293
-#else
23294
- { "fchmod", (sqlite3_syscall_ptr)0, 0 },
23295
-#endif
2329623311
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
2329723312
2329823313
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
2329923314
{ "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
2330023315
#else
@@ -23315,13 +23330,10 @@
2331523330
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
2331623331
2331723332
{ "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
2331823333
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
2331923334
23320
- { "umask", (sqlite3_syscall_ptr)umask, 0 },
23321
-#define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent)
23322
-
2332323335
}; /* End of the overrideable system calls */
2332423336
2332523337
/*
2332623338
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
2332723339
** "unix" VFSes. Return SQLITE_OK opon successfully updating the
@@ -23422,31 +23434,29 @@
2342223434
** process that is able to write to the database will also be able to
2342323435
** recover the hot journals.
2342423436
*/
2342523437
static int robust_open(const char *z, int f, mode_t m){
2342623438
int fd;
23427
- mode_t m2;
23428
- mode_t origM = 0;
23429
- if( m==0 ){
23430
- m2 = SQLITE_DEFAULT_FILE_PERMISSIONS;
23431
- }else{
23432
- m2 = m;
23433
- origM = osUmask(0);
23434
- }
23439
+ mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
2343523440
do{
2343623441
#if defined(O_CLOEXEC)
2343723442
fd = osOpen(z,f|O_CLOEXEC,m2);
2343823443
#else
2343923444
fd = osOpen(z,f,m2);
2344023445
#endif
2344123446
}while( fd<0 && errno==EINTR );
23442
- if( m ){
23443
- osUmask(origM);
23444
- }
23447
+ if( fd>=0 ){
23448
+ if( m!=0 ){
23449
+ struct stat statbuf;
23450
+ if( osFstat(fd, &statbuf)==0 && (statbuf.st_mode&0777)!=m ){
23451
+ osFchmod(fd, m);
23452
+ }
23453
+ }
2344523454
#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0)
23446
- if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
23455
+ osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
2344723456
#endif
23457
+ }
2344823458
return fd;
2344923459
}
2345023460
2345123461
/*
2345223462
** Helper functions to obtain and relinquish the global mutex. The
@@ -29868,11 +29878,11 @@
2986829878
};
2986929879
unsigned int i; /* Loop counter */
2987029880
2987129881
/* Double-check that the aSyscall[] array has been constructed
2987229882
** correctly. See ticket [bb3a86e890c8e96ab] */
29873
- assert( ArraySize(aSyscall)==22 );
29883
+ assert( ArraySize(aSyscall)==21 );
2987429884
2987529885
/* Register all VFSes defined in the aVfs[] array */
2987629886
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
2987729887
sqlite3_vfs_register(&aVfs[i], i==0);
2987829888
}
@@ -56334,11 +56344,11 @@
5633456344
sqlite3BtreeLeave(p);
5633556345
return 0;
5633656346
}
5633756347
i = PENDING_BYTE_PAGE(pBt);
5633856348
if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
56339
- sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
56349
+ sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
5634056350
sCheck.errMsg.useMalloc = 2;
5634156351
5634256352
/* Check the integrity of the freelist
5634356353
*/
5634456354
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
@@ -56869,11 +56879,16 @@
5686956879
/*
5687056880
** Parameter zSrcData points to a buffer containing the data for
5687156881
** page iSrcPg from the source database. Copy this data into the
5687256882
** destination database.
5687356883
*/
56874
-static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
56884
+static int backupOnePage(
56885
+ sqlite3_backup *p, /* Backup handle */
56886
+ Pgno iSrcPg, /* Source database page to backup */
56887
+ const u8 *zSrcData, /* Source database page data */
56888
+ int bUpdate /* True for an update, false otherwise */
56889
+){
5687556890
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
5687656891
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
5687756892
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
5687856893
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
5687956894
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
@@ -56942,10 +56957,13 @@
5694256957
** cached parse of the page). MemPage.isInit is marked
5694356958
** "MUST BE FIRST" for this purpose.
5694456959
*/
5694556960
memcpy(zOut, zIn, nCopy);
5694656961
((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
56962
+ if( iOff==0 && bUpdate==0 ){
56963
+ sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc));
56964
+ }
5694756965
}
5694856966
sqlite3PagerUnref(pDestPg);
5694956967
}
5695056968
5695156969
return rc;
@@ -57048,11 +57066,11 @@
5704857066
const Pgno iSrcPg = p->iNext; /* Source page number */
5704957067
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
5705057068
DbPage *pSrcPg; /* Source page object */
5705157069
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
5705257070
if( rc==SQLITE_OK ){
57053
- rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
57071
+ rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
5705457072
sqlite3PagerUnref(pSrcPg);
5705557073
}
5705657074
}
5705757075
p->iNext++;
5705857076
}
@@ -57296,11 +57314,11 @@
5729657314
** the new data into the backup.
5729757315
*/
5729857316
int rc;
5729957317
assert( p->pDestDb );
5730057318
sqlite3_mutex_enter(p->pDestDb->mutex);
57301
- rc = backupOnePage(p, iPage, aData);
57319
+ rc = backupOnePage(p, iPage, aData, 1);
5730257320
sqlite3_mutex_leave(p->pDestDb->mutex);
5730357321
assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
5730457322
if( rc!=SQLITE_OK ){
5730557323
p->rc = rc;
5730657324
}
@@ -71890,10 +71908,18 @@
7189071908
p->pReal = pReal;
7189171909
if( p->iSize>0 ){
7189271910
assert(p->iSize<=p->nBuf);
7189371911
rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
7189471912
}
71913
+ if( rc!=SQLITE_OK ){
71914
+ /* If an error occurred while writing to the file, close it before
71915
+ ** returning. This way, SQLite uses the in-memory journal data to
71916
+ ** roll back changes made to the internal page-cache before this
71917
+ ** function was called. */
71918
+ sqlite3OsClose(pReal);
71919
+ p->pReal = 0;
71920
+ }
7189571921
}
7189671922
}
7189771923
return rc;
7189871924
}
7189971925
@@ -72636,10 +72662,39 @@
7263672662
}
7263772663
}
7263872664
return 0;
7263972665
}
7264072666
72667
+/*
72668
+** Subqueries stores the original database, table and column names for their
72669
+** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
72670
+** Check to see if the zSpan given to this routine matches the zDb, zTab,
72671
+** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
72672
+** match anything.
72673
+*/
72674
+SQLITE_PRIVATE int sqlite3MatchSpanName(
72675
+ const char *zSpan,
72676
+ const char *zCol,
72677
+ const char *zTab,
72678
+ const char *zDb
72679
+){
72680
+ int n;
72681
+ for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
72682
+ if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){
72683
+ return 0;
72684
+ }
72685
+ zSpan += n+1;
72686
+ for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
72687
+ if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){
72688
+ return 0;
72689
+ }
72690
+ zSpan += n+1;
72691
+ if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
72692
+ return 0;
72693
+ }
72694
+ return 1;
72695
+}
7264172696
7264272697
/*
7264372698
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
7264472699
** that name in the set of source tables in pSrcList and make the pExpr
7264572700
** expression node refer back to that source column. The following changes
@@ -72691,44 +72746,63 @@
7269172746
7269272747
/* Initialize the node to no-match */
7269372748
pExpr->iTable = -1;
7269472749
pExpr->pTab = 0;
7269572750
ExprSetIrreducible(pExpr);
72751
+
72752
+ /* Translate the schema name in zDb into a pointer to the corresponding
72753
+ ** schema. If not found, pSchema will remain NULL and nothing will match
72754
+ ** resulting in an appropriate error message toward the end of this routine
72755
+ */
72756
+ if( zDb ){
72757
+ for(i=0; i<db->nDb; i++){
72758
+ assert( db->aDb[i].zName );
72759
+ if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
72760
+ pSchema = db->aDb[i].pSchema;
72761
+ break;
72762
+ }
72763
+ }
72764
+ }
7269672765
7269772766
/* Start at the inner-most context and move outward until a match is found */
7269872767
while( pNC && cnt==0 ){
7269972768
ExprList *pEList;
7270072769
SrcList *pSrcList = pNC->pSrcList;
7270172770
7270272771
if( pSrcList ){
7270372772
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
7270472773
Table *pTab;
72705
- int iDb;
7270672774
Column *pCol;
7270772775
7270872776
pTab = pItem->pTab;
7270972777
assert( pTab!=0 && pTab->zName!=0 );
72710
- iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
7271172778
assert( pTab->nCol>0 );
72779
+ if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
72780
+ ExprList *pEList = pItem->pSelect->pEList;
72781
+ int hit = 0;
72782
+ for(j=0; j<pEList->nExpr; j++){
72783
+ if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){
72784
+ cnt++;
72785
+ cntTab = 2;
72786
+ pMatch = pItem;
72787
+ pExpr->iColumn = j;
72788
+ hit = 1;
72789
+ }
72790
+ }
72791
+ if( hit || zTab==0 ) continue;
72792
+ }
72793
+ if( zDb && pTab->pSchema!=pSchema ){
72794
+ continue;
72795
+ }
7271272796
if( zTab ){
72713
- if( pItem->zAlias ){
72714
- char *zTabName = pItem->zAlias;
72715
- if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
72716
- }else{
72717
- char *zTabName = pTab->zName;
72718
- if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){
72719
- continue;
72720
- }
72721
- if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
72722
- continue;
72723
- }
72797
+ const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
72798
+ assert( zTabName!=0 );
72799
+ if( sqlite3StrICmp(zTabName, zTab)!=0 ){
72800
+ continue;
7272472801
}
7272572802
}
7272672803
if( 0==(cntTab++) ){
72727
- pExpr->iTable = pItem->iCursor;
72728
- pExpr->pTab = pTab;
72729
- pSchema = pTab->pSchema;
7273072804
pMatch = pItem;
7273172805
}
7273272806
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
7273372807
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
7273472808
/* If there has been exactly one prior match and this match
@@ -72738,21 +72812,23 @@
7273872812
if( cnt==1 ){
7273972813
if( pItem->jointype & JT_NATURAL ) continue;
7274072814
if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
7274172815
}
7274272816
cnt++;
72743
- pExpr->iTable = pItem->iCursor;
72744
- pExpr->pTab = pTab;
7274572817
pMatch = pItem;
72746
- pSchema = pTab->pSchema;
7274772818
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
7274872819
pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
7274972820
break;
7275072821
}
7275172822
}
7275272823
}
72753
- }
72824
+ if( pMatch ){
72825
+ pExpr->iTable = pMatch->iCursor;
72826
+ pExpr->pTab = pMatch->pTab;
72827
+ pSchema = pExpr->pTab->pSchema;
72828
+ }
72829
+ } /* if( pSrcList ) */
7275472830
7275572831
#ifndef SQLITE_OMIT_TRIGGER
7275672832
/* If we have not already resolved the name, then maybe
7275772833
** it is a new.* or old.* trigger argument reference
7275872834
*/
@@ -73083,11 +73159,11 @@
7308373159
#endif
7308473160
if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
7308573161
sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
7308673162
pNC->nErr++;
7308773163
is_agg = 0;
73088
- }else if( no_such_func ){
73164
+ }else if( no_such_func && pParse->db->init.busy==0 ){
7308973165
sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
7309073166
pNC->nErr++;
7309173167
}else if( wrong_num_args ){
7309273168
sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
7309373169
nId, zId);
@@ -73519,27 +73595,10 @@
7351973595
if( sqlite3ResolveExprNames(&sNC, p->pLimit) ||
7352073596
sqlite3ResolveExprNames(&sNC, p->pOffset) ){
7352173597
return WRC_Abort;
7352273598
}
7352373599
73524
- /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
73525
- ** resolve the result-set expression list.
73526
- */
73527
- sNC.ncFlags = NC_AllowAgg;
73528
- sNC.pSrcList = p->pSrc;
73529
- sNC.pNext = pOuterNC;
73530
-
73531
- /* Resolve names in the result set. */
73532
- pEList = p->pEList;
73533
- assert( pEList!=0 );
73534
- for(i=0; i<pEList->nExpr; i++){
73535
- Expr *pX = pEList->a[i].pExpr;
73536
- if( sqlite3ResolveExprNames(&sNC, pX) ){
73537
- return WRC_Abort;
73538
- }
73539
- }
73540
-
7354173600
/* Recursively resolve names in all subqueries
7354273601
*/
7354373602
for(i=0; i<p->pSrc->nSrc; i++){
7354473603
struct SrcList_item *pItem = &p->pSrc->a[i];
7354573604
if( pItem->pSelect ){
@@ -73562,10 +73621,27 @@
7356273621
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
7356373622
assert( pItem->isCorrelated==0 && nRef<=0 );
7356473623
pItem->isCorrelated = (nRef!=0);
7356573624
}
7356673625
}
73626
+
73627
+ /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
73628
+ ** resolve the result-set expression list.
73629
+ */
73630
+ sNC.ncFlags = NC_AllowAgg;
73631
+ sNC.pSrcList = p->pSrc;
73632
+ sNC.pNext = pOuterNC;
73633
+
73634
+ /* Resolve names in the result set. */
73635
+ pEList = p->pEList;
73636
+ assert( pEList!=0 );
73637
+ for(i=0; i<pEList->nExpr; i++){
73638
+ Expr *pX = pEList->a[i].pExpr;
73639
+ if( sqlite3ResolveExprNames(&sNC, pX) ){
73640
+ return WRC_Abort;
73641
+ }
73642
+ }
7356773643
7356873644
/* If there are no aggregate functions in the result-set, and no GROUP BY
7356973645
** expression, do not allow aggregates in any of the other expressions.
7357073646
*/
7357173647
assert( (p->selFlags & SF_Aggregate)==0 );
@@ -77046,10 +77122,16 @@
7704677122
for(i=0; i<pList->nExpr; i++){
7704777123
sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
7704877124
sqlite3ExplainPush(pOut);
7704977125
sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
7705077126
sqlite3ExplainPop(pOut);
77127
+ if( pList->a[i].zName ){
77128
+ sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
77129
+ }
77130
+ if( pList->a[i].bSpanIsTab ){
77131
+ sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
77132
+ }
7705177133
if( i<pList->nExpr-1 ){
7705277134
sqlite3ExplainNL(pOut);
7705377135
}
7705477136
}
7705577137
sqlite3ExplainPop(pOut);
@@ -87523,11 +87605,11 @@
8752387605
8752487606
/*
8752587607
** A foreign key constraint requires that the key columns in the parent
8752687608
** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
8752787609
** Given that pParent is the parent table for foreign key constraint pFKey,
87528
-** search the schema a unique index on the parent key columns.
87610
+** search the schema for a unique index on the parent key columns.
8752987611
**
8753087612
** If successful, zero is returned. If the parent key is an INTEGER PRIMARY
8753187613
** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx
8753287614
** is set to point to the unique index.
8753387615
**
@@ -87559,11 +87641,11 @@
8755987641
**
8756087642
** then non-zero is returned, and a "foreign key mismatch" error loaded
8756187643
** into pParse. If an OOM error occurs, non-zero is returned and the
8756287644
** pParse->db->mallocFailed flag is set.
8756387645
*/
87564
-static int locateFkeyIndex(
87646
+SQLITE_PRIVATE int sqlite3FkLocateIndex(
8756587647
Parse *pParse, /* Parse context to store any error in */
8756687648
Table *pParent, /* Parent table of FK constraint pFKey */
8756787649
FKey *pFKey, /* Foreign key to find index for */
8756887650
Index **ppIdx, /* OUT: Unique index on parent table */
8756987651
int **paiCol /* OUT: Map of index columns in pFKey */
@@ -87656,11 +87738,13 @@
8765687738
}
8765787739
}
8765887740
8765987741
if( !pIdx ){
8766087742
if( !pParse->disableTriggers ){
87661
- sqlite3ErrorMsg(pParse, "foreign key mismatch");
87743
+ sqlite3ErrorMsg(pParse,
87744
+ "foreign key mismatch - \"%w\" referencing \"%w\"",
87745
+ pFKey->pFrom->zName, pFKey->zTo);
8766287746
}
8766387747
sqlite3DbFree(pParse->db, aiCol);
8766487748
return 1;
8766587749
}
8766687750
@@ -88117,11 +88201,11 @@
8811788201
if( pParse->disableTriggers ){
8811888202
pTo = sqlite3FindTable(db, pFKey->zTo, zDb);
8811988203
}else{
8812088204
pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
8812188205
}
88122
- if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
88206
+ if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
8812388207
assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) );
8812488208
if( !isIgnoreErrors || db->mallocFailed ) return;
8812588209
if( pTo==0 ){
8812688210
/* If isIgnoreErrors is true, then a table is being dropped. In this
8812788211
** case SQLite runs a "DELETE FROM xxx" on the table being dropped
@@ -88197,11 +88281,11 @@
8819788281
/* Inserting a single row into a parent table cannot cause an immediate
8819888282
** foreign key violation. So do nothing in this case. */
8819988283
continue;
8820088284
}
8820188285
88202
- if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
88286
+ if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
8820388287
if( !isIgnoreErrors || db->mallocFailed ) return;
8820488288
continue;
8820588289
}
8820688290
assert( aiCol || pFKey->nCol==1 );
8820788291
@@ -88252,11 +88336,11 @@
8825288336
for(p=pTab->pFKey; p; p=p->pNextFrom){
8825388337
for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom);
8825488338
}
8825588339
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
8825688340
Index *pIdx = 0;
88257
- locateFkeyIndex(pParse, pTab, p, &pIdx, 0);
88341
+ sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
8825888342
if( pIdx ){
8825988343
for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
8826088344
}
8826188345
}
8826288346
}
@@ -88378,11 +88462,11 @@
8837888462
ExprList *pList = 0; /* Changes list if ON UPDATE CASCADE */
8837988463
Select *pSelect = 0; /* If RESTRICT, "SELECT RAISE(...)" */
8838088464
int i; /* Iterator variable */
8838188465
Expr *pWhen = 0; /* WHEN clause for the trigger */
8838288466
88383
- if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
88467
+ if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
8838488468
assert( aiCol || pFKey->nCol==1 );
8838588469
8838688470
for(i=0; i<pFKey->nCol; i++){
8838788471
Token tOld = { "old", 3 }; /* Literal "old" token */
8838888472
Token tNew = { "new", 3 }; /* Literal "new" token */
@@ -92718,13 +92802,15 @@
9271892802
if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){
9271992803
Table *pTab;
9272092804
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
9272192805
pTab = sqlite3FindTable(db, zRight, zDb);
9272292806
if( pTab ){
92723
- int i;
92807
+ int i, k;
9272492808
int nHidden = 0;
9272592809
Column *pCol;
92810
+ Index *pPk;
92811
+ for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){}
9272692812
sqlite3VdbeSetNumCols(v, 6);
9272792813
pParse->nMem = 6;
9272892814
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC);
9272992815
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
9273092816
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC);
@@ -92745,12 +92831,18 @@
9274592831
if( pCol->zDflt ){
9274692832
sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0);
9274792833
}else{
9274892834
sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
9274992835
}
92750
- sqlite3VdbeAddOp2(v, OP_Integer,
92751
- (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6);
92836
+ if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
92837
+ k = 0;
92838
+ }else if( pPk==0 ){
92839
+ k = 1;
92840
+ }else{
92841
+ for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){}
92842
+ }
92843
+ sqlite3VdbeAddOp2(v, OP_Integer, k, 6);
9275292844
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
9275392845
}
9275492846
}
9275592847
}else
9275692848
@@ -92881,10 +92973,124 @@
9288192973
++i;
9288292974
pFK = pFK->pNextFrom;
9288392975
}
9288492976
}
9288592977
}
92978
+ }else
92979
+#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
92980
+
92981
+#ifndef SQLITE_OMIT_FOREIGN_KEY
92982
+ if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){
92983
+ FKey *pFK; /* A foreign key constraint */
92984
+ Table *pTab; /* Child table contain "REFERENCES" keyword */
92985
+ Table *pParent; /* Parent table that child points to */
92986
+ Index *pIdx; /* Index in the parent table */
92987
+ int i; /* Loop counter: Foreign key number for pTab */
92988
+ int j; /* Loop counter: Field of the foreign key */
92989
+ HashElem *k; /* Loop counter: Next table in schema */
92990
+ int x; /* result variable */
92991
+ int regResult; /* 3 registers to hold a result row */
92992
+ int regKey; /* Register to hold key for checking the FK */
92993
+ int regRow; /* Registers to hold a row from pTab */
92994
+ int addrTop; /* Top of a loop checking foreign keys */
92995
+ int addrOk; /* Jump here if the key is OK */
92996
+ int *aiCols; /* child to parent column mapping */
92997
+
92998
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
92999
+ regResult = pParse->nMem+1;
93000
+ pParse->nMem += 4;
93001
+ regKey = ++pParse->nMem;
93002
+ regRow = ++pParse->nMem;
93003
+ v = sqlite3GetVdbe(pParse);
93004
+ sqlite3VdbeSetNumCols(v, 4);
93005
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC);
93006
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC);
93007
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "parent", SQLITE_STATIC);
93008
+ sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "fkid", SQLITE_STATIC);
93009
+ sqlite3CodeVerifySchema(pParse, iDb);
93010
+ k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
93011
+ while( k ){
93012
+ if( zRight ){
93013
+ pTab = sqlite3LocateTable(pParse, 0, zRight, zDb);
93014
+ k = 0;
93015
+ }else{
93016
+ pTab = (Table*)sqliteHashData(k);
93017
+ k = sqliteHashNext(k);
93018
+ }
93019
+ if( pTab==0 || pTab->pFKey==0 ) continue;
93020
+ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
93021
+ if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
93022
+ sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
93023
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName,
93024
+ P4_TRANSIENT);
93025
+ for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
93026
+ pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
93027
+ if( pParent==0 ) break;
93028
+ pIdx = 0;
93029
+ sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
93030
+ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
93031
+ if( x==0 ){
93032
+ if( pIdx==0 ){
93033
+ sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
93034
+ }else{
93035
+ KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
93036
+ sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
93037
+ sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
93038
+ }
93039
+ }else{
93040
+ k = 0;
93041
+ break;
93042
+ }
93043
+ }
93044
+ if( pFK ) break;
93045
+ if( pParse->nTab<i ) pParse->nTab = i;
93046
+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0);
93047
+ for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
93048
+ pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
93049
+ assert( pParent!=0 );
93050
+ pIdx = 0;
93051
+ aiCols = 0;
93052
+ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
93053
+ assert( x==0 );
93054
+ addrOk = sqlite3VdbeMakeLabel(v);
93055
+ if( pIdx==0 ){
93056
+ int iKey = pFK->aCol[0].iFrom;
93057
+ assert( iKey>=0 && iKey<pTab->nCol );
93058
+ if( iKey!=pTab->iPKey ){
93059
+ sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
93060
+ sqlite3ColumnDefault(v, pTab, iKey, regRow);
93061
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk);
93062
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow,
93063
+ sqlite3VdbeCurrentAddr(v)+3);
93064
+ }else{
93065
+ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
93066
+ }
93067
+ sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow);
93068
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk);
93069
+ sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
93070
+ }else{
93071
+ for(j=0; j<pFK->nCol; j++){
93072
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, 0,
93073
+ aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j);
93074
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk);
93075
+ }
93076
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey);
93077
+ sqlite3VdbeChangeP4(v, -1,
93078
+ sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
93079
+ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
93080
+ }
93081
+ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
93082
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0,
93083
+ pFK->zTo, P4_TRANSIENT);
93084
+ sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3);
93085
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4);
93086
+ sqlite3VdbeResolveLabel(v, addrOk);
93087
+ sqlite3DbFree(db, aiCols);
93088
+ }
93089
+ sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1);
93090
+ sqlite3VdbeJumpHere(v, addrTop);
93091
+ }
9288693092
}else
9288793093
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
9288893094
9288993095
#ifndef NDEBUG
9289093096
if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
@@ -93381,11 +93587,11 @@
9338193587
sqlite3_rekey(db, zKey, i/2);
9338293588
}
9338393589
}else
9338493590
#endif
9338593591
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
93386
- if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){
93592
+ if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){
9338793593
#ifdef SQLITE_HAS_CODEC
9338893594
if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
9338993595
sqlite3_activate_see(&zRight[4]);
9339093596
}
9339193597
#endif
@@ -94340,11 +94546,11 @@
9434094546
SrcList *pSrc, /* the FROM clause -- which tables to scan */
9434194547
Expr *pWhere, /* the WHERE clause */
9434294548
ExprList *pGroupBy, /* the GROUP BY clause */
9434394549
Expr *pHaving, /* the HAVING clause */
9434494550
ExprList *pOrderBy, /* the ORDER BY clause */
94345
- int isDistinct, /* true if the DISTINCT keyword is present */
94551
+ u16 selFlags, /* Flag parameters, such as SF_Distinct */
9434694552
Expr *pLimit, /* LIMIT value. NULL means not used */
9434794553
Expr *pOffset /* OFFSET value. NULL means no offset */
9434894554
){
9434994555
Select *pNew;
9435094556
Select standin;
@@ -94364,11 +94570,11 @@
9436494570
pNew->pSrc = pSrc;
9436594571
pNew->pWhere = pWhere;
9436694572
pNew->pGroupBy = pGroupBy;
9436794573
pNew->pHaving = pHaving;
9436894574
pNew->pOrderBy = pOrderBy;
94369
- pNew->selFlags = isDistinct ? SF_Distinct : 0;
94575
+ pNew->selFlags = selFlags;
9437094576
pNew->op = TK_SELECT;
9437194577
pNew->pLimit = pLimit;
9437294578
pNew->pOffset = pOffset;
9437394579
assert( pOffset==0 || pLimit!=0 );
9437494580
pNew->addrOpenEphm[0] = -1;
@@ -95621,12 +95827,10 @@
9562195827
9562295828
for(i=0, pCol=aCol; i<nCol; i++, pCol++){
9562395829
/* Get an appropriate name for the column
9562495830
*/
9562595831
p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
95626
- assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue)
95627
- || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 );
9562895832
if( (zName = pEList->a[i].zName)!=0 ){
9562995833
/* If the column contains an "AS <name>" phrase, use <name> as the name */
9563095834
zName = sqlite3DbStrDup(db, zName);
9563195835
}else{
9563295836
Expr *pColExpr = p; /* The expression that is the result column name */
@@ -95660,10 +95864,13 @@
9566095864
*/
9566195865
nName = sqlite3Strlen30(zName);
9566295866
for(j=cnt=0; j<i; j++){
9566395867
if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
9566495868
char *zNewName;
95869
+ int k;
95870
+ for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){}
95871
+ if( zName[k]==':' ) nName = k;
9566595872
zName[nName] = 0;
9566695873
zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
9566795874
sqlite3DbFree(db, zName);
9566895875
zName = zNewName;
9566995876
j = -1;
@@ -97445,38 +97652,47 @@
9744597652
return 1;
9744697653
}
9744797654
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
9744897655
9744997656
/*
97450
-** Analyze the SELECT statement passed as an argument to see if it
97451
-** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if
97452
-** it is, or 0 otherwise. At present, a query is considered to be
97453
-** a min()/max() query if:
97454
-**
97455
-** 1. There is a single object in the FROM clause.
97456
-**
97457
-** 2. There is a single expression in the result set, and it is
97458
-** either min(x) or max(x), where x is a column reference.
97459
-*/
97460
-static u8 minMaxQuery(Select *p){
97461
- Expr *pExpr;
97462
- ExprList *pEList = p->pEList;
97463
-
97464
- if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
97465
- pExpr = pEList->a[0].pExpr;
97466
- if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
97467
- if( NEVER(ExprHasProperty(pExpr, EP_xIsSelect)) ) return 0;
97468
- pEList = pExpr->x.pList;
97469
- if( pEList==0 || pEList->nExpr!=1 ) return 0;
97470
- if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
97471
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
97472
- if( sqlite3StrICmp(pExpr->u.zToken,"min")==0 ){
97473
- return WHERE_ORDERBY_MIN;
97474
- }else if( sqlite3StrICmp(pExpr->u.zToken,"max")==0 ){
97475
- return WHERE_ORDERBY_MAX;
97476
- }
97477
- return WHERE_ORDERBY_NORMAL;
97657
+** Based on the contents of the AggInfo structure indicated by the first
97658
+** argument, this function checks if the following are true:
97659
+**
97660
+** * the query contains just a single aggregate function,
97661
+** * the aggregate function is either min() or max(), and
97662
+** * the argument to the aggregate function is a column value.
97663
+**
97664
+** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX
97665
+** is returned as appropriate. Also, *ppMinMax is set to point to the
97666
+** list of arguments passed to the aggregate before returning.
97667
+**
97668
+** Or, if the conditions above are not met, *ppMinMax is set to 0 and
97669
+** WHERE_ORDERBY_NORMAL is returned.
97670
+*/
97671
+static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){
97672
+ int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
97673
+
97674
+ *ppMinMax = 0;
97675
+ if( pAggInfo->nFunc==1 ){
97676
+ Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */
97677
+ ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */
97678
+
97679
+ assert( pExpr->op==TK_AGG_FUNCTION );
97680
+ if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){
97681
+ const char *zFunc = pExpr->u.zToken;
97682
+ if( sqlite3StrICmp(zFunc, "min")==0 ){
97683
+ eRet = WHERE_ORDERBY_MIN;
97684
+ *ppMinMax = pEList;
97685
+ }else if( sqlite3StrICmp(zFunc, "max")==0 ){
97686
+ eRet = WHERE_ORDERBY_MAX;
97687
+ *ppMinMax = pEList;
97688
+ }
97689
+ }
97690
+ }
97691
+
97692
+ assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 );
97693
+ return eRet;
9747897694
}
9747997695
9748097696
/*
9748197697
** The select statement passed as the first argument is an aggregate query.
9748297698
** The second argment is the associated aggregate-info object. This
@@ -97567,10 +97783,11 @@
9756797783
int i, j, k;
9756897784
SrcList *pTabList;
9756997785
ExprList *pEList;
9757097786
struct SrcList_item *pFrom;
9757197787
sqlite3 *db = pParse->db;
97788
+ Expr *pE, *pRight, *pExpr;
9757297789
9757397790
if( db->mallocFailed ){
9757497791
return WRC_Abort;
9757597792
}
9757697793
if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){
@@ -97652,11 +97869,11 @@
9765297869
**
9765397870
** The first loop just checks to see if there are any "*" operators
9765497871
** that need expanding.
9765597872
*/
9765697873
for(k=0; k<pEList->nExpr; k++){
97657
- Expr *pE = pEList->a[k].pExpr;
97874
+ pE = pEList->a[k].pExpr;
9765897875
if( pE->op==TK_ALL ) break;
9765997876
assert( pE->op!=TK_DOT || pE->pRight!=0 );
9766097877
assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
9766197878
if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break;
9766297879
}
@@ -97670,14 +97887,22 @@
9767097887
ExprList *pNew = 0;
9767197888
int flags = pParse->db->flags;
9767297889
int longNames = (flags & SQLITE_FullColNames)!=0
9767397890
&& (flags & SQLITE_ShortColNames)==0;
9767497891
97892
+ /* When processing FROM-clause subqueries, it is always the case
97893
+ ** that full_column_names=OFF and short_column_names=ON. The
97894
+ ** sqlite3ResultSetOfSelect() routine makes it so. */
97895
+ assert( (p->selFlags & SF_NestedFrom)==0
97896
+ || ((flags & SQLITE_FullColNames)==0 &&
97897
+ (flags & SQLITE_ShortColNames)!=0) );
97898
+
9767597899
for(k=0; k<pEList->nExpr; k++){
97676
- Expr *pE = a[k].pExpr;
97677
- assert( pE->op!=TK_DOT || pE->pRight!=0 );
97678
- if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){
97900
+ pE = a[k].pExpr;
97901
+ pRight = pE->pRight;
97902
+ assert( pE->op!=TK_DOT || pRight!=0 );
97903
+ if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){
9767997904
/* This particular expression does not need to be expanded.
9768097905
*/
9768197906
pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
9768297907
if( pNew ){
9768397908
pNew->a[pNew->nExpr-1].zName = a[k].zName;
@@ -97688,44 +97913,56 @@
9768897913
a[k].pExpr = 0;
9768997914
}else{
9769097915
/* This expression is a "*" or a "TABLE.*" and needs to be
9769197916
** expanded. */
9769297917
int tableSeen = 0; /* Set to 1 when TABLE matches */
97693
- char *zTName; /* text of name of TABLE */
97918
+ char *zTName = 0; /* text of name of TABLE */
9769497919
if( pE->op==TK_DOT ){
9769597920
assert( pE->pLeft!=0 );
9769697921
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
9769797922
zTName = pE->pLeft->u.zToken;
97698
- }else{
97699
- zTName = 0;
9770097923
}
9770197924
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
9770297925
Table *pTab = pFrom->pTab;
97926
+ Select *pSub = pFrom->pSelect;
9770397927
char *zTabName = pFrom->zAlias;
97928
+ const char *zSchemaName = 0;
97929
+ int iDb;
9770497930
if( zTabName==0 ){
9770597931
zTabName = pTab->zName;
9770697932
}
9770797933
if( db->mallocFailed ) break;
97708
- if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
97709
- continue;
97934
+ if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){
97935
+ pSub = 0;
97936
+ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
97937
+ continue;
97938
+ }
97939
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
97940
+ zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*";
9771097941
}
97711
- tableSeen = 1;
9771297942
for(j=0; j<pTab->nCol; j++){
97713
- Expr *pExpr, *pRight;
9771497943
char *zName = pTab->aCol[j].zName;
9771597944
char *zColname; /* The computed column name */
9771697945
char *zToFree; /* Malloced string that needs to be freed */
9771797946
Token sColname; /* Computed column name as a token */
97947
+
97948
+ assert( zName );
97949
+ if( zTName && pSub
97950
+ && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0
97951
+ ){
97952
+ continue;
97953
+ }
9771897954
9771997955
/* If a column is marked as 'hidden' (currently only possible
9772097956
** for virtual tables), do not include it in the expanded
9772197957
** result-set list.
9772297958
*/
9772397959
if( IsHiddenColumn(&pTab->aCol[j]) ){
9772497960
assert(IsVirtual(pTab));
9772597961
continue;
9772697962
}
97963
+ tableSeen = 1;
9772797964
9772897965
if( i>0 && zTName==0 ){
9772997966
if( (pFrom->jointype & JT_NATURAL)!=0
9773097967
&& tableAndColumnIndex(pTabList, i, zName, 0, 0)
9773197968
){
@@ -97744,10 +97981,14 @@
9774497981
zToFree = 0;
9774597982
if( longNames || pTabList->nSrc>1 ){
9774697983
Expr *pLeft;
9774797984
pLeft = sqlite3Expr(db, TK_ID, zTabName);
9774897985
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
97986
+ if( zSchemaName ){
97987
+ pLeft = sqlite3Expr(db, TK_ID, zSchemaName);
97988
+ pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0);
97989
+ }
9774997990
if( longNames ){
9775097991
zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
9775197992
zToFree = zColname;
9775297993
}
9775397994
}else{
@@ -97755,10 +97996,22 @@
9775597996
}
9775697997
pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
9775797998
sColname.z = zColname;
9775897999
sColname.n = sqlite3Strlen30(zColname);
9775998000
sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
98001
+ if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
98002
+ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
98003
+ if( pSub ){
98004
+ pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan);
98005
+ testcase( pX->zSpan==0 );
98006
+ }else{
98007
+ pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s",
98008
+ zSchemaName, zTabName, zColname);
98009
+ testcase( pX->zSpan==0 );
98010
+ }
98011
+ pX->bSpanIsTab = 1;
98012
+ }
9776098013
sqlite3DbFree(db, zToFree);
9776198014
}
9776298015
}
9776398016
if( !tableSeen ){
9776498017
if( zTName ){
@@ -98812,15 +99065,21 @@
9881299065
** index or indices to use) should place a different priority on
9881399066
** satisfying the 'ORDER BY' clause than it does in other cases.
9881499067
** Refer to code and comments in where.c for details.
9881599068
*/
9881699069
ExprList *pMinMax = 0;
98817
- u8 flag = minMaxQuery(p);
99070
+ u8 flag = WHERE_ORDERBY_NORMAL;
99071
+
99072
+ assert( p->pGroupBy==0 );
99073
+ assert( flag==0 );
99074
+ if( p->pHaving==0 ){
99075
+ flag = minMaxQuery(&sAggInfo, &pMinMax);
99076
+ }
99077
+ assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) );
99078
+
9881899079
if( flag ){
98819
- assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) );
98820
- assert( p->pEList->a[0].pExpr->x.pList->nExpr==1 );
98821
- pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0);
99080
+ pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
9882299081
pDel = pMinMax;
9882399082
if( pMinMax && !db->mallocFailed ){
9882499083
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
9882599084
pMinMax->a[0].pExpr->op = TK_COLUMN;
9882699085
}
@@ -102549,12 +102808,12 @@
102549102808
Expr *pExpr; /* Pointer to the subexpression that is this term */
102550102809
int iParent; /* Disable pWC->a[iParent] when this term disabled */
102551102810
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
102552102811
union {
102553102812
int leftColumn; /* Column number of X in "X <op> <expr>" */
102554
- WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */
102555
- WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */
102813
+ WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
102814
+ WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
102556102815
} u;
102557102816
u16 eOperator; /* A WO_xx value describing <op> */
102558102817
u8 wtFlags; /* TERM_xxx bit flags. See below */
102559102818
u8 nChild; /* Number of children that must disable us */
102560102819
WhereClause *pWC; /* The clause this term is part of */
@@ -102678,10 +102937,11 @@
102678102937
#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
102679102938
#define WO_MATCH 0x040
102680102939
#define WO_ISNULL 0x080
102681102940
#define WO_OR 0x100 /* Two or more OR-connected terms */
102682102941
#define WO_AND 0x200 /* Two or more AND-connected terms */
102942
+#define WO_EQUIV 0x400 /* Of the form A==B, both columns */
102683102943
#define WO_NOOP 0x800 /* This term does not restrict search space */
102684102944
102685102945
#define WO_ALL 0xfff /* Mask of all possible WO_* values */
102686102946
#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
102687102947
@@ -102704,11 +102964,11 @@
102704102964
#define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */
102705102965
#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
102706102966
#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
102707102967
#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
102708102968
#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
102709
-#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
102969
+#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */
102710102970
#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
102711102971
#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
102712102972
#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
102713102973
#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
102714102974
#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
@@ -102854,11 +103114,11 @@
102854103114
sqlite3DbFree(db, pOld);
102855103115
}
102856103116
pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
102857103117
}
102858103118
pTerm = &pWC->a[idx = pWC->nTerm++];
102859
- pTerm->pExpr = p;
103119
+ pTerm->pExpr = sqlite3ExprSkipCollate(p);
102860103120
pTerm->wtFlags = wtFlags;
102861103121
pTerm->pWC = pWC;
102862103122
pTerm->iParent = -1;
102863103123
return idx;
102864103124
}
@@ -103080,58 +103340,112 @@
103080103340
/*
103081103341
** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
103082103342
** where X is a reference to the iColumn of table iCur and <op> is one of
103083103343
** the WO_xx operator codes specified by the op parameter.
103084103344
** Return a pointer to the term. Return 0 if not found.
103345
+**
103346
+** The term returned might by Y=<expr> if there is another constraint in
103347
+** the WHERE clause that specifies that X=Y. Any such constraints will be
103348
+** identified by the WO_EQUIV bit in the pTerm->eOperator field. The
103349
+** aEquiv[] array holds X and all its equivalents, with each SQL variable
103350
+** taking up two slots in aEquiv[]. The first slot is for the cursor number
103351
+** and the second is for the column number. There are 22 slots in aEquiv[]
103352
+** so that means we can look for X plus up to 10 other equivalent values.
103353
+** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3
103354
+** and ... and A9=A10 and A10=<expr>.
103355
+**
103356
+** If there are multiple terms in the WHERE clause of the form "X <op> <expr>"
103357
+** then try for the one with no dependencies on <expr> - in other words where
103358
+** <expr> is a constant expression of some kind. Only return entries of
103359
+** the form "X <op> Y" where Y is a column in another table if no terms of
103360
+** the form "X <op> <const-expr>" exist. Other than this priority, if there
103361
+** are two or more terms that match, then the choice of which term to return
103362
+** is arbitrary.
103085103363
*/
103086103364
static WhereTerm *findTerm(
103087103365
WhereClause *pWC, /* The WHERE clause to be searched */
103088103366
int iCur, /* Cursor number of LHS */
103089103367
int iColumn, /* Column number of LHS */
103090103368
Bitmask notReady, /* RHS must not overlap with this mask */
103091103369
u32 op, /* Mask of WO_xx values describing operator */
103092103370
Index *pIdx /* Must be compatible with this index, if not NULL */
103093103371
){
103094
- WhereTerm *pTerm;
103095
- int k;
103372
+ WhereTerm *pTerm; /* Term being examined as possible result */
103373
+ WhereTerm *pResult = 0; /* The answer to return */
103374
+ WhereClause *pWCOrig = pWC; /* Original pWC value */
103375
+ int j, k; /* Loop counters */
103376
+ Expr *pX; /* Pointer to an expression */
103377
+ Parse *pParse; /* Parsing context */
103378
+ int iOrigCol = iColumn; /* Original value of iColumn */
103379
+ int nEquiv = 2; /* Number of entires in aEquiv[] */
103380
+ int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */
103381
+ int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */
103382
+
103096103383
assert( iCur>=0 );
103097
- op &= WO_ALL;
103098
- for(; pWC; pWC=pWC->pOuter){
103099
- for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
103100
- if( pTerm->leftCursor==iCur
103101
- && (pTerm->prereqRight & notReady)==0
103102
- && pTerm->u.leftColumn==iColumn
103103
- && (pTerm->eOperator & op)!=0
103104
- ){
103105
- if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){
103106
- Expr *pX = pTerm->pExpr;
103107
- CollSeq *pColl;
103108
- char idxaff;
103109
- int j;
103110
- Parse *pParse = pWC->pParse;
103111
-
103112
- idxaff = pIdx->pTable->aCol[iColumn].affinity;
103113
- if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
103114
-
103115
- /* Figure out the collation sequence required from an index for
103116
- ** it to be useful for optimising expression pX. Store this
103117
- ** value in variable pColl.
103118
- */
103119
- assert(pX->pLeft);
103120
- pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
103121
- if( pColl==0 ) pColl = pParse->db->pDfltColl;
103122
-
103123
- for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
103124
- if( NEVER(j>=pIdx->nColumn) ) return 0;
103125
- }
103126
- if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
103127
- }
103128
- return pTerm;
103129
- }
103130
- }
103131
- }
103132
- return 0;
103384
+ aEquiv[0] = iCur;
103385
+ aEquiv[1] = iColumn;
103386
+ for(;;){
103387
+ for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){
103388
+ for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
103389
+ if( pTerm->leftCursor==iCur
103390
+ && pTerm->u.leftColumn==iColumn
103391
+ ){
103392
+ if( (pTerm->prereqRight & notReady)==0
103393
+ && (pTerm->eOperator & op & WO_ALL)!=0
103394
+ ){
103395
+ if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){
103396
+ CollSeq *pColl;
103397
+ char idxaff;
103398
+
103399
+ pX = pTerm->pExpr;
103400
+ pParse = pWC->pParse;
103401
+ idxaff = pIdx->pTable->aCol[iOrigCol].affinity;
103402
+ if( !sqlite3IndexAffinityOk(pX, idxaff) ){
103403
+ continue;
103404
+ }
103405
+
103406
+ /* Figure out the collation sequence required from an index for
103407
+ ** it to be useful for optimising expression pX. Store this
103408
+ ** value in variable pColl.
103409
+ */
103410
+ assert(pX->pLeft);
103411
+ pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight);
103412
+ if( pColl==0 ) pColl = pParse->db->pDfltColl;
103413
+
103414
+ for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){
103415
+ if( NEVER(j>=pIdx->nColumn) ) return 0;
103416
+ }
103417
+ if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){
103418
+ continue;
103419
+ }
103420
+ }
103421
+ pResult = pTerm;
103422
+ if( pTerm->prereqRight==0 ) goto findTerm_success;
103423
+ }
103424
+ if( (pTerm->eOperator & WO_EQUIV)!=0
103425
+ && nEquiv<ArraySize(aEquiv)
103426
+ ){
103427
+ pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
103428
+ assert( pX->op==TK_COLUMN );
103429
+ for(j=0; j<nEquiv; j+=2){
103430
+ if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break;
103431
+ }
103432
+ if( j==nEquiv ){
103433
+ aEquiv[j] = pX->iTable;
103434
+ aEquiv[j+1] = pX->iColumn;
103435
+ nEquiv += 2;
103436
+ }
103437
+ }
103438
+ }
103439
+ }
103440
+ }
103441
+ if( iEquiv>=nEquiv ) break;
103442
+ iCur = aEquiv[iEquiv++];
103443
+ iColumn = aEquiv[iEquiv++];
103444
+ }
103445
+findTerm_success:
103446
+ return pResult;
103133103447
}
103134103448
103135103449
/* Forward reference */
103136103450
static void exprAnalyze(SrcList*, WhereClause*, int);
103137103451
@@ -103405,11 +103719,10 @@
103405103719
indexable = ~(Bitmask)0;
103406103720
chngToIN = ~(pWC->vmask);
103407103721
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){
103408103722
if( (pOrTerm->eOperator & WO_SINGLE)==0 ){
103409103723
WhereAndInfo *pAndInfo;
103410
- assert( pOrTerm->eOperator==0 );
103411103724
assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
103412103725
chngToIN = 0;
103413103726
pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
103414103727
if( pAndInfo ){
103415103728
WhereClause *pAndWC;
@@ -103444,11 +103757,11 @@
103444103757
if( pOrTerm->wtFlags & TERM_VIRTUAL ){
103445103758
WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
103446103759
b |= getMask(pMaskSet, pOther->leftCursor);
103447103760
}
103448103761
indexable &= b;
103449
- if( pOrTerm->eOperator!=WO_EQ ){
103762
+ if( (pOrTerm->eOperator & WO_EQ)==0 ){
103450103763
chngToIN = 0;
103451103764
}else{
103452103765
chngToIN &= b;
103453103766
}
103454103767
}
@@ -103495,11 +103808,11 @@
103495103808
** and column is found but leave okToChngToIN false if not found.
103496103809
*/
103497103810
for(j=0; j<2 && !okToChngToIN; j++){
103498103811
pOrTerm = pOrWc->a;
103499103812
for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
103500
- assert( pOrTerm->eOperator==WO_EQ );
103813
+ assert( pOrTerm->eOperator & WO_EQ );
103501103814
pOrTerm->wtFlags &= ~TERM_OR_OK;
103502103815
if( pOrTerm->leftCursor==iCursor ){
103503103816
/* This is the 2-bit case and we are on the second iteration and
103504103817
** current term is from the first iteration. So skip this term. */
103505103818
assert( j==1 );
@@ -103521,21 +103834,21 @@
103521103834
}
103522103835
if( i<0 ){
103523103836
/* No candidate table+column was found. This can only occur
103524103837
** on the second iteration */
103525103838
assert( j==1 );
103526
- assert( (chngToIN&(chngToIN-1))==0 );
103839
+ assert( IsPowerOfTwo(chngToIN) );
103527103840
assert( chngToIN==getMask(pMaskSet, iCursor) );
103528103841
break;
103529103842
}
103530103843
testcase( j==1 );
103531103844
103532103845
/* We have found a candidate table and column. Check to see if that
103533103846
** table and column is common to every term in the OR clause */
103534103847
okToChngToIN = 1;
103535103848
for(; i>=0 && okToChngToIN; i--, pOrTerm++){
103536
- assert( pOrTerm->eOperator==WO_EQ );
103849
+ assert( pOrTerm->eOperator & WO_EQ );
103537103850
if( pOrTerm->leftCursor!=iCursor ){
103538103851
pOrTerm->wtFlags &= ~TERM_OR_OK;
103539103852
}else if( pOrTerm->u.leftColumn!=iColumn ){
103540103853
okToChngToIN = 0;
103541103854
}else{
@@ -103567,11 +103880,11 @@
103567103880
Expr *pLeft = 0; /* The LHS of the IN operator */
103568103881
Expr *pNew; /* The complete IN operator */
103569103882
103570103883
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
103571103884
if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
103572
- assert( pOrTerm->eOperator==WO_EQ );
103885
+ assert( pOrTerm->eOperator & WO_EQ );
103573103886
assert( pOrTerm->leftCursor==iCursor );
103574103887
assert( pOrTerm->u.leftColumn==iColumn );
103575103888
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
103576103889
pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup);
103577103890
pLeft = pOrTerm->pExpr->pLeft;
@@ -103596,11 +103909,10 @@
103596103909
pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
103597103910
}
103598103911
}
103599103912
}
103600103913
#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
103601
-
103602103914
103603103915
/*
103604103916
** The input to this routine is an WhereTerm structure with only the
103605103917
** "pExpr" field filled in. The job of this routine is to analyze the
103606103918
** subexpression and populate all the other fields of the WhereTerm
@@ -103639,11 +103951,12 @@
103639103951
if( db->mallocFailed ){
103640103952
return;
103641103953
}
103642103954
pTerm = &pWC->a[idxTerm];
103643103955
pMaskSet = pWC->pMaskSet;
103644
- pExpr = sqlite3ExprSkipCollate(pTerm->pExpr);
103956
+ pExpr = pTerm->pExpr;
103957
+ assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
103645103958
prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
103646103959
op = pExpr->op;
103647103960
if( op==TK_IN ){
103648103961
assert( pExpr->pRight==0 );
103649103962
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
@@ -103665,21 +103978,23 @@
103665103978
}
103666103979
pTerm->prereqAll = prereqAll;
103667103980
pTerm->leftCursor = -1;
103668103981
pTerm->iParent = -1;
103669103982
pTerm->eOperator = 0;
103670
- if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){
103983
+ if( allowedOp(op) ){
103671103984
Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
103672103985
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
103986
+ u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
103673103987
if( pLeft->op==TK_COLUMN ){
103674103988
pTerm->leftCursor = pLeft->iTable;
103675103989
pTerm->u.leftColumn = pLeft->iColumn;
103676
- pTerm->eOperator = operatorMask(op);
103990
+ pTerm->eOperator = operatorMask(op) & opMask;
103677103991
}
103678103992
if( pRight && pRight->op==TK_COLUMN ){
103679103993
WhereTerm *pNew;
103680103994
Expr *pDup;
103995
+ u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
103681103996
if( pTerm->leftCursor>=0 ){
103682103997
int idxNew;
103683103998
pDup = sqlite3ExprDup(db, pExpr, 0);
103684103999
if( db->mallocFailed ){
103685104000
sqlite3ExprDelete(db, pDup);
@@ -103690,10 +104005,17 @@
103690104005
pNew = &pWC->a[idxNew];
103691104006
pNew->iParent = idxTerm;
103692104007
pTerm = &pWC->a[idxTerm];
103693104008
pTerm->nChild = 1;
103694104009
pTerm->wtFlags |= TERM_COPIED;
104010
+ if( pExpr->op==TK_EQ
104011
+ && !ExprHasProperty(pExpr, EP_FromJoin)
104012
+ && OptimizationEnabled(db, SQLITE_Transitive)
104013
+ ){
104014
+ pTerm->eOperator |= WO_EQUIV;
104015
+ eExtraOp = WO_EQUIV;
104016
+ }
103695104017
}else{
103696104018
pDup = pExpr;
103697104019
pNew = pTerm;
103698104020
}
103699104021
exprCommute(pParse, pDup);
@@ -103701,11 +104023,11 @@
103701104023
pNew->leftCursor = pLeft->iTable;
103702104024
pNew->u.leftColumn = pLeft->iColumn;
103703104025
testcase( (prereqLeft | extraRight) != prereqLeft );
103704104026
pNew->prereqRight = prereqLeft | extraRight;
103705104027
pNew->prereqAll = prereqAll;
103706
- pNew->eOperator = operatorMask(pDup->op);
104028
+ pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
103707104029
}
103708104030
}
103709104031
103710104032
#ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION
103711104033
/* If a term is the BETWEEN operator, create two new virtual terms
@@ -104160,11 +104482,11 @@
104160104482
return;
104161104483
}
104162104484
104163104485
/* Search the WHERE clause terms for a usable WO_OR term. */
104164104486
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
104165
- if( pTerm->eOperator==WO_OR
104487
+ if( (pTerm->eOperator & WO_OR)!=0
104166104488
&& ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0
104167104489
&& (pTerm->u.pOrInfo->indexable & maskSrc)!=0
104168104490
){
104169104491
WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
104170104492
WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
@@ -104181,11 +104503,11 @@
104181104503
sBOI.ppIdxInfo = 0;
104182104504
for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
104183104505
WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
104184104506
(pOrTerm - pOrWC->a), (pTerm - pWC->a)
104185104507
));
104186
- if( pOrTerm->eOperator==WO_AND ){
104508
+ if( (pOrTerm->eOperator& WO_AND)!=0 ){
104187104509
sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
104188104510
bestIndex(&sBOI);
104189104511
}else if( pOrTerm->leftCursor==iCur ){
104190104512
WhereClause tempWC;
104191104513
tempWC.pParse = pWC->pParse;
@@ -104242,11 +104564,11 @@
104242104564
struct SrcList_item *pSrc, /* Table we are trying to access */
104243104565
Bitmask notReady /* Tables in outer loops of the join */
104244104566
){
104245104567
char aff;
104246104568
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
104247
- if( pTerm->eOperator!=WO_EQ ) return 0;
104569
+ if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
104248104570
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
104249104571
aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
104250104572
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
104251104573
return 1;
104252104574
}
@@ -104504,14 +104826,14 @@
104504104826
104505104827
/* Count the number of possible WHERE clause constraints referring
104506104828
** to this virtual table */
104507104829
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
104508104830
if( pTerm->leftCursor != pSrc->iCursor ) continue;
104509
- assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
104510
- testcase( pTerm->eOperator==WO_IN );
104511
- testcase( pTerm->eOperator==WO_ISNULL );
104512
- if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
104831
+ assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
104832
+ testcase( pTerm->eOperator & WO_IN );
104833
+ testcase( pTerm->eOperator & WO_ISNULL );
104834
+ if( pTerm->eOperator & (WO_ISNULL) ) continue;
104513104835
if( pTerm->wtFlags & TERM_VNULL ) continue;
104514104836
nTerm++;
104515104837
}
104516104838
104517104839
/* If the ORDER BY clause contains only columns in the current
@@ -104555,29 +104877,32 @@
104555104877
*(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy;
104556104878
*(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
104557104879
pUsage;
104558104880
104559104881
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
104882
+ u8 op;
104560104883
if( pTerm->leftCursor != pSrc->iCursor ) continue;
104561
- assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
104562
- testcase( pTerm->eOperator==WO_IN );
104563
- testcase( pTerm->eOperator==WO_ISNULL );
104564
- if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
104884
+ assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
104885
+ testcase( pTerm->eOperator & WO_IN );
104886
+ testcase( pTerm->eOperator & WO_ISNULL );
104887
+ if( pTerm->eOperator & (WO_ISNULL) ) continue;
104565104888
if( pTerm->wtFlags & TERM_VNULL ) continue;
104566104889
pIdxCons[j].iColumn = pTerm->u.leftColumn;
104567104890
pIdxCons[j].iTermOffset = i;
104568
- pIdxCons[j].op = (u8)pTerm->eOperator;
104891
+ op = (u8)pTerm->eOperator & WO_ALL;
104892
+ if( op==WO_IN ) op = WO_EQ;
104893
+ pIdxCons[j].op = op;
104569104894
/* The direct assignment in the previous line is possible only because
104570104895
** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
104571104896
** following asserts verify this fact. */
104572104897
assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
104573104898
assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
104574104899
assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE );
104575104900
assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
104576104901
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
104577104902
assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
104578
- assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
104903
+ assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
104579104904
j++;
104580104905
}
104581104906
for(i=0; i<nOrderBy; i++){
104582104907
Expr *pExpr = pOrderBy->a[i].pExpr;
104583104908
pIdxOrderBy[i].iColumn = pExpr->iColumn;
@@ -104659,10 +104984,11 @@
104659104984
struct sqlite3_index_constraint *pIdxCons;
104660104985
struct sqlite3_index_constraint_usage *pUsage;
104661104986
WhereTerm *pTerm;
104662104987
int i, j;
104663104988
int nOrderBy;
104989
+ int bAllowIN; /* Allow IN optimizations */
104664104990
double rCost;
104665104991
104666104992
/* Make sure wsFlags is initialized to some sane value. Otherwise, if the
104667104993
** malloc in allocateIndexInfo() fails and this function returns leaving
104668104994
** wsFlags in an uninitialized state, the caller may behave unpredictably.
@@ -104693,63 +105019,91 @@
104693105019
** sqlite3ViewGetColumnNames() would have picked up the error.
104694105020
*/
104695105021
assert( pTab->azModuleArg && pTab->azModuleArg[0] );
104696105022
assert( sqlite3GetVTable(pParse->db, pTab) );
104697105023
104698
- /* Set the aConstraint[].usable fields and initialize all
104699
- ** output variables to zero.
104700
- **
104701
- ** aConstraint[].usable is true for constraints where the right-hand
104702
- ** side contains only references to tables to the left of the current
104703
- ** table. In other words, if the constraint is of the form:
104704
- **
104705
- ** column = expr
104706
- **
104707
- ** and we are evaluating a join, then the constraint on column is
104708
- ** only valid if all tables referenced in expr occur to the left
104709
- ** of the table containing column.
104710
- **
104711
- ** The aConstraints[] array contains entries for all constraints
104712
- ** on the current table. That way we only have to compute it once
104713
- ** even though we might try to pick the best index multiple times.
104714
- ** For each attempt at picking an index, the order of tables in the
104715
- ** join might be different so we have to recompute the usable flag
104716
- ** each time.
104717
- */
104718
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
104719
- pUsage = pIdxInfo->aConstraintUsage;
104720
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
104721
- j = pIdxCons->iTermOffset;
104722
- pTerm = &pWC->a[j];
104723
- pIdxCons->usable = (pTerm->prereqRight&p->notReady) ? 0 : 1;
104724
- }
104725
- memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
104726
- if( pIdxInfo->needToFreeIdxStr ){
104727
- sqlite3_free(pIdxInfo->idxStr);
104728
- }
104729
- pIdxInfo->idxStr = 0;
104730
- pIdxInfo->idxNum = 0;
104731
- pIdxInfo->needToFreeIdxStr = 0;
104732
- pIdxInfo->orderByConsumed = 0;
104733
- /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
104734
- pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
104735
- nOrderBy = pIdxInfo->nOrderBy;
104736
- if( !p->pOrderBy ){
104737
- pIdxInfo->nOrderBy = 0;
104738
- }
104739
-
104740
- if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
104741
- return;
104742
- }
104743
-
104744
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
104745
- for(i=0; i<pIdxInfo->nConstraint; i++){
104746
- if( pUsage[i].argvIndex>0 ){
104747
- p->cost.used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight;
104748
- }
104749
- }
104750
-
105024
+ /* Try once or twice. On the first attempt, allow IN optimizations.
105025
+ ** If an IN optimization is accepted by the virtual table xBestIndex
105026
+ ** method, but the pInfo->aConstrainUsage.omit flag is not set, then
105027
+ ** the query will not work because it might allow duplicate rows in
105028
+ ** output. In that case, run the xBestIndex method a second time
105029
+ ** without the IN constraints. Usually this loop only runs once.
105030
+ ** The loop will exit using a "break" statement.
105031
+ */
105032
+ for(bAllowIN=1; 1; bAllowIN--){
105033
+ assert( bAllowIN==0 || bAllowIN==1 );
105034
+
105035
+ /* Set the aConstraint[].usable fields and initialize all
105036
+ ** output variables to zero.
105037
+ **
105038
+ ** aConstraint[].usable is true for constraints where the right-hand
105039
+ ** side contains only references to tables to the left of the current
105040
+ ** table. In other words, if the constraint is of the form:
105041
+ **
105042
+ ** column = expr
105043
+ **
105044
+ ** and we are evaluating a join, then the constraint on column is
105045
+ ** only valid if all tables referenced in expr occur to the left
105046
+ ** of the table containing column.
105047
+ **
105048
+ ** The aConstraints[] array contains entries for all constraints
105049
+ ** on the current table. That way we only have to compute it once
105050
+ ** even though we might try to pick the best index multiple times.
105051
+ ** For each attempt at picking an index, the order of tables in the
105052
+ ** join might be different so we have to recompute the usable flag
105053
+ ** each time.
105054
+ */
105055
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
105056
+ pUsage = pIdxInfo->aConstraintUsage;
105057
+ for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
105058
+ j = pIdxCons->iTermOffset;
105059
+ pTerm = &pWC->a[j];
105060
+ if( (pTerm->prereqRight&p->notReady)==0
105061
+ && (bAllowIN || (pTerm->eOperator & WO_IN)==0)
105062
+ ){
105063
+ pIdxCons->usable = 1;
105064
+ }else{
105065
+ pIdxCons->usable = 0;
105066
+ }
105067
+ }
105068
+ memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
105069
+ if( pIdxInfo->needToFreeIdxStr ){
105070
+ sqlite3_free(pIdxInfo->idxStr);
105071
+ }
105072
+ pIdxInfo->idxStr = 0;
105073
+ pIdxInfo->idxNum = 0;
105074
+ pIdxInfo->needToFreeIdxStr = 0;
105075
+ pIdxInfo->orderByConsumed = 0;
105076
+ /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
105077
+ pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
105078
+ nOrderBy = pIdxInfo->nOrderBy;
105079
+ if( !p->pOrderBy ){
105080
+ pIdxInfo->nOrderBy = 0;
105081
+ }
105082
+
105083
+ if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
105084
+ return;
105085
+ }
105086
+
105087
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
105088
+ for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
105089
+ if( pUsage[i].argvIndex>0 ){
105090
+ j = pIdxCons->iTermOffset;
105091
+ pTerm = &pWC->a[j];
105092
+ p->cost.used |= pTerm->prereqRight;
105093
+ if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){
105094
+ /* Do not attempt to use an IN constraint if the virtual table
105095
+ ** says that the equivalent EQ constraint cannot be safely omitted.
105096
+ ** If we do attempt to use such a constraint, some rows might be
105097
+ ** repeated in the output. */
105098
+ break;
105099
+ }
105100
+ }
105101
+ }
105102
+ if( i>=pIdxInfo->nConstraint ) break;
105103
+ }
105104
+
104751105105
/* If there is an ORDER BY clause, and the selected virtual table index
104752105106
** does not satisfy it, increase the cost of the scan accordingly. This
104753105107
** matches the processing for non-virtual tables in bestBtreeIndex().
104754105108
*/
104755105109
rCost = pIdxInfo->estimatedCost;
@@ -105040,28 +105394,28 @@
105040105394
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
105041105395
105042105396
if( pLower ){
105043105397
Expr *pExpr = pLower->pExpr->pRight;
105044105398
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
105045
- assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
105399
+ assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
105046105400
if( rc==SQLITE_OK
105047105401
&& whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
105048105402
){
105049105403
iLower = a[0];
105050
- if( pLower->eOperator==WO_GT ) iLower += a[1];
105404
+ if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1];
105051105405
}
105052105406
sqlite3ValueFree(pRangeVal);
105053105407
}
105054105408
if( rc==SQLITE_OK && pUpper ){
105055105409
Expr *pExpr = pUpper->pExpr->pRight;
105056105410
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
105057
- assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
105411
+ assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
105058105412
if( rc==SQLITE_OK
105059105413
&& whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
105060105414
){
105061105415
iUpper = a[0];
105062
- if( pUpper->eOperator==WO_LE ) iUpper += a[1];
105416
+ if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1];
105063105417
}
105064105418
sqlite3ValueFree(pRangeVal);
105065105419
}
105066105420
if( rc==SQLITE_OK ){
105067105421
if( iUpper<=iLower ){
@@ -105365,16 +105719,16 @@
105365105719
** if there are any X= or X IS NULL constraints in the WHERE clause. */
105366105720
pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
105367105721
WO_EQ|WO_ISNULL|WO_IN, pIdx);
105368105722
if( pConstraint==0 ){
105369105723
isEq = 0;
105370
- }else if( pConstraint->eOperator==WO_IN ){
105724
+ }else if( (pConstraint->eOperator & WO_IN)!=0 ){
105371105725
/* Constraints of the form: "X IN ..." cannot be used with an ORDER BY
105372105726
** because we do not know in what order the values on the RHS of the IN
105373105727
** operator will occur. */
105374105728
break;
105375
- }else if( pConstraint->eOperator==WO_ISNULL ){
105729
+ }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){
105376105730
uniqueNotNull = 0;
105377105731
isEq = 1; /* "X IS NULL" means X has only a single value */
105378105732
}else if( pConstraint->prereqRight==0 ){
105379105733
isEq = 1; /* Constraint "X=constant" means X has only a single value */
105380105734
}else{
@@ -105720,11 +106074,11 @@
105720106074
int bRev = 2;
105721106075
WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat));
105722106076
pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev);
105723106077
WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n",
105724106078
bRev, pc.plan.nOBSat));
105725
- if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){
106079
+ if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
105726106080
pc.plan.wsFlags |= WHERE_ORDERED;
105727106081
}
105728106082
if( nOrderBy==pc.plan.nOBSat ){
105729106083
bSort = 0;
105730106084
pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
@@ -105783,16 +106137,17 @@
105783106137
*/
105784106138
if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
105785106139
&& pFirstTerm!=0 && aiRowEst[1]>1 ){
105786106140
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
105787106141
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
105788
- testcase( pFirstTerm->eOperator==WO_EQ );
105789
- testcase( pFirstTerm->eOperator==WO_ISNULL );
106142
+ testcase( pFirstTerm->eOperator & WO_EQ );
106143
+ testcase( pFirstTerm->eOperator & WO_EQUIV );
106144
+ testcase( pFirstTerm->eOperator & WO_ISNULL );
105790106145
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
105791106146
&pc.plan.nRow);
105792106147
}else if( bInEst==0 ){
105793
- assert( pFirstTerm->eOperator==WO_IN );
106148
+ assert( pFirstTerm->eOperator & WO_IN );
105794106149
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
105795106150
&pc.plan.nRow);
105796106151
}
105797106152
}
105798106153
#endif /* SQLITE_ENABLE_STAT3 */
@@ -105935,11 +106290,11 @@
105935106290
** more selective intentionally because of the subjective
105936106291
** observation that indexed range constraints really are more
105937106292
** selective in practice, on average. */
105938106293
pc.plan.nRow /= 3;
105939106294
}
105940
- }else if( pTerm->eOperator!=WO_NOOP ){
106295
+ }else if( (pTerm->eOperator & WO_NOOP)==0 ){
105941106296
/* Any other expression lowers the output row count by half */
105942106297
pc.plan.nRow /= 2;
105943106298
}
105944106299
}
105945106300
if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
@@ -105987,12 +106342,13 @@
105987106342
assert( pSrc->pIndex==0
105988106343
|| p->cost.plan.u.pIdx==0
105989106344
|| p->cost.plan.u.pIdx==pSrc->pIndex
105990106345
);
105991106346
105992
- WHERETRACE((" best index is: %s\n",
105993
- p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
106347
+ WHERETRACE((" best index is %s cost=%.1f\n",
106348
+ p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk",
106349
+ p->cost.rCost));
105994106350
105995106351
bestOrClauseIndex(p);
105996106352
bestAutomaticIndex(p);
105997106353
p->cost.plan.wsFlags |= eqTermMask;
105998106354
}
@@ -106514,32 +106870,40 @@
106514106870
if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
106515106871
/* Case 0: The table is a virtual-table. Use the VFilter and VNext
106516106872
** to access the data.
106517106873
*/
106518106874
int iReg; /* P3 Value for OP_VFilter */
106875
+ int addrNotFound;
106519106876
sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
106520106877
int nConstraint = pVtabIdx->nConstraint;
106521106878
struct sqlite3_index_constraint_usage *aUsage =
106522106879
pVtabIdx->aConstraintUsage;
106523106880
const struct sqlite3_index_constraint *aConstraint =
106524106881
pVtabIdx->aConstraint;
106525106882
106526106883
sqlite3ExprCachePush(pParse);
106527106884
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
106885
+ addrNotFound = pLevel->addrBrk;
106528106886
for(j=1; j<=nConstraint; j++){
106529106887
for(k=0; k<nConstraint; k++){
106530106888
if( aUsage[k].argvIndex==j ){
106531
- int iTerm = aConstraint[k].iTermOffset;
106532
- sqlite3ExprCode(pParse, pWC->a[iTerm].pExpr->pRight, iReg+j+1);
106889
+ WhereTerm *pTerm = &pWC->a[aConstraint[k].iTermOffset];
106890
+ int iTarget = iReg+j+1;
106891
+ if( pTerm->eOperator & WO_IN ){
106892
+ codeEqualityTerm(pParse, pTerm, pLevel, iTarget);
106893
+ addrNotFound = pLevel->addrNxt;
106894
+ }else{
106895
+ sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
106896
+ }
106533106897
break;
106534106898
}
106535106899
}
106536106900
if( k==nConstraint ) break;
106537106901
}
106538106902
sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg);
106539106903
sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
106540
- sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr,
106904
+ sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr,
106541106905
pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
106542106906
pVtabIdx->needToFreeIdxStr = 0;
106543106907
for(j=0; j<nConstraint; j++){
106544106908
if( aUsage[j].omit ){
106545106909
int iTerm = aConstraint[j].iTermOffset;
@@ -106562,11 +106926,10 @@
106562106926
*/
106563106927
iReleaseReg = sqlite3GetTempReg(pParse);
106564106928
pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
106565106929
assert( pTerm!=0 );
106566106930
assert( pTerm->pExpr!=0 );
106567
- assert( pTerm->leftCursor==iCur );
106568106931
assert( omitTable==0 );
106569106932
testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
106570106933
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg);
106571106934
addrNxt = pLevel->addrNxt;
106572106935
sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
@@ -106953,11 +107316,11 @@
106953107316
int ii; /* Loop counter */
106954107317
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
106955107318
106956107319
pTerm = pLevel->plan.u.pTerm;
106957107320
assert( pTerm!=0 );
106958
- assert( pTerm->eOperator==WO_OR );
107321
+ assert( pTerm->eOperator & WO_OR );
106959107322
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
106960107323
pOrWc = &pTerm->u.pOrInfo->wc;
106961107324
pLevel->op = OP_Return;
106962107325
pLevel->p1 = regReturn;
106963107326
@@ -107026,11 +107389,11 @@
107026107389
}
107027107390
}
107028107391
107029107392
for(ii=0; ii<pOrWc->nTerm; ii++){
107030107393
WhereTerm *pOrTerm = &pOrWc->a[ii];
107031
- if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
107394
+ if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
107032107395
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
107033107396
Expr *pOrExpr = pOrTerm->pExpr;
107034107397
if( pAndExpr ){
107035107398
pAndExpr->pLeft = pOrExpr;
107036107399
pOrExpr = pAndExpr;
@@ -107481,10 +107844,11 @@
107481107844
Index *pIdx; /* Index for FROM table at pTabItem */
107482107845
int j; /* For looping over FROM tables */
107483107846
int bestJ = -1; /* The value of j */
107484107847
Bitmask m; /* Bitmask value for j or bestJ */
107485107848
int isOptimal; /* Iterator for optimal/non-optimal search */
107849
+ int ckOptimal; /* Do the optimal scan check */
107486107850
int nUnconstrained; /* Number tables without INDEXED BY */
107487107851
Bitmask notIndexed; /* Mask of tables that cannot use an index */
107488107852
107489107853
memset(&bestPlan, 0, sizeof(bestPlan));
107490107854
bestPlan.rCost = SQLITE_BIG_DBL;
@@ -107515,14 +107879,12 @@
107515107879
**
107516107880
** The second loop iteration is only performed if no optimal scan
107517107881
** strategies were found by the first iteration. This second iteration
107518107882
** is used to search for the lowest cost scan overall.
107519107883
**
107520
- ** Previous versions of SQLite performed only the second iteration -
107521
- ** the next outermost loop was always that with the lowest overall
107522
- ** cost. However, this meant that SQLite could select the wrong plan
107523
- ** for scripts such as the following:
107884
+ ** Without the optimal scan step (the first iteration) a suboptimal
107885
+ ** plan might be chosen for queries like this:
107524107886
**
107525107887
** CREATE TABLE t1(a, b);
107526107888
** CREATE TABLE t2(c, d);
107527107889
** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
107528107890
**
@@ -107533,20 +107895,44 @@
107533107895
** algorithm may choose to use t2 for the outer loop, which is a much
107534107896
** costlier approach.
107535107897
*/
107536107898
nUnconstrained = 0;
107537107899
notIndexed = 0;
107538
- for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){
107900
+
107901
+ /* The optimal scan check only occurs if there are two or more tables
107902
+ ** available to be reordered */
107903
+ if( iFrom==nTabList-1 ){
107904
+ ckOptimal = 0; /* Common case of just one table in the FROM clause */
107905
+ }else{
107906
+ ckOptimal = -1;
107539107907
for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
107540
- int doNotReorder; /* True if this table should not be reordered */
107541
-
107542
- doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0;
107543
- if( j!=iFrom && doNotReorder ) break;
107544107908
m = getMask(pMaskSet, sWBI.pSrc->iCursor);
107545107909
if( (m & sWBI.notValid)==0 ){
107546107910
if( j==iFrom ) iFrom++;
107547107911
continue;
107912
+ }
107913
+ if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break;
107914
+ if( ++ckOptimal ) break;
107915
+ if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
107916
+ }
107917
+ }
107918
+ assert( ckOptimal==0 || ckOptimal==1 );
107919
+
107920
+ for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){
107921
+ for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
107922
+ if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){
107923
+ /* This break and one like it in the ckOptimal computation loop
107924
+ ** above prevent table reordering across LEFT and CROSS JOINs.
107925
+ ** The LEFT JOIN case is necessary for correctness. The prohibition
107926
+ ** against reordering across a CROSS JOIN is an SQLite feature that
107927
+ ** allows the developer to control table reordering */
107928
+ break;
107929
+ }
107930
+ m = getMask(pMaskSet, sWBI.pSrc->iCursor);
107931
+ if( (m & sWBI.notValid)==0 ){
107932
+ assert( j>iFrom );
107933
+ continue;
107548107934
}
107549107935
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
107550107936
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
107551107937
107552107938
WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
@@ -107572,12 +107958,12 @@
107572107958
if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
107573107959
notIndexed |= m;
107574107960
}
107575107961
if( isOptimal ){
107576107962
pWInfo->a[j].rOptCost = sWBI.cost.rCost;
107577
- }else if( iFrom<nTabList-1 ){
107578
- /* If two or more tables have nearly the same outer loop cost,
107963
+ }else if( ckOptimal ){
107964
+ /* If two or more tables have nearly the same outer loop cost, but
107579107965
** very different inner loop (optimal) cost, we want to choose
107580107966
** for the outer loop that table which benefits the least from
107581107967
** being in the inner loop. The following code scales the
107582107968
** outer loop cost estimate to accomplish that. */
107583107969
WHERETRACE((" scaling cost from %.1f to %.1f\n",
@@ -107618,15 +108004,23 @@
107618108004
sWBI.cost.rCost, sWBI.cost.plan.nRow,
107619108005
sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
107620108006
bestPlan = sWBI.cost;
107621108007
bestJ = j;
107622108008
}
107623
- if( doNotReorder ) break;
108009
+
108010
+ /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that
108011
+ ** table y (and not table z) is always the next inner loop inside
108012
+ ** of table x. */
108013
+ if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
107624108014
}
107625108015
}
107626108016
assert( bestJ>=0 );
107627108017
assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
108018
+ assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 );
108019
+ testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 );
108020
+ testcase( bestJ>iFrom && bestJ<nTabList-1
108021
+ && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 );
107628108022
WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
107629108023
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
107630108024
bestJ, pTabList->a[bestJ].pTab->zName,
107631108025
pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
107632108026
bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
@@ -108182,10 +108576,11 @@
108182108576
Expr* yy122;
108183108577
Select* yy159;
108184108578
IdList* yy180;
108185108579
struct {int value; int mask;} yy207;
108186108580
u8 yy258;
108581
+ u16 yy305;
108187108582
struct LikeOp yy318;
108188108583
TriggerStep* yy327;
108189108584
ExprSpan yy342;
108190108585
SrcList* yy347;
108191108586
int yy392;
@@ -110132,22 +110527,19 @@
110132110527
case 82: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==82);
110133110528
case 84: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==84);
110134110529
case 86: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==86);
110135110530
case 98: /* defer_subclause_opt ::= */ yytestcase(yyruleno==98);
110136110531
case 109: /* ifexists ::= */ yytestcase(yyruleno==109);
110137
- case 120: /* distinct ::= ALL */ yytestcase(yyruleno==120);
110138
- case 121: /* distinct ::= */ yytestcase(yyruleno==121);
110139110532
case 221: /* between_op ::= BETWEEN */ yytestcase(yyruleno==221);
110140110533
case 224: /* in_op ::= IN */ yytestcase(yyruleno==224);
110141110534
{yygotominor.yy392 = 0;}
110142110535
break;
110143110536
case 29: /* ifnotexists ::= IF NOT EXISTS */
110144110537
case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30);
110145110538
case 70: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==70);
110146110539
case 85: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==85);
110147110540
case 108: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==108);
110148
- case 119: /* distinct ::= DISTINCT */ yytestcase(yyruleno==119);
110149110541
case 222: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==222);
110150110542
case 225: /* in_op ::= NOT IN */ yytestcase(yyruleno==225);
110151110543
{yygotominor.yy392 = 1;}
110152110544
break;
110153110545
case 32: /* create_table_args ::= LP columnlist conslist_opt RP */
@@ -110383,12 +110775,19 @@
110383110775
case 116: /* multiselect_op ::= UNION ALL */
110384110776
{yygotominor.yy392 = TK_ALL;}
110385110777
break;
110386110778
case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
110387110779
{
110388
- yygotominor.yy159 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy442,yymsp[-5].minor.yy347,yymsp[-4].minor.yy122,yymsp[-3].minor.yy442,yymsp[-2].minor.yy122,yymsp[-1].minor.yy442,yymsp[-7].minor.yy392,yymsp[0].minor.yy64.pLimit,yymsp[0].minor.yy64.pOffset);
110780
+ yygotominor.yy159 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy442,yymsp[-5].minor.yy347,yymsp[-4].minor.yy122,yymsp[-3].minor.yy442,yymsp[-2].minor.yy122,yymsp[-1].minor.yy442,yymsp[-7].minor.yy305,yymsp[0].minor.yy64.pLimit,yymsp[0].minor.yy64.pOffset);
110389110781
}
110782
+ break;
110783
+ case 119: /* distinct ::= DISTINCT */
110784
+{yygotominor.yy305 = SF_Distinct;}
110785
+ break;
110786
+ case 120: /* distinct ::= ALL */
110787
+ case 121: /* distinct ::= */ yytestcase(yyruleno==121);
110788
+{yygotominor.yy305 = 0;}
110390110789
break;
110391110790
case 122: /* sclp ::= selcollist COMMA */
110392110791
case 246: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==246);
110393110792
{yygotominor.yy442 = yymsp[-1].minor.yy442;}
110394110793
break;
@@ -110454,14 +110853,24 @@
110454110853
break;
110455110854
case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
110456110855
{
110457110856
if( yymsp[-6].minor.yy347==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy122==0 && yymsp[0].minor.yy180==0 ){
110458110857
yygotominor.yy347 = yymsp[-4].minor.yy347;
110858
+ }else if( yymsp[-4].minor.yy347->nSrc==1 ){
110859
+ yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
110860
+ if( yygotominor.yy347 ){
110861
+ struct SrcList_item *pNew = &yygotominor.yy347->a[yygotominor.yy347->nSrc-1];
110862
+ struct SrcList_item *pOld = yymsp[-4].minor.yy347->a;
110863
+ pNew->zName = pOld->zName;
110864
+ pNew->zDatabase = pOld->zDatabase;
110865
+ pOld->zName = pOld->zDatabase = 0;
110866
+ }
110867
+ sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy347);
110459110868
}else{
110460110869
Select *pSubquery;
110461110870
sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy347);
110462
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy347,0,0,0,0,0,0,0);
110871
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy347,0,0,0,0,SF_NestedFrom,0,0);
110463110872
yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
110464110873
}
110465110874
}
110466110875
break;
110467110876
case 137: /* dbnm ::= */
@@ -110690,11 +111099,11 @@
110690111099
if( yymsp[-1].minor.yy442 && yymsp[-1].minor.yy442->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
110691111100
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
110692111101
}
110693111102
yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy442, &yymsp[-4].minor.yy0);
110694111103
spanSet(&yygotominor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
110695
- if( yymsp[-2].minor.yy392 && yygotominor.yy342.pExpr ){
111104
+ if( yymsp[-2].minor.yy305 && yygotominor.yy342.pExpr ){
110696111105
yygotominor.yy342.pExpr->flags |= EP_Distinct;
110697111106
}
110698111107
}
110699111108
break;
110700111109
case 197: /* expr ::= ID LP STAR RP */
@@ -136337,11 +136746,12 @@
136337136746
** would fit in a single node, use a smaller node-size.
136338136747
*/
136339136748
static int getNodeSize(
136340136749
sqlite3 *db, /* Database handle */
136341136750
Rtree *pRtree, /* Rtree handle */
136342
- int isCreate /* True for xCreate, false for xConnect */
136751
+ int isCreate, /* True for xCreate, false for xConnect */
136752
+ char **pzErr /* OUT: Error message, if any */
136343136753
){
136344136754
int rc;
136345136755
char *zSql;
136346136756
if( isCreate ){
136347136757
int iPageSize = 0;
@@ -136350,17 +136760,22 @@
136350136760
if( rc==SQLITE_OK ){
136351136761
pRtree->iNodeSize = iPageSize-64;
136352136762
if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
136353136763
pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
136354136764
}
136765
+ }else{
136766
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
136355136767
}
136356136768
}else{
136357136769
zSql = sqlite3_mprintf(
136358136770
"SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1",
136359136771
pRtree->zDb, pRtree->zName
136360136772
);
136361136773
rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
136774
+ if( rc!=SQLITE_OK ){
136775
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
136776
+ }
136362136777
}
136363136778
136364136779
sqlite3_free(zSql);
136365136780
return rc;
136366136781
}
@@ -136420,11 +136835,11 @@
136420136835
pRtree->eCoordType = eCoordType;
136421136836
memcpy(pRtree->zDb, argv[1], nDb);
136422136837
memcpy(pRtree->zName, argv[2], nName);
136423136838
136424136839
/* Figure out the node size to use. */
136425
- rc = getNodeSize(db, pRtree, isCreate);
136840
+ rc = getNodeSize(db, pRtree, isCreate, pzErr);
136426136841
136427136842
/* Create/Connect to the underlying relational database schema. If
136428136843
** that is successful, call sqlite3_declare_vtab() to configure
136429136844
** the r-tree table schema.
136430136845
*/
136431136846
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.7.15. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a single translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% or more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -671,13 +671,13 @@
671 **
672 ** See also: [sqlite3_libversion()],
673 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
674 ** [sqlite_version()] and [sqlite_source_id()].
675 */
676 #define SQLITE_VERSION "3.7.15"
677 #define SQLITE_VERSION_NUMBER 3007015
678 #define SQLITE_SOURCE_ID "2012-12-10 22:19:14 bd7aeeb691fee69dd6a562138a7aba8e8e192272"
679
680 /*
681 ** CAPI3REF: Run-Time Library Version Numbers
682 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
683 **
@@ -2156,11 +2156,11 @@
2156 ** database connection is opened. By default, URI handling is globally
2157 ** disabled. The default value may be changed by compiling with the
2158 ** [SQLITE_USE_URI] symbol defined.
2159 **
2160 ** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
2161 ** <dd> This option taks a single integer argument which is interpreted as
2162 ** a boolean in order to enable or disable the use of covering indices for
2163 ** full table scans in the query optimizer. The default setting is determined
2164 ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
2165 ** if that compile-time option is omitted.
2166 ** The ability to disable the use of covering indices for full table scans
@@ -8238,10 +8238,15 @@
8238 ** A convenience macro that returns the number of elements in
8239 ** an array.
8240 */
8241 #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0])))
8242
 
 
 
 
 
8243 /*
8244 ** The following value as a destructor means to use sqlite3DbFree().
8245 ** The sqlite3DbFree() routine requires two parameters instead of the
8246 ** one parameter that destructors normally want. So we have to introduce
8247 ** this magic value that the code knows to handle differently. Any
@@ -10042,10 +10047,11 @@
10042 #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */
10043 #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
10044 #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
10045 #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
10046 #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
 
10047 #define SQLITE_AllOpts 0xffff /* All optimizations */
10048
10049 /*
10050 ** Macros for testing whether or not optimizations are enabled or disabled.
10051 */
@@ -10553,24 +10559,24 @@
10553 ** and the value of Index.onError indicate the which conflict resolution
10554 ** algorithm to employ whenever an attempt is made to insert a non-unique
10555 ** element.
10556 */
10557 struct Index {
10558 char *zName; /* Name of this index */
10559 int *aiColumn; /* Which columns are used by this index. 1st is 0 */
10560 tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
10561 Table *pTable; /* The SQL table being indexed */
10562 char *zColAff; /* String defining the affinity of each column */
10563 Index *pNext; /* The next index associated with the same table */
10564 Schema *pSchema; /* Schema containing this index */
10565 u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
10566 char **azColl; /* Array of collation sequence names for index */
10567 int nColumn; /* Number of columns in the table used by this index */
10568 int tnum; /* Page containing root of this index in database file */
10569 u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
10570 u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
10571 u8 bUnordered; /* Use this index for == or IN queries only */
10572 #ifdef SQLITE_ENABLE_STAT3
10573 int nSample; /* Number of elements in aSample[] */
10574 tRowcnt avgEq; /* Average nEq value for key values not in aSample */
10575 IndexSample *aSample; /* Samples of the left-most key */
10576 #endif
@@ -10840,22 +10846,31 @@
10840 ** name. An expr/name combination can be used in several ways, such
10841 ** as the list of "expr AS ID" fields following a "SELECT" or in the
10842 ** list of "ID = expr" items in an UPDATE. A list of expressions can
10843 ** also be used as the argument to a function, in which case the a.zName
10844 ** field is not used.
 
 
 
 
 
 
 
 
10845 */
10846 struct ExprList {
10847 int nExpr; /* Number of expressions on the list */
10848 int iECursor; /* VDBE Cursor associated with this ExprList */
10849 struct ExprList_item { /* For each expression in the list */
10850 Expr *pExpr; /* The list of expressions */
10851 char *zName; /* Token associated with this expression */
10852 char *zSpan; /* Original text of the expression */
10853 u8 sortOrder; /* 1 for DESC or 0 for ASC */
10854 u8 done; /* A flag to indicate when processing is finished */
10855 u16 iOrderByCol; /* For ORDER BY, column number in result set */
10856 u16 iAlias; /* Index into Parse.aAlias[] for zName */
 
10857 } *a; /* Alloc a power of two greater or equal to nExpr */
10858 };
10859
10860 /*
10861 ** An instance of this structure is used by the parser to record both
@@ -11171,10 +11186,11 @@
11171 #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
11172 #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
11173 #define SF_UseSorter 0x0040 /* Sort using a sorter */
11174 #define SF_Values 0x0080 /* Synthesized from VALUES clause */
11175 #define SF_Materialize 0x0100 /* Force materialization of views */
 
11176
11177
11178 /*
11179 ** The results of a select can be distributed in several ways. The
11180 ** "SRT" prefix means "SELECT Result Type".
@@ -11883,11 +11899,11 @@
11883 SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
11884 Token*, int, int);
11885 SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
11886 SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
11887 SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
11888 Expr*,ExprList*,int,Expr*,Expr*);
11889 SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
11890 SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
11891 SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
11892 SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
11893 #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
@@ -12140,10 +12156,11 @@
12140 SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
12141 SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
12142 SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
12143 SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int);
12144 SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
 
12145 SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
12146 SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
12147 SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
12148 SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
12149 SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
@@ -12278,12 +12295,14 @@
12278 #define sqlite3FkOldmask(a,b) 0
12279 #define sqlite3FkRequired(a,b,c,d) 0
12280 #endif
12281 #ifndef SQLITE_OMIT_FOREIGN_KEY
12282 SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*);
 
12283 #else
12284 #define sqlite3FkDelete(a,b)
 
12285 #endif
12286
12287
12288 /*
12289 ** Available fault injectors. Should be numbered beginning with 0.
@@ -23286,15 +23305,11 @@
23286 { "pwrite64", (sqlite3_syscall_ptr)0, 0 },
23287 #endif
23288 #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
23289 aSyscall[13].pCurrent)
23290
23291 #if SQLITE_ENABLE_LOCKING_STYLE
23292 { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
23293 #else
23294 { "fchmod", (sqlite3_syscall_ptr)0, 0 },
23295 #endif
23296 #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
23297
23298 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
23299 { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
23300 #else
@@ -23315,13 +23330,10 @@
23315 #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
23316
23317 { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
23318 #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
23319
23320 { "umask", (sqlite3_syscall_ptr)umask, 0 },
23321 #define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent)
23322
23323 }; /* End of the overrideable system calls */
23324
23325 /*
23326 ** This is the xSetSystemCall() method of sqlite3_vfs for all of the
23327 ** "unix" VFSes. Return SQLITE_OK opon successfully updating the
@@ -23422,31 +23434,29 @@
23422 ** process that is able to write to the database will also be able to
23423 ** recover the hot journals.
23424 */
23425 static int robust_open(const char *z, int f, mode_t m){
23426 int fd;
23427 mode_t m2;
23428 mode_t origM = 0;
23429 if( m==0 ){
23430 m2 = SQLITE_DEFAULT_FILE_PERMISSIONS;
23431 }else{
23432 m2 = m;
23433 origM = osUmask(0);
23434 }
23435 do{
23436 #if defined(O_CLOEXEC)
23437 fd = osOpen(z,f|O_CLOEXEC,m2);
23438 #else
23439 fd = osOpen(z,f,m2);
23440 #endif
23441 }while( fd<0 && errno==EINTR );
23442 if( m ){
23443 osUmask(origM);
23444 }
 
 
 
 
23445 #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0)
23446 if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
23447 #endif
 
23448 return fd;
23449 }
23450
23451 /*
23452 ** Helper functions to obtain and relinquish the global mutex. The
@@ -29868,11 +29878,11 @@
29868 };
29869 unsigned int i; /* Loop counter */
29870
29871 /* Double-check that the aSyscall[] array has been constructed
29872 ** correctly. See ticket [bb3a86e890c8e96ab] */
29873 assert( ArraySize(aSyscall)==22 );
29874
29875 /* Register all VFSes defined in the aVfs[] array */
29876 for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
29877 sqlite3_vfs_register(&aVfs[i], i==0);
29878 }
@@ -56334,11 +56344,11 @@
56334 sqlite3BtreeLeave(p);
56335 return 0;
56336 }
56337 i = PENDING_BYTE_PAGE(pBt);
56338 if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
56339 sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
56340 sCheck.errMsg.useMalloc = 2;
56341
56342 /* Check the integrity of the freelist
56343 */
56344 checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
@@ -56869,11 +56879,16 @@
56869 /*
56870 ** Parameter zSrcData points to a buffer containing the data for
56871 ** page iSrcPg from the source database. Copy this data into the
56872 ** destination database.
56873 */
56874 static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
 
 
 
 
 
56875 Pager * const pDestPager = sqlite3BtreePager(p->pDest);
56876 const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
56877 int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
56878 const int nCopy = MIN(nSrcPgsz, nDestPgsz);
56879 const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
@@ -56942,10 +56957,13 @@
56942 ** cached parse of the page). MemPage.isInit is marked
56943 ** "MUST BE FIRST" for this purpose.
56944 */
56945 memcpy(zOut, zIn, nCopy);
56946 ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
 
 
 
56947 }
56948 sqlite3PagerUnref(pDestPg);
56949 }
56950
56951 return rc;
@@ -57048,11 +57066,11 @@
57048 const Pgno iSrcPg = p->iNext; /* Source page number */
57049 if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
57050 DbPage *pSrcPg; /* Source page object */
57051 rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
57052 if( rc==SQLITE_OK ){
57053 rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
57054 sqlite3PagerUnref(pSrcPg);
57055 }
57056 }
57057 p->iNext++;
57058 }
@@ -57296,11 +57314,11 @@
57296 ** the new data into the backup.
57297 */
57298 int rc;
57299 assert( p->pDestDb );
57300 sqlite3_mutex_enter(p->pDestDb->mutex);
57301 rc = backupOnePage(p, iPage, aData);
57302 sqlite3_mutex_leave(p->pDestDb->mutex);
57303 assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
57304 if( rc!=SQLITE_OK ){
57305 p->rc = rc;
57306 }
@@ -71890,10 +71908,18 @@
71890 p->pReal = pReal;
71891 if( p->iSize>0 ){
71892 assert(p->iSize<=p->nBuf);
71893 rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
71894 }
 
 
 
 
 
 
 
 
71895 }
71896 }
71897 return rc;
71898 }
71899
@@ -72636,10 +72662,39 @@
72636 }
72637 }
72638 return 0;
72639 }
72640
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72641
72642 /*
72643 ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
72644 ** that name in the set of source tables in pSrcList and make the pExpr
72645 ** expression node refer back to that source column. The following changes
@@ -72691,44 +72746,63 @@
72691
72692 /* Initialize the node to no-match */
72693 pExpr->iTable = -1;
72694 pExpr->pTab = 0;
72695 ExprSetIrreducible(pExpr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72696
72697 /* Start at the inner-most context and move outward until a match is found */
72698 while( pNC && cnt==0 ){
72699 ExprList *pEList;
72700 SrcList *pSrcList = pNC->pSrcList;
72701
72702 if( pSrcList ){
72703 for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
72704 Table *pTab;
72705 int iDb;
72706 Column *pCol;
72707
72708 pTab = pItem->pTab;
72709 assert( pTab!=0 && pTab->zName!=0 );
72710 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
72711 assert( pTab->nCol>0 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72712 if( zTab ){
72713 if( pItem->zAlias ){
72714 char *zTabName = pItem->zAlias;
72715 if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
72716 }else{
72717 char *zTabName = pTab->zName;
72718 if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){
72719 continue;
72720 }
72721 if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
72722 continue;
72723 }
72724 }
72725 }
72726 if( 0==(cntTab++) ){
72727 pExpr->iTable = pItem->iCursor;
72728 pExpr->pTab = pTab;
72729 pSchema = pTab->pSchema;
72730 pMatch = pItem;
72731 }
72732 for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
72733 if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
72734 /* If there has been exactly one prior match and this match
@@ -72738,21 +72812,23 @@
72738 if( cnt==1 ){
72739 if( pItem->jointype & JT_NATURAL ) continue;
72740 if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
72741 }
72742 cnt++;
72743 pExpr->iTable = pItem->iCursor;
72744 pExpr->pTab = pTab;
72745 pMatch = pItem;
72746 pSchema = pTab->pSchema;
72747 /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
72748 pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
72749 break;
72750 }
72751 }
72752 }
72753 }
 
 
 
 
 
72754
72755 #ifndef SQLITE_OMIT_TRIGGER
72756 /* If we have not already resolved the name, then maybe
72757 ** it is a new.* or old.* trigger argument reference
72758 */
@@ -73083,11 +73159,11 @@
73083 #endif
73084 if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
73085 sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
73086 pNC->nErr++;
73087 is_agg = 0;
73088 }else if( no_such_func ){
73089 sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
73090 pNC->nErr++;
73091 }else if( wrong_num_args ){
73092 sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
73093 nId, zId);
@@ -73519,27 +73595,10 @@
73519 if( sqlite3ResolveExprNames(&sNC, p->pLimit) ||
73520 sqlite3ResolveExprNames(&sNC, p->pOffset) ){
73521 return WRC_Abort;
73522 }
73523
73524 /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
73525 ** resolve the result-set expression list.
73526 */
73527 sNC.ncFlags = NC_AllowAgg;
73528 sNC.pSrcList = p->pSrc;
73529 sNC.pNext = pOuterNC;
73530
73531 /* Resolve names in the result set. */
73532 pEList = p->pEList;
73533 assert( pEList!=0 );
73534 for(i=0; i<pEList->nExpr; i++){
73535 Expr *pX = pEList->a[i].pExpr;
73536 if( sqlite3ResolveExprNames(&sNC, pX) ){
73537 return WRC_Abort;
73538 }
73539 }
73540
73541 /* Recursively resolve names in all subqueries
73542 */
73543 for(i=0; i<p->pSrc->nSrc; i++){
73544 struct SrcList_item *pItem = &p->pSrc->a[i];
73545 if( pItem->pSelect ){
@@ -73562,10 +73621,27 @@
73562 for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
73563 assert( pItem->isCorrelated==0 && nRef<=0 );
73564 pItem->isCorrelated = (nRef!=0);
73565 }
73566 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73567
73568 /* If there are no aggregate functions in the result-set, and no GROUP BY
73569 ** expression, do not allow aggregates in any of the other expressions.
73570 */
73571 assert( (p->selFlags & SF_Aggregate)==0 );
@@ -77046,10 +77122,16 @@
77046 for(i=0; i<pList->nExpr; i++){
77047 sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
77048 sqlite3ExplainPush(pOut);
77049 sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
77050 sqlite3ExplainPop(pOut);
 
 
 
 
 
 
77051 if( i<pList->nExpr-1 ){
77052 sqlite3ExplainNL(pOut);
77053 }
77054 }
77055 sqlite3ExplainPop(pOut);
@@ -87523,11 +87605,11 @@
87523
87524 /*
87525 ** A foreign key constraint requires that the key columns in the parent
87526 ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
87527 ** Given that pParent is the parent table for foreign key constraint pFKey,
87528 ** search the schema a unique index on the parent key columns.
87529 **
87530 ** If successful, zero is returned. If the parent key is an INTEGER PRIMARY
87531 ** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx
87532 ** is set to point to the unique index.
87533 **
@@ -87559,11 +87641,11 @@
87559 **
87560 ** then non-zero is returned, and a "foreign key mismatch" error loaded
87561 ** into pParse. If an OOM error occurs, non-zero is returned and the
87562 ** pParse->db->mallocFailed flag is set.
87563 */
87564 static int locateFkeyIndex(
87565 Parse *pParse, /* Parse context to store any error in */
87566 Table *pParent, /* Parent table of FK constraint pFKey */
87567 FKey *pFKey, /* Foreign key to find index for */
87568 Index **ppIdx, /* OUT: Unique index on parent table */
87569 int **paiCol /* OUT: Map of index columns in pFKey */
@@ -87656,11 +87738,13 @@
87656 }
87657 }
87658
87659 if( !pIdx ){
87660 if( !pParse->disableTriggers ){
87661 sqlite3ErrorMsg(pParse, "foreign key mismatch");
 
 
87662 }
87663 sqlite3DbFree(pParse->db, aiCol);
87664 return 1;
87665 }
87666
@@ -88117,11 +88201,11 @@
88117 if( pParse->disableTriggers ){
88118 pTo = sqlite3FindTable(db, pFKey->zTo, zDb);
88119 }else{
88120 pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
88121 }
88122 if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
88123 assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) );
88124 if( !isIgnoreErrors || db->mallocFailed ) return;
88125 if( pTo==0 ){
88126 /* If isIgnoreErrors is true, then a table is being dropped. In this
88127 ** case SQLite runs a "DELETE FROM xxx" on the table being dropped
@@ -88197,11 +88281,11 @@
88197 /* Inserting a single row into a parent table cannot cause an immediate
88198 ** foreign key violation. So do nothing in this case. */
88199 continue;
88200 }
88201
88202 if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
88203 if( !isIgnoreErrors || db->mallocFailed ) return;
88204 continue;
88205 }
88206 assert( aiCol || pFKey->nCol==1 );
88207
@@ -88252,11 +88336,11 @@
88252 for(p=pTab->pFKey; p; p=p->pNextFrom){
88253 for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom);
88254 }
88255 for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
88256 Index *pIdx = 0;
88257 locateFkeyIndex(pParse, pTab, p, &pIdx, 0);
88258 if( pIdx ){
88259 for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
88260 }
88261 }
88262 }
@@ -88378,11 +88462,11 @@
88378 ExprList *pList = 0; /* Changes list if ON UPDATE CASCADE */
88379 Select *pSelect = 0; /* If RESTRICT, "SELECT RAISE(...)" */
88380 int i; /* Iterator variable */
88381 Expr *pWhen = 0; /* WHEN clause for the trigger */
88382
88383 if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
88384 assert( aiCol || pFKey->nCol==1 );
88385
88386 for(i=0; i<pFKey->nCol; i++){
88387 Token tOld = { "old", 3 }; /* Literal "old" token */
88388 Token tNew = { "new", 3 }; /* Literal "new" token */
@@ -92718,13 +92802,15 @@
92718 if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){
92719 Table *pTab;
92720 if( sqlite3ReadSchema(pParse) ) goto pragma_out;
92721 pTab = sqlite3FindTable(db, zRight, zDb);
92722 if( pTab ){
92723 int i;
92724 int nHidden = 0;
92725 Column *pCol;
 
 
92726 sqlite3VdbeSetNumCols(v, 6);
92727 pParse->nMem = 6;
92728 sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC);
92729 sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
92730 sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC);
@@ -92745,12 +92831,18 @@
92745 if( pCol->zDflt ){
92746 sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0);
92747 }else{
92748 sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
92749 }
92750 sqlite3VdbeAddOp2(v, OP_Integer,
92751 (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6);
 
 
 
 
 
 
92752 sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
92753 }
92754 }
92755 }else
92756
@@ -92881,10 +92973,124 @@
92881 ++i;
92882 pFK = pFK->pNextFrom;
92883 }
92884 }
92885 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92886 }else
92887 #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
92888
92889 #ifndef NDEBUG
92890 if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
@@ -93381,11 +93587,11 @@
93381 sqlite3_rekey(db, zKey, i/2);
93382 }
93383 }else
93384 #endif
93385 #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
93386 if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){
93387 #ifdef SQLITE_HAS_CODEC
93388 if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
93389 sqlite3_activate_see(&zRight[4]);
93390 }
93391 #endif
@@ -94340,11 +94546,11 @@
94340 SrcList *pSrc, /* the FROM clause -- which tables to scan */
94341 Expr *pWhere, /* the WHERE clause */
94342 ExprList *pGroupBy, /* the GROUP BY clause */
94343 Expr *pHaving, /* the HAVING clause */
94344 ExprList *pOrderBy, /* the ORDER BY clause */
94345 int isDistinct, /* true if the DISTINCT keyword is present */
94346 Expr *pLimit, /* LIMIT value. NULL means not used */
94347 Expr *pOffset /* OFFSET value. NULL means no offset */
94348 ){
94349 Select *pNew;
94350 Select standin;
@@ -94364,11 +94570,11 @@
94364 pNew->pSrc = pSrc;
94365 pNew->pWhere = pWhere;
94366 pNew->pGroupBy = pGroupBy;
94367 pNew->pHaving = pHaving;
94368 pNew->pOrderBy = pOrderBy;
94369 pNew->selFlags = isDistinct ? SF_Distinct : 0;
94370 pNew->op = TK_SELECT;
94371 pNew->pLimit = pLimit;
94372 pNew->pOffset = pOffset;
94373 assert( pOffset==0 || pLimit!=0 );
94374 pNew->addrOpenEphm[0] = -1;
@@ -95621,12 +95827,10 @@
95621
95622 for(i=0, pCol=aCol; i<nCol; i++, pCol++){
95623 /* Get an appropriate name for the column
95624 */
95625 p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
95626 assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue)
95627 || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 );
95628 if( (zName = pEList->a[i].zName)!=0 ){
95629 /* If the column contains an "AS <name>" phrase, use <name> as the name */
95630 zName = sqlite3DbStrDup(db, zName);
95631 }else{
95632 Expr *pColExpr = p; /* The expression that is the result column name */
@@ -95660,10 +95864,13 @@
95660 */
95661 nName = sqlite3Strlen30(zName);
95662 for(j=cnt=0; j<i; j++){
95663 if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
95664 char *zNewName;
 
 
 
95665 zName[nName] = 0;
95666 zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
95667 sqlite3DbFree(db, zName);
95668 zName = zNewName;
95669 j = -1;
@@ -97445,38 +97652,47 @@
97445 return 1;
97446 }
97447 #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
97448
97449 /*
97450 ** Analyze the SELECT statement passed as an argument to see if it
97451 ** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if
97452 ** it is, or 0 otherwise. At present, a query is considered to be
97453 ** a min()/max() query if:
97454 **
97455 ** 1. There is a single object in the FROM clause.
97456 **
97457 ** 2. There is a single expression in the result set, and it is
97458 ** either min(x) or max(x), where x is a column reference.
97459 */
97460 static u8 minMaxQuery(Select *p){
97461 Expr *pExpr;
97462 ExprList *pEList = p->pEList;
97463
97464 if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
97465 pExpr = pEList->a[0].pExpr;
97466 if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
97467 if( NEVER(ExprHasProperty(pExpr, EP_xIsSelect)) ) return 0;
97468 pEList = pExpr->x.pList;
97469 if( pEList==0 || pEList->nExpr!=1 ) return 0;
97470 if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
97471 assert( !ExprHasProperty(pExpr, EP_IntValue) );
97472 if( sqlite3StrICmp(pExpr->u.zToken,"min")==0 ){
97473 return WHERE_ORDERBY_MIN;
97474 }else if( sqlite3StrICmp(pExpr->u.zToken,"max")==0 ){
97475 return WHERE_ORDERBY_MAX;
97476 }
97477 return WHERE_ORDERBY_NORMAL;
 
 
 
 
 
 
 
 
 
97478 }
97479
97480 /*
97481 ** The select statement passed as the first argument is an aggregate query.
97482 ** The second argment is the associated aggregate-info object. This
@@ -97567,10 +97783,11 @@
97567 int i, j, k;
97568 SrcList *pTabList;
97569 ExprList *pEList;
97570 struct SrcList_item *pFrom;
97571 sqlite3 *db = pParse->db;
 
97572
97573 if( db->mallocFailed ){
97574 return WRC_Abort;
97575 }
97576 if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){
@@ -97652,11 +97869,11 @@
97652 **
97653 ** The first loop just checks to see if there are any "*" operators
97654 ** that need expanding.
97655 */
97656 for(k=0; k<pEList->nExpr; k++){
97657 Expr *pE = pEList->a[k].pExpr;
97658 if( pE->op==TK_ALL ) break;
97659 assert( pE->op!=TK_DOT || pE->pRight!=0 );
97660 assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
97661 if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break;
97662 }
@@ -97670,14 +97887,22 @@
97670 ExprList *pNew = 0;
97671 int flags = pParse->db->flags;
97672 int longNames = (flags & SQLITE_FullColNames)!=0
97673 && (flags & SQLITE_ShortColNames)==0;
97674
 
 
 
 
 
 
 
97675 for(k=0; k<pEList->nExpr; k++){
97676 Expr *pE = a[k].pExpr;
97677 assert( pE->op!=TK_DOT || pE->pRight!=0 );
97678 if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){
 
97679 /* This particular expression does not need to be expanded.
97680 */
97681 pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
97682 if( pNew ){
97683 pNew->a[pNew->nExpr-1].zName = a[k].zName;
@@ -97688,44 +97913,56 @@
97688 a[k].pExpr = 0;
97689 }else{
97690 /* This expression is a "*" or a "TABLE.*" and needs to be
97691 ** expanded. */
97692 int tableSeen = 0; /* Set to 1 when TABLE matches */
97693 char *zTName; /* text of name of TABLE */
97694 if( pE->op==TK_DOT ){
97695 assert( pE->pLeft!=0 );
97696 assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
97697 zTName = pE->pLeft->u.zToken;
97698 }else{
97699 zTName = 0;
97700 }
97701 for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
97702 Table *pTab = pFrom->pTab;
 
97703 char *zTabName = pFrom->zAlias;
 
 
97704 if( zTabName==0 ){
97705 zTabName = pTab->zName;
97706 }
97707 if( db->mallocFailed ) break;
97708 if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
97709 continue;
 
 
 
 
 
97710 }
97711 tableSeen = 1;
97712 for(j=0; j<pTab->nCol; j++){
97713 Expr *pExpr, *pRight;
97714 char *zName = pTab->aCol[j].zName;
97715 char *zColname; /* The computed column name */
97716 char *zToFree; /* Malloced string that needs to be freed */
97717 Token sColname; /* Computed column name as a token */
 
 
 
 
 
 
 
97718
97719 /* If a column is marked as 'hidden' (currently only possible
97720 ** for virtual tables), do not include it in the expanded
97721 ** result-set list.
97722 */
97723 if( IsHiddenColumn(&pTab->aCol[j]) ){
97724 assert(IsVirtual(pTab));
97725 continue;
97726 }
 
97727
97728 if( i>0 && zTName==0 ){
97729 if( (pFrom->jointype & JT_NATURAL)!=0
97730 && tableAndColumnIndex(pTabList, i, zName, 0, 0)
97731 ){
@@ -97744,10 +97981,14 @@
97744 zToFree = 0;
97745 if( longNames || pTabList->nSrc>1 ){
97746 Expr *pLeft;
97747 pLeft = sqlite3Expr(db, TK_ID, zTabName);
97748 pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
 
 
 
 
97749 if( longNames ){
97750 zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
97751 zToFree = zColname;
97752 }
97753 }else{
@@ -97755,10 +97996,22 @@
97755 }
97756 pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
97757 sColname.z = zColname;
97758 sColname.n = sqlite3Strlen30(zColname);
97759 sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
 
 
 
 
 
 
 
 
 
 
 
 
97760 sqlite3DbFree(db, zToFree);
97761 }
97762 }
97763 if( !tableSeen ){
97764 if( zTName ){
@@ -98812,15 +99065,21 @@
98812 ** index or indices to use) should place a different priority on
98813 ** satisfying the 'ORDER BY' clause than it does in other cases.
98814 ** Refer to code and comments in where.c for details.
98815 */
98816 ExprList *pMinMax = 0;
98817 u8 flag = minMaxQuery(p);
 
 
 
 
 
 
 
 
98818 if( flag ){
98819 assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) );
98820 assert( p->pEList->a[0].pExpr->x.pList->nExpr==1 );
98821 pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0);
98822 pDel = pMinMax;
98823 if( pMinMax && !db->mallocFailed ){
98824 pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
98825 pMinMax->a[0].pExpr->op = TK_COLUMN;
98826 }
@@ -102549,12 +102808,12 @@
102549 Expr *pExpr; /* Pointer to the subexpression that is this term */
102550 int iParent; /* Disable pWC->a[iParent] when this term disabled */
102551 int leftCursor; /* Cursor number of X in "X <op> <expr>" */
102552 union {
102553 int leftColumn; /* Column number of X in "X <op> <expr>" */
102554 WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */
102555 WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */
102556 } u;
102557 u16 eOperator; /* A WO_xx value describing <op> */
102558 u8 wtFlags; /* TERM_xxx bit flags. See below */
102559 u8 nChild; /* Number of children that must disable us */
102560 WhereClause *pWC; /* The clause this term is part of */
@@ -102678,10 +102937,11 @@
102678 #define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
102679 #define WO_MATCH 0x040
102680 #define WO_ISNULL 0x080
102681 #define WO_OR 0x100 /* Two or more OR-connected terms */
102682 #define WO_AND 0x200 /* Two or more AND-connected terms */
 
102683 #define WO_NOOP 0x800 /* This term does not restrict search space */
102684
102685 #define WO_ALL 0xfff /* Mask of all possible WO_* values */
102686 #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
102687
@@ -102704,11 +102964,11 @@
102704 #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */
102705 #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
102706 #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
102707 #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
102708 #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
102709 #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
102710 #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
102711 #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
102712 #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
102713 #define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
102714 #define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
@@ -102854,11 +103114,11 @@
102854 sqlite3DbFree(db, pOld);
102855 }
102856 pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
102857 }
102858 pTerm = &pWC->a[idx = pWC->nTerm++];
102859 pTerm->pExpr = p;
102860 pTerm->wtFlags = wtFlags;
102861 pTerm->pWC = pWC;
102862 pTerm->iParent = -1;
102863 return idx;
102864 }
@@ -103080,58 +103340,112 @@
103080 /*
103081 ** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
103082 ** where X is a reference to the iColumn of table iCur and <op> is one of
103083 ** the WO_xx operator codes specified by the op parameter.
103084 ** Return a pointer to the term. Return 0 if not found.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103085 */
103086 static WhereTerm *findTerm(
103087 WhereClause *pWC, /* The WHERE clause to be searched */
103088 int iCur, /* Cursor number of LHS */
103089 int iColumn, /* Column number of LHS */
103090 Bitmask notReady, /* RHS must not overlap with this mask */
103091 u32 op, /* Mask of WO_xx values describing operator */
103092 Index *pIdx /* Must be compatible with this index, if not NULL */
103093 ){
103094 WhereTerm *pTerm;
103095 int k;
 
 
 
 
 
 
 
 
 
103096 assert( iCur>=0 );
103097 op &= WO_ALL;
103098 for(; pWC; pWC=pWC->pOuter){
103099 for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
103100 if( pTerm->leftCursor==iCur
103101 && (pTerm->prereqRight & notReady)==0
103102 && pTerm->u.leftColumn==iColumn
103103 && (pTerm->eOperator & op)!=0
103104 ){
103105 if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){
103106 Expr *pX = pTerm->pExpr;
103107 CollSeq *pColl;
103108 char idxaff;
103109 int j;
103110 Parse *pParse = pWC->pParse;
103111
103112 idxaff = pIdx->pTable->aCol[iColumn].affinity;
103113 if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
103114
103115 /* Figure out the collation sequence required from an index for
103116 ** it to be useful for optimising expression pX. Store this
103117 ** value in variable pColl.
103118 */
103119 assert(pX->pLeft);
103120 pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
103121 if( pColl==0 ) pColl = pParse->db->pDfltColl;
103122
103123 for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
103124 if( NEVER(j>=pIdx->nColumn) ) return 0;
103125 }
103126 if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
103127 }
103128 return pTerm;
103129 }
103130 }
103131 }
103132 return 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103133 }
103134
103135 /* Forward reference */
103136 static void exprAnalyze(SrcList*, WhereClause*, int);
103137
@@ -103405,11 +103719,10 @@
103405 indexable = ~(Bitmask)0;
103406 chngToIN = ~(pWC->vmask);
103407 for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){
103408 if( (pOrTerm->eOperator & WO_SINGLE)==0 ){
103409 WhereAndInfo *pAndInfo;
103410 assert( pOrTerm->eOperator==0 );
103411 assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
103412 chngToIN = 0;
103413 pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
103414 if( pAndInfo ){
103415 WhereClause *pAndWC;
@@ -103444,11 +103757,11 @@
103444 if( pOrTerm->wtFlags & TERM_VIRTUAL ){
103445 WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
103446 b |= getMask(pMaskSet, pOther->leftCursor);
103447 }
103448 indexable &= b;
103449 if( pOrTerm->eOperator!=WO_EQ ){
103450 chngToIN = 0;
103451 }else{
103452 chngToIN &= b;
103453 }
103454 }
@@ -103495,11 +103808,11 @@
103495 ** and column is found but leave okToChngToIN false if not found.
103496 */
103497 for(j=0; j<2 && !okToChngToIN; j++){
103498 pOrTerm = pOrWc->a;
103499 for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
103500 assert( pOrTerm->eOperator==WO_EQ );
103501 pOrTerm->wtFlags &= ~TERM_OR_OK;
103502 if( pOrTerm->leftCursor==iCursor ){
103503 /* This is the 2-bit case and we are on the second iteration and
103504 ** current term is from the first iteration. So skip this term. */
103505 assert( j==1 );
@@ -103521,21 +103834,21 @@
103521 }
103522 if( i<0 ){
103523 /* No candidate table+column was found. This can only occur
103524 ** on the second iteration */
103525 assert( j==1 );
103526 assert( (chngToIN&(chngToIN-1))==0 );
103527 assert( chngToIN==getMask(pMaskSet, iCursor) );
103528 break;
103529 }
103530 testcase( j==1 );
103531
103532 /* We have found a candidate table and column. Check to see if that
103533 ** table and column is common to every term in the OR clause */
103534 okToChngToIN = 1;
103535 for(; i>=0 && okToChngToIN; i--, pOrTerm++){
103536 assert( pOrTerm->eOperator==WO_EQ );
103537 if( pOrTerm->leftCursor!=iCursor ){
103538 pOrTerm->wtFlags &= ~TERM_OR_OK;
103539 }else if( pOrTerm->u.leftColumn!=iColumn ){
103540 okToChngToIN = 0;
103541 }else{
@@ -103567,11 +103880,11 @@
103567 Expr *pLeft = 0; /* The LHS of the IN operator */
103568 Expr *pNew; /* The complete IN operator */
103569
103570 for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
103571 if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
103572 assert( pOrTerm->eOperator==WO_EQ );
103573 assert( pOrTerm->leftCursor==iCursor );
103574 assert( pOrTerm->u.leftColumn==iColumn );
103575 pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
103576 pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup);
103577 pLeft = pOrTerm->pExpr->pLeft;
@@ -103596,11 +103909,10 @@
103596 pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
103597 }
103598 }
103599 }
103600 #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
103601
103602
103603 /*
103604 ** The input to this routine is an WhereTerm structure with only the
103605 ** "pExpr" field filled in. The job of this routine is to analyze the
103606 ** subexpression and populate all the other fields of the WhereTerm
@@ -103639,11 +103951,12 @@
103639 if( db->mallocFailed ){
103640 return;
103641 }
103642 pTerm = &pWC->a[idxTerm];
103643 pMaskSet = pWC->pMaskSet;
103644 pExpr = sqlite3ExprSkipCollate(pTerm->pExpr);
 
103645 prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
103646 op = pExpr->op;
103647 if( op==TK_IN ){
103648 assert( pExpr->pRight==0 );
103649 if( ExprHasProperty(pExpr, EP_xIsSelect) ){
@@ -103665,21 +103978,23 @@
103665 }
103666 pTerm->prereqAll = prereqAll;
103667 pTerm->leftCursor = -1;
103668 pTerm->iParent = -1;
103669 pTerm->eOperator = 0;
103670 if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){
103671 Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
103672 Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
 
103673 if( pLeft->op==TK_COLUMN ){
103674 pTerm->leftCursor = pLeft->iTable;
103675 pTerm->u.leftColumn = pLeft->iColumn;
103676 pTerm->eOperator = operatorMask(op);
103677 }
103678 if( pRight && pRight->op==TK_COLUMN ){
103679 WhereTerm *pNew;
103680 Expr *pDup;
 
103681 if( pTerm->leftCursor>=0 ){
103682 int idxNew;
103683 pDup = sqlite3ExprDup(db, pExpr, 0);
103684 if( db->mallocFailed ){
103685 sqlite3ExprDelete(db, pDup);
@@ -103690,10 +104005,17 @@
103690 pNew = &pWC->a[idxNew];
103691 pNew->iParent = idxTerm;
103692 pTerm = &pWC->a[idxTerm];
103693 pTerm->nChild = 1;
103694 pTerm->wtFlags |= TERM_COPIED;
 
 
 
 
 
 
 
103695 }else{
103696 pDup = pExpr;
103697 pNew = pTerm;
103698 }
103699 exprCommute(pParse, pDup);
@@ -103701,11 +104023,11 @@
103701 pNew->leftCursor = pLeft->iTable;
103702 pNew->u.leftColumn = pLeft->iColumn;
103703 testcase( (prereqLeft | extraRight) != prereqLeft );
103704 pNew->prereqRight = prereqLeft | extraRight;
103705 pNew->prereqAll = prereqAll;
103706 pNew->eOperator = operatorMask(pDup->op);
103707 }
103708 }
103709
103710 #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION
103711 /* If a term is the BETWEEN operator, create two new virtual terms
@@ -104160,11 +104482,11 @@
104160 return;
104161 }
104162
104163 /* Search the WHERE clause terms for a usable WO_OR term. */
104164 for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
104165 if( pTerm->eOperator==WO_OR
104166 && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0
104167 && (pTerm->u.pOrInfo->indexable & maskSrc)!=0
104168 ){
104169 WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
104170 WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
@@ -104181,11 +104503,11 @@
104181 sBOI.ppIdxInfo = 0;
104182 for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
104183 WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
104184 (pOrTerm - pOrWC->a), (pTerm - pWC->a)
104185 ));
104186 if( pOrTerm->eOperator==WO_AND ){
104187 sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
104188 bestIndex(&sBOI);
104189 }else if( pOrTerm->leftCursor==iCur ){
104190 WhereClause tempWC;
104191 tempWC.pParse = pWC->pParse;
@@ -104242,11 +104564,11 @@
104242 struct SrcList_item *pSrc, /* Table we are trying to access */
104243 Bitmask notReady /* Tables in outer loops of the join */
104244 ){
104245 char aff;
104246 if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
104247 if( pTerm->eOperator!=WO_EQ ) return 0;
104248 if( (pTerm->prereqRight & notReady)!=0 ) return 0;
104249 aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
104250 if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
104251 return 1;
104252 }
@@ -104504,14 +104826,14 @@
104504
104505 /* Count the number of possible WHERE clause constraints referring
104506 ** to this virtual table */
104507 for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
104508 if( pTerm->leftCursor != pSrc->iCursor ) continue;
104509 assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
104510 testcase( pTerm->eOperator==WO_IN );
104511 testcase( pTerm->eOperator==WO_ISNULL );
104512 if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
104513 if( pTerm->wtFlags & TERM_VNULL ) continue;
104514 nTerm++;
104515 }
104516
104517 /* If the ORDER BY clause contains only columns in the current
@@ -104555,29 +104877,32 @@
104555 *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy;
104556 *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
104557 pUsage;
104558
104559 for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
 
104560 if( pTerm->leftCursor != pSrc->iCursor ) continue;
104561 assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
104562 testcase( pTerm->eOperator==WO_IN );
104563 testcase( pTerm->eOperator==WO_ISNULL );
104564 if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
104565 if( pTerm->wtFlags & TERM_VNULL ) continue;
104566 pIdxCons[j].iColumn = pTerm->u.leftColumn;
104567 pIdxCons[j].iTermOffset = i;
104568 pIdxCons[j].op = (u8)pTerm->eOperator;
 
 
104569 /* The direct assignment in the previous line is possible only because
104570 ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
104571 ** following asserts verify this fact. */
104572 assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
104573 assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
104574 assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE );
104575 assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
104576 assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
104577 assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
104578 assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
104579 j++;
104580 }
104581 for(i=0; i<nOrderBy; i++){
104582 Expr *pExpr = pOrderBy->a[i].pExpr;
104583 pIdxOrderBy[i].iColumn = pExpr->iColumn;
@@ -104659,10 +104984,11 @@
104659 struct sqlite3_index_constraint *pIdxCons;
104660 struct sqlite3_index_constraint_usage *pUsage;
104661 WhereTerm *pTerm;
104662 int i, j;
104663 int nOrderBy;
 
104664 double rCost;
104665
104666 /* Make sure wsFlags is initialized to some sane value. Otherwise, if the
104667 ** malloc in allocateIndexInfo() fails and this function returns leaving
104668 ** wsFlags in an uninitialized state, the caller may behave unpredictably.
@@ -104693,63 +105019,91 @@
104693 ** sqlite3ViewGetColumnNames() would have picked up the error.
104694 */
104695 assert( pTab->azModuleArg && pTab->azModuleArg[0] );
104696 assert( sqlite3GetVTable(pParse->db, pTab) );
104697
104698 /* Set the aConstraint[].usable fields and initialize all
104699 ** output variables to zero.
104700 **
104701 ** aConstraint[].usable is true for constraints where the right-hand
104702 ** side contains only references to tables to the left of the current
104703 ** table. In other words, if the constraint is of the form:
104704 **
104705 ** column = expr
104706 **
104707 ** and we are evaluating a join, then the constraint on column is
104708 ** only valid if all tables referenced in expr occur to the left
104709 ** of the table containing column.
104710 **
104711 ** The aConstraints[] array contains entries for all constraints
104712 ** on the current table. That way we only have to compute it once
104713 ** even though we might try to pick the best index multiple times.
104714 ** For each attempt at picking an index, the order of tables in the
104715 ** join might be different so we have to recompute the usable flag
104716 ** each time.
104717 */
104718 pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
104719 pUsage = pIdxInfo->aConstraintUsage;
104720 for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
104721 j = pIdxCons->iTermOffset;
104722 pTerm = &pWC->a[j];
104723 pIdxCons->usable = (pTerm->prereqRight&p->notReady) ? 0 : 1;
104724 }
104725 memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
104726 if( pIdxInfo->needToFreeIdxStr ){
104727 sqlite3_free(pIdxInfo->idxStr);
104728 }
104729 pIdxInfo->idxStr = 0;
104730 pIdxInfo->idxNum = 0;
104731 pIdxInfo->needToFreeIdxStr = 0;
104732 pIdxInfo->orderByConsumed = 0;
104733 /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
104734 pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
104735 nOrderBy = pIdxInfo->nOrderBy;
104736 if( !p->pOrderBy ){
104737 pIdxInfo->nOrderBy = 0;
104738 }
104739
104740 if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
104741 return;
104742 }
104743
104744 pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
104745 for(i=0; i<pIdxInfo->nConstraint; i++){
104746 if( pUsage[i].argvIndex>0 ){
104747 p->cost.used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight;
104748 }
104749 }
104750
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104751 /* If there is an ORDER BY clause, and the selected virtual table index
104752 ** does not satisfy it, increase the cost of the scan accordingly. This
104753 ** matches the processing for non-virtual tables in bestBtreeIndex().
104754 */
104755 rCost = pIdxInfo->estimatedCost;
@@ -105040,28 +105394,28 @@
105040 u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
105041
105042 if( pLower ){
105043 Expr *pExpr = pLower->pExpr->pRight;
105044 rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
105045 assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
105046 if( rc==SQLITE_OK
105047 && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
105048 ){
105049 iLower = a[0];
105050 if( pLower->eOperator==WO_GT ) iLower += a[1];
105051 }
105052 sqlite3ValueFree(pRangeVal);
105053 }
105054 if( rc==SQLITE_OK && pUpper ){
105055 Expr *pExpr = pUpper->pExpr->pRight;
105056 rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
105057 assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
105058 if( rc==SQLITE_OK
105059 && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
105060 ){
105061 iUpper = a[0];
105062 if( pUpper->eOperator==WO_LE ) iUpper += a[1];
105063 }
105064 sqlite3ValueFree(pRangeVal);
105065 }
105066 if( rc==SQLITE_OK ){
105067 if( iUpper<=iLower ){
@@ -105365,16 +105719,16 @@
105365 ** if there are any X= or X IS NULL constraints in the WHERE clause. */
105366 pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
105367 WO_EQ|WO_ISNULL|WO_IN, pIdx);
105368 if( pConstraint==0 ){
105369 isEq = 0;
105370 }else if( pConstraint->eOperator==WO_IN ){
105371 /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY
105372 ** because we do not know in what order the values on the RHS of the IN
105373 ** operator will occur. */
105374 break;
105375 }else if( pConstraint->eOperator==WO_ISNULL ){
105376 uniqueNotNull = 0;
105377 isEq = 1; /* "X IS NULL" means X has only a single value */
105378 }else if( pConstraint->prereqRight==0 ){
105379 isEq = 1; /* Constraint "X=constant" means X has only a single value */
105380 }else{
@@ -105720,11 +106074,11 @@
105720 int bRev = 2;
105721 WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat));
105722 pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev);
105723 WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n",
105724 bRev, pc.plan.nOBSat));
105725 if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){
105726 pc.plan.wsFlags |= WHERE_ORDERED;
105727 }
105728 if( nOrderBy==pc.plan.nOBSat ){
105729 bSort = 0;
105730 pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
@@ -105783,16 +106137,17 @@
105783 */
105784 if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
105785 && pFirstTerm!=0 && aiRowEst[1]>1 ){
105786 assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
105787 if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
105788 testcase( pFirstTerm->eOperator==WO_EQ );
105789 testcase( pFirstTerm->eOperator==WO_ISNULL );
 
105790 whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
105791 &pc.plan.nRow);
105792 }else if( bInEst==0 ){
105793 assert( pFirstTerm->eOperator==WO_IN );
105794 whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
105795 &pc.plan.nRow);
105796 }
105797 }
105798 #endif /* SQLITE_ENABLE_STAT3 */
@@ -105935,11 +106290,11 @@
105935 ** more selective intentionally because of the subjective
105936 ** observation that indexed range constraints really are more
105937 ** selective in practice, on average. */
105938 pc.plan.nRow /= 3;
105939 }
105940 }else if( pTerm->eOperator!=WO_NOOP ){
105941 /* Any other expression lowers the output row count by half */
105942 pc.plan.nRow /= 2;
105943 }
105944 }
105945 if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
@@ -105987,12 +106342,13 @@
105987 assert( pSrc->pIndex==0
105988 || p->cost.plan.u.pIdx==0
105989 || p->cost.plan.u.pIdx==pSrc->pIndex
105990 );
105991
105992 WHERETRACE((" best index is: %s\n",
105993 p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
 
105994
105995 bestOrClauseIndex(p);
105996 bestAutomaticIndex(p);
105997 p->cost.plan.wsFlags |= eqTermMask;
105998 }
@@ -106514,32 +106870,40 @@
106514 if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
106515 /* Case 0: The table is a virtual-table. Use the VFilter and VNext
106516 ** to access the data.
106517 */
106518 int iReg; /* P3 Value for OP_VFilter */
 
106519 sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
106520 int nConstraint = pVtabIdx->nConstraint;
106521 struct sqlite3_index_constraint_usage *aUsage =
106522 pVtabIdx->aConstraintUsage;
106523 const struct sqlite3_index_constraint *aConstraint =
106524 pVtabIdx->aConstraint;
106525
106526 sqlite3ExprCachePush(pParse);
106527 iReg = sqlite3GetTempRange(pParse, nConstraint+2);
 
106528 for(j=1; j<=nConstraint; j++){
106529 for(k=0; k<nConstraint; k++){
106530 if( aUsage[k].argvIndex==j ){
106531 int iTerm = aConstraint[k].iTermOffset;
106532 sqlite3ExprCode(pParse, pWC->a[iTerm].pExpr->pRight, iReg+j+1);
 
 
 
 
 
 
106533 break;
106534 }
106535 }
106536 if( k==nConstraint ) break;
106537 }
106538 sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg);
106539 sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
106540 sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr,
106541 pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
106542 pVtabIdx->needToFreeIdxStr = 0;
106543 for(j=0; j<nConstraint; j++){
106544 if( aUsage[j].omit ){
106545 int iTerm = aConstraint[j].iTermOffset;
@@ -106562,11 +106926,10 @@
106562 */
106563 iReleaseReg = sqlite3GetTempReg(pParse);
106564 pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
106565 assert( pTerm!=0 );
106566 assert( pTerm->pExpr!=0 );
106567 assert( pTerm->leftCursor==iCur );
106568 assert( omitTable==0 );
106569 testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
106570 iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg);
106571 addrNxt = pLevel->addrNxt;
106572 sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
@@ -106953,11 +107316,11 @@
106953 int ii; /* Loop counter */
106954 Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
106955
106956 pTerm = pLevel->plan.u.pTerm;
106957 assert( pTerm!=0 );
106958 assert( pTerm->eOperator==WO_OR );
106959 assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
106960 pOrWc = &pTerm->u.pOrInfo->wc;
106961 pLevel->op = OP_Return;
106962 pLevel->p1 = regReturn;
106963
@@ -107026,11 +107389,11 @@
107026 }
107027 }
107028
107029 for(ii=0; ii<pOrWc->nTerm; ii++){
107030 WhereTerm *pOrTerm = &pOrWc->a[ii];
107031 if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
107032 WhereInfo *pSubWInfo; /* Info for single OR-term scan */
107033 Expr *pOrExpr = pOrTerm->pExpr;
107034 if( pAndExpr ){
107035 pAndExpr->pLeft = pOrExpr;
107036 pOrExpr = pAndExpr;
@@ -107481,10 +107844,11 @@
107481 Index *pIdx; /* Index for FROM table at pTabItem */
107482 int j; /* For looping over FROM tables */
107483 int bestJ = -1; /* The value of j */
107484 Bitmask m; /* Bitmask value for j or bestJ */
107485 int isOptimal; /* Iterator for optimal/non-optimal search */
 
107486 int nUnconstrained; /* Number tables without INDEXED BY */
107487 Bitmask notIndexed; /* Mask of tables that cannot use an index */
107488
107489 memset(&bestPlan, 0, sizeof(bestPlan));
107490 bestPlan.rCost = SQLITE_BIG_DBL;
@@ -107515,14 +107879,12 @@
107515 **
107516 ** The second loop iteration is only performed if no optimal scan
107517 ** strategies were found by the first iteration. This second iteration
107518 ** is used to search for the lowest cost scan overall.
107519 **
107520 ** Previous versions of SQLite performed only the second iteration -
107521 ** the next outermost loop was always that with the lowest overall
107522 ** cost. However, this meant that SQLite could select the wrong plan
107523 ** for scripts such as the following:
107524 **
107525 ** CREATE TABLE t1(a, b);
107526 ** CREATE TABLE t2(c, d);
107527 ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
107528 **
@@ -107533,20 +107895,44 @@
107533 ** algorithm may choose to use t2 for the outer loop, which is a much
107534 ** costlier approach.
107535 */
107536 nUnconstrained = 0;
107537 notIndexed = 0;
107538 for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){
 
 
 
 
 
 
107539 for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
107540 int doNotReorder; /* True if this table should not be reordered */
107541
107542 doNotReorder = (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0;
107543 if( j!=iFrom && doNotReorder ) break;
107544 m = getMask(pMaskSet, sWBI.pSrc->iCursor);
107545 if( (m & sWBI.notValid)==0 ){
107546 if( j==iFrom ) iFrom++;
107547 continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107548 }
107549 sWBI.notReady = (isOptimal ? m : sWBI.notValid);
107550 if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
107551
107552 WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
@@ -107572,12 +107958,12 @@
107572 if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
107573 notIndexed |= m;
107574 }
107575 if( isOptimal ){
107576 pWInfo->a[j].rOptCost = sWBI.cost.rCost;
107577 }else if( iFrom<nTabList-1 ){
107578 /* If two or more tables have nearly the same outer loop cost,
107579 ** very different inner loop (optimal) cost, we want to choose
107580 ** for the outer loop that table which benefits the least from
107581 ** being in the inner loop. The following code scales the
107582 ** outer loop cost estimate to accomplish that. */
107583 WHERETRACE((" scaling cost from %.1f to %.1f\n",
@@ -107618,15 +108004,23 @@
107618 sWBI.cost.rCost, sWBI.cost.plan.nRow,
107619 sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
107620 bestPlan = sWBI.cost;
107621 bestJ = j;
107622 }
107623 if( doNotReorder ) break;
 
 
 
 
107624 }
107625 }
107626 assert( bestJ>=0 );
107627 assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
 
 
 
 
107628 WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
107629 " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
107630 bestJ, pTabList->a[bestJ].pTab->zName,
107631 pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
107632 bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
@@ -108182,10 +108576,11 @@
108182 Expr* yy122;
108183 Select* yy159;
108184 IdList* yy180;
108185 struct {int value; int mask;} yy207;
108186 u8 yy258;
 
108187 struct LikeOp yy318;
108188 TriggerStep* yy327;
108189 ExprSpan yy342;
108190 SrcList* yy347;
108191 int yy392;
@@ -110132,22 +110527,19 @@
110132 case 82: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==82);
110133 case 84: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==84);
110134 case 86: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==86);
110135 case 98: /* defer_subclause_opt ::= */ yytestcase(yyruleno==98);
110136 case 109: /* ifexists ::= */ yytestcase(yyruleno==109);
110137 case 120: /* distinct ::= ALL */ yytestcase(yyruleno==120);
110138 case 121: /* distinct ::= */ yytestcase(yyruleno==121);
110139 case 221: /* between_op ::= BETWEEN */ yytestcase(yyruleno==221);
110140 case 224: /* in_op ::= IN */ yytestcase(yyruleno==224);
110141 {yygotominor.yy392 = 0;}
110142 break;
110143 case 29: /* ifnotexists ::= IF NOT EXISTS */
110144 case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30);
110145 case 70: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==70);
110146 case 85: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==85);
110147 case 108: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==108);
110148 case 119: /* distinct ::= DISTINCT */ yytestcase(yyruleno==119);
110149 case 222: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==222);
110150 case 225: /* in_op ::= NOT IN */ yytestcase(yyruleno==225);
110151 {yygotominor.yy392 = 1;}
110152 break;
110153 case 32: /* create_table_args ::= LP columnlist conslist_opt RP */
@@ -110383,12 +110775,19 @@
110383 case 116: /* multiselect_op ::= UNION ALL */
110384 {yygotominor.yy392 = TK_ALL;}
110385 break;
110386 case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
110387 {
110388 yygotominor.yy159 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy442,yymsp[-5].minor.yy347,yymsp[-4].minor.yy122,yymsp[-3].minor.yy442,yymsp[-2].minor.yy122,yymsp[-1].minor.yy442,yymsp[-7].minor.yy392,yymsp[0].minor.yy64.pLimit,yymsp[0].minor.yy64.pOffset);
110389 }
 
 
 
 
 
 
 
110390 break;
110391 case 122: /* sclp ::= selcollist COMMA */
110392 case 246: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==246);
110393 {yygotominor.yy442 = yymsp[-1].minor.yy442;}
110394 break;
@@ -110454,14 +110853,24 @@
110454 break;
110455 case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
110456 {
110457 if( yymsp[-6].minor.yy347==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy122==0 && yymsp[0].minor.yy180==0 ){
110458 yygotominor.yy347 = yymsp[-4].minor.yy347;
 
 
 
 
 
 
 
 
 
 
110459 }else{
110460 Select *pSubquery;
110461 sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy347);
110462 pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy347,0,0,0,0,0,0,0);
110463 yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
110464 }
110465 }
110466 break;
110467 case 137: /* dbnm ::= */
@@ -110690,11 +111099,11 @@
110690 if( yymsp[-1].minor.yy442 && yymsp[-1].minor.yy442->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
110691 sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
110692 }
110693 yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy442, &yymsp[-4].minor.yy0);
110694 spanSet(&yygotominor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
110695 if( yymsp[-2].minor.yy392 && yygotominor.yy342.pExpr ){
110696 yygotominor.yy342.pExpr->flags |= EP_Distinct;
110697 }
110698 }
110699 break;
110700 case 197: /* expr ::= ID LP STAR RP */
@@ -136337,11 +136746,12 @@
136337 ** would fit in a single node, use a smaller node-size.
136338 */
136339 static int getNodeSize(
136340 sqlite3 *db, /* Database handle */
136341 Rtree *pRtree, /* Rtree handle */
136342 int isCreate /* True for xCreate, false for xConnect */
 
136343 ){
136344 int rc;
136345 char *zSql;
136346 if( isCreate ){
136347 int iPageSize = 0;
@@ -136350,17 +136760,22 @@
136350 if( rc==SQLITE_OK ){
136351 pRtree->iNodeSize = iPageSize-64;
136352 if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
136353 pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
136354 }
 
 
136355 }
136356 }else{
136357 zSql = sqlite3_mprintf(
136358 "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1",
136359 pRtree->zDb, pRtree->zName
136360 );
136361 rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
 
 
 
136362 }
136363
136364 sqlite3_free(zSql);
136365 return rc;
136366 }
@@ -136420,11 +136835,11 @@
136420 pRtree->eCoordType = eCoordType;
136421 memcpy(pRtree->zDb, argv[1], nDb);
136422 memcpy(pRtree->zName, argv[2], nName);
136423
136424 /* Figure out the node size to use. */
136425 rc = getNodeSize(db, pRtree, isCreate);
136426
136427 /* Create/Connect to the underlying relational database schema. If
136428 ** that is successful, call sqlite3_declare_vtab() to configure
136429 ** the r-tree table schema.
136430 */
136431
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -1,8 +1,8 @@
1 /******************************************************************************
2 ** This file is an amalgamation of many separate C source files from SQLite
3 ** version 3.7.16. By combining all the individual C code files into this
4 ** single large file, the entire code can be compiled as a single translation
5 ** unit. This allows many compilers to do optimizations that would not be
6 ** possible if the files were compiled separately. Performance improvements
7 ** of 5% or more are commonly seen when SQLite is compiled as a single
8 ** translation unit.
@@ -671,13 +671,13 @@
671 **
672 ** See also: [sqlite3_libversion()],
673 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
674 ** [sqlite_version()] and [sqlite_source_id()].
675 */
676 #define SQLITE_VERSION "3.7.16"
677 #define SQLITE_VERSION_NUMBER 3007016
678 #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3"
679
680 /*
681 ** CAPI3REF: Run-Time Library Version Numbers
682 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
683 **
@@ -2156,11 +2156,11 @@
2156 ** database connection is opened. By default, URI handling is globally
2157 ** disabled. The default value may be changed by compiling with the
2158 ** [SQLITE_USE_URI] symbol defined.
2159 **
2160 ** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
2161 ** <dd> This option takes a single integer argument which is interpreted as
2162 ** a boolean in order to enable or disable the use of covering indices for
2163 ** full table scans in the query optimizer. The default setting is determined
2164 ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
2165 ** if that compile-time option is omitted.
2166 ** The ability to disable the use of covering indices for full table scans
@@ -8238,10 +8238,15 @@
8238 ** A convenience macro that returns the number of elements in
8239 ** an array.
8240 */
8241 #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0])))
8242
8243 /*
8244 ** Determine if the argument is a power of two
8245 */
8246 #define IsPowerOfTwo(X) (((X)&((X)-1))==0)
8247
8248 /*
8249 ** The following value as a destructor means to use sqlite3DbFree().
8250 ** The sqlite3DbFree() routine requires two parameters instead of the
8251 ** one parameter that destructors normally want. So we have to introduce
8252 ** this magic value that the code knows to handle differently. Any
@@ -10042,10 +10047,11 @@
10047 #define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */
10048 #define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
10049 #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
10050 #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
10051 #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
10052 #define SQLITE_Transitive 0x0200 /* Transitive constraints */
10053 #define SQLITE_AllOpts 0xffff /* All optimizations */
10054
10055 /*
10056 ** Macros for testing whether or not optimizations are enabled or disabled.
10057 */
@@ -10553,24 +10559,24 @@
10559 ** and the value of Index.onError indicate the which conflict resolution
10560 ** algorithm to employ whenever an attempt is made to insert a non-unique
10561 ** element.
10562 */
10563 struct Index {
10564 char *zName; /* Name of this index */
10565 int *aiColumn; /* Which columns are used by this index. 1st is 0 */
10566 tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */
10567 Table *pTable; /* The SQL table being indexed */
10568 char *zColAff; /* String defining the affinity of each column */
10569 Index *pNext; /* The next index associated with the same table */
10570 Schema *pSchema; /* Schema containing this index */
10571 u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
10572 char **azColl; /* Array of collation sequence names for index */
10573 int tnum; /* DB Page containing root of this index */
10574 u16 nColumn; /* Number of columns in table used by this index */
10575 u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
10576 unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
10577 unsigned bUnordered:1; /* Use this index for == or IN queries only */
10578 #ifdef SQLITE_ENABLE_STAT3
10579 int nSample; /* Number of elements in aSample[] */
10580 tRowcnt avgEq; /* Average nEq value for key values not in aSample */
10581 IndexSample *aSample; /* Samples of the left-most key */
10582 #endif
@@ -10840,22 +10846,31 @@
10846 ** name. An expr/name combination can be used in several ways, such
10847 ** as the list of "expr AS ID" fields following a "SELECT" or in the
10848 ** list of "ID = expr" items in an UPDATE. A list of expressions can
10849 ** also be used as the argument to a function, in which case the a.zName
10850 ** field is not used.
10851 **
10852 ** By default the Expr.zSpan field holds a human-readable description of
10853 ** the expression that is used in the generation of error messages and
10854 ** column labels. In this case, Expr.zSpan is typically the text of a
10855 ** column expression as it exists in a SELECT statement. However, if
10856 ** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name
10857 ** of the result column in the form: DATABASE.TABLE.COLUMN. This later
10858 ** form is used for name resolution with nested FROM clauses.
10859 */
10860 struct ExprList {
10861 int nExpr; /* Number of expressions on the list */
10862 int iECursor; /* VDBE Cursor associated with this ExprList */
10863 struct ExprList_item { /* For each expression in the list */
10864 Expr *pExpr; /* The list of expressions */
10865 char *zName; /* Token associated with this expression */
10866 char *zSpan; /* Original text of the expression */
10867 u8 sortOrder; /* 1 for DESC or 0 for ASC */
10868 unsigned done :1; /* A flag to indicate when processing is finished */
10869 unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
10870 u16 iOrderByCol; /* For ORDER BY, column number in result set */
10871 u16 iAlias; /* Index into Parse.aAlias[] for zName */
10872 } *a; /* Alloc a power of two greater or equal to nExpr */
10873 };
10874
10875 /*
10876 ** An instance of this structure is used by the parser to record both
@@ -11171,10 +11186,11 @@
11186 #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
11187 #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
11188 #define SF_UseSorter 0x0040 /* Sort using a sorter */
11189 #define SF_Values 0x0080 /* Synthesized from VALUES clause */
11190 #define SF_Materialize 0x0100 /* Force materialization of views */
11191 #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
11192
11193
11194 /*
11195 ** The results of a select can be distributed in several ways. The
11196 ** "SRT" prefix means "SELECT Result Type".
@@ -11883,11 +11899,11 @@
11899 SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
11900 Token*, int, int);
11901 SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
11902 SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
11903 SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
11904 Expr*,ExprList*,u16,Expr*,Expr*);
11905 SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
11906 SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
11907 SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
11908 SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
11909 #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
@@ -12140,10 +12156,11 @@
12156 SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
12157 SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
12158 SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
12159 SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int);
12160 SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
12161 SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
12162 SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
12163 SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
12164 SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
12165 SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
12166 SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
@@ -12278,12 +12295,14 @@
12295 #define sqlite3FkOldmask(a,b) 0
12296 #define sqlite3FkRequired(a,b,c,d) 0
12297 #endif
12298 #ifndef SQLITE_OMIT_FOREIGN_KEY
12299 SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*);
12300 SQLITE_PRIVATE int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**);
12301 #else
12302 #define sqlite3FkDelete(a,b)
12303 #define sqlite3FkLocateIndex(a,b,c,d,e)
12304 #endif
12305
12306
12307 /*
12308 ** Available fault injectors. Should be numbered beginning with 0.
@@ -23286,15 +23305,11 @@
23305 { "pwrite64", (sqlite3_syscall_ptr)0, 0 },
23306 #endif
23307 #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
23308 aSyscall[13].pCurrent)
23309
 
23310 { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
 
 
 
23311 #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
23312
23313 #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
23314 { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
23315 #else
@@ -23315,13 +23330,10 @@
23330 #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
23331
23332 { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
23333 #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
23334
 
 
 
23335 }; /* End of the overrideable system calls */
23336
23337 /*
23338 ** This is the xSetSystemCall() method of sqlite3_vfs for all of the
23339 ** "unix" VFSes. Return SQLITE_OK opon successfully updating the
@@ -23422,31 +23434,29 @@
23434 ** process that is able to write to the database will also be able to
23435 ** recover the hot journals.
23436 */
23437 static int robust_open(const char *z, int f, mode_t m){
23438 int fd;
23439 mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
 
 
 
 
 
 
 
23440 do{
23441 #if defined(O_CLOEXEC)
23442 fd = osOpen(z,f|O_CLOEXEC,m2);
23443 #else
23444 fd = osOpen(z,f,m2);
23445 #endif
23446 }while( fd<0 && errno==EINTR );
23447 if( fd>=0 ){
23448 if( m!=0 ){
23449 struct stat statbuf;
23450 if( osFstat(fd, &statbuf)==0 && (statbuf.st_mode&0777)!=m ){
23451 osFchmod(fd, m);
23452 }
23453 }
23454 #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0)
23455 osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
23456 #endif
23457 }
23458 return fd;
23459 }
23460
23461 /*
23462 ** Helper functions to obtain and relinquish the global mutex. The
@@ -29868,11 +29878,11 @@
29878 };
29879 unsigned int i; /* Loop counter */
29880
29881 /* Double-check that the aSyscall[] array has been constructed
29882 ** correctly. See ticket [bb3a86e890c8e96ab] */
29883 assert( ArraySize(aSyscall)==21 );
29884
29885 /* Register all VFSes defined in the aVfs[] array */
29886 for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
29887 sqlite3_vfs_register(&aVfs[i], i==0);
29888 }
@@ -56334,11 +56344,11 @@
56344 sqlite3BtreeLeave(p);
56345 return 0;
56346 }
56347 i = PENDING_BYTE_PAGE(pBt);
56348 if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
56349 sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
56350 sCheck.errMsg.useMalloc = 2;
56351
56352 /* Check the integrity of the freelist
56353 */
56354 checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
@@ -56869,11 +56879,16 @@
56879 /*
56880 ** Parameter zSrcData points to a buffer containing the data for
56881 ** page iSrcPg from the source database. Copy this data into the
56882 ** destination database.
56883 */
56884 static int backupOnePage(
56885 sqlite3_backup *p, /* Backup handle */
56886 Pgno iSrcPg, /* Source database page to backup */
56887 const u8 *zSrcData, /* Source database page data */
56888 int bUpdate /* True for an update, false otherwise */
56889 ){
56890 Pager * const pDestPager = sqlite3BtreePager(p->pDest);
56891 const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
56892 int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
56893 const int nCopy = MIN(nSrcPgsz, nDestPgsz);
56894 const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
@@ -56942,10 +56957,13 @@
56957 ** cached parse of the page). MemPage.isInit is marked
56958 ** "MUST BE FIRST" for this purpose.
56959 */
56960 memcpy(zOut, zIn, nCopy);
56961 ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
56962 if( iOff==0 && bUpdate==0 ){
56963 sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc));
56964 }
56965 }
56966 sqlite3PagerUnref(pDestPg);
56967 }
56968
56969 return rc;
@@ -57048,11 +57066,11 @@
57066 const Pgno iSrcPg = p->iNext; /* Source page number */
57067 if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
57068 DbPage *pSrcPg; /* Source page object */
57069 rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
57070 if( rc==SQLITE_OK ){
57071 rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
57072 sqlite3PagerUnref(pSrcPg);
57073 }
57074 }
57075 p->iNext++;
57076 }
@@ -57296,11 +57314,11 @@
57314 ** the new data into the backup.
57315 */
57316 int rc;
57317 assert( p->pDestDb );
57318 sqlite3_mutex_enter(p->pDestDb->mutex);
57319 rc = backupOnePage(p, iPage, aData, 1);
57320 sqlite3_mutex_leave(p->pDestDb->mutex);
57321 assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
57322 if( rc!=SQLITE_OK ){
57323 p->rc = rc;
57324 }
@@ -71890,10 +71908,18 @@
71908 p->pReal = pReal;
71909 if( p->iSize>0 ){
71910 assert(p->iSize<=p->nBuf);
71911 rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
71912 }
71913 if( rc!=SQLITE_OK ){
71914 /* If an error occurred while writing to the file, close it before
71915 ** returning. This way, SQLite uses the in-memory journal data to
71916 ** roll back changes made to the internal page-cache before this
71917 ** function was called. */
71918 sqlite3OsClose(pReal);
71919 p->pReal = 0;
71920 }
71921 }
71922 }
71923 return rc;
71924 }
71925
@@ -72636,10 +72662,39 @@
72662 }
72663 }
72664 return 0;
72665 }
72666
72667 /*
72668 ** Subqueries stores the original database, table and column names for their
72669 ** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
72670 ** Check to see if the zSpan given to this routine matches the zDb, zTab,
72671 ** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
72672 ** match anything.
72673 */
72674 SQLITE_PRIVATE int sqlite3MatchSpanName(
72675 const char *zSpan,
72676 const char *zCol,
72677 const char *zTab,
72678 const char *zDb
72679 ){
72680 int n;
72681 for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
72682 if( zDb && sqlite3StrNICmp(zSpan, zDb, n)!=0 ){
72683 return 0;
72684 }
72685 zSpan += n+1;
72686 for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
72687 if( zTab && sqlite3StrNICmp(zSpan, zTab, n)!=0 ){
72688 return 0;
72689 }
72690 zSpan += n+1;
72691 if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
72692 return 0;
72693 }
72694 return 1;
72695 }
72696
72697 /*
72698 ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
72699 ** that name in the set of source tables in pSrcList and make the pExpr
72700 ** expression node refer back to that source column. The following changes
@@ -72691,44 +72746,63 @@
72746
72747 /* Initialize the node to no-match */
72748 pExpr->iTable = -1;
72749 pExpr->pTab = 0;
72750 ExprSetIrreducible(pExpr);
72751
72752 /* Translate the schema name in zDb into a pointer to the corresponding
72753 ** schema. If not found, pSchema will remain NULL and nothing will match
72754 ** resulting in an appropriate error message toward the end of this routine
72755 */
72756 if( zDb ){
72757 for(i=0; i<db->nDb; i++){
72758 assert( db->aDb[i].zName );
72759 if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
72760 pSchema = db->aDb[i].pSchema;
72761 break;
72762 }
72763 }
72764 }
72765
72766 /* Start at the inner-most context and move outward until a match is found */
72767 while( pNC && cnt==0 ){
72768 ExprList *pEList;
72769 SrcList *pSrcList = pNC->pSrcList;
72770
72771 if( pSrcList ){
72772 for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
72773 Table *pTab;
 
72774 Column *pCol;
72775
72776 pTab = pItem->pTab;
72777 assert( pTab!=0 && pTab->zName!=0 );
 
72778 assert( pTab->nCol>0 );
72779 if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
72780 ExprList *pEList = pItem->pSelect->pEList;
72781 int hit = 0;
72782 for(j=0; j<pEList->nExpr; j++){
72783 if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){
72784 cnt++;
72785 cntTab = 2;
72786 pMatch = pItem;
72787 pExpr->iColumn = j;
72788 hit = 1;
72789 }
72790 }
72791 if( hit || zTab==0 ) continue;
72792 }
72793 if( zDb && pTab->pSchema!=pSchema ){
72794 continue;
72795 }
72796 if( zTab ){
72797 const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
72798 assert( zTabName!=0 );
72799 if( sqlite3StrICmp(zTabName, zTab)!=0 ){
72800 continue;
 
 
 
 
 
 
 
72801 }
72802 }
72803 if( 0==(cntTab++) ){
 
 
 
72804 pMatch = pItem;
72805 }
72806 for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
72807 if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
72808 /* If there has been exactly one prior match and this match
@@ -72738,21 +72812,23 @@
72812 if( cnt==1 ){
72813 if( pItem->jointype & JT_NATURAL ) continue;
72814 if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
72815 }
72816 cnt++;
 
 
72817 pMatch = pItem;
 
72818 /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
72819 pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
72820 break;
72821 }
72822 }
72823 }
72824 if( pMatch ){
72825 pExpr->iTable = pMatch->iCursor;
72826 pExpr->pTab = pMatch->pTab;
72827 pSchema = pExpr->pTab->pSchema;
72828 }
72829 } /* if( pSrcList ) */
72830
72831 #ifndef SQLITE_OMIT_TRIGGER
72832 /* If we have not already resolved the name, then maybe
72833 ** it is a new.* or old.* trigger argument reference
72834 */
@@ -73083,11 +73159,11 @@
73159 #endif
73160 if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
73161 sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
73162 pNC->nErr++;
73163 is_agg = 0;
73164 }else if( no_such_func && pParse->db->init.busy==0 ){
73165 sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
73166 pNC->nErr++;
73167 }else if( wrong_num_args ){
73168 sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
73169 nId, zId);
@@ -73519,27 +73595,10 @@
73595 if( sqlite3ResolveExprNames(&sNC, p->pLimit) ||
73596 sqlite3ResolveExprNames(&sNC, p->pOffset) ){
73597 return WRC_Abort;
73598 }
73599
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73600 /* Recursively resolve names in all subqueries
73601 */
73602 for(i=0; i<p->pSrc->nSrc; i++){
73603 struct SrcList_item *pItem = &p->pSrc->a[i];
73604 if( pItem->pSelect ){
@@ -73562,10 +73621,27 @@
73621 for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
73622 assert( pItem->isCorrelated==0 && nRef<=0 );
73623 pItem->isCorrelated = (nRef!=0);
73624 }
73625 }
73626
73627 /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
73628 ** resolve the result-set expression list.
73629 */
73630 sNC.ncFlags = NC_AllowAgg;
73631 sNC.pSrcList = p->pSrc;
73632 sNC.pNext = pOuterNC;
73633
73634 /* Resolve names in the result set. */
73635 pEList = p->pEList;
73636 assert( pEList!=0 );
73637 for(i=0; i<pEList->nExpr; i++){
73638 Expr *pX = pEList->a[i].pExpr;
73639 if( sqlite3ResolveExprNames(&sNC, pX) ){
73640 return WRC_Abort;
73641 }
73642 }
73643
73644 /* If there are no aggregate functions in the result-set, and no GROUP BY
73645 ** expression, do not allow aggregates in any of the other expressions.
73646 */
73647 assert( (p->selFlags & SF_Aggregate)==0 );
@@ -77046,10 +77122,16 @@
77122 for(i=0; i<pList->nExpr; i++){
77123 sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
77124 sqlite3ExplainPush(pOut);
77125 sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
77126 sqlite3ExplainPop(pOut);
77127 if( pList->a[i].zName ){
77128 sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName);
77129 }
77130 if( pList->a[i].bSpanIsTab ){
77131 sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan);
77132 }
77133 if( i<pList->nExpr-1 ){
77134 sqlite3ExplainNL(pOut);
77135 }
77136 }
77137 sqlite3ExplainPop(pOut);
@@ -87523,11 +87605,11 @@
87605
87606 /*
87607 ** A foreign key constraint requires that the key columns in the parent
87608 ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
87609 ** Given that pParent is the parent table for foreign key constraint pFKey,
87610 ** search the schema for a unique index on the parent key columns.
87611 **
87612 ** If successful, zero is returned. If the parent key is an INTEGER PRIMARY
87613 ** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx
87614 ** is set to point to the unique index.
87615 **
@@ -87559,11 +87641,11 @@
87641 **
87642 ** then non-zero is returned, and a "foreign key mismatch" error loaded
87643 ** into pParse. If an OOM error occurs, non-zero is returned and the
87644 ** pParse->db->mallocFailed flag is set.
87645 */
87646 SQLITE_PRIVATE int sqlite3FkLocateIndex(
87647 Parse *pParse, /* Parse context to store any error in */
87648 Table *pParent, /* Parent table of FK constraint pFKey */
87649 FKey *pFKey, /* Foreign key to find index for */
87650 Index **ppIdx, /* OUT: Unique index on parent table */
87651 int **paiCol /* OUT: Map of index columns in pFKey */
@@ -87656,11 +87738,13 @@
87738 }
87739 }
87740
87741 if( !pIdx ){
87742 if( !pParse->disableTriggers ){
87743 sqlite3ErrorMsg(pParse,
87744 "foreign key mismatch - \"%w\" referencing \"%w\"",
87745 pFKey->pFrom->zName, pFKey->zTo);
87746 }
87747 sqlite3DbFree(pParse->db, aiCol);
87748 return 1;
87749 }
87750
@@ -88117,11 +88201,11 @@
88201 if( pParse->disableTriggers ){
88202 pTo = sqlite3FindTable(db, pFKey->zTo, zDb);
88203 }else{
88204 pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
88205 }
88206 if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
88207 assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) );
88208 if( !isIgnoreErrors || db->mallocFailed ) return;
88209 if( pTo==0 ){
88210 /* If isIgnoreErrors is true, then a table is being dropped. In this
88211 ** case SQLite runs a "DELETE FROM xxx" on the table being dropped
@@ -88197,11 +88281,11 @@
88281 /* Inserting a single row into a parent table cannot cause an immediate
88282 ** foreign key violation. So do nothing in this case. */
88283 continue;
88284 }
88285
88286 if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
88287 if( !isIgnoreErrors || db->mallocFailed ) return;
88288 continue;
88289 }
88290 assert( aiCol || pFKey->nCol==1 );
88291
@@ -88252,11 +88336,11 @@
88336 for(p=pTab->pFKey; p; p=p->pNextFrom){
88337 for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom);
88338 }
88339 for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
88340 Index *pIdx = 0;
88341 sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
88342 if( pIdx ){
88343 for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
88344 }
88345 }
88346 }
@@ -88378,11 +88462,11 @@
88462 ExprList *pList = 0; /* Changes list if ON UPDATE CASCADE */
88463 Select *pSelect = 0; /* If RESTRICT, "SELECT RAISE(...)" */
88464 int i; /* Iterator variable */
88465 Expr *pWhen = 0; /* WHEN clause for the trigger */
88466
88467 if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
88468 assert( aiCol || pFKey->nCol==1 );
88469
88470 for(i=0; i<pFKey->nCol; i++){
88471 Token tOld = { "old", 3 }; /* Literal "old" token */
88472 Token tNew = { "new", 3 }; /* Literal "new" token */
@@ -92718,13 +92802,15 @@
92802 if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){
92803 Table *pTab;
92804 if( sqlite3ReadSchema(pParse) ) goto pragma_out;
92805 pTab = sqlite3FindTable(db, zRight, zDb);
92806 if( pTab ){
92807 int i, k;
92808 int nHidden = 0;
92809 Column *pCol;
92810 Index *pPk;
92811 for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){}
92812 sqlite3VdbeSetNumCols(v, 6);
92813 pParse->nMem = 6;
92814 sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC);
92815 sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
92816 sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC);
@@ -92745,12 +92831,18 @@
92831 if( pCol->zDflt ){
92832 sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0);
92833 }else{
92834 sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
92835 }
92836 if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
92837 k = 0;
92838 }else if( pPk==0 ){
92839 k = 1;
92840 }else{
92841 for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){}
92842 }
92843 sqlite3VdbeAddOp2(v, OP_Integer, k, 6);
92844 sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
92845 }
92846 }
92847 }else
92848
@@ -92881,10 +92973,124 @@
92973 ++i;
92974 pFK = pFK->pNextFrom;
92975 }
92976 }
92977 }
92978 }else
92979 #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
92980
92981 #ifndef SQLITE_OMIT_FOREIGN_KEY
92982 if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){
92983 FKey *pFK; /* A foreign key constraint */
92984 Table *pTab; /* Child table contain "REFERENCES" keyword */
92985 Table *pParent; /* Parent table that child points to */
92986 Index *pIdx; /* Index in the parent table */
92987 int i; /* Loop counter: Foreign key number for pTab */
92988 int j; /* Loop counter: Field of the foreign key */
92989 HashElem *k; /* Loop counter: Next table in schema */
92990 int x; /* result variable */
92991 int regResult; /* 3 registers to hold a result row */
92992 int regKey; /* Register to hold key for checking the FK */
92993 int regRow; /* Registers to hold a row from pTab */
92994 int addrTop; /* Top of a loop checking foreign keys */
92995 int addrOk; /* Jump here if the key is OK */
92996 int *aiCols; /* child to parent column mapping */
92997
92998 if( sqlite3ReadSchema(pParse) ) goto pragma_out;
92999 regResult = pParse->nMem+1;
93000 pParse->nMem += 4;
93001 regKey = ++pParse->nMem;
93002 regRow = ++pParse->nMem;
93003 v = sqlite3GetVdbe(pParse);
93004 sqlite3VdbeSetNumCols(v, 4);
93005 sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC);
93006 sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC);
93007 sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "parent", SQLITE_STATIC);
93008 sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "fkid", SQLITE_STATIC);
93009 sqlite3CodeVerifySchema(pParse, iDb);
93010 k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
93011 while( k ){
93012 if( zRight ){
93013 pTab = sqlite3LocateTable(pParse, 0, zRight, zDb);
93014 k = 0;
93015 }else{
93016 pTab = (Table*)sqliteHashData(k);
93017 k = sqliteHashNext(k);
93018 }
93019 if( pTab==0 || pTab->pFKey==0 ) continue;
93020 sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
93021 if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
93022 sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
93023 sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName,
93024 P4_TRANSIENT);
93025 for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
93026 pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
93027 if( pParent==0 ) break;
93028 pIdx = 0;
93029 sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
93030 x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
93031 if( x==0 ){
93032 if( pIdx==0 ){
93033 sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
93034 }else{
93035 KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
93036 sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
93037 sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
93038 }
93039 }else{
93040 k = 0;
93041 break;
93042 }
93043 }
93044 if( pFK ) break;
93045 if( pParse->nTab<i ) pParse->nTab = i;
93046 addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0);
93047 for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
93048 pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
93049 assert( pParent!=0 );
93050 pIdx = 0;
93051 aiCols = 0;
93052 x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
93053 assert( x==0 );
93054 addrOk = sqlite3VdbeMakeLabel(v);
93055 if( pIdx==0 ){
93056 int iKey = pFK->aCol[0].iFrom;
93057 assert( iKey>=0 && iKey<pTab->nCol );
93058 if( iKey!=pTab->iPKey ){
93059 sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
93060 sqlite3ColumnDefault(v, pTab, iKey, regRow);
93061 sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk);
93062 sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow,
93063 sqlite3VdbeCurrentAddr(v)+3);
93064 }else{
93065 sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
93066 }
93067 sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow);
93068 sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk);
93069 sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
93070 }else{
93071 for(j=0; j<pFK->nCol; j++){
93072 sqlite3ExprCodeGetColumnOfTable(v, pTab, 0,
93073 aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j);
93074 sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk);
93075 }
93076 sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey);
93077 sqlite3VdbeChangeP4(v, -1,
93078 sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
93079 sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
93080 }
93081 sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
93082 sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0,
93083 pFK->zTo, P4_TRANSIENT);
93084 sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3);
93085 sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4);
93086 sqlite3VdbeResolveLabel(v, addrOk);
93087 sqlite3DbFree(db, aiCols);
93088 }
93089 sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1);
93090 sqlite3VdbeJumpHere(v, addrTop);
93091 }
93092 }else
93093 #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
93094
93095 #ifndef NDEBUG
93096 if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
@@ -93381,11 +93587,11 @@
93587 sqlite3_rekey(db, zKey, i/2);
93588 }
93589 }else
93590 #endif
93591 #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
93592 if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){
93593 #ifdef SQLITE_HAS_CODEC
93594 if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
93595 sqlite3_activate_see(&zRight[4]);
93596 }
93597 #endif
@@ -94340,11 +94546,11 @@
94546 SrcList *pSrc, /* the FROM clause -- which tables to scan */
94547 Expr *pWhere, /* the WHERE clause */
94548 ExprList *pGroupBy, /* the GROUP BY clause */
94549 Expr *pHaving, /* the HAVING clause */
94550 ExprList *pOrderBy, /* the ORDER BY clause */
94551 u16 selFlags, /* Flag parameters, such as SF_Distinct */
94552 Expr *pLimit, /* LIMIT value. NULL means not used */
94553 Expr *pOffset /* OFFSET value. NULL means no offset */
94554 ){
94555 Select *pNew;
94556 Select standin;
@@ -94364,11 +94570,11 @@
94570 pNew->pSrc = pSrc;
94571 pNew->pWhere = pWhere;
94572 pNew->pGroupBy = pGroupBy;
94573 pNew->pHaving = pHaving;
94574 pNew->pOrderBy = pOrderBy;
94575 pNew->selFlags = selFlags;
94576 pNew->op = TK_SELECT;
94577 pNew->pLimit = pLimit;
94578 pNew->pOffset = pOffset;
94579 assert( pOffset==0 || pLimit!=0 );
94580 pNew->addrOpenEphm[0] = -1;
@@ -95621,12 +95827,10 @@
95827
95828 for(i=0, pCol=aCol; i<nCol; i++, pCol++){
95829 /* Get an appropriate name for the column
95830 */
95831 p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
 
 
95832 if( (zName = pEList->a[i].zName)!=0 ){
95833 /* If the column contains an "AS <name>" phrase, use <name> as the name */
95834 zName = sqlite3DbStrDup(db, zName);
95835 }else{
95836 Expr *pColExpr = p; /* The expression that is the result column name */
@@ -95660,10 +95864,13 @@
95864 */
95865 nName = sqlite3Strlen30(zName);
95866 for(j=cnt=0; j<i; j++){
95867 if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
95868 char *zNewName;
95869 int k;
95870 for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){}
95871 if( zName[k]==':' ) nName = k;
95872 zName[nName] = 0;
95873 zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
95874 sqlite3DbFree(db, zName);
95875 zName = zNewName;
95876 j = -1;
@@ -97445,38 +97652,47 @@
97652 return 1;
97653 }
97654 #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
97655
97656 /*
97657 ** Based on the contents of the AggInfo structure indicated by the first
97658 ** argument, this function checks if the following are true:
97659 **
97660 ** * the query contains just a single aggregate function,
97661 ** * the aggregate function is either min() or max(), and
97662 ** * the argument to the aggregate function is a column value.
97663 **
97664 ** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX
97665 ** is returned as appropriate. Also, *ppMinMax is set to point to the
97666 ** list of arguments passed to the aggregate before returning.
97667 **
97668 ** Or, if the conditions above are not met, *ppMinMax is set to 0 and
97669 ** WHERE_ORDERBY_NORMAL is returned.
97670 */
97671 static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){
97672 int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
97673
97674 *ppMinMax = 0;
97675 if( pAggInfo->nFunc==1 ){
97676 Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */
97677 ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */
97678
97679 assert( pExpr->op==TK_AGG_FUNCTION );
97680 if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){
97681 const char *zFunc = pExpr->u.zToken;
97682 if( sqlite3StrICmp(zFunc, "min")==0 ){
97683 eRet = WHERE_ORDERBY_MIN;
97684 *ppMinMax = pEList;
97685 }else if( sqlite3StrICmp(zFunc, "max")==0 ){
97686 eRet = WHERE_ORDERBY_MAX;
97687 *ppMinMax = pEList;
97688 }
97689 }
97690 }
97691
97692 assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 );
97693 return eRet;
97694 }
97695
97696 /*
97697 ** The select statement passed as the first argument is an aggregate query.
97698 ** The second argment is the associated aggregate-info object. This
@@ -97567,10 +97783,11 @@
97783 int i, j, k;
97784 SrcList *pTabList;
97785 ExprList *pEList;
97786 struct SrcList_item *pFrom;
97787 sqlite3 *db = pParse->db;
97788 Expr *pE, *pRight, *pExpr;
97789
97790 if( db->mallocFailed ){
97791 return WRC_Abort;
97792 }
97793 if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){
@@ -97652,11 +97869,11 @@
97869 **
97870 ** The first loop just checks to see if there are any "*" operators
97871 ** that need expanding.
97872 */
97873 for(k=0; k<pEList->nExpr; k++){
97874 pE = pEList->a[k].pExpr;
97875 if( pE->op==TK_ALL ) break;
97876 assert( pE->op!=TK_DOT || pE->pRight!=0 );
97877 assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
97878 if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break;
97879 }
@@ -97670,14 +97887,22 @@
97887 ExprList *pNew = 0;
97888 int flags = pParse->db->flags;
97889 int longNames = (flags & SQLITE_FullColNames)!=0
97890 && (flags & SQLITE_ShortColNames)==0;
97891
97892 /* When processing FROM-clause subqueries, it is always the case
97893 ** that full_column_names=OFF and short_column_names=ON. The
97894 ** sqlite3ResultSetOfSelect() routine makes it so. */
97895 assert( (p->selFlags & SF_NestedFrom)==0
97896 || ((flags & SQLITE_FullColNames)==0 &&
97897 (flags & SQLITE_ShortColNames)!=0) );
97898
97899 for(k=0; k<pEList->nExpr; k++){
97900 pE = a[k].pExpr;
97901 pRight = pE->pRight;
97902 assert( pE->op!=TK_DOT || pRight!=0 );
97903 if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){
97904 /* This particular expression does not need to be expanded.
97905 */
97906 pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
97907 if( pNew ){
97908 pNew->a[pNew->nExpr-1].zName = a[k].zName;
@@ -97688,44 +97913,56 @@
97913 a[k].pExpr = 0;
97914 }else{
97915 /* This expression is a "*" or a "TABLE.*" and needs to be
97916 ** expanded. */
97917 int tableSeen = 0; /* Set to 1 when TABLE matches */
97918 char *zTName = 0; /* text of name of TABLE */
97919 if( pE->op==TK_DOT ){
97920 assert( pE->pLeft!=0 );
97921 assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
97922 zTName = pE->pLeft->u.zToken;
 
 
97923 }
97924 for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
97925 Table *pTab = pFrom->pTab;
97926 Select *pSub = pFrom->pSelect;
97927 char *zTabName = pFrom->zAlias;
97928 const char *zSchemaName = 0;
97929 int iDb;
97930 if( zTabName==0 ){
97931 zTabName = pTab->zName;
97932 }
97933 if( db->mallocFailed ) break;
97934 if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){
97935 pSub = 0;
97936 if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
97937 continue;
97938 }
97939 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
97940 zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*";
97941 }
 
97942 for(j=0; j<pTab->nCol; j++){
 
97943 char *zName = pTab->aCol[j].zName;
97944 char *zColname; /* The computed column name */
97945 char *zToFree; /* Malloced string that needs to be freed */
97946 Token sColname; /* Computed column name as a token */
97947
97948 assert( zName );
97949 if( zTName && pSub
97950 && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0
97951 ){
97952 continue;
97953 }
97954
97955 /* If a column is marked as 'hidden' (currently only possible
97956 ** for virtual tables), do not include it in the expanded
97957 ** result-set list.
97958 */
97959 if( IsHiddenColumn(&pTab->aCol[j]) ){
97960 assert(IsVirtual(pTab));
97961 continue;
97962 }
97963 tableSeen = 1;
97964
97965 if( i>0 && zTName==0 ){
97966 if( (pFrom->jointype & JT_NATURAL)!=0
97967 && tableAndColumnIndex(pTabList, i, zName, 0, 0)
97968 ){
@@ -97744,10 +97981,14 @@
97981 zToFree = 0;
97982 if( longNames || pTabList->nSrc>1 ){
97983 Expr *pLeft;
97984 pLeft = sqlite3Expr(db, TK_ID, zTabName);
97985 pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
97986 if( zSchemaName ){
97987 pLeft = sqlite3Expr(db, TK_ID, zSchemaName);
97988 pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0);
97989 }
97990 if( longNames ){
97991 zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
97992 zToFree = zColname;
97993 }
97994 }else{
@@ -97755,10 +97996,22 @@
97996 }
97997 pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
97998 sColname.z = zColname;
97999 sColname.n = sqlite3Strlen30(zColname);
98000 sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
98001 if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
98002 struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
98003 if( pSub ){
98004 pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan);
98005 testcase( pX->zSpan==0 );
98006 }else{
98007 pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s",
98008 zSchemaName, zTabName, zColname);
98009 testcase( pX->zSpan==0 );
98010 }
98011 pX->bSpanIsTab = 1;
98012 }
98013 sqlite3DbFree(db, zToFree);
98014 }
98015 }
98016 if( !tableSeen ){
98017 if( zTName ){
@@ -98812,15 +99065,21 @@
99065 ** index or indices to use) should place a different priority on
99066 ** satisfying the 'ORDER BY' clause than it does in other cases.
99067 ** Refer to code and comments in where.c for details.
99068 */
99069 ExprList *pMinMax = 0;
99070 u8 flag = WHERE_ORDERBY_NORMAL;
99071
99072 assert( p->pGroupBy==0 );
99073 assert( flag==0 );
99074 if( p->pHaving==0 ){
99075 flag = minMaxQuery(&sAggInfo, &pMinMax);
99076 }
99077 assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) );
99078
99079 if( flag ){
99080 pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
 
 
99081 pDel = pMinMax;
99082 if( pMinMax && !db->mallocFailed ){
99083 pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
99084 pMinMax->a[0].pExpr->op = TK_COLUMN;
99085 }
@@ -102549,12 +102808,12 @@
102808 Expr *pExpr; /* Pointer to the subexpression that is this term */
102809 int iParent; /* Disable pWC->a[iParent] when this term disabled */
102810 int leftCursor; /* Cursor number of X in "X <op> <expr>" */
102811 union {
102812 int leftColumn; /* Column number of X in "X <op> <expr>" */
102813 WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
102814 WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
102815 } u;
102816 u16 eOperator; /* A WO_xx value describing <op> */
102817 u8 wtFlags; /* TERM_xxx bit flags. See below */
102818 u8 nChild; /* Number of children that must disable us */
102819 WhereClause *pWC; /* The clause this term is part of */
@@ -102678,10 +102937,11 @@
102937 #define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
102938 #define WO_MATCH 0x040
102939 #define WO_ISNULL 0x080
102940 #define WO_OR 0x100 /* Two or more OR-connected terms */
102941 #define WO_AND 0x200 /* Two or more AND-connected terms */
102942 #define WO_EQUIV 0x400 /* Of the form A==B, both columns */
102943 #define WO_NOOP 0x800 /* This term does not restrict search space */
102944
102945 #define WO_ALL 0xfff /* Mask of all possible WO_* values */
102946 #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
102947
@@ -102704,11 +102964,11 @@
102964 #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */
102965 #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
102966 #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
102967 #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
102968 #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
102969 #define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */
102970 #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
102971 #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
102972 #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
102973 #define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
102974 #define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
@@ -102854,11 +103114,11 @@
103114 sqlite3DbFree(db, pOld);
103115 }
103116 pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
103117 }
103118 pTerm = &pWC->a[idx = pWC->nTerm++];
103119 pTerm->pExpr = sqlite3ExprSkipCollate(p);
103120 pTerm->wtFlags = wtFlags;
103121 pTerm->pWC = pWC;
103122 pTerm->iParent = -1;
103123 return idx;
103124 }
@@ -103080,58 +103340,112 @@
103340 /*
103341 ** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
103342 ** where X is a reference to the iColumn of table iCur and <op> is one of
103343 ** the WO_xx operator codes specified by the op parameter.
103344 ** Return a pointer to the term. Return 0 if not found.
103345 **
103346 ** The term returned might by Y=<expr> if there is another constraint in
103347 ** the WHERE clause that specifies that X=Y. Any such constraints will be
103348 ** identified by the WO_EQUIV bit in the pTerm->eOperator field. The
103349 ** aEquiv[] array holds X and all its equivalents, with each SQL variable
103350 ** taking up two slots in aEquiv[]. The first slot is for the cursor number
103351 ** and the second is for the column number. There are 22 slots in aEquiv[]
103352 ** so that means we can look for X plus up to 10 other equivalent values.
103353 ** Hence a search for X will return <expr> if X=A1 and A1=A2 and A2=A3
103354 ** and ... and A9=A10 and A10=<expr>.
103355 **
103356 ** If there are multiple terms in the WHERE clause of the form "X <op> <expr>"
103357 ** then try for the one with no dependencies on <expr> - in other words where
103358 ** <expr> is a constant expression of some kind. Only return entries of
103359 ** the form "X <op> Y" where Y is a column in another table if no terms of
103360 ** the form "X <op> <const-expr>" exist. Other than this priority, if there
103361 ** are two or more terms that match, then the choice of which term to return
103362 ** is arbitrary.
103363 */
103364 static WhereTerm *findTerm(
103365 WhereClause *pWC, /* The WHERE clause to be searched */
103366 int iCur, /* Cursor number of LHS */
103367 int iColumn, /* Column number of LHS */
103368 Bitmask notReady, /* RHS must not overlap with this mask */
103369 u32 op, /* Mask of WO_xx values describing operator */
103370 Index *pIdx /* Must be compatible with this index, if not NULL */
103371 ){
103372 WhereTerm *pTerm; /* Term being examined as possible result */
103373 WhereTerm *pResult = 0; /* The answer to return */
103374 WhereClause *pWCOrig = pWC; /* Original pWC value */
103375 int j, k; /* Loop counters */
103376 Expr *pX; /* Pointer to an expression */
103377 Parse *pParse; /* Parsing context */
103378 int iOrigCol = iColumn; /* Original value of iColumn */
103379 int nEquiv = 2; /* Number of entires in aEquiv[] */
103380 int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */
103381 int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */
103382
103383 assert( iCur>=0 );
103384 aEquiv[0] = iCur;
103385 aEquiv[1] = iColumn;
103386 for(;;){
103387 for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){
103388 for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
103389 if( pTerm->leftCursor==iCur
103390 && pTerm->u.leftColumn==iColumn
103391 ){
103392 if( (pTerm->prereqRight & notReady)==0
103393 && (pTerm->eOperator & op & WO_ALL)!=0
103394 ){
103395 if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){
103396 CollSeq *pColl;
103397 char idxaff;
103398
103399 pX = pTerm->pExpr;
103400 pParse = pWC->pParse;
103401 idxaff = pIdx->pTable->aCol[iOrigCol].affinity;
103402 if( !sqlite3IndexAffinityOk(pX, idxaff) ){
103403 continue;
103404 }
103405
103406 /* Figure out the collation sequence required from an index for
103407 ** it to be useful for optimising expression pX. Store this
103408 ** value in variable pColl.
103409 */
103410 assert(pX->pLeft);
103411 pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight);
103412 if( pColl==0 ) pColl = pParse->db->pDfltColl;
103413
103414 for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){
103415 if( NEVER(j>=pIdx->nColumn) ) return 0;
103416 }
103417 if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){
103418 continue;
103419 }
103420 }
103421 pResult = pTerm;
103422 if( pTerm->prereqRight==0 ) goto findTerm_success;
103423 }
103424 if( (pTerm->eOperator & WO_EQUIV)!=0
103425 && nEquiv<ArraySize(aEquiv)
103426 ){
103427 pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
103428 assert( pX->op==TK_COLUMN );
103429 for(j=0; j<nEquiv; j+=2){
103430 if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break;
103431 }
103432 if( j==nEquiv ){
103433 aEquiv[j] = pX->iTable;
103434 aEquiv[j+1] = pX->iColumn;
103435 nEquiv += 2;
103436 }
103437 }
103438 }
103439 }
103440 }
103441 if( iEquiv>=nEquiv ) break;
103442 iCur = aEquiv[iEquiv++];
103443 iColumn = aEquiv[iEquiv++];
103444 }
103445 findTerm_success:
103446 return pResult;
103447 }
103448
103449 /* Forward reference */
103450 static void exprAnalyze(SrcList*, WhereClause*, int);
103451
@@ -103405,11 +103719,10 @@
103719 indexable = ~(Bitmask)0;
103720 chngToIN = ~(pWC->vmask);
103721 for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){
103722 if( (pOrTerm->eOperator & WO_SINGLE)==0 ){
103723 WhereAndInfo *pAndInfo;
 
103724 assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
103725 chngToIN = 0;
103726 pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
103727 if( pAndInfo ){
103728 WhereClause *pAndWC;
@@ -103444,11 +103757,11 @@
103757 if( pOrTerm->wtFlags & TERM_VIRTUAL ){
103758 WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
103759 b |= getMask(pMaskSet, pOther->leftCursor);
103760 }
103761 indexable &= b;
103762 if( (pOrTerm->eOperator & WO_EQ)==0 ){
103763 chngToIN = 0;
103764 }else{
103765 chngToIN &= b;
103766 }
103767 }
@@ -103495,11 +103808,11 @@
103808 ** and column is found but leave okToChngToIN false if not found.
103809 */
103810 for(j=0; j<2 && !okToChngToIN; j++){
103811 pOrTerm = pOrWc->a;
103812 for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
103813 assert( pOrTerm->eOperator & WO_EQ );
103814 pOrTerm->wtFlags &= ~TERM_OR_OK;
103815 if( pOrTerm->leftCursor==iCursor ){
103816 /* This is the 2-bit case and we are on the second iteration and
103817 ** current term is from the first iteration. So skip this term. */
103818 assert( j==1 );
@@ -103521,21 +103834,21 @@
103834 }
103835 if( i<0 ){
103836 /* No candidate table+column was found. This can only occur
103837 ** on the second iteration */
103838 assert( j==1 );
103839 assert( IsPowerOfTwo(chngToIN) );
103840 assert( chngToIN==getMask(pMaskSet, iCursor) );
103841 break;
103842 }
103843 testcase( j==1 );
103844
103845 /* We have found a candidate table and column. Check to see if that
103846 ** table and column is common to every term in the OR clause */
103847 okToChngToIN = 1;
103848 for(; i>=0 && okToChngToIN; i--, pOrTerm++){
103849 assert( pOrTerm->eOperator & WO_EQ );
103850 if( pOrTerm->leftCursor!=iCursor ){
103851 pOrTerm->wtFlags &= ~TERM_OR_OK;
103852 }else if( pOrTerm->u.leftColumn!=iColumn ){
103853 okToChngToIN = 0;
103854 }else{
@@ -103567,11 +103880,11 @@
103880 Expr *pLeft = 0; /* The LHS of the IN operator */
103881 Expr *pNew; /* The complete IN operator */
103882
103883 for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
103884 if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
103885 assert( pOrTerm->eOperator & WO_EQ );
103886 assert( pOrTerm->leftCursor==iCursor );
103887 assert( pOrTerm->u.leftColumn==iColumn );
103888 pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
103889 pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup);
103890 pLeft = pOrTerm->pExpr->pLeft;
@@ -103596,11 +103909,10 @@
103909 pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
103910 }
103911 }
103912 }
103913 #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
 
103914
103915 /*
103916 ** The input to this routine is an WhereTerm structure with only the
103917 ** "pExpr" field filled in. The job of this routine is to analyze the
103918 ** subexpression and populate all the other fields of the WhereTerm
@@ -103639,11 +103951,12 @@
103951 if( db->mallocFailed ){
103952 return;
103953 }
103954 pTerm = &pWC->a[idxTerm];
103955 pMaskSet = pWC->pMaskSet;
103956 pExpr = pTerm->pExpr;
103957 assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
103958 prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
103959 op = pExpr->op;
103960 if( op==TK_IN ){
103961 assert( pExpr->pRight==0 );
103962 if( ExprHasProperty(pExpr, EP_xIsSelect) ){
@@ -103665,21 +103978,23 @@
103978 }
103979 pTerm->prereqAll = prereqAll;
103980 pTerm->leftCursor = -1;
103981 pTerm->iParent = -1;
103982 pTerm->eOperator = 0;
103983 if( allowedOp(op) ){
103984 Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
103985 Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
103986 u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
103987 if( pLeft->op==TK_COLUMN ){
103988 pTerm->leftCursor = pLeft->iTable;
103989 pTerm->u.leftColumn = pLeft->iColumn;
103990 pTerm->eOperator = operatorMask(op) & opMask;
103991 }
103992 if( pRight && pRight->op==TK_COLUMN ){
103993 WhereTerm *pNew;
103994 Expr *pDup;
103995 u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
103996 if( pTerm->leftCursor>=0 ){
103997 int idxNew;
103998 pDup = sqlite3ExprDup(db, pExpr, 0);
103999 if( db->mallocFailed ){
104000 sqlite3ExprDelete(db, pDup);
@@ -103690,10 +104005,17 @@
104005 pNew = &pWC->a[idxNew];
104006 pNew->iParent = idxTerm;
104007 pTerm = &pWC->a[idxTerm];
104008 pTerm->nChild = 1;
104009 pTerm->wtFlags |= TERM_COPIED;
104010 if( pExpr->op==TK_EQ
104011 && !ExprHasProperty(pExpr, EP_FromJoin)
104012 && OptimizationEnabled(db, SQLITE_Transitive)
104013 ){
104014 pTerm->eOperator |= WO_EQUIV;
104015 eExtraOp = WO_EQUIV;
104016 }
104017 }else{
104018 pDup = pExpr;
104019 pNew = pTerm;
104020 }
104021 exprCommute(pParse, pDup);
@@ -103701,11 +104023,11 @@
104023 pNew->leftCursor = pLeft->iTable;
104024 pNew->u.leftColumn = pLeft->iColumn;
104025 testcase( (prereqLeft | extraRight) != prereqLeft );
104026 pNew->prereqRight = prereqLeft | extraRight;
104027 pNew->prereqAll = prereqAll;
104028 pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
104029 }
104030 }
104031
104032 #ifndef SQLITE_OMIT_BETWEEN_OPTIMIZATION
104033 /* If a term is the BETWEEN operator, create two new virtual terms
@@ -104160,11 +104482,11 @@
104482 return;
104483 }
104484
104485 /* Search the WHERE clause terms for a usable WO_OR term. */
104486 for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
104487 if( (pTerm->eOperator & WO_OR)!=0
104488 && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0
104489 && (pTerm->u.pOrInfo->indexable & maskSrc)!=0
104490 ){
104491 WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
104492 WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
@@ -104181,11 +104503,11 @@
104503 sBOI.ppIdxInfo = 0;
104504 for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
104505 WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
104506 (pOrTerm - pOrWC->a), (pTerm - pWC->a)
104507 ));
104508 if( (pOrTerm->eOperator& WO_AND)!=0 ){
104509 sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
104510 bestIndex(&sBOI);
104511 }else if( pOrTerm->leftCursor==iCur ){
104512 WhereClause tempWC;
104513 tempWC.pParse = pWC->pParse;
@@ -104242,11 +104564,11 @@
104564 struct SrcList_item *pSrc, /* Table we are trying to access */
104565 Bitmask notReady /* Tables in outer loops of the join */
104566 ){
104567 char aff;
104568 if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
104569 if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
104570 if( (pTerm->prereqRight & notReady)!=0 ) return 0;
104571 aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
104572 if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
104573 return 1;
104574 }
@@ -104504,14 +104826,14 @@
104826
104827 /* Count the number of possible WHERE clause constraints referring
104828 ** to this virtual table */
104829 for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
104830 if( pTerm->leftCursor != pSrc->iCursor ) continue;
104831 assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
104832 testcase( pTerm->eOperator & WO_IN );
104833 testcase( pTerm->eOperator & WO_ISNULL );
104834 if( pTerm->eOperator & (WO_ISNULL) ) continue;
104835 if( pTerm->wtFlags & TERM_VNULL ) continue;
104836 nTerm++;
104837 }
104838
104839 /* If the ORDER BY clause contains only columns in the current
@@ -104555,29 +104877,32 @@
104877 *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy;
104878 *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
104879 pUsage;
104880
104881 for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
104882 u8 op;
104883 if( pTerm->leftCursor != pSrc->iCursor ) continue;
104884 assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
104885 testcase( pTerm->eOperator & WO_IN );
104886 testcase( pTerm->eOperator & WO_ISNULL );
104887 if( pTerm->eOperator & (WO_ISNULL) ) continue;
104888 if( pTerm->wtFlags & TERM_VNULL ) continue;
104889 pIdxCons[j].iColumn = pTerm->u.leftColumn;
104890 pIdxCons[j].iTermOffset = i;
104891 op = (u8)pTerm->eOperator & WO_ALL;
104892 if( op==WO_IN ) op = WO_EQ;
104893 pIdxCons[j].op = op;
104894 /* The direct assignment in the previous line is possible only because
104895 ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
104896 ** following asserts verify this fact. */
104897 assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ );
104898 assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT );
104899 assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE );
104900 assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
104901 assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
104902 assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
104903 assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
104904 j++;
104905 }
104906 for(i=0; i<nOrderBy; i++){
104907 Expr *pExpr = pOrderBy->a[i].pExpr;
104908 pIdxOrderBy[i].iColumn = pExpr->iColumn;
@@ -104659,10 +104984,11 @@
104984 struct sqlite3_index_constraint *pIdxCons;
104985 struct sqlite3_index_constraint_usage *pUsage;
104986 WhereTerm *pTerm;
104987 int i, j;
104988 int nOrderBy;
104989 int bAllowIN; /* Allow IN optimizations */
104990 double rCost;
104991
104992 /* Make sure wsFlags is initialized to some sane value. Otherwise, if the
104993 ** malloc in allocateIndexInfo() fails and this function returns leaving
104994 ** wsFlags in an uninitialized state, the caller may behave unpredictably.
@@ -104693,63 +105019,91 @@
105019 ** sqlite3ViewGetColumnNames() would have picked up the error.
105020 */
105021 assert( pTab->azModuleArg && pTab->azModuleArg[0] );
105022 assert( sqlite3GetVTable(pParse->db, pTab) );
105023
105024 /* Try once or twice. On the first attempt, allow IN optimizations.
105025 ** If an IN optimization is accepted by the virtual table xBestIndex
105026 ** method, but the pInfo->aConstrainUsage.omit flag is not set, then
105027 ** the query will not work because it might allow duplicate rows in
105028 ** output. In that case, run the xBestIndex method a second time
105029 ** without the IN constraints. Usually this loop only runs once.
105030 ** The loop will exit using a "break" statement.
105031 */
105032 for(bAllowIN=1; 1; bAllowIN--){
105033 assert( bAllowIN==0 || bAllowIN==1 );
105034
105035 /* Set the aConstraint[].usable fields and initialize all
105036 ** output variables to zero.
105037 **
105038 ** aConstraint[].usable is true for constraints where the right-hand
105039 ** side contains only references to tables to the left of the current
105040 ** table. In other words, if the constraint is of the form:
105041 **
105042 ** column = expr
105043 **
105044 ** and we are evaluating a join, then the constraint on column is
105045 ** only valid if all tables referenced in expr occur to the left
105046 ** of the table containing column.
105047 **
105048 ** The aConstraints[] array contains entries for all constraints
105049 ** on the current table. That way we only have to compute it once
105050 ** even though we might try to pick the best index multiple times.
105051 ** For each attempt at picking an index, the order of tables in the
105052 ** join might be different so we have to recompute the usable flag
105053 ** each time.
105054 */
105055 pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
105056 pUsage = pIdxInfo->aConstraintUsage;
105057 for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
105058 j = pIdxCons->iTermOffset;
105059 pTerm = &pWC->a[j];
105060 if( (pTerm->prereqRight&p->notReady)==0
105061 && (bAllowIN || (pTerm->eOperator & WO_IN)==0)
105062 ){
105063 pIdxCons->usable = 1;
105064 }else{
105065 pIdxCons->usable = 0;
105066 }
105067 }
105068 memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
105069 if( pIdxInfo->needToFreeIdxStr ){
105070 sqlite3_free(pIdxInfo->idxStr);
105071 }
105072 pIdxInfo->idxStr = 0;
105073 pIdxInfo->idxNum = 0;
105074 pIdxInfo->needToFreeIdxStr = 0;
105075 pIdxInfo->orderByConsumed = 0;
105076 /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
105077 pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
105078 nOrderBy = pIdxInfo->nOrderBy;
105079 if( !p->pOrderBy ){
105080 pIdxInfo->nOrderBy = 0;
105081 }
105082
105083 if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
105084 return;
105085 }
105086
105087 pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
105088 for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
105089 if( pUsage[i].argvIndex>0 ){
105090 j = pIdxCons->iTermOffset;
105091 pTerm = &pWC->a[j];
105092 p->cost.used |= pTerm->prereqRight;
105093 if( (pTerm->eOperator & WO_IN)!=0 && pUsage[i].omit==0 ){
105094 /* Do not attempt to use an IN constraint if the virtual table
105095 ** says that the equivalent EQ constraint cannot be safely omitted.
105096 ** If we do attempt to use such a constraint, some rows might be
105097 ** repeated in the output. */
105098 break;
105099 }
105100 }
105101 }
105102 if( i>=pIdxInfo->nConstraint ) break;
105103 }
105104
105105 /* If there is an ORDER BY clause, and the selected virtual table index
105106 ** does not satisfy it, increase the cost of the scan accordingly. This
105107 ** matches the processing for non-virtual tables in bestBtreeIndex().
105108 */
105109 rCost = pIdxInfo->estimatedCost;
@@ -105040,28 +105394,28 @@
105394 u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
105395
105396 if( pLower ){
105397 Expr *pExpr = pLower->pExpr->pRight;
105398 rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
105399 assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
105400 if( rc==SQLITE_OK
105401 && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
105402 ){
105403 iLower = a[0];
105404 if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1];
105405 }
105406 sqlite3ValueFree(pRangeVal);
105407 }
105408 if( rc==SQLITE_OK && pUpper ){
105409 Expr *pExpr = pUpper->pExpr->pRight;
105410 rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
105411 assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
105412 if( rc==SQLITE_OK
105413 && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
105414 ){
105415 iUpper = a[0];
105416 if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1];
105417 }
105418 sqlite3ValueFree(pRangeVal);
105419 }
105420 if( rc==SQLITE_OK ){
105421 if( iUpper<=iLower ){
@@ -105365,16 +105719,16 @@
105719 ** if there are any X= or X IS NULL constraints in the WHERE clause. */
105720 pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
105721 WO_EQ|WO_ISNULL|WO_IN, pIdx);
105722 if( pConstraint==0 ){
105723 isEq = 0;
105724 }else if( (pConstraint->eOperator & WO_IN)!=0 ){
105725 /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY
105726 ** because we do not know in what order the values on the RHS of the IN
105727 ** operator will occur. */
105728 break;
105729 }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){
105730 uniqueNotNull = 0;
105731 isEq = 1; /* "X IS NULL" means X has only a single value */
105732 }else if( pConstraint->prereqRight==0 ){
105733 isEq = 1; /* Constraint "X=constant" means X has only a single value */
105734 }else{
@@ -105720,11 +106074,11 @@
106074 int bRev = 2;
106075 WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat));
106076 pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev);
106077 WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n",
106078 bRev, pc.plan.nOBSat));
106079 if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
106080 pc.plan.wsFlags |= WHERE_ORDERED;
106081 }
106082 if( nOrderBy==pc.plan.nOBSat ){
106083 bSort = 0;
106084 pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
@@ -105783,16 +106137,17 @@
106137 */
106138 if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
106139 && pFirstTerm!=0 && aiRowEst[1]>1 ){
106140 assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
106141 if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
106142 testcase( pFirstTerm->eOperator & WO_EQ );
106143 testcase( pFirstTerm->eOperator & WO_EQUIV );
106144 testcase( pFirstTerm->eOperator & WO_ISNULL );
106145 whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
106146 &pc.plan.nRow);
106147 }else if( bInEst==0 ){
106148 assert( pFirstTerm->eOperator & WO_IN );
106149 whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
106150 &pc.plan.nRow);
106151 }
106152 }
106153 #endif /* SQLITE_ENABLE_STAT3 */
@@ -105935,11 +106290,11 @@
106290 ** more selective intentionally because of the subjective
106291 ** observation that indexed range constraints really are more
106292 ** selective in practice, on average. */
106293 pc.plan.nRow /= 3;
106294 }
106295 }else if( (pTerm->eOperator & WO_NOOP)==0 ){
106296 /* Any other expression lowers the output row count by half */
106297 pc.plan.nRow /= 2;
106298 }
106299 }
106300 if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
@@ -105987,12 +106342,13 @@
106342 assert( pSrc->pIndex==0
106343 || p->cost.plan.u.pIdx==0
106344 || p->cost.plan.u.pIdx==pSrc->pIndex
106345 );
106346
106347 WHERETRACE((" best index is %s cost=%.1f\n",
106348 p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk",
106349 p->cost.rCost));
106350
106351 bestOrClauseIndex(p);
106352 bestAutomaticIndex(p);
106353 p->cost.plan.wsFlags |= eqTermMask;
106354 }
@@ -106514,32 +106870,40 @@
106870 if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
106871 /* Case 0: The table is a virtual-table. Use the VFilter and VNext
106872 ** to access the data.
106873 */
106874 int iReg; /* P3 Value for OP_VFilter */
106875 int addrNotFound;
106876 sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
106877 int nConstraint = pVtabIdx->nConstraint;
106878 struct sqlite3_index_constraint_usage *aUsage =
106879 pVtabIdx->aConstraintUsage;
106880 const struct sqlite3_index_constraint *aConstraint =
106881 pVtabIdx->aConstraint;
106882
106883 sqlite3ExprCachePush(pParse);
106884 iReg = sqlite3GetTempRange(pParse, nConstraint+2);
106885 addrNotFound = pLevel->addrBrk;
106886 for(j=1; j<=nConstraint; j++){
106887 for(k=0; k<nConstraint; k++){
106888 if( aUsage[k].argvIndex==j ){
106889 WhereTerm *pTerm = &pWC->a[aConstraint[k].iTermOffset];
106890 int iTarget = iReg+j+1;
106891 if( pTerm->eOperator & WO_IN ){
106892 codeEqualityTerm(pParse, pTerm, pLevel, iTarget);
106893 addrNotFound = pLevel->addrNxt;
106894 }else{
106895 sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
106896 }
106897 break;
106898 }
106899 }
106900 if( k==nConstraint ) break;
106901 }
106902 sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg);
106903 sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
106904 sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr,
106905 pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
106906 pVtabIdx->needToFreeIdxStr = 0;
106907 for(j=0; j<nConstraint; j++){
106908 if( aUsage[j].omit ){
106909 int iTerm = aConstraint[j].iTermOffset;
@@ -106562,11 +106926,10 @@
106926 */
106927 iReleaseReg = sqlite3GetTempReg(pParse);
106928 pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
106929 assert( pTerm!=0 );
106930 assert( pTerm->pExpr!=0 );
 
106931 assert( omitTable==0 );
106932 testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
106933 iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg);
106934 addrNxt = pLevel->addrNxt;
106935 sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
@@ -106953,11 +107316,11 @@
107316 int ii; /* Loop counter */
107317 Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
107318
107319 pTerm = pLevel->plan.u.pTerm;
107320 assert( pTerm!=0 );
107321 assert( pTerm->eOperator & WO_OR );
107322 assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
107323 pOrWc = &pTerm->u.pOrInfo->wc;
107324 pLevel->op = OP_Return;
107325 pLevel->p1 = regReturn;
107326
@@ -107026,11 +107389,11 @@
107389 }
107390 }
107391
107392 for(ii=0; ii<pOrWc->nTerm; ii++){
107393 WhereTerm *pOrTerm = &pOrWc->a[ii];
107394 if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
107395 WhereInfo *pSubWInfo; /* Info for single OR-term scan */
107396 Expr *pOrExpr = pOrTerm->pExpr;
107397 if( pAndExpr ){
107398 pAndExpr->pLeft = pOrExpr;
107399 pOrExpr = pAndExpr;
@@ -107481,10 +107844,11 @@
107844 Index *pIdx; /* Index for FROM table at pTabItem */
107845 int j; /* For looping over FROM tables */
107846 int bestJ = -1; /* The value of j */
107847 Bitmask m; /* Bitmask value for j or bestJ */
107848 int isOptimal; /* Iterator for optimal/non-optimal search */
107849 int ckOptimal; /* Do the optimal scan check */
107850 int nUnconstrained; /* Number tables without INDEXED BY */
107851 Bitmask notIndexed; /* Mask of tables that cannot use an index */
107852
107853 memset(&bestPlan, 0, sizeof(bestPlan));
107854 bestPlan.rCost = SQLITE_BIG_DBL;
@@ -107515,14 +107879,12 @@
107879 **
107880 ** The second loop iteration is only performed if no optimal scan
107881 ** strategies were found by the first iteration. This second iteration
107882 ** is used to search for the lowest cost scan overall.
107883 **
107884 ** Without the optimal scan step (the first iteration) a suboptimal
107885 ** plan might be chosen for queries like this:
 
 
107886 **
107887 ** CREATE TABLE t1(a, b);
107888 ** CREATE TABLE t2(c, d);
107889 ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
107890 **
@@ -107533,20 +107895,44 @@
107895 ** algorithm may choose to use t2 for the outer loop, which is a much
107896 ** costlier approach.
107897 */
107898 nUnconstrained = 0;
107899 notIndexed = 0;
107900
107901 /* The optimal scan check only occurs if there are two or more tables
107902 ** available to be reordered */
107903 if( iFrom==nTabList-1 ){
107904 ckOptimal = 0; /* Common case of just one table in the FROM clause */
107905 }else{
107906 ckOptimal = -1;
107907 for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
 
 
 
 
107908 m = getMask(pMaskSet, sWBI.pSrc->iCursor);
107909 if( (m & sWBI.notValid)==0 ){
107910 if( j==iFrom ) iFrom++;
107911 continue;
107912 }
107913 if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break;
107914 if( ++ckOptimal ) break;
107915 if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
107916 }
107917 }
107918 assert( ckOptimal==0 || ckOptimal==1 );
107919
107920 for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){
107921 for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
107922 if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){
107923 /* This break and one like it in the ckOptimal computation loop
107924 ** above prevent table reordering across LEFT and CROSS JOINs.
107925 ** The LEFT JOIN case is necessary for correctness. The prohibition
107926 ** against reordering across a CROSS JOIN is an SQLite feature that
107927 ** allows the developer to control table reordering */
107928 break;
107929 }
107930 m = getMask(pMaskSet, sWBI.pSrc->iCursor);
107931 if( (m & sWBI.notValid)==0 ){
107932 assert( j>iFrom );
107933 continue;
107934 }
107935 sWBI.notReady = (isOptimal ? m : sWBI.notValid);
107936 if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
107937
107938 WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
@@ -107572,12 +107958,12 @@
107958 if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
107959 notIndexed |= m;
107960 }
107961 if( isOptimal ){
107962 pWInfo->a[j].rOptCost = sWBI.cost.rCost;
107963 }else if( ckOptimal ){
107964 /* If two or more tables have nearly the same outer loop cost, but
107965 ** very different inner loop (optimal) cost, we want to choose
107966 ** for the outer loop that table which benefits the least from
107967 ** being in the inner loop. The following code scales the
107968 ** outer loop cost estimate to accomplish that. */
107969 WHERETRACE((" scaling cost from %.1f to %.1f\n",
@@ -107618,15 +108004,23 @@
108004 sWBI.cost.rCost, sWBI.cost.plan.nRow,
108005 sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
108006 bestPlan = sWBI.cost;
108007 bestJ = j;
108008 }
108009
108010 /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that
108011 ** table y (and not table z) is always the next inner loop inside
108012 ** of table x. */
108013 if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
108014 }
108015 }
108016 assert( bestJ>=0 );
108017 assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
108018 assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 );
108019 testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 );
108020 testcase( bestJ>iFrom && bestJ<nTabList-1
108021 && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 );
108022 WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
108023 " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
108024 bestJ, pTabList->a[bestJ].pTab->zName,
108025 pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
108026 bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
@@ -108182,10 +108576,11 @@
108576 Expr* yy122;
108577 Select* yy159;
108578 IdList* yy180;
108579 struct {int value; int mask;} yy207;
108580 u8 yy258;
108581 u16 yy305;
108582 struct LikeOp yy318;
108583 TriggerStep* yy327;
108584 ExprSpan yy342;
108585 SrcList* yy347;
108586 int yy392;
@@ -110132,22 +110527,19 @@
110527 case 82: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==82);
110528 case 84: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==84);
110529 case 86: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==86);
110530 case 98: /* defer_subclause_opt ::= */ yytestcase(yyruleno==98);
110531 case 109: /* ifexists ::= */ yytestcase(yyruleno==109);
 
 
110532 case 221: /* between_op ::= BETWEEN */ yytestcase(yyruleno==221);
110533 case 224: /* in_op ::= IN */ yytestcase(yyruleno==224);
110534 {yygotominor.yy392 = 0;}
110535 break;
110536 case 29: /* ifnotexists ::= IF NOT EXISTS */
110537 case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30);
110538 case 70: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==70);
110539 case 85: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==85);
110540 case 108: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==108);
 
110541 case 222: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==222);
110542 case 225: /* in_op ::= NOT IN */ yytestcase(yyruleno==225);
110543 {yygotominor.yy392 = 1;}
110544 break;
110545 case 32: /* create_table_args ::= LP columnlist conslist_opt RP */
@@ -110383,12 +110775,19 @@
110775 case 116: /* multiselect_op ::= UNION ALL */
110776 {yygotominor.yy392 = TK_ALL;}
110777 break;
110778 case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
110779 {
110780 yygotominor.yy159 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy442,yymsp[-5].minor.yy347,yymsp[-4].minor.yy122,yymsp[-3].minor.yy442,yymsp[-2].minor.yy122,yymsp[-1].minor.yy442,yymsp[-7].minor.yy305,yymsp[0].minor.yy64.pLimit,yymsp[0].minor.yy64.pOffset);
110781 }
110782 break;
110783 case 119: /* distinct ::= DISTINCT */
110784 {yygotominor.yy305 = SF_Distinct;}
110785 break;
110786 case 120: /* distinct ::= ALL */
110787 case 121: /* distinct ::= */ yytestcase(yyruleno==121);
110788 {yygotominor.yy305 = 0;}
110789 break;
110790 case 122: /* sclp ::= selcollist COMMA */
110791 case 246: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==246);
110792 {yygotominor.yy442 = yymsp[-1].minor.yy442;}
110793 break;
@@ -110454,14 +110853,24 @@
110853 break;
110854 case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
110855 {
110856 if( yymsp[-6].minor.yy347==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy122==0 && yymsp[0].minor.yy180==0 ){
110857 yygotominor.yy347 = yymsp[-4].minor.yy347;
110858 }else if( yymsp[-4].minor.yy347->nSrc==1 ){
110859 yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
110860 if( yygotominor.yy347 ){
110861 struct SrcList_item *pNew = &yygotominor.yy347->a[yygotominor.yy347->nSrc-1];
110862 struct SrcList_item *pOld = yymsp[-4].minor.yy347->a;
110863 pNew->zName = pOld->zName;
110864 pNew->zDatabase = pOld->zDatabase;
110865 pOld->zName = pOld->zDatabase = 0;
110866 }
110867 sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy347);
110868 }else{
110869 Select *pSubquery;
110870 sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy347);
110871 pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy347,0,0,0,0,SF_NestedFrom,0,0);
110872 yygotominor.yy347 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy347,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy122,yymsp[0].minor.yy180);
110873 }
110874 }
110875 break;
110876 case 137: /* dbnm ::= */
@@ -110690,11 +111099,11 @@
111099 if( yymsp[-1].minor.yy442 && yymsp[-1].minor.yy442->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
111100 sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
111101 }
111102 yygotominor.yy342.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy442, &yymsp[-4].minor.yy0);
111103 spanSet(&yygotominor.yy342,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
111104 if( yymsp[-2].minor.yy305 && yygotominor.yy342.pExpr ){
111105 yygotominor.yy342.pExpr->flags |= EP_Distinct;
111106 }
111107 }
111108 break;
111109 case 197: /* expr ::= ID LP STAR RP */
@@ -136337,11 +136746,12 @@
136746 ** would fit in a single node, use a smaller node-size.
136747 */
136748 static int getNodeSize(
136749 sqlite3 *db, /* Database handle */
136750 Rtree *pRtree, /* Rtree handle */
136751 int isCreate, /* True for xCreate, false for xConnect */
136752 char **pzErr /* OUT: Error message, if any */
136753 ){
136754 int rc;
136755 char *zSql;
136756 if( isCreate ){
136757 int iPageSize = 0;
@@ -136350,17 +136760,22 @@
136760 if( rc==SQLITE_OK ){
136761 pRtree->iNodeSize = iPageSize-64;
136762 if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
136763 pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
136764 }
136765 }else{
136766 *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
136767 }
136768 }else{
136769 zSql = sqlite3_mprintf(
136770 "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1",
136771 pRtree->zDb, pRtree->zName
136772 );
136773 rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
136774 if( rc!=SQLITE_OK ){
136775 *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
136776 }
136777 }
136778
136779 sqlite3_free(zSql);
136780 return rc;
136781 }
@@ -136420,11 +136835,11 @@
136835 pRtree->eCoordType = eCoordType;
136836 memcpy(pRtree->zDb, argv[1], nDb);
136837 memcpy(pRtree->zName, argv[2], nName);
136838
136839 /* Figure out the node size to use. */
136840 rc = getNodeSize(db, pRtree, isCreate, pzErr);
136841
136842 /* Create/Connect to the underlying relational database schema. If
136843 ** that is successful, call sqlite3_declare_vtab() to configure
136844 ** the r-tree table schema.
136845 */
136846
+4 -4
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -105,13 +105,13 @@
105105
**
106106
** See also: [sqlite3_libversion()],
107107
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108108
** [sqlite_version()] and [sqlite_source_id()].
109109
*/
110
-#define SQLITE_VERSION "3.7.15"
111
-#define SQLITE_VERSION_NUMBER 3007015
112
-#define SQLITE_SOURCE_ID "2012-12-10 22:19:14 bd7aeeb691fee69dd6a562138a7aba8e8e192272"
110
+#define SQLITE_VERSION "3.7.16"
111
+#define SQLITE_VERSION_NUMBER 3007016
112
+#define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3"
113113
114114
/*
115115
** CAPI3REF: Run-Time Library Version Numbers
116116
** KEYWORDS: sqlite3_version, sqlite3_sourceid
117117
**
@@ -1590,11 +1590,11 @@
15901590
** database connection is opened. By default, URI handling is globally
15911591
** disabled. The default value may be changed by compiling with the
15921592
** [SQLITE_USE_URI] symbol defined.
15931593
**
15941594
** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
1595
-** <dd> This option taks a single integer argument which is interpreted as
1595
+** <dd> This option takes a single integer argument which is interpreted as
15961596
** a boolean in order to enable or disable the use of covering indices for
15971597
** full table scans in the query optimizer. The default setting is determined
15981598
** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
15991599
** if that compile-time option is omitted.
16001600
** The ability to disable the use of covering indices for full table scans
16011601
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -105,13 +105,13 @@
105 **
106 ** See also: [sqlite3_libversion()],
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.15"
111 #define SQLITE_VERSION_NUMBER 3007015
112 #define SQLITE_SOURCE_ID "2012-12-10 22:19:14 bd7aeeb691fee69dd6a562138a7aba8e8e192272"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -1590,11 +1590,11 @@
1590 ** database connection is opened. By default, URI handling is globally
1591 ** disabled. The default value may be changed by compiling with the
1592 ** [SQLITE_USE_URI] symbol defined.
1593 **
1594 ** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
1595 ** <dd> This option taks a single integer argument which is interpreted as
1596 ** a boolean in order to enable or disable the use of covering indices for
1597 ** full table scans in the query optimizer. The default setting is determined
1598 ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
1599 ** if that compile-time option is omitted.
1600 ** The ability to disable the use of covering indices for full table scans
1601
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -105,13 +105,13 @@
105 **
106 ** See also: [sqlite3_libversion()],
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.16"
111 #define SQLITE_VERSION_NUMBER 3007016
112 #define SQLITE_SOURCE_ID "2013-01-17 17:20:49 38852f158ab20bb4d7b264af987ec1538052bec3"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -1590,11 +1590,11 @@
1590 ** database connection is opened. By default, URI handling is globally
1591 ** disabled. The default value may be changed by compiling with the
1592 ** [SQLITE_USE_URI] symbol defined.
1593 **
1594 ** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
1595 ** <dd> This option takes a single integer argument which is interpreted as
1596 ** a boolean in order to enable or disable the use of covering indices for
1597 ** full table scans in the query optimizer. The default setting is determined
1598 ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
1599 ** if that compile-time option is omitted.
1600 ** The ability to disable the use of covering indices for full table scans
1601
+97 -6
--- src/stat.c
+++ src/stat.c
@@ -66,11 +66,11 @@
6666
@ </td></tr>
6767
if( !brief ){
6868
@ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
6969
n = db_int(0, "SELECT count(*) FROM blob");
7070
m = db_int(0, "SELECT count(*) FROM delta");
71
- @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
71
+ @ %d(n) (%d(n-m) fulltext and %d(m) deltas)
7272
@ </td></tr>
7373
if( n>0 ){
7474
int a, b;
7575
Stmt q;
7676
@ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
@@ -94,11 +94,11 @@
9494
a = t/fsize;
9595
@ %d(a):%d(b)
9696
@ </td></tr>
9797
}
9898
@ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
99
- n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/");
99
+ n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/");
100100
@ %d(n)
101101
@ </td></tr>
102102
@ <tr><th>Number&nbsp;Of&nbsp;Files:</th><td>
103103
n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
104104
@ %d(n)
@@ -115,17 +115,16 @@
115115
@ </td></tr>
116116
}
117117
@ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
118118
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
119119
" + 0.99");
120
- @ %d(n) days or approximately %.2f(n/356.24) years.
120
+ @ %d(n) days or approximately %.2f(n/365.2425) years.
121121
@ </td></tr>
122122
@ <tr><th>Project&nbsp;ID:</th><td>%h(db_get("project-code",""))</td></tr>
123
- @ <tr><th>Server&nbsp;ID:</th><td>%h(db_get("server-code",""))</td></tr>
124123
@ <tr><th>Fossil&nbsp;Version:</th><td>
125
- @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
126
- @ (%h(COMPILER_NAME))
124
+ @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
125
+ @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)]
127126
@ </td></tr>
128127
@ <tr><th>SQLite&nbsp;Version:</th><td>%.19s(SQLITE_SOURCE_ID)
129128
@ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr>
130129
@ <tr><th>Database&nbsp;Stats:</th><td>
131130
zDb = db_name("repository");
@@ -137,10 +136,102 @@
137136
@ </td></tr>
138137
139138
@ </table>
140139
style_footer();
141140
}
141
+
142
+/*
143
+** COMMAND: dbstat*
144
+**
145
+** Show statistics and global information about the repository.
146
+*/
147
+void dbstat_cmd(void){
148
+ i64 t, fsize;
149
+ int n, m;
150
+ int szMax, szAvg;
151
+ const char *zDb;
152
+ int brief;
153
+ char zBuf[100];
154
+ const int colWidth = -20 /* printf alignment/width for left column */;
155
+ brief = find_option("brief", "b",0)!=0;
156
+ db_find_and_open_repository(0,0);
157
+ fsize = file_size(g.zRepositoryName);
158
+ bigSizeName(sizeof(zBuf), zBuf, fsize);
159
+ fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf );
160
+ if( !brief ){
161
+ n = db_int(0, "SELECT count(*) FROM blob");
162
+ m = db_int(0, "SELECT count(*) FROM delta");
163
+ fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n",
164
+ colWidth, "artifact-count:",
165
+ n, n-m, m);
166
+ if( n>0 ){
167
+ int a, b;
168
+ Stmt q;
169
+ db_prepare(&q, "SELECT total(size), avg(size), max(size)"
170
+ " FROM blob WHERE size>0");
171
+ db_step(&q);
172
+ t = db_column_int64(&q, 0);
173
+ szAvg = db_column_int(&q, 1);
174
+ szMax = db_column_int(&q, 2);
175
+ db_finalize(&q);
176
+ bigSizeName(sizeof(zBuf), zBuf, t);
177
+ fossil_print( "%*s%d bytes average, "
178
+ "%d bytes max, %s total\n",
179
+ colWidth, "artifact-sizes:",
180
+ szAvg, szMax, zBuf);
181
+ if( t/fsize < 5 ){
182
+ b = 10;
183
+ fsize /= 10;
184
+ }else{
185
+ b = 1;
186
+ }
187
+ a = t/fsize;
188
+ fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b);
189
+ }
190
+ n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'");
191
+ fossil_print("%*s%d\n", colWidth, "checkins:", n);
192
+ n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
193
+ fossil_print("%*s%d across all branches\n", colWidth, "files:", n);
194
+ n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
195
+ " WHERE tagname GLOB 'wiki-*'");
196
+ m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'");
197
+ fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m);
198
+ n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
199
+ " WHERE tagname GLOB 'tkt-*'");
200
+ m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'");
201
+ fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m);
202
+ n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'");
203
+ fossil_print("%*s%d\n", colWidth, "events:", n);
204
+ n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'");
205
+ fossil_print("%*s%d\n", colWidth, "tagchanges:", n);
206
+ }
207
+ n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
208
+ " + 0.99");
209
+ fossil_print("%*s%d days or approximately %.2f years.\n",
210
+ colWidth, "project-age:", n, n/365.2425);
211
+ fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code",""));
212
+ fossil_print("%*s%s %s %s (%s)\n",
213
+ colWidth, "fossil-version:",
214
+ RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION,
215
+ COMPILER_NAME);
216
+ fossil_print("%*s%.19s [%.10s] (%s)\n",
217
+ colWidth, "sqlite-version:",
218
+ SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20],
219
+ SQLITE_VERSION);
220
+ zDb = db_name("repository");
221
+ fossil_print("%*s%d pages, %d bytes/page, %d free pages, "
222
+ "%s, %s mode\n",
223
+ colWidth, "database-stats:",
224
+ db_int(0, "PRAGMA %s.page_count", zDb),
225
+ db_int(0, "PRAGMA %s.page_size", zDb),
226
+ db_int(0, "PRAGMA %s.freelist_count", zDb),
227
+ db_text(0, "PRAGMA %s.encoding", zDb),
228
+ db_text(0, "PRAGMA %s.journal_mode", zDb));
229
+
230
+}
231
+
232
+
142233
143234
/*
144235
** WEBPAGE: urllist
145236
**
146237
** Show ways in which this repository has been accessed
147238
--- src/stat.c
+++ src/stat.c
@@ -66,11 +66,11 @@
66 @ </td></tr>
67 if( !brief ){
68 @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
69 n = db_int(0, "SELECT count(*) FROM blob");
70 m = db_int(0, "SELECT count(*) FROM delta");
71 @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
72 @ </td></tr>
73 if( n>0 ){
74 int a, b;
75 Stmt q;
76 @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
@@ -94,11 +94,11 @@
94 a = t/fsize;
95 @ %d(a):%d(b)
96 @ </td></tr>
97 }
98 @ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
99 n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/");
100 @ %d(n)
101 @ </td></tr>
102 @ <tr><th>Number&nbsp;Of&nbsp;Files:</th><td>
103 n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
104 @ %d(n)
@@ -115,17 +115,16 @@
115 @ </td></tr>
116 }
117 @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
118 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
119 " + 0.99");
120 @ %d(n) days or approximately %.2f(n/356.24) years.
121 @ </td></tr>
122 @ <tr><th>Project&nbsp;ID:</th><td>%h(db_get("project-code",""))</td></tr>
123 @ <tr><th>Server&nbsp;ID:</th><td>%h(db_get("server-code",""))</td></tr>
124 @ <tr><th>Fossil&nbsp;Version:</th><td>
125 @ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
126 @ (%h(COMPILER_NAME))
127 @ </td></tr>
128 @ <tr><th>SQLite&nbsp;Version:</th><td>%.19s(SQLITE_SOURCE_ID)
129 @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr>
130 @ <tr><th>Database&nbsp;Stats:</th><td>
131 zDb = db_name("repository");
@@ -137,10 +136,102 @@
137 @ </td></tr>
138
139 @ </table>
140 style_footer();
141 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
143 /*
144 ** WEBPAGE: urllist
145 **
146 ** Show ways in which this repository has been accessed
147
--- src/stat.c
+++ src/stat.c
@@ -66,11 +66,11 @@
66 @ </td></tr>
67 if( !brief ){
68 @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
69 n = db_int(0, "SELECT count(*) FROM blob");
70 m = db_int(0, "SELECT count(*) FROM delta");
71 @ %d(n) (%d(n-m) fulltext and %d(m) deltas)
72 @ </td></tr>
73 if( n>0 ){
74 int a, b;
75 Stmt q;
76 @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
@@ -94,11 +94,11 @@
94 a = t/fsize;
95 @ %d(a):%d(b)
96 @ </td></tr>
97 }
98 @ <tr><th>Number&nbsp;Of&nbsp;Check-ins:</th><td>
99 n = db_int(0, "SELECT count(*) FROM event WHERE type='ci' /*scan*/");
100 @ %d(n)
101 @ </td></tr>
102 @ <tr><th>Number&nbsp;Of&nbsp;Files:</th><td>
103 n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
104 @ %d(n)
@@ -115,17 +115,16 @@
115 @ </td></tr>
116 }
117 @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
118 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
119 " + 0.99");
120 @ %d(n) days or approximately %.2f(n/365.2425) years.
121 @ </td></tr>
122 @ <tr><th>Project&nbsp;ID:</th><td>%h(db_get("project-code",""))</td></tr>
 
123 @ <tr><th>Fossil&nbsp;Version:</th><td>
124 @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
125 @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)]
126 @ </td></tr>
127 @ <tr><th>SQLite&nbsp;Version:</th><td>%.19s(SQLITE_SOURCE_ID)
128 @ [%.10s(&SQLITE_SOURCE_ID[20])] (%s(SQLITE_VERSION))</td></tr>
129 @ <tr><th>Database&nbsp;Stats:</th><td>
130 zDb = db_name("repository");
@@ -137,10 +136,102 @@
136 @ </td></tr>
137
138 @ </table>
139 style_footer();
140 }
141
142 /*
143 ** COMMAND: dbstat*
144 **
145 ** Show statistics and global information about the repository.
146 */
147 void dbstat_cmd(void){
148 i64 t, fsize;
149 int n, m;
150 int szMax, szAvg;
151 const char *zDb;
152 int brief;
153 char zBuf[100];
154 const int colWidth = -20 /* printf alignment/width for left column */;
155 brief = find_option("brief", "b",0)!=0;
156 db_find_and_open_repository(0,0);
157 fsize = file_size(g.zRepositoryName);
158 bigSizeName(sizeof(zBuf), zBuf, fsize);
159 fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf );
160 if( !brief ){
161 n = db_int(0, "SELECT count(*) FROM blob");
162 m = db_int(0, "SELECT count(*) FROM delta");
163 fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n",
164 colWidth, "artifact-count:",
165 n, n-m, m);
166 if( n>0 ){
167 int a, b;
168 Stmt q;
169 db_prepare(&q, "SELECT total(size), avg(size), max(size)"
170 " FROM blob WHERE size>0");
171 db_step(&q);
172 t = db_column_int64(&q, 0);
173 szAvg = db_column_int(&q, 1);
174 szMax = db_column_int(&q, 2);
175 db_finalize(&q);
176 bigSizeName(sizeof(zBuf), zBuf, t);
177 fossil_print( "%*s%d bytes average, "
178 "%d bytes max, %s total\n",
179 colWidth, "artifact-sizes:",
180 szAvg, szMax, zBuf);
181 if( t/fsize < 5 ){
182 b = 10;
183 fsize /= 10;
184 }else{
185 b = 1;
186 }
187 a = t/fsize;
188 fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b);
189 }
190 n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'");
191 fossil_print("%*s%d\n", colWidth, "checkins:", n);
192 n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
193 fossil_print("%*s%d across all branches\n", colWidth, "files:", n);
194 n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
195 " WHERE tagname GLOB 'wiki-*'");
196 m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'");
197 fossil_print("%*s%d (%d changes)\n", colWidth, "wikipages:", n, m);
198 n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
199 " WHERE tagname GLOB 'tkt-*'");
200 m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'");
201 fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m);
202 n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'");
203 fossil_print("%*s%d\n", colWidth, "events:", n);
204 n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'");
205 fossil_print("%*s%d\n", colWidth, "tagchanges:", n);
206 }
207 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
208 " + 0.99");
209 fossil_print("%*s%d days or approximately %.2f years.\n",
210 colWidth, "project-age:", n, n/365.2425);
211 fossil_print("%*s%s\n", colWidth, "project-id:", db_get("project-code",""));
212 fossil_print("%*s%s %s %s (%s)\n",
213 colWidth, "fossil-version:",
214 RELEASE_VERSION, MANIFEST_DATE, MANIFEST_VERSION,
215 COMPILER_NAME);
216 fossil_print("%*s%.19s [%.10s] (%s)\n",
217 colWidth, "sqlite-version:",
218 SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20],
219 SQLITE_VERSION);
220 zDb = db_name("repository");
221 fossil_print("%*s%d pages, %d bytes/page, %d free pages, "
222 "%s, %s mode\n",
223 colWidth, "database-stats:",
224 db_int(0, "PRAGMA %s.page_count", zDb),
225 db_int(0, "PRAGMA %s.page_size", zDb),
226 db_int(0, "PRAGMA %s.freelist_count", zDb),
227 db_text(0, "PRAGMA %s.encoding", zDb),
228 db_text(0, "PRAGMA %s.journal_mode", zDb));
229
230 }
231
232
233
234 /*
235 ** WEBPAGE: urllist
236 **
237 ** Show ways in which this repository has been accessed
238
+18 -4
--- src/style.c
+++ src/style.c
@@ -439,11 +439,13 @@
439439
/*
440440
** The default page footer
441441
*/
442442
const char zDefaultFooter[] =
443443
@ <div class="footer">
444
-@ Fossil version $release_version $manifest_version $manifest_date
444
+@ This page was generated in about
445
+@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
446
+@ Fossil version $manifest_version $manifest_date
445447
@ </div>
446448
@ </body></html>
447449
;
448450
449451
/*
@@ -586,12 +588,13 @@
586588
@ div.footer a:visited { color: white; }
587589
@ div.footer a:hover { background-color: white; color: #558195; }
588590
@
589591
@ /* verbatim blocks */
590592
@ pre.verbatim {
591
-@ background-color: #f5f5f5;
592
-@ padding: 0.5em;
593
+@ background-color: #f5f5f5;
594
+@ padding: 0.5em;
595
+@ white-space: pre-wrap;
593596
@}
594597
@
595598
@ /* The label/value pairs on (for example) the ci page */
596599
@ table.label-value th {
597600
@ vertical-align: top;
@@ -959,11 +962,11 @@
959962
@ line-height: 100%;
960963
},
961964
{ "div.sbsdiff",
962965
"side-by-side diff display",
963966
@ font-family: monospace;
964
- @ font-size: smaller;
967
+ @ font-size: xx-small;
965968
@ white-space: pre;
966969
},
967970
{ "div.udiff",
968971
"context diff display",
969972
@ font-family: monospace;
@@ -991,10 +994,21 @@
991994
},
992995
{ "span.modpending",
993996
"Moderation Pending message on timeline",
994997
@ color: #b03800;
995998
@ font-style: italic;
999
+ },
1000
+ { "pre.th1result",
1001
+ "format for th1 script results",
1002
+ @ white-space: pre-wrap;
1003
+ @ word-wrap: break-word;
1004
+ },
1005
+ { "pre.th1error",
1006
+ "format for th1 script errors",
1007
+ @ white-space: pre-wrap;
1008
+ @ word-wrap: break-word;
1009
+ @ color: red;
9961010
},
9971011
{ 0,
9981012
0,
9991013
0
10001014
}
10011015
--- src/style.c
+++ src/style.c
@@ -439,11 +439,13 @@
439 /*
440 ** The default page footer
441 */
442 const char zDefaultFooter[] =
443 @ <div class="footer">
444 @ Fossil version $release_version $manifest_version $manifest_date
 
 
445 @ </div>
446 @ </body></html>
447 ;
448
449 /*
@@ -586,12 +588,13 @@
586 @ div.footer a:visited { color: white; }
587 @ div.footer a:hover { background-color: white; color: #558195; }
588 @
589 @ /* verbatim blocks */
590 @ pre.verbatim {
591 @ background-color: #f5f5f5;
592 @ padding: 0.5em;
 
593 @}
594 @
595 @ /* The label/value pairs on (for example) the ci page */
596 @ table.label-value th {
597 @ vertical-align: top;
@@ -959,11 +962,11 @@
959 @ line-height: 100%;
960 },
961 { "div.sbsdiff",
962 "side-by-side diff display",
963 @ font-family: monospace;
964 @ font-size: smaller;
965 @ white-space: pre;
966 },
967 { "div.udiff",
968 "context diff display",
969 @ font-family: monospace;
@@ -991,10 +994,21 @@
991 },
992 { "span.modpending",
993 "Moderation Pending message on timeline",
994 @ color: #b03800;
995 @ font-style: italic;
 
 
 
 
 
 
 
 
 
 
 
996 },
997 { 0,
998 0,
999 0
1000 }
1001
--- src/style.c
+++ src/style.c
@@ -439,11 +439,13 @@
439 /*
440 ** The default page footer
441 */
442 const char zDefaultFooter[] =
443 @ <div class="footer">
444 @ This page was generated in about
445 @ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
446 @ Fossil version $manifest_version $manifest_date
447 @ </div>
448 @ </body></html>
449 ;
450
451 /*
@@ -586,12 +588,13 @@
588 @ div.footer a:visited { color: white; }
589 @ div.footer a:hover { background-color: white; color: #558195; }
590 @
591 @ /* verbatim blocks */
592 @ pre.verbatim {
593 @ background-color: #f5f5f5;
594 @ padding: 0.5em;
595 @ white-space: pre-wrap;
596 @}
597 @
598 @ /* The label/value pairs on (for example) the ci page */
599 @ table.label-value th {
600 @ vertical-align: top;
@@ -959,11 +962,11 @@
962 @ line-height: 100%;
963 },
964 { "div.sbsdiff",
965 "side-by-side diff display",
966 @ font-family: monospace;
967 @ font-size: xx-small;
968 @ white-space: pre;
969 },
970 { "div.udiff",
971 "context diff display",
972 @ font-family: monospace;
@@ -991,10 +994,21 @@
994 },
995 { "span.modpending",
996 "Moderation Pending message on timeline",
997 @ color: #b03800;
998 @ font-style: italic;
999 },
1000 { "pre.th1result",
1001 "format for th1 script results",
1002 @ white-space: pre-wrap;
1003 @ word-wrap: break-word;
1004 },
1005 { "pre.th1error",
1006 "format for th1 script errors",
1007 @ white-space: pre-wrap;
1008 @ word-wrap: break-word;
1009 @ color: red;
1010 },
1011 { 0,
1012 0,
1013 0
1014 }
1015
+22 -35
--- src/timeline.c
+++ src/timeline.c
@@ -211,19 +211,20 @@
211211
if( tmFlags & TIMELINE_GRAPH ){
212212
pGraph = graph_init();
213213
/* style is not moved to css, because this is
214214
** a technical div for the timeline graph
215215
*/
216
- @ <div id="canvas" style="position:relative;width:1px;height:1px;"
216
+ @ <div id="canvas" style="position:relative;height:0px;width:0px;"
217217
@ onclick="clickOnGraph(event)"></div>
218218
}
219219
db_static_prepare(&qbranch,
220220
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
221221
TAG_BRANCH
222222
);
223223
224
- @ <table id="timelineTable" class="timelineTable">
224
+ @ <table id="timelineTable" class="timelineTable"
225
+ @ onclick="clickOnGraph(event)">
225226
blob_zero(&comment);
226227
while( db_step(pQuery)==SQLITE_ROW ){
227228
int rid = db_column_int(pQuery, 0);
228229
const char *zUuid = db_column_text(pQuery, 1);
229230
int isLeaf = db_column_int(pQuery, 5);
@@ -426,10 +427,12 @@
426427
" (SELECT uuid FROM blob WHERE rid=fid),"
427428
" (SELECT uuid FROM blob WHERE rid=pid),"
428429
" (SELECT name FROM filename WHERE fnid=mlink.pfnid) AS oldnm"
429430
" FROM mlink"
430431
" WHERE mid=:mid AND (pid!=fid OR pfnid>0)"
432
+ " AND (fid>0 OR"
433
+ " fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=:mid))"
431434
" ORDER BY 3 /*sort*/"
432435
);
433436
fchngQueryInit = 1;
434437
}
435438
db_bind_int(&fchngQuery, ":mid", rid);
@@ -725,42 +728,16 @@
725728
@ for(var i in rowinfo){
726729
@ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY;
727730
@ rowinfo[i].x = left + rowinfo[i].r*railPitch;
728731
@ }
729732
@ var btm = absoluteY("grbtm") + 10 - canvasY;
730
-#if 0
731
- @ if( btm<32768 ){
732
- @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+
733
- @ 'style="position:absolute;left:'+(left-5)+'px;"' +
734
- @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>';
735
- @ realCanvas = gebi('timeline-canvas');
736
- @ }else{
737
- @ realCanvas = 0;
738
- @ }
739
- @ var context;
740
- @ if( realCanvas && realCanvas.getContext
741
- @ && (context = realCanvas.getContext('2d'))) {
742
- @ drawBox = function(color,x0,y0,x1,y1) {
743
- @ if( y0>32767 || y1>32767 ) return;
744
- @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
745
- @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
746
- @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return;
747
- @ context.fillStyle = color;
748
- @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1);
749
- @ };
750
- @ }
751
-#endif
752733
@ for(var i in rowinfo){
753734
@ drawNode(rowinfo[i], left, btm);
754735
@ }
755736
@ if( selRow!=null ) clickOnRow(selRow);
756737
@ }
757738
@ function clickOnGraph(event){
758
-#ifdef OMIT_IE8_SUPPORT
759
- @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset;
760
- @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset;
761
-#else
762739
@ var x=event.clientX-absoluteX("canvas");
763740
@ var y=event.clientY-absoluteY("canvas");
764741
@ if(window.pageXOffset!=null){
765742
@ x += window.pageXOffset;
766743
@ y += window.pageYOffset;
@@ -768,16 +745,18 @@
768745
@ var d = window.document.documentElement;
769746
@ if(document.compatMode!="CSS1Compat") d = d.body;
770747
@ x += d.scrollLeft;
771748
@ y += d.scrollTop;
772749
@ }
773
-#endif
750
+ if( P("clicktest")!=0 ){
751
+ @ alert("click at "+x+","+y)
752
+ }
774753
@ for(var i in rowinfo){
775754
@ p = rowinfo[i];
776
- @ if( p.y<y-10 ) continue;
777
- @ if( p.y>y+10 ) break;
778
- @ if( p.x>x-10 && p.x<x+10 ){
755
+ @ if( p.y<y-11 ) continue;
756
+ @ if( p.y>y+9 ) break;
757
+ @ if( p.x>x-11 && p.x<x+9 ){
779758
@ clickOnRow(p);
780759
@ break;
781760
@ }
782761
@ }
783762
@ }
@@ -1413,10 +1392,12 @@
14131392
** 1. uuid
14141393
** 2. Date/Time
14151394
** 3. Comment string and user
14161395
** 4. Number of non-merge children
14171396
** 5. Number of parents
1397
+** 6. mtime
1398
+** 7. branch
14181399
*/
14191400
void print_timeline(Stmt *q, int mxLine, int showfiles){
14201401
int nLine = 0;
14211402
char zPrevDate[20];
14221403
const char *zCurrentUuid=0;
@@ -1521,15 +1502,21 @@
15211502
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
15221503
@ FROM tag, tagxref
15231504
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
15241505
@ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
15251506
@ || ')' as comment,
1526
- @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount,
1507
+ @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
1508
+ @ AS primPlinkCount,
15271509
@ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
1528
- @ event.mtime AS mtime
1529
- @ FROM event, blob
1510
+ @ event.mtime AS mtime,
1511
+ @ tagxref.value AS branch
1512
+ @ FROM tag CROSS JOIN event CROSS JOIN blob
1513
+ @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
1514
+ @ AND tagxref.tagtype>0
1515
+ @ AND tagxref.rid=blob.rid
15301516
@ WHERE blob.rid=event.objid
1517
+ @ AND tag.tagname='branch'
15311518
;
15321519
return zBaseSql;
15331520
}
15341521
15351522
/*
15361523
--- src/timeline.c
+++ src/timeline.c
@@ -211,19 +211,20 @@
211 if( tmFlags & TIMELINE_GRAPH ){
212 pGraph = graph_init();
213 /* style is not moved to css, because this is
214 ** a technical div for the timeline graph
215 */
216 @ <div id="canvas" style="position:relative;width:1px;height:1px;"
217 @ onclick="clickOnGraph(event)"></div>
218 }
219 db_static_prepare(&qbranch,
220 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
221 TAG_BRANCH
222 );
223
224 @ <table id="timelineTable" class="timelineTable">
 
225 blob_zero(&comment);
226 while( db_step(pQuery)==SQLITE_ROW ){
227 int rid = db_column_int(pQuery, 0);
228 const char *zUuid = db_column_text(pQuery, 1);
229 int isLeaf = db_column_int(pQuery, 5);
@@ -426,10 +427,12 @@
426 " (SELECT uuid FROM blob WHERE rid=fid),"
427 " (SELECT uuid FROM blob WHERE rid=pid),"
428 " (SELECT name FROM filename WHERE fnid=mlink.pfnid) AS oldnm"
429 " FROM mlink"
430 " WHERE mid=:mid AND (pid!=fid OR pfnid>0)"
 
 
431 " ORDER BY 3 /*sort*/"
432 );
433 fchngQueryInit = 1;
434 }
435 db_bind_int(&fchngQuery, ":mid", rid);
@@ -725,42 +728,16 @@
725 @ for(var i in rowinfo){
726 @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY;
727 @ rowinfo[i].x = left + rowinfo[i].r*railPitch;
728 @ }
729 @ var btm = absoluteY("grbtm") + 10 - canvasY;
730 #if 0
731 @ if( btm<32768 ){
732 @ canvasDiv.innerHTML = '<canvas id="timeline-canvas" '+
733 @ 'style="position:absolute;left:'+(left-5)+'px;"' +
734 @ ' width="'+width+'" height="'+btm+'"><'+'/canvas>';
735 @ realCanvas = gebi('timeline-canvas');
736 @ }else{
737 @ realCanvas = 0;
738 @ }
739 @ var context;
740 @ if( realCanvas && realCanvas.getContext
741 @ && (context = realCanvas.getContext('2d'))) {
742 @ drawBox = function(color,x0,y0,x1,y1) {
743 @ if( y0>32767 || y1>32767 ) return;
744 @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; }
745 @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; }
746 @ if(isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) return;
747 @ context.fillStyle = color;
748 @ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1);
749 @ };
750 @ }
751 #endif
752 @ for(var i in rowinfo){
753 @ drawNode(rowinfo[i], left, btm);
754 @ }
755 @ if( selRow!=null ) clickOnRow(selRow);
756 @ }
757 @ function clickOnGraph(event){
758 #ifdef OMIT_IE8_SUPPORT
759 @ var x=event.clientX-absoluteX("canvas")+window.pageXOffset;
760 @ var y=event.clientY-absoluteY("canvas")+window.pageYOffset;
761 #else
762 @ var x=event.clientX-absoluteX("canvas");
763 @ var y=event.clientY-absoluteY("canvas");
764 @ if(window.pageXOffset!=null){
765 @ x += window.pageXOffset;
766 @ y += window.pageYOffset;
@@ -768,16 +745,18 @@
768 @ var d = window.document.documentElement;
769 @ if(document.compatMode!="CSS1Compat") d = d.body;
770 @ x += d.scrollLeft;
771 @ y += d.scrollTop;
772 @ }
773 #endif
 
 
774 @ for(var i in rowinfo){
775 @ p = rowinfo[i];
776 @ if( p.y<y-10 ) continue;
777 @ if( p.y>y+10 ) break;
778 @ if( p.x>x-10 && p.x<x+10 ){
779 @ clickOnRow(p);
780 @ break;
781 @ }
782 @ }
783 @ }
@@ -1413,10 +1392,12 @@
1413 ** 1. uuid
1414 ** 2. Date/Time
1415 ** 3. Comment string and user
1416 ** 4. Number of non-merge children
1417 ** 5. Number of parents
 
 
1418 */
1419 void print_timeline(Stmt *q, int mxLine, int showfiles){
1420 int nLine = 0;
1421 char zPrevDate[20];
1422 const char *zCurrentUuid=0;
@@ -1521,15 +1502,21 @@
1521 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
1522 @ FROM tag, tagxref
1523 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
1524 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
1525 @ || ')' as comment,
1526 @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount,
 
1527 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
1528 @ event.mtime AS mtime
1529 @ FROM event, blob
 
 
 
 
1530 @ WHERE blob.rid=event.objid
 
1531 ;
1532 return zBaseSql;
1533 }
1534
1535 /*
1536
--- src/timeline.c
+++ src/timeline.c
@@ -211,19 +211,20 @@
211 if( tmFlags & TIMELINE_GRAPH ){
212 pGraph = graph_init();
213 /* style is not moved to css, because this is
214 ** a technical div for the timeline graph
215 */
216 @ <div id="canvas" style="position:relative;height:0px;width:0px;"
217 @ onclick="clickOnGraph(event)"></div>
218 }
219 db_static_prepare(&qbranch,
220 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
221 TAG_BRANCH
222 );
223
224 @ <table id="timelineTable" class="timelineTable"
225 @ onclick="clickOnGraph(event)">
226 blob_zero(&comment);
227 while( db_step(pQuery)==SQLITE_ROW ){
228 int rid = db_column_int(pQuery, 0);
229 const char *zUuid = db_column_text(pQuery, 1);
230 int isLeaf = db_column_int(pQuery, 5);
@@ -426,10 +427,12 @@
427 " (SELECT uuid FROM blob WHERE rid=fid),"
428 " (SELECT uuid FROM blob WHERE rid=pid),"
429 " (SELECT name FROM filename WHERE fnid=mlink.pfnid) AS oldnm"
430 " FROM mlink"
431 " WHERE mid=:mid AND (pid!=fid OR pfnid>0)"
432 " AND (fid>0 OR"
433 " fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=:mid))"
434 " ORDER BY 3 /*sort*/"
435 );
436 fchngQueryInit = 1;
437 }
438 db_bind_int(&fchngQuery, ":mid", rid);
@@ -725,42 +728,16 @@
728 @ for(var i in rowinfo){
729 @ rowinfo[i].y = absoluteY("m"+rowinfo[i].id) + 10 - canvasY;
730 @ rowinfo[i].x = left + rowinfo[i].r*railPitch;
731 @ }
732 @ var btm = absoluteY("grbtm") + 10 - canvasY;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733 @ for(var i in rowinfo){
734 @ drawNode(rowinfo[i], left, btm);
735 @ }
736 @ if( selRow!=null ) clickOnRow(selRow);
737 @ }
738 @ function clickOnGraph(event){
 
 
 
 
739 @ var x=event.clientX-absoluteX("canvas");
740 @ var y=event.clientY-absoluteY("canvas");
741 @ if(window.pageXOffset!=null){
742 @ x += window.pageXOffset;
743 @ y += window.pageYOffset;
@@ -768,16 +745,18 @@
745 @ var d = window.document.documentElement;
746 @ if(document.compatMode!="CSS1Compat") d = d.body;
747 @ x += d.scrollLeft;
748 @ y += d.scrollTop;
749 @ }
750 if( P("clicktest")!=0 ){
751 @ alert("click at "+x+","+y)
752 }
753 @ for(var i in rowinfo){
754 @ p = rowinfo[i];
755 @ if( p.y<y-11 ) continue;
756 @ if( p.y>y+9 ) break;
757 @ if( p.x>x-11 && p.x<x+9 ){
758 @ clickOnRow(p);
759 @ break;
760 @ }
761 @ }
762 @ }
@@ -1413,10 +1392,12 @@
1392 ** 1. uuid
1393 ** 2. Date/Time
1394 ** 3. Comment string and user
1395 ** 4. Number of non-merge children
1396 ** 5. Number of parents
1397 ** 6. mtime
1398 ** 7. branch
1399 */
1400 void print_timeline(Stmt *q, int mxLine, int showfiles){
1401 int nLine = 0;
1402 char zPrevDate[20];
1403 const char *zCurrentUuid=0;
@@ -1521,15 +1502,21 @@
1502 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
1503 @ FROM tag, tagxref
1504 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
1505 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
1506 @ || ')' as comment,
1507 @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
1508 @ AS primPlinkCount,
1509 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
1510 @ event.mtime AS mtime,
1511 @ tagxref.value AS branch
1512 @ FROM tag CROSS JOIN event CROSS JOIN blob
1513 @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
1514 @ AND tagxref.tagtype>0
1515 @ AND tagxref.rid=blob.rid
1516 @ WHERE blob.rid=event.objid
1517 @ AND tag.tagname='branch'
1518 ;
1519 return zBaseSql;
1520 }
1521
1522 /*
1523
+73 -16
--- src/tkt.c
+++ src/tkt.c
@@ -34,12 +34,15 @@
3434
char *zAppend; /* Value to append */
3535
unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */
3636
} *aField;
3737
#define USEDBY_TICKET 01
3838
#define USEDBY_TICKETCHNG 02
39
-static int haveTicket = 0; /* True if the TICKET table exists */
40
-static int haveTicketChng = 0; /* True if the TICKETCHNG table exists */
39
+#define USEDBY_BOTH 03
40
+static u8 haveTicket = 0; /* True if the TICKET table exists */
41
+static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */
42
+static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */
43
+static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */
4144
4245
/*
4346
** Compare two entries in aField[] for sorting purposes
4447
*/
4548
static int nameCmpr(const void *a, const void *b){
@@ -74,11 +77,14 @@
7477
once = 1;
7578
db_prepare(&q, "PRAGMA table_info(ticket)");
7679
while( db_step(&q)==SQLITE_ROW ){
7780
const char *zFieldName = db_column_text(&q, 1);
7881
haveTicket = 1;
79
- if( memcmp(zFieldName,"tkt_",4)==0 ) continue;
82
+ if( memcmp(zFieldName,"tkt_",4)==0 ){
83
+ if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1;
84
+ continue;
85
+ }
8086
if( nField%10==0 ){
8187
aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
8288
}
8389
aField[nField].zName = mprintf("%s", zFieldName);
8490
aField[nField].mUsed = USEDBY_TICKET;
@@ -87,11 +93,14 @@
8793
db_finalize(&q);
8894
db_prepare(&q, "PRAGMA table_info(ticketchng)");
8995
while( db_step(&q)==SQLITE_ROW ){
9096
const char *zFieldName = db_column_text(&q, 1);
9197
haveTicketChng = 1;
92
- if( memcmp(zFieldName,"tkt_",4)==0 ) continue;
98
+ if( memcmp(zFieldName,"tkt_",4)==0 ){
99
+ if( strcmp(zFieldName,"tkt_rid")==0 ) haveTicketChngRid = 1;
100
+ continue;
101
+ }
93102
if( (i = fieldId(zFieldName))>=0 ){
94103
aField[i].mUsed |= USEDBY_TICKETCHNG;
95104
continue;
96105
}
97106
if( nField%10==0 ){
@@ -183,10 +192,11 @@
183192
*/
184193
static int ticket_insert(const Manifest *p, int rid, int tktid){
185194
Blob sql1, sql2, sql3;
186195
Stmt q;
187196
int i, j;
197
+ char *aUsed;
188198
189199
if( tktid==0 ){
190200
db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
191201
"VALUES(%Q, 0)", p->zTicketUuid);
192202
tktid = db_last_insert_rowid();
@@ -193,22 +203,25 @@
193203
}
194204
blob_zero(&sql1);
195205
blob_zero(&sql2);
196206
blob_zero(&sql3);
197207
blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
208
+ if( haveTicketCTime ){
209
+ blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
210
+ }
211
+ aUsed = fossil_malloc( nField );
212
+ memset(aUsed, 0, nField);
198213
for(i=0; i<p->nField; i++){
199214
const char *zName = p->aField[i].zName;
200
- if( zName[0]=='+' ){
201
- zName++;
202
- if( (j = fieldId(zName))<0 ) continue;
203
- if( aField[j].mUsed & USEDBY_TICKET ){
215
+ if( (j = fieldId(zName))<0 ) continue;
216
+ aUsed[j] = 1;
217
+ if( aField[j].mUsed & USEDBY_TICKET ){
218
+ if( zName[0]=='+' ){
219
+ zName++;
204220
blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q",
205221
zName, zName, p->aField[i].zValue);
206
- }
207
- }else{
208
- if( (j = fieldId(zName))<0 ) continue;
209
- if( aField[j].mUsed & USEDBY_TICKET ){
222
+ }else{
210223
blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue);
211224
}
212225
}
213226
if( aField[j].mUsed & USEDBY_TICKETCHNG ){
214227
blob_appendf(&sql2, ",%s", zName);
@@ -222,20 +235,41 @@
222235
db_prepare(&q, "%s", blob_str(&sql1));
223236
db_bind_double(&q, ":mtime", p->rDate);
224237
db_step(&q);
225238
db_finalize(&q);
226239
blob_reset(&sql1);
227
- if( blob_size(&sql2)>0 ){
228
- db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
229
- "VALUES(%d,:mtime%s)",
230
- blob_str(&sql2), tktid, blob_str(&sql3));
240
+ if( blob_size(&sql2)>0 || haveTicketChngRid ){
241
+ int fromTkt = 0;
242
+ if( haveTicketChngRid ){
243
+ blob_append(&sql2, ",tkt_rid", -1);
244
+ blob_appendf(&sql3, ",%d", rid);
245
+ }
246
+ for(i=0; i<nField; i++){
247
+ if( aUsed[i]==0
248
+ && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH
249
+ ){
250
+ fromTkt = 1;
251
+ blob_appendf(&sql2, ",%s", aField[i].zName);
252
+ blob_appendf(&sql3, ",%s", aField[i].zName);
253
+ }
254
+ }
255
+ if( fromTkt ){
256
+ db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
257
+ "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d",
258
+ blob_str(&sql2), tktid, blob_str(&sql3), tktid);
259
+ }else{
260
+ db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
261
+ "VALUES(%d,:mtime%s)",
262
+ blob_str(&sql2), tktid, blob_str(&sql3));
263
+ }
231264
db_bind_double(&q, ":mtime", p->rDate);
232265
db_step(&q);
233266
db_finalize(&q);
234267
}
235268
blob_reset(&sql2);
236269
blob_reset(&sql3);
270
+ fossil_free(aUsed);
237271
return tktid;
238272
}
239273
240274
/*
241275
** Rebuild an entire entry in the TICKET table
@@ -268,10 +302,11 @@
268302
}
269303
createFlag = 0;
270304
}
271305
db_finalize(&q);
272306
}
307
+
273308
274309
/*
275310
** Create the TH1 interpreter and load the "common" code.
276311
*/
277312
void ticket_init(void){
@@ -328,10 +363,32 @@
328363
ticket_rebuild_entry(zName);
329364
}
330365
db_finalize(&q);
331366
db_end_transaction(0);
332367
}
368
+
369
+/*
370
+** COMMAND: test-ticket-rebuild
371
+**
372
+** Usage: %fossil test-ticket-rebuild TICKETID|all
373
+**
374
+** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID
375
+** or for ALL.
376
+*/
377
+void test_ticket_rebuild(void){
378
+ db_find_and_open_repository(0, 0);
379
+ if( g.argc!=3 ) usage("TICKETID|all");
380
+ if( fossil_strcmp(g.argv[2], "all")==0 ){
381
+ ticket_rebuild();
382
+ }else{
383
+ const char *zUuid;
384
+ zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag"
385
+ " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]);
386
+ if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]);
387
+ ticket_rebuild_entry(zUuid);
388
+ }
389
+}
333390
334391
/*
335392
** For trouble-shooting purposes, render a dump of the aField[] table to
336393
** the webpage currently under construction.
337394
*/
338395
--- src/tkt.c
+++ src/tkt.c
@@ -34,12 +34,15 @@
34 char *zAppend; /* Value to append */
35 unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */
36 } *aField;
37 #define USEDBY_TICKET 01
38 #define USEDBY_TICKETCHNG 02
39 static int haveTicket = 0; /* True if the TICKET table exists */
40 static int haveTicketChng = 0; /* True if the TICKETCHNG table exists */
 
 
 
41
42 /*
43 ** Compare two entries in aField[] for sorting purposes
44 */
45 static int nameCmpr(const void *a, const void *b){
@@ -74,11 +77,14 @@
74 once = 1;
75 db_prepare(&q, "PRAGMA table_info(ticket)");
76 while( db_step(&q)==SQLITE_ROW ){
77 const char *zFieldName = db_column_text(&q, 1);
78 haveTicket = 1;
79 if( memcmp(zFieldName,"tkt_",4)==0 ) continue;
 
 
 
80 if( nField%10==0 ){
81 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
82 }
83 aField[nField].zName = mprintf("%s", zFieldName);
84 aField[nField].mUsed = USEDBY_TICKET;
@@ -87,11 +93,14 @@
87 db_finalize(&q);
88 db_prepare(&q, "PRAGMA table_info(ticketchng)");
89 while( db_step(&q)==SQLITE_ROW ){
90 const char *zFieldName = db_column_text(&q, 1);
91 haveTicketChng = 1;
92 if( memcmp(zFieldName,"tkt_",4)==0 ) continue;
 
 
 
93 if( (i = fieldId(zFieldName))>=0 ){
94 aField[i].mUsed |= USEDBY_TICKETCHNG;
95 continue;
96 }
97 if( nField%10==0 ){
@@ -183,10 +192,11 @@
183 */
184 static int ticket_insert(const Manifest *p, int rid, int tktid){
185 Blob sql1, sql2, sql3;
186 Stmt q;
187 int i, j;
 
188
189 if( tktid==0 ){
190 db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
191 "VALUES(%Q, 0)", p->zTicketUuid);
192 tktid = db_last_insert_rowid();
@@ -193,22 +203,25 @@
193 }
194 blob_zero(&sql1);
195 blob_zero(&sql2);
196 blob_zero(&sql3);
197 blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
 
 
 
 
 
198 for(i=0; i<p->nField; i++){
199 const char *zName = p->aField[i].zName;
200 if( zName[0]=='+' ){
201 zName++;
202 if( (j = fieldId(zName))<0 ) continue;
203 if( aField[j].mUsed & USEDBY_TICKET ){
 
204 blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q",
205 zName, zName, p->aField[i].zValue);
206 }
207 }else{
208 if( (j = fieldId(zName))<0 ) continue;
209 if( aField[j].mUsed & USEDBY_TICKET ){
210 blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue);
211 }
212 }
213 if( aField[j].mUsed & USEDBY_TICKETCHNG ){
214 blob_appendf(&sql2, ",%s", zName);
@@ -222,20 +235,41 @@
222 db_prepare(&q, "%s", blob_str(&sql1));
223 db_bind_double(&q, ":mtime", p->rDate);
224 db_step(&q);
225 db_finalize(&q);
226 blob_reset(&sql1);
227 if( blob_size(&sql2)>0 ){
228 db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
229 "VALUES(%d,:mtime%s)",
230 blob_str(&sql2), tktid, blob_str(&sql3));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231 db_bind_double(&q, ":mtime", p->rDate);
232 db_step(&q);
233 db_finalize(&q);
234 }
235 blob_reset(&sql2);
236 blob_reset(&sql3);
 
237 return tktid;
238 }
239
240 /*
241 ** Rebuild an entire entry in the TICKET table
@@ -268,10 +302,11 @@
268 }
269 createFlag = 0;
270 }
271 db_finalize(&q);
272 }
 
273
274 /*
275 ** Create the TH1 interpreter and load the "common" code.
276 */
277 void ticket_init(void){
@@ -328,10 +363,32 @@
328 ticket_rebuild_entry(zName);
329 }
330 db_finalize(&q);
331 db_end_transaction(0);
332 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
334 /*
335 ** For trouble-shooting purposes, render a dump of the aField[] table to
336 ** the webpage currently under construction.
337 */
338
--- src/tkt.c
+++ src/tkt.c
@@ -34,12 +34,15 @@
34 char *zAppend; /* Value to append */
35 unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */
36 } *aField;
37 #define USEDBY_TICKET 01
38 #define USEDBY_TICKETCHNG 02
39 #define USEDBY_BOTH 03
40 static u8 haveTicket = 0; /* True if the TICKET table exists */
41 static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */
42 static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */
43 static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */
44
45 /*
46 ** Compare two entries in aField[] for sorting purposes
47 */
48 static int nameCmpr(const void *a, const void *b){
@@ -74,11 +77,14 @@
77 once = 1;
78 db_prepare(&q, "PRAGMA table_info(ticket)");
79 while( db_step(&q)==SQLITE_ROW ){
80 const char *zFieldName = db_column_text(&q, 1);
81 haveTicket = 1;
82 if( memcmp(zFieldName,"tkt_",4)==0 ){
83 if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1;
84 continue;
85 }
86 if( nField%10==0 ){
87 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
88 }
89 aField[nField].zName = mprintf("%s", zFieldName);
90 aField[nField].mUsed = USEDBY_TICKET;
@@ -87,11 +93,14 @@
93 db_finalize(&q);
94 db_prepare(&q, "PRAGMA table_info(ticketchng)");
95 while( db_step(&q)==SQLITE_ROW ){
96 const char *zFieldName = db_column_text(&q, 1);
97 haveTicketChng = 1;
98 if( memcmp(zFieldName,"tkt_",4)==0 ){
99 if( strcmp(zFieldName,"tkt_rid")==0 ) haveTicketChngRid = 1;
100 continue;
101 }
102 if( (i = fieldId(zFieldName))>=0 ){
103 aField[i].mUsed |= USEDBY_TICKETCHNG;
104 continue;
105 }
106 if( nField%10==0 ){
@@ -183,10 +192,11 @@
192 */
193 static int ticket_insert(const Manifest *p, int rid, int tktid){
194 Blob sql1, sql2, sql3;
195 Stmt q;
196 int i, j;
197 char *aUsed;
198
199 if( tktid==0 ){
200 db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
201 "VALUES(%Q, 0)", p->zTicketUuid);
202 tktid = db_last_insert_rowid();
@@ -193,22 +203,25 @@
203 }
204 blob_zero(&sql1);
205 blob_zero(&sql2);
206 blob_zero(&sql3);
207 blob_appendf(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
208 if( haveTicketCTime ){
209 blob_appendf(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
210 }
211 aUsed = fossil_malloc( nField );
212 memset(aUsed, 0, nField);
213 for(i=0; i<p->nField; i++){
214 const char *zName = p->aField[i].zName;
215 if( (j = fieldId(zName))<0 ) continue;
216 aUsed[j] = 1;
217 if( aField[j].mUsed & USEDBY_TICKET ){
218 if( zName[0]=='+' ){
219 zName++;
220 blob_appendf(&sql1,", %s=coalesce(%s,'') || %Q",
221 zName, zName, p->aField[i].zValue);
222 }else{
 
 
 
223 blob_appendf(&sql1,", %s=%Q", zName, p->aField[i].zValue);
224 }
225 }
226 if( aField[j].mUsed & USEDBY_TICKETCHNG ){
227 blob_appendf(&sql2, ",%s", zName);
@@ -222,20 +235,41 @@
235 db_prepare(&q, "%s", blob_str(&sql1));
236 db_bind_double(&q, ":mtime", p->rDate);
237 db_step(&q);
238 db_finalize(&q);
239 blob_reset(&sql1);
240 if( blob_size(&sql2)>0 || haveTicketChngRid ){
241 int fromTkt = 0;
242 if( haveTicketChngRid ){
243 blob_append(&sql2, ",tkt_rid", -1);
244 blob_appendf(&sql3, ",%d", rid);
245 }
246 for(i=0; i<nField; i++){
247 if( aUsed[i]==0
248 && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH
249 ){
250 fromTkt = 1;
251 blob_appendf(&sql2, ",%s", aField[i].zName);
252 blob_appendf(&sql3, ",%s", aField[i].zName);
253 }
254 }
255 if( fromTkt ){
256 db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
257 "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d",
258 blob_str(&sql2), tktid, blob_str(&sql3), tktid);
259 }else{
260 db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)"
261 "VALUES(%d,:mtime%s)",
262 blob_str(&sql2), tktid, blob_str(&sql3));
263 }
264 db_bind_double(&q, ":mtime", p->rDate);
265 db_step(&q);
266 db_finalize(&q);
267 }
268 blob_reset(&sql2);
269 blob_reset(&sql3);
270 fossil_free(aUsed);
271 return tktid;
272 }
273
274 /*
275 ** Rebuild an entire entry in the TICKET table
@@ -268,10 +302,11 @@
302 }
303 createFlag = 0;
304 }
305 db_finalize(&q);
306 }
307
308
309 /*
310 ** Create the TH1 interpreter and load the "common" code.
311 */
312 void ticket_init(void){
@@ -328,10 +363,32 @@
363 ticket_rebuild_entry(zName);
364 }
365 db_finalize(&q);
366 db_end_transaction(0);
367 }
368
369 /*
370 ** COMMAND: test-ticket-rebuild
371 **
372 ** Usage: %fossil test-ticket-rebuild TICKETID|all
373 **
374 ** Rebuild the TICKET and TICKETCHNG tables for the given ticket ID
375 ** or for ALL.
376 */
377 void test_ticket_rebuild(void){
378 db_find_and_open_repository(0, 0);
379 if( g.argc!=3 ) usage("TICKETID|all");
380 if( fossil_strcmp(g.argv[2], "all")==0 ){
381 ticket_rebuild();
382 }else{
383 const char *zUuid;
384 zUuid = db_text(0, "SELECT substr(tagname,5) FROM tag"
385 " WHERE tagname GLOB 'tkt-%q*'", g.argv[2]);
386 if( zUuid==0 ) fossil_fatal("no such ticket: %s", g.argv[2]);
387 ticket_rebuild_entry(zUuid);
388 }
389 }
390
391 /*
392 ** For trouble-shooting purposes, render a dump of the aField[] table to
393 ** the webpage currently under construction.
394 */
395
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -67,10 +67,11 @@
6767
@ CREATE TABLE ticket(
6868
@ -- Do not change any column that begins with tkt_
6969
@ tkt_id INTEGER PRIMARY KEY,
7070
@ tkt_uuid TEXT UNIQUE,
7171
@ tkt_mtime DATE,
72
+@ tkt_ctime DATE,
7273
@ -- Add as many fields as required below this line
7374
@ type TEXT,
7475
@ status TEXT,
7576
@ subsystem TEXT,
7677
@ priority TEXT,
@@ -82,10 +83,11 @@
8283
@ comment TEXT
8384
@ );
8485
@ CREATE TABLE ticketchng(
8586
@ -- Do not change any column that begins with tkt_
8687
@ tkt_id INTEGER REFERENCES ticket,
88
+@ tkt_rid INTEGER REFERENCES blob,
8789
@ tkt_mtime DATE,
8890
@ -- Add as many fields as required below this line
8991
@ login TEXT,
9092
@ username TEXT,
9193
@ mimetype TEXT,
9294
9395
ADDED src/unicode.c
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -67,10 +67,11 @@
67 @ CREATE TABLE ticket(
68 @ -- Do not change any column that begins with tkt_
69 @ tkt_id INTEGER PRIMARY KEY,
70 @ tkt_uuid TEXT UNIQUE,
71 @ tkt_mtime DATE,
 
72 @ -- Add as many fields as required below this line
73 @ type TEXT,
74 @ status TEXT,
75 @ subsystem TEXT,
76 @ priority TEXT,
@@ -82,10 +83,11 @@
82 @ comment TEXT
83 @ );
84 @ CREATE TABLE ticketchng(
85 @ -- Do not change any column that begins with tkt_
86 @ tkt_id INTEGER REFERENCES ticket,
 
87 @ tkt_mtime DATE,
88 @ -- Add as many fields as required below this line
89 @ login TEXT,
90 @ username TEXT,
91 @ mimetype TEXT,
92
93 DDED src/unicode.c
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -67,10 +67,11 @@
67 @ CREATE TABLE ticket(
68 @ -- Do not change any column that begins with tkt_
69 @ tkt_id INTEGER PRIMARY KEY,
70 @ tkt_uuid TEXT UNIQUE,
71 @ tkt_mtime DATE,
72 @ tkt_ctime DATE,
73 @ -- Add as many fields as required below this line
74 @ type TEXT,
75 @ status TEXT,
76 @ subsystem TEXT,
77 @ priority TEXT,
@@ -82,10 +83,11 @@
83 @ comment TEXT
84 @ );
85 @ CREATE TABLE ticketchng(
86 @ -- Do not change any column that begins with tkt_
87 @ tkt_id INTEGER REFERENCES ticket,
88 @ tkt_rid INTEGER REFERENCES blob,
89 @ tkt_mtime DATE,
90 @ -- Add as many fields as required below this line
91 @ login TEXT,
92 @ username TEXT,
93 @ mimetype TEXT,
94
95 DDED src/unicode.c
+528
--- a/src/unicode.c
+++ b/src/unicode.c
@@ -0,0 +1,528 @@
1
+/*
2
+*8028020F606809F8C0B, 0x03600001,
3
+ 0x03ECA401,
4
+ 0x03F88033,
5
+ 0x03FC6807,
6
+ 0x03FFE405,
7
+ 0x0406400C,
8
+ 0x040E7C01,
9
+ 0x04247C01,
10
+ 0x0428E003,
11
+ 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
12
+ 0x04450004, 0x04451402,
13
+ 0x044B7C0C, 0x044C0004,
14
+ 0x044D2C03, 0x0FC01, 0x00320404,
15
+ 0x00335402,
16
+ 0x00351803,
17
+ 0x0035E401,
18
+ 0x00376008,
19
+ 0x00391C09,
20
+ 6, 0x003AEC02C80331, 0x00AF2835,
21
+ 0x00B39406,
22
+ 0x00B5F
23
+ 0x00BC00D6,
24
+ 0x00C0D802,
25
+ 0x00C64002,
26
+ 0x00C94001,
27
+ 0x01370040,
28
+ 0x029A7802,
29
+ 0x02A00801,
30
+ 0x02A1D004,2A3E003,
31
+ 0x02A57C01,
32
+3/fts302A8A40E,
33
+ 0x02A9EC03,
34
+ 0x02AB0401,
35
+ 0x02AF8C0B,
36
+ 0x03EC7801, 0x03ECA
37
+ 0x03F8001A,
38
+ 0x03FC040F,
39
+ 0x03FFA 0x0421DC02,
40
+ 0x00292C03,
41
+ 0x042B2001,
42
+ 0x 0x002AF001,
43
+ 0x04471409,*
44
+*8028 0x002BC002,
45
+ 0x002D1C02, 0x002D2C03,
46
+ 0x002E0801, 0x002EF805,
47
+ 0x002FCC08, 0x00300004,
48
+ 0x00315402, 0x00318802,
49
+ 0x0032F807, 0x00331803,
50
+ 0x00340403, 0x0034F807688, 61700351803, 0x00352804,
51
+ 0x0035E401, 0x00360802,
52
+ 0x00376008, 0x0037C803,
53
+ 0x00391C09, 0x00396802,
54
+ 0x003B2006, 0x003C041F,
55
+ 0x003E6424, 0x003EF80F,
56
+ 0x00415804, 0x00417803,
57
+ 0x0042080C, 0x00423C01,
58
+ 0x004E400A, 0
59
+ **, 'y', 'y', 'a', 'c0x005F6004,
60
+ 0x0062A401, 0x0064800C,
61
+ 0x00677822, 0x00685C05,
62
+ 0x0069FC01, 0x006A8007,
63
+
64
+ 0x006FF004, 0x00709014,, 0x03600001,
65
+ 0x03ECA401,
66
+ 0x03F88033,
67
+ 0x03FC6807,
68
+ 0x03FFE405,
69
+ 0x0406400C
70
+ 0x007FB403, 0x007FF402,
71
+ 0 007FB403, 0x007FF402,
72
+ 0x00822805, 0x0082801F,
73
+ 0x00842002, 0x00845001,
74
+ 0x00const static84A401,
75
+ 0x00852804, 0x00853C01, 0x00862802, 0x0086426F, 0x00900027,
76
+ 0x009E53E0, 0x00ADD820,
77
+ 00391C09,
78
+ 6, 0x003AEC02C80331, 8, 0x00AFB004, 0x00B394060x05BE3C000B5F
79
+ 0x00BC00D6,
80
+ 0x00C0 0x00B5C001, 0x00B5FC01,
81
+ 00C94001,
82
+ 0x0137005, 0x00BA001A, 0x00C0A807, 0x00C0DC01,
83
+ 0x00C05, 0x00181816,
84
+ 0x001B9C07,2A00801,
85
+ 0x02A0D8
86
+ 0x001CC01B,x0472A40E,
87
+ 0x02A380
88
+ 0x00206C09,
89
+ 0x02A8A40E, 0x02
90
+ 0x00217x03600001,
91
+ 0x03EC7801, 0x03ECA401,
92
+ 0x03F8001A, 0x03F88033,
93
+ 0x03FC040F, 0x03FC6807,
94
+ 0x03FFA007, 0x03FFE405,
95
+ 0x04063003, 0x0406400C,
96
+ 0x040DD805, 0x040E7C01,
97
+ 0x0421DC02, 0x04247C01,
98
+ 0x04d', 'e', 'e', 'g', 'h', 'i 0x04283004, 0x0428E003,
99
+ 0x042B2001, 0x042B9402,
100
+ 0x04400003, 0x0440E016,
101
+ 0x04449C0E, 0x04450004,
102
+ 0x04471409, 0x04476C01,
103
+ 0F606809F8C0B, 0x03600001,
104
+ 0x044D2C03, 0x044D5C01,
105
+ 0x0450D412, 0x04512C05,
106
+ 0x04531801, 0x0456BC07,
107
+ 0x0459800D, 0x045AAC0D,
108
+ 040DD805,
109
+ 0x0421DC02,
110
+ 41FC04,
111
+ 0x04450004,
112
+ 0x0
113
+ 0x0471C9800D,
114
+ 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
115
+ 0x0474FC07, 0x04751C01,
116
+ 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC05C01, 0x00358802, 0x0035E401,
117
+ 01, 0x006A8007,
118
+ 0 0x00376008,
119
+ 709014,, 0x03600001,
120
+ 0x00391C09,
121
+ 807,
122
+ 0x03FFE405,
123
+ 0x003B2006,
124
+ 0x00822805, 0x008 0x003E6424,
125
+ 849C01, 0x0084A401,
126
+ 0x00415804,
127
+ 86426F, 0x00900027,
128
+ 0x0042080C,
129
+ 6, 0x003AEC02C80331, 0x004E400A,
130
+ 0x00BC00D6,
131
+ 0x00 0x005BAC03,
132
+ 0x0137005, 0x00BA001 0x005ED023,
133
+ 0x00C64002,
134
+ A401, 0x0064800C,
135
+ 0x00677822, 0x00685C05,
136
+ 0x0069FC01, 0x006A8007,
137
+ 0x006CD011, 0x006D6823,
138
+ 0x006FF004, 0x007090140442C012,
139
+/*
140
+*8028020F606809F8C0B, 0x0360809F8C0B, 0x03600001,
141
+ 0x 0x0077F004, 0x007EF401,
142
+ 0x007FB403, 0x007FF402,
143
+ 0x00822805, 0x0082801F,
144
+ 0x00842002, 0x00845001,
145
+ 0x00849C01, 0x0084A401,
146
+ 0x00852804, 0x00853C016C011, 0x00672002,
147
+
148
+ 0x0069FC01, 0x006A8007,
149
+ 0x006CD011, 0x006D6823,
150
+ 0x006FF004, 0x00709014,
151
+ 0x00734019, 0x0073B401,, 0x03600001,
152
+ 0x03ECA401
153
+ 0x007FB403, 0x007FFEC01, 0x0 0x02A6CC1B, 0x02A77802,
154
+
155
+ 0x00822805, 0x0082801E,05BE3C000842002, 0x00845001,
156
+ 0x00849C01, 0x0084A401,
157
+ 0x00852804, 0x00853C01 0x040400
158
+ 0x0092704E, 0x0406400C, 0x040F4
159
+ 0x00AEF40C, 0x00AF28082CE407,
160
+ 0x0441FC04, 0x0442C012,
161
+ 0x0445CC03, 0x04460003,
162
+ 0x04477403, 0x0448B012,
163
+ 0F606809F8C0B, 0x03600001,
164
+ 0x044D8802,8, 1168660, 186, 6},
165
+ {7976, 18686, 8}, {8008, 18687, 8}, {8040,86, 8}, {8104, 18686, 2},
166
+ {8122,0, 1},
167
+ {8136,86, 2},
168
+ {8154, 15{8170, 154, 2},
169
+ {8172, 18848, 2}, {81860, 112, 1},
170
+ {8491, 1140836, 1},
171
+ {11364, 1104, 1},
172
+ {11374, 1060, 1}, {11376, 1029846209, 84, 0, 86},
173
+{42930, 86},
174
+ {43888, 92 30204,
175
+ 54793, 54809,
176
+ 8028020F606809F020F606809F8C0B, 65268, 65341,
177
+ 65436, 65439,
178
+ 65482, 65488,
179
+ bRemoveDiacritic ));/446, 1},
180
+ 4, 8},
181
+ {7960, 184, 4980A, 0x02A51C0D,
182
+ , 184, 8}, {79 0x02A79401,
183
+ {8040,{8088, 184, 0x02A9DC03,
184
+ 2}, {8122, 16 0x02AAF802,
185
+ 0, 1}, {8136, 0x02AD6C01,
186
+
187
+ {81868, 182
188
+ 0x037FFC01,
189
+
190
+ {11363, 136, 1}, 0x03F7F002,
191
+ 1506, 0, 1},*8028020F0E, 0x03F8C02 {42877, 94, 1},
192
+ {42893, 86, 1},
193
+ {42922, 80, 1},
194
+ {42925, 82, 1},
195
+ {42929, 840, 831, 68, 1},
196
+ {429325268, 65341, 65373,
197
+ 65450,
198
+ 65506,
199
+ 8028020F606809F8C0B, 0x03600001,
200
+ 03600001,
201
+ 0x03ECA401,
202
+ 0x03F88033,
203
+ 0x03FC6807,
204
+ 0x03FFE405,
205
+ 0x0406400C,
206
+ 0x040E7C01,
207
+ 0x04247C01,
208
+ 0x0428E003,
209
+ 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
210
+ 0x04450004, 0x04451402,
211
+ 0x044B7C0C, 0x044C00sizeof(aEntry)/sizeof(aEntry[0] 0x0035E401,
212
+ 0x00376008,
213
+ 0x00391C09,
214
+ 6, 0x003AEC02C80331, 0x00AF2835,
215
+ 0x00B39406,
216
+ 0x00B5F
217
+ 0x00B4, 0x04473401, 0x0448B012, 0x044B7C0C,
218
+ 0x044C0403, 0x044CF001, 0x044CF807, 0x044DC005, 0x0452C014,
219
+ 8687, 8}, {8040,86, 8}, 186, 8}, {8104, 1868 {8136,86, 2},
220
+ {8154, 15{8170, 154, 2},
221
+ {8172, 18848, 2}, {81860, 112, 1},
222
+ {8491, 1140836, 1},
223
+ {11364, 1104, 1},
224
+ B5{11374, 1060, 1}, {11376, 1029846209, 84, 1},
225
+ {42930, 86},
226
+ {43888, 92 30204,
227
+ 54793, 54809,
228
+ 8028020F606809F020F606809F8C0B, 65268, 65341,
229
+ A34007, 0x07BBC002,
230
+
231
+ 0x07C34425, 0x07C4401F, 0x04247C01,
232
+ 0x0428E003,
233
+ 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
234
+ 0x04450004, 0x04451402,
235
+ 0x044B7C0C, 0x044C00sizeof(aEntry)/sizeof(aEntry[0] 0x0035E401,
236
+ 0x00376008,
237
+ 0x00391C09,
238
+ 6, 0x003AEC02C80331, 0x00AF2835,
239
+ 0x00B39406,
240
+ 0x00B5F
241
+ 0x00BC00D6,
242
+ 0x00C0D802,
243
+ 0x00C64002,
244
+ 0x00C94001,
245
+ 0x01370040,
246
+ 0x029A7802,
247
+ 0x02A00801,
248
+ 0x02A1D004,2A3E003,
249
+ 0x02A57C01,
250
+ 0x02A8A40E,
251
+ 0x02A9EC03,
252
+ 0x02AB0401,
253
+ 0x02AF8C0B,
254
+ 0x03EC7801, 0x03ECA
255
+ 0x03F8001A,
256
+ 0x03FC040F,
257
+ 0x03FFA007,
258
+ 0x04063003,
259
+ 0x040DD805,
260
+ 0x0421DC02,
261
+ 0x04283004,
262
+ 0x042B2001,
263
+ 0x04349004,
264
+ 0x0441FC04,
265
+ 0x04450004,
266
+ 0x04471409,*
267
+*8028020F606809F8C0, 0x03600001,
268
+
269
+ 0x044D2C03,
270
+ 0x0450D412,
271
+ 0x04531801,
272
+ 0x0459800D,
273
+ 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
274
+ 0x0474FC07, 0x04751C01,
275
+ 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010,
276
+ 442E, 0x05BE3C04, 0x06F27008,
277
+
278
+ 0x075B0401, 0x075B6C01,
279
+ 0x075D3C01, 0x075DBC01,
280
+ 0x0760028C, 0x076A6C05,
281
+ 0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007,
282
+
283
+ 0x07C0C064, 0x07C2800F,
284
+ 0x07C4405C, 0x07C5C03D,
285
+ 0x07C94A,
286
+ 0x07DC0074, 0x07DE0059
287
+ 0x07E18028,402F, 0x07E50031,
288
+ 0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003,
289
+ 0x07E74030, 0x07E9800E, 0x38000401, 0x38008060,){896, 3912, 3928,
290
+
291
+ 4408, 4424, 447
292
+ 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
293
+ 36, 61880, 61914, 61948, 61998, 62122,
294
+ 62154, 62200, 62218, 62302, 62364
295
+ 62554,
296
+ 62924char aChar[] = {
297
+ '\0', '/*
298
+*8028'u', 'y', 'y', 'a', 'c',
299
+ 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
300
+ 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
301
+ 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
302
+ 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
303
+ '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
304
+ 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
305
+ 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
306
+ 'e', 'i', 'o', 'u', return (
307
+*8028020F606809F8C0B,09F8C0B, 0x0360b2058, 1},
308
+ {503, 1704627678, 1},
309
+ {981, 1828284, 1, 24},
310
+ {10081009, 174, 1}, {108846862, 1},
311
+ {7297,*8028020F606809F8C0B, 0x03600001,
312
+ sizeof(aDia)/sizeof(aDia[0]
313
+ 0x040DD805,
314
+ 0x0421DC02,
315
+ 0x04283004,
316
+ 0x042B2001,
317
+ 0x04349004,
318
+ 0x0441FC04,
319
+ 0x04450004,
320
+ 0x04471409,*
321
+*8028020F606809F8C0, 0x03600001,
322
+
323
+ 0x044D2C03,
324
+ 0x0450D412,
325
+ 0x04531801,
326
+ 0x0459800D,
327
+ 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
328
+ 0x0474FC07, 0x04751C01,
329
+ 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010,
330
+ 442E, 0x05BE3C04, 0x06F27008,
331
+
332
+ 0x075B0401, 0x075B6C01,
333
+ 0x075D3C01, 0x075DBC01,
334
+ 0x0760028C, 0x076A6C05,
335
+ 0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007,
336
+
337
+ 0x07C0C064, 0x07C2800F,
338
+ 0x07C4405C, 0x07C5C03D,
339
+ 0x07C94A,
340
+ 0x07DC0074, 0x07DE0059
341
+ 0x07E18028,402F, 0x07E50031,
342
+ 0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003,
343
+ 0x07E74030, 0x07E9800E, 0x38000401, 0x38008060,){896, 3912, 3928,
344
+
345
+ 4408, 4424, 447
346
+ 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
347
+ 36, 61880, 61914, 61948, 61998, 62122,
348
+ 62154, 62200, 62218, 62302, 62364
349
+ 62554,
350
+ 62924char aChar[] = {
351
+ '\0', '/*
352
+*8028'u', 'y', 'y', 'a', 'c',
353
+ 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
354
+ 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
355
+ 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
356
+ 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
357
+ '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
358
+ 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
359
+ 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
360
+ ';e', 'i', 'o', 'u', return (
361
+*8028020F606809F8C0B,09F8C0B, 0x0360b2058, 1},
362
+ {503, 1704627678, 1},
363
+ {981, 1828284, 1, 24},
364
+ {10081009, 174, 1}, {108846862, 1},
365
+ {7297,*8028020F606809F8C0B, 0x03600001,
366
+ 0x03ECA401,
367
+ 0x03F88033,
368
+ 0x03FC6807,
369
+ 0x03FFE405,
370
+ 0x0406400C,
371
+ 0x040E7C01,
372
+ 0x04247C01,
373
+ 0x0428E003,
374
+ 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
375
+ 0x04450004, 0x04451402,
376
+ 0x044B7C0C, 0x044C0004,
377
+ 0x044D2C03, 0x0600001,
378
+ 0x03ECA402, 1324, 1},
379
+ {7304, 96, 1}, {7312, 138, 43}, {7357, 13867838, 1168660, 186, 6},
380
+ {7976, 18686, 8}, {8008, 18687, 8}, {8040,86, 8}, {8104, 18686, 2},
381
+ {8122,0, 1},
382
+ {8136,86, 2},
383
+ {8154, 15{8170, 154, 2},
384
+ {8172, 18848, 2}, {81860, 112, 1},
385
+ {8491, 1140836, 1},
386
+ {11364, 1104, 1},
387
+ {11374, 1060, 1}, {11376, 1029846209, 84, 1},
388
+ {42930, 86},
389
+ {43888, 92 30204,
390
+ 54793, 54809,
391
+ 8028020F606809F020F606809F8C0B, 65268, 65341,
392
+ 65436, 65439,
393
+ 65482, 65488,
394
+ bRemoveDiacritic ));count(aEntry
395
+ {42925, 82, 1},
396
+sizeof(aEntry)/sizeof(aEntry[0] 0x0456E020,
397
+ 7976, 18686, 8}, {8008,AAC0D, 0x045C740F, 0x045CF004,
398
+
399
+ 0x05BD442E, 0x05BE3C04,
400
+ 0x0744A4C0, 0x07480046,
401
+ 0x075BEC01, 0x075C5401, 'm', 75E2401, 0x075EA401,05BE3C0076A840F, 0x07A340078},
402
+ {8120, 184, 2}, {8122, 160, 2}, {8124, 182, 1},
403
+ {8126, 120, 1}, {82B},
404
+ {8170,6, 2},
405
+ B{81868, 1827A, 0x07D5EC29, 0x07D6952C, 0x07DB800D,
406
+ 0x07DBC004, 0x07DC0074,247C01,
407
+7E1400A, 0x07E18028, 0x07E24,
408
+ 0x044D2C03, 0x0600001,
409
+ 0x03ECA402, 1324, 1},
410
+ {7304, 96, 1}, {7312, 138, 43}, {7357, 13867838, 1168660, 186, 6},
411
+ {7976, 18686, 8}, {8008, 18687, 8}, {8040,86, 8}, {8104, 18686, 2},
412
+ {8122,0, 1},
413
+ {8136,86, 2},
414
+ {8154, 15{8170, 154, 2},
415
+ {8172, 18848, 2}, {81860, 112, 1},
416
+ {8491, 1140836, 1},
417
+ {11364, 1104, 1},
418
+ {11374, 1060, 1}, {11376, 1029846209, 84, 1},
419
+ {42930, 86},
420
+ {43888, 92 30204,
421
+ 54793, 54809,
422
+ 8028020F606809F020F606809F8C0B, 65268, 65341,
423
+ 65436, 65439,
424
+ 65482, 65488,
425
+ bRemoveDiacritic ));/446, 1},
426
+ 4, 8},
427
+ {7960, 184, 6},7976, 184, 8}, {7992, 184, 88008, 184, 6},{8025, 185, 8}, {8040,{8088, 184, 8}, {8104, 184, 8},
428
+ {8120, 184, 2}, {8122, 160, 2}, {8124, 182, 1},
429
+ {8126, 120, 1}, {8136, 1582, 1},
430
+ {8152,4, 2},
431
+ {8170,6, 2},
432
+ {81868, 182
433
+
434
+ {8544, 8, 16},
435
+ {11264, 24, 47},
436
+ {11363, 136, 1},
437
+ {11373, 104, 1},
438
+
439
+
440
+ {11506, 0, 1},*8028020F606606809F8C0B, 0x03600001,
441
+ {42877, 94, 1},
442
+ {42893, 86, 1},
443
+ {42922, 80, 1},
444
+ {42925, 82, 1},
445
+ {42929, 840, 831, 68, 1},
446
+ {429325268, 65341, 65373,
447
+ 65450,
448
+ 65506,
449
+ 8028020F606809F8C0B, 0x03600001,
450
+ 03600001,
451
+ 0x03ECA401,
452
+ 0x03F88033,
453
+ 0x03FC6807,
454
+ 0x03FFE405,
455
+ 0x0406400C,
456
+ 0x040E7C01,
457
+ 0x04247C01,
458
+ 0x0428E003,
459
+ 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
460
+ 0x04450004, 0x04451402,
461
+ 0x044B7C0C, 0x044C00sizeof(aEntry)/sizeof(aEntry[0] 0x0035E401,
462
+ 0x00376008,
463
+ 0x00391C09,
464
+ 6, 0x003AEC02C80331, 0x00AF2835,
465
+ 0x00B39406,
466
+ 0x00B5F
467
+ 0x00BC00D6,
468
+ 0x00C0D802,
469
+ 0x00C64002,
470
+ 0x00C94001,
471
+ 0x01370040,
472
+ 0x029A7802,
473
+ 0x02A00801,
474
+ 0x02A1D004,2A3E003,
475
+ 0x02A57C01,
476
+ 0x02A8A40E,
477
+ 0x02A9EC03,
478
+ 0x02AB0401,
479
+ 0x02AF8C0B,
480
+ 0x03EC7801, 0x03ECA
481
+ 0x03F8001A,
482
+ 0x03FC040F,
483
+ 0x03FFA007,
484
+ 0x04063003,
485
+ 0x040DD805,
486
+ 0x0421DC02,
487
+ 0x04283004,
488
+ 0x042B2001,
489
+ 0x04349004,
490
+ 0x0441FC04,
491
+ 0x04450004,
492
+ 0x04471409,*
493
+*8028020F606809F8C0, 0x03600001,
494
+
495
+ 0x044D2C03,
496
+ 0x0450D412,
497
+ 0x04531801,
498
+ 0x0459800D,
499
+ 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
500
+ 0x0474FC07, 0x04751C01,
501
+ 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010,
502
+ 442E, 0x05BE3C04, 0x06F27008,
503
+
504
+ 0x075B0401, 0x075B6C01,
505
+ 0x075D3C01, 0x075DBC01,
506
+ 0x0760028C, 0x076A6C05,
507
+ 0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007,
508
+
509
+ 0x07C0C064, 0x07C2800F,
510
+ 0x07C4405C, 0x07C5C03D,
511
+ 0x07C94A,
512
+ 0x07DC0074, 0x07DE0059
513
+ 0x07E18028,402F, 0x07E50031,
514
+ 0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003,
515
+ 0x07E74030, 0x07E9800E, 0x38000401, 0x38008060,){896, 3912, 3928,
516
+
517
+ 4408, 4424, 447
518
+ 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
519
+ 36, 61880, 61914, 61948, 61998, 62122,
520
+ 62154, 62200, 62218, 62302, 62364
521
+ 62554,
522
+ 62924char aChar[] = {
523
+ '\0', '/*
524
+*8028'u', 'y', 'y', 'a', 'c',
525
+ 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
526
+ 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
527
+ 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
528
+ 'u', 's', 't', 'h'
--- a/src/unicode.c
+++ b/src/unicode.c
@@ -0,0 +1,528 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/unicode.c
+++ b/src/unicode.c
@@ -0,0 +1,528 @@
1 /*
2 *8028020F606809F8C0B, 0x03600001,
3 0x03ECA401,
4 0x03F88033,
5 0x03FC6807,
6 0x03FFE405,
7 0x0406400C,
8 0x040E7C01,
9 0x04247C01,
10 0x0428E003,
11 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
12 0x04450004, 0x04451402,
13 0x044B7C0C, 0x044C0004,
14 0x044D2C03, 0x0FC01, 0x00320404,
15 0x00335402,
16 0x00351803,
17 0x0035E401,
18 0x00376008,
19 0x00391C09,
20 6, 0x003AEC02C80331, 0x00AF2835,
21 0x00B39406,
22 0x00B5F
23 0x00BC00D6,
24 0x00C0D802,
25 0x00C64002,
26 0x00C94001,
27 0x01370040,
28 0x029A7802,
29 0x02A00801,
30 0x02A1D004,2A3E003,
31 0x02A57C01,
32 3/fts302A8A40E,
33 0x02A9EC03,
34 0x02AB0401,
35 0x02AF8C0B,
36 0x03EC7801, 0x03ECA
37 0x03F8001A,
38 0x03FC040F,
39 0x03FFA 0x0421DC02,
40 0x00292C03,
41 0x042B2001,
42 0x 0x002AF001,
43 0x04471409,*
44 *8028 0x002BC002,
45 0x002D1C02, 0x002D2C03,
46 0x002E0801, 0x002EF805,
47 0x002FCC08, 0x00300004,
48 0x00315402, 0x00318802,
49 0x0032F807, 0x00331803,
50 0x00340403, 0x0034F807688, 61700351803, 0x00352804,
51 0x0035E401, 0x00360802,
52 0x00376008, 0x0037C803,
53 0x00391C09, 0x00396802,
54 0x003B2006, 0x003C041F,
55 0x003E6424, 0x003EF80F,
56 0x00415804, 0x00417803,
57 0x0042080C, 0x00423C01,
58 0x004E400A, 0
59 **, 'y', 'y', 'a', 'c0x005F6004,
60 0x0062A401, 0x0064800C,
61 0x00677822, 0x00685C05,
62 0x0069FC01, 0x006A8007,
63
64 0x006FF004, 0x00709014,, 0x03600001,
65 0x03ECA401,
66 0x03F88033,
67 0x03FC6807,
68 0x03FFE405,
69 0x0406400C
70 0x007FB403, 0x007FF402,
71 0 007FB403, 0x007FF402,
72 0x00822805, 0x0082801F,
73 0x00842002, 0x00845001,
74 0x00const static84A401,
75 0x00852804, 0x00853C01, 0x00862802, 0x0086426F, 0x00900027,
76 0x009E53E0, 0x00ADD820,
77 00391C09,
78 6, 0x003AEC02C80331, 8, 0x00AFB004, 0x00B394060x05BE3C000B5F
79 0x00BC00D6,
80 0x00C0 0x00B5C001, 0x00B5FC01,
81 00C94001,
82 0x0137005, 0x00BA001A, 0x00C0A807, 0x00C0DC01,
83 0x00C05, 0x00181816,
84 0x001B9C07,2A00801,
85 0x02A0D8
86 0x001CC01B,x0472A40E,
87 0x02A380
88 0x00206C09,
89 0x02A8A40E, 0x02
90 0x00217x03600001,
91 0x03EC7801, 0x03ECA401,
92 0x03F8001A, 0x03F88033,
93 0x03FC040F, 0x03FC6807,
94 0x03FFA007, 0x03FFE405,
95 0x04063003, 0x0406400C,
96 0x040DD805, 0x040E7C01,
97 0x0421DC02, 0x04247C01,
98 0x04d', 'e', 'e', 'g', 'h', 'i 0x04283004, 0x0428E003,
99 0x042B2001, 0x042B9402,
100 0x04400003, 0x0440E016,
101 0x04449C0E, 0x04450004,
102 0x04471409, 0x04476C01,
103 0F606809F8C0B, 0x03600001,
104 0x044D2C03, 0x044D5C01,
105 0x0450D412, 0x04512C05,
106 0x04531801, 0x0456BC07,
107 0x0459800D, 0x045AAC0D,
108 040DD805,
109 0x0421DC02,
110 41FC04,
111 0x04450004,
112 0x0
113 0x0471C9800D,
114 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
115 0x0474FC07, 0x04751C01,
116 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC05C01, 0x00358802, 0x0035E401,
117 01, 0x006A8007,
118 0 0x00376008,
119 709014,, 0x03600001,
120 0x00391C09,
121 807,
122 0x03FFE405,
123 0x003B2006,
124 0x00822805, 0x008 0x003E6424,
125 849C01, 0x0084A401,
126 0x00415804,
127 86426F, 0x00900027,
128 0x0042080C,
129 6, 0x003AEC02C80331, 0x004E400A,
130 0x00BC00D6,
131 0x00 0x005BAC03,
132 0x0137005, 0x00BA001 0x005ED023,
133 0x00C64002,
134 A401, 0x0064800C,
135 0x00677822, 0x00685C05,
136 0x0069FC01, 0x006A8007,
137 0x006CD011, 0x006D6823,
138 0x006FF004, 0x007090140442C012,
139 /*
140 *8028020F606809F8C0B, 0x0360809F8C0B, 0x03600001,
141 0x 0x0077F004, 0x007EF401,
142 0x007FB403, 0x007FF402,
143 0x00822805, 0x0082801F,
144 0x00842002, 0x00845001,
145 0x00849C01, 0x0084A401,
146 0x00852804, 0x00853C016C011, 0x00672002,
147
148 0x0069FC01, 0x006A8007,
149 0x006CD011, 0x006D6823,
150 0x006FF004, 0x00709014,
151 0x00734019, 0x0073B401,, 0x03600001,
152 0x03ECA401
153 0x007FB403, 0x007FFEC01, 0x0 0x02A6CC1B, 0x02A77802,
154
155 0x00822805, 0x0082801E,05BE3C000842002, 0x00845001,
156 0x00849C01, 0x0084A401,
157 0x00852804, 0x00853C01 0x040400
158 0x0092704E, 0x0406400C, 0x040F4
159 0x00AEF40C, 0x00AF28082CE407,
160 0x0441FC04, 0x0442C012,
161 0x0445CC03, 0x04460003,
162 0x04477403, 0x0448B012,
163 0F606809F8C0B, 0x03600001,
164 0x044D8802,8, 1168660, 186, 6},
165 {7976, 18686, 8}, {8008, 18687, 8}, {8040,86, 8}, {8104, 18686, 2},
166 {8122,0, 1},
167 {8136,86, 2},
168 {8154, 15{8170, 154, 2},
169 {8172, 18848, 2}, {81860, 112, 1},
170 {8491, 1140836, 1},
171 {11364, 1104, 1},
172 {11374, 1060, 1}, {11376, 1029846209, 84, 0, 86},
173 {42930, 86},
174 {43888, 92 30204,
175 54793, 54809,
176 8028020F606809F020F606809F8C0B, 65268, 65341,
177 65436, 65439,
178 65482, 65488,
179 bRemoveDiacritic ));/446, 1},
180 4, 8},
181 {7960, 184, 4980A, 0x02A51C0D,
182 , 184, 8}, {79 0x02A79401,
183 {8040,{8088, 184, 0x02A9DC03,
184 2}, {8122, 16 0x02AAF802,
185 0, 1}, {8136, 0x02AD6C01,
186
187 {81868, 182
188 0x037FFC01,
189
190 {11363, 136, 1}, 0x03F7F002,
191 1506, 0, 1},*8028020F0E, 0x03F8C02 {42877, 94, 1},
192 {42893, 86, 1},
193 {42922, 80, 1},
194 {42925, 82, 1},
195 {42929, 840, 831, 68, 1},
196 {429325268, 65341, 65373,
197 65450,
198 65506,
199 8028020F606809F8C0B, 0x03600001,
200 03600001,
201 0x03ECA401,
202 0x03F88033,
203 0x03FC6807,
204 0x03FFE405,
205 0x0406400C,
206 0x040E7C01,
207 0x04247C01,
208 0x0428E003,
209 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
210 0x04450004, 0x04451402,
211 0x044B7C0C, 0x044C00sizeof(aEntry)/sizeof(aEntry[0] 0x0035E401,
212 0x00376008,
213 0x00391C09,
214 6, 0x003AEC02C80331, 0x00AF2835,
215 0x00B39406,
216 0x00B5F
217 0x00B4, 0x04473401, 0x0448B012, 0x044B7C0C,
218 0x044C0403, 0x044CF001, 0x044CF807, 0x044DC005, 0x0452C014,
219 8687, 8}, {8040,86, 8}, 186, 8}, {8104, 1868 {8136,86, 2},
220 {8154, 15{8170, 154, 2},
221 {8172, 18848, 2}, {81860, 112, 1},
222 {8491, 1140836, 1},
223 {11364, 1104, 1},
224 B5{11374, 1060, 1}, {11376, 1029846209, 84, 1},
225 {42930, 86},
226 {43888, 92 30204,
227 54793, 54809,
228 8028020F606809F020F606809F8C0B, 65268, 65341,
229 A34007, 0x07BBC002,
230
231 0x07C34425, 0x07C4401F, 0x04247C01,
232 0x0428E003,
233 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
234 0x04450004, 0x04451402,
235 0x044B7C0C, 0x044C00sizeof(aEntry)/sizeof(aEntry[0] 0x0035E401,
236 0x00376008,
237 0x00391C09,
238 6, 0x003AEC02C80331, 0x00AF2835,
239 0x00B39406,
240 0x00B5F
241 0x00BC00D6,
242 0x00C0D802,
243 0x00C64002,
244 0x00C94001,
245 0x01370040,
246 0x029A7802,
247 0x02A00801,
248 0x02A1D004,2A3E003,
249 0x02A57C01,
250 0x02A8A40E,
251 0x02A9EC03,
252 0x02AB0401,
253 0x02AF8C0B,
254 0x03EC7801, 0x03ECA
255 0x03F8001A,
256 0x03FC040F,
257 0x03FFA007,
258 0x04063003,
259 0x040DD805,
260 0x0421DC02,
261 0x04283004,
262 0x042B2001,
263 0x04349004,
264 0x0441FC04,
265 0x04450004,
266 0x04471409,*
267 *8028020F606809F8C0, 0x03600001,
268
269 0x044D2C03,
270 0x0450D412,
271 0x04531801,
272 0x0459800D,
273 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
274 0x0474FC07, 0x04751C01,
275 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010,
276 442E, 0x05BE3C04, 0x06F27008,
277
278 0x075B0401, 0x075B6C01,
279 0x075D3C01, 0x075DBC01,
280 0x0760028C, 0x076A6C05,
281 0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007,
282
283 0x07C0C064, 0x07C2800F,
284 0x07C4405C, 0x07C5C03D,
285 0x07C94A,
286 0x07DC0074, 0x07DE0059
287 0x07E18028,402F, 0x07E50031,
288 0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003,
289 0x07E74030, 0x07E9800E, 0x38000401, 0x38008060,){896, 3912, 3928,
290
291 4408, 4424, 447
292 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
293 36, 61880, 61914, 61948, 61998, 62122,
294 62154, 62200, 62218, 62302, 62364
295 62554,
296 62924char aChar[] = {
297 '\0', '/*
298 *8028'u', 'y', 'y', 'a', 'c',
299 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
300 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
301 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
302 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
303 '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
304 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
305 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
306 'e', 'i', 'o', 'u', return (
307 *8028020F606809F8C0B,09F8C0B, 0x0360b2058, 1},
308 {503, 1704627678, 1},
309 {981, 1828284, 1, 24},
310 {10081009, 174, 1}, {108846862, 1},
311 {7297,*8028020F606809F8C0B, 0x03600001,
312 sizeof(aDia)/sizeof(aDia[0]
313 0x040DD805,
314 0x0421DC02,
315 0x04283004,
316 0x042B2001,
317 0x04349004,
318 0x0441FC04,
319 0x04450004,
320 0x04471409,*
321 *8028020F606809F8C0, 0x03600001,
322
323 0x044D2C03,
324 0x0450D412,
325 0x04531801,
326 0x0459800D,
327 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
328 0x0474FC07, 0x04751C01,
329 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010,
330 442E, 0x05BE3C04, 0x06F27008,
331
332 0x075B0401, 0x075B6C01,
333 0x075D3C01, 0x075DBC01,
334 0x0760028C, 0x076A6C05,
335 0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007,
336
337 0x07C0C064, 0x07C2800F,
338 0x07C4405C, 0x07C5C03D,
339 0x07C94A,
340 0x07DC0074, 0x07DE0059
341 0x07E18028,402F, 0x07E50031,
342 0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003,
343 0x07E74030, 0x07E9800E, 0x38000401, 0x38008060,){896, 3912, 3928,
344
345 4408, 4424, 447
346 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
347 36, 61880, 61914, 61948, 61998, 62122,
348 62154, 62200, 62218, 62302, 62364
349 62554,
350 62924char aChar[] = {
351 '\0', '/*
352 *8028'u', 'y', 'y', 'a', 'c',
353 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
354 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
355 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
356 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
357 '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
358 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
359 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
360 ';e', 'i', 'o', 'u', return (
361 *8028020F606809F8C0B,09F8C0B, 0x0360b2058, 1},
362 {503, 1704627678, 1},
363 {981, 1828284, 1, 24},
364 {10081009, 174, 1}, {108846862, 1},
365 {7297,*8028020F606809F8C0B, 0x03600001,
366 0x03ECA401,
367 0x03F88033,
368 0x03FC6807,
369 0x03FFE405,
370 0x0406400C,
371 0x040E7C01,
372 0x04247C01,
373 0x0428E003,
374 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
375 0x04450004, 0x04451402,
376 0x044B7C0C, 0x044C0004,
377 0x044D2C03, 0x0600001,
378 0x03ECA402, 1324, 1},
379 {7304, 96, 1}, {7312, 138, 43}, {7357, 13867838, 1168660, 186, 6},
380 {7976, 18686, 8}, {8008, 18687, 8}, {8040,86, 8}, {8104, 18686, 2},
381 {8122,0, 1},
382 {8136,86, 2},
383 {8154, 15{8170, 154, 2},
384 {8172, 18848, 2}, {81860, 112, 1},
385 {8491, 1140836, 1},
386 {11364, 1104, 1},
387 {11374, 1060, 1}, {11376, 1029846209, 84, 1},
388 {42930, 86},
389 {43888, 92 30204,
390 54793, 54809,
391 8028020F606809F020F606809F8C0B, 65268, 65341,
392 65436, 65439,
393 65482, 65488,
394 bRemoveDiacritic ));count(aEntry
395 {42925, 82, 1},
396 sizeof(aEntry)/sizeof(aEntry[0] 0x0456E020,
397 7976, 18686, 8}, {8008,AAC0D, 0x045C740F, 0x045CF004,
398
399 0x05BD442E, 0x05BE3C04,
400 0x0744A4C0, 0x07480046,
401 0x075BEC01, 0x075C5401, 'm', 75E2401, 0x075EA401,05BE3C0076A840F, 0x07A340078},
402 {8120, 184, 2}, {8122, 160, 2}, {8124, 182, 1},
403 {8126, 120, 1}, {82B},
404 {8170,6, 2},
405 B{81868, 1827A, 0x07D5EC29, 0x07D6952C, 0x07DB800D,
406 0x07DBC004, 0x07DC0074,247C01,
407 7E1400A, 0x07E18028, 0x07E24,
408 0x044D2C03, 0x0600001,
409 0x03ECA402, 1324, 1},
410 {7304, 96, 1}, {7312, 138, 43}, {7357, 13867838, 1168660, 186, 6},
411 {7976, 18686, 8}, {8008, 18687, 8}, {8040,86, 8}, {8104, 18686, 2},
412 {8122,0, 1},
413 {8136,86, 2},
414 {8154, 15{8170, 154, 2},
415 {8172, 18848, 2}, {81860, 112, 1},
416 {8491, 1140836, 1},
417 {11364, 1104, 1},
418 {11374, 1060, 1}, {11376, 1029846209, 84, 1},
419 {42930, 86},
420 {43888, 92 30204,
421 54793, 54809,
422 8028020F606809F020F606809F8C0B, 65268, 65341,
423 65436, 65439,
424 65482, 65488,
425 bRemoveDiacritic ));/446, 1},
426 4, 8},
427 {7960, 184, 6},7976, 184, 8}, {7992, 184, 88008, 184, 6},{8025, 185, 8}, {8040,{8088, 184, 8}, {8104, 184, 8},
428 {8120, 184, 2}, {8122, 160, 2}, {8124, 182, 1},
429 {8126, 120, 1}, {8136, 1582, 1},
430 {8152,4, 2},
431 {8170,6, 2},
432 {81868, 182
433
434 {8544, 8, 16},
435 {11264, 24, 47},
436 {11363, 136, 1},
437 {11373, 104, 1},
438
439
440 {11506, 0, 1},*8028020F606606809F8C0B, 0x03600001,
441 {42877, 94, 1},
442 {42893, 86, 1},
443 {42922, 80, 1},
444 {42925, 82, 1},
445 {42929, 840, 831, 68, 1},
446 {429325268, 65341, 65373,
447 65450,
448 65506,
449 8028020F606809F8C0B, 0x03600001,
450 03600001,
451 0x03ECA401,
452 0x03F88033,
453 0x03FC6807,
454 0x03FFE405,
455 0x0406400C,
456 0x040E7C01,
457 0x04247C01,
458 0x0428E003,
459 0x042B9402 0x043D18 0x0441FC04, 0x0442C012,
460 0x04450004, 0x04451402,
461 0x044B7C0C, 0x044C00sizeof(aEntry)/sizeof(aEntry[0] 0x0035E401,
462 0x00376008,
463 0x00391C09,
464 6, 0x003AEC02C80331, 0x00AF2835,
465 0x00B39406,
466 0x00B5F
467 0x00BC00D6,
468 0x00C0D802,
469 0x00C64002,
470 0x00C94001,
471 0x01370040,
472 0x029A7802,
473 0x02A00801,
474 0x02A1D004,2A3E003,
475 0x02A57C01,
476 0x02A8A40E,
477 0x02A9EC03,
478 0x02AB0401,
479 0x02AF8C0B,
480 0x03EC7801, 0x03ECA
481 0x03F8001A,
482 0x03FC040F,
483 0x03FFA007,
484 0x04063003,
485 0x040DD805,
486 0x0421DC02,
487 0x04283004,
488 0x042B2001,
489 0x04349004,
490 0x0441FC04,
491 0x04450004,
492 0x04471409,*
493 *8028020F606809F8C0, 0x03600001,
494
495 0x044D2C03,
496 0x0450D412,
497 0x04531801,
498 0x0459800D,
499 0x0468040A, 0x0468CC07,46A7805, 0x0470BC08,4724816, 0x0472A40E,
500 0x0474FC07, 0x04751C01,
501 0x047BCC06, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010,
502 442E, 0x05BE3C04, 0x06F27008,
503
504 0x075B0401, 0x075B6C01,
505 0x075D3C01, 0x075DBC01,
506 0x0760028C, 0x076A6C05,
507 0x07806C07, 0x07808C02, 0x07809805, 0x07A34007, 0x07A51007,
508
509 0x07C0C064, 0x07C2800F,
510 0x07C4405C, 0x07C5C03D,
511 0x07C94A,
512 0x07DC0074, 0x07DE0059
513 0x07E18028,402F, 0x07E50031,
514 0x07E5CC04, 0x07E5E801, 0x07E5F027, 0x07E6C00A, 0x07E70003,
515 0x07E74030, 0x07E9800E, 0x38000401, 0x38008060,){896, 3912, 3928,
516
517 4408, 4424, 447
518 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
519 36, 61880, 61914, 61948, 61998, 62122,
520 62154, 62200, 62218, 62302, 62364
521 62554,
522 62924char aChar[] = {
523 '\0', '/*
524 *8028'u', 'y', 'y', 'a', 'c',
525 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
526 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
527 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
528 'u', 's', 't', 'h'
+10 -5
--- src/update.c
+++ src/update.c
@@ -715,14 +715,18 @@
715715
int vid;
716716
vid = db_lget_int("checkout", 0);
717717
vfile_check_signature(vid, 0);
718718
db_multi_exec(
719719
"DELETE FROM vmerge;"
720
- "INSERT INTO torevert "
721
- "SELECT pathname"
722
- " FROM vfile "
723
- " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
720
+ "INSERT OR IGNORE INTO torevert "
721
+ " SELECT pathname"
722
+ " FROM vfile "
723
+ " WHERE chnged OR deleted OR rid=0 OR pathname!=origname "
724
+ " UNION ALL "
725
+ " SELECT origname"
726
+ " FROM vfile"
727
+ " WHERE origname!=pathname;"
724728
);
725729
}
726730
blob_zero(&record);
727731
db_prepare(&q, "SELECT name FROM torevert");
728732
if( zRevision==0 ){
@@ -736,11 +740,12 @@
736740
zFile = db_column_text(&q, 0);
737741
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
738742
errCode = historical_version_of_file(zRevision, zFile, &record,
739743
&isLink, &isExe, 0, 2);
740744
if( errCode==2 ){
741
- if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
745
+ if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
746
+ zFile, zFile)==0 ){
742747
fossil_print("UNMANAGE: %s\n", zFile);
743748
}else{
744749
undo_save(zFile);
745750
file_delete(zFull);
746751
fossil_print("DELETE: %s\n", zFile);
747752
--- src/update.c
+++ src/update.c
@@ -715,14 +715,18 @@
715 int vid;
716 vid = db_lget_int("checkout", 0);
717 vfile_check_signature(vid, 0);
718 db_multi_exec(
719 "DELETE FROM vmerge;"
720 "INSERT INTO torevert "
721 "SELECT pathname"
722 " FROM vfile "
723 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
 
 
 
 
724 );
725 }
726 blob_zero(&record);
727 db_prepare(&q, "SELECT name FROM torevert");
728 if( zRevision==0 ){
@@ -736,11 +740,12 @@
736 zFile = db_column_text(&q, 0);
737 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
738 errCode = historical_version_of_file(zRevision, zFile, &record,
739 &isLink, &isExe, 0, 2);
740 if( errCode==2 ){
741 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
 
742 fossil_print("UNMANAGE: %s\n", zFile);
743 }else{
744 undo_save(zFile);
745 file_delete(zFull);
746 fossil_print("DELETE: %s\n", zFile);
747
--- src/update.c
+++ src/update.c
@@ -715,14 +715,18 @@
715 int vid;
716 vid = db_lget_int("checkout", 0);
717 vfile_check_signature(vid, 0);
718 db_multi_exec(
719 "DELETE FROM vmerge;"
720 "INSERT OR IGNORE INTO torevert "
721 " SELECT pathname"
722 " FROM vfile "
723 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname "
724 " UNION ALL "
725 " SELECT origname"
726 " FROM vfile"
727 " WHERE origname!=pathname;"
728 );
729 }
730 blob_zero(&record);
731 db_prepare(&q, "SELECT name FROM torevert");
732 if( zRevision==0 ){
@@ -736,11 +740,12 @@
740 zFile = db_column_text(&q, 0);
741 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
742 errCode = historical_version_of_file(zRevision, zFile, &record,
743 &isLink, &isExe, 0, 2);
744 if( errCode==2 ){
745 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
746 zFile, zFile)==0 ){
747 fossil_print("UNMANAGE: %s\n", zFile);
748 }else{
749 undo_save(zFile);
750 file_delete(zFull);
751 fossil_print("DELETE: %s\n", zFile);
752
+7 -3
--- src/user.c
+++ src/user.c
@@ -303,15 +303,17 @@
303303
**
304304
** (2) If the local database is open, check in VVAR.
305305
**
306306
** (3) Check the default user in the repository
307307
**
308
-** (4) Try the USER environment variable.
308
+** (4) Try the FOSSIL_USER environment variable.
309
+**
310
+** (5) Try the USER environment variable.
309311
**
310
-** (5) Try the USERNAME environment variable.
312
+** (6) Try the USERNAME environment variable.
311313
**
312
-** (6) Check if the user can be extracted from the remote URL.
314
+** (7) Check if the user can be extracted from the remote URL.
313315
**
314316
** The user name is stored in g.zLogin. The uid is in g.userUid.
315317
*/
316318
void user_select(void){
317319
char *zUrl;
@@ -326,10 +328,12 @@
326328
}
327329
328330
if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return;
329331
330332
if( attempt_user(db_get("default-user", 0)) ) return;
333
+
334
+ if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return;
331335
332336
if( attempt_user(fossil_getenv("USER")) ) return;
333337
334338
if( attempt_user(fossil_getenv("USERNAME")) ) return;
335339
336340
--- src/user.c
+++ src/user.c
@@ -303,15 +303,17 @@
303 **
304 ** (2) If the local database is open, check in VVAR.
305 **
306 ** (3) Check the default user in the repository
307 **
308 ** (4) Try the USER environment variable.
 
 
309 **
310 ** (5) Try the USERNAME environment variable.
311 **
312 ** (6) Check if the user can be extracted from the remote URL.
313 **
314 ** The user name is stored in g.zLogin. The uid is in g.userUid.
315 */
316 void user_select(void){
317 char *zUrl;
@@ -326,10 +328,12 @@
326 }
327
328 if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return;
329
330 if( attempt_user(db_get("default-user", 0)) ) return;
 
 
331
332 if( attempt_user(fossil_getenv("USER")) ) return;
333
334 if( attempt_user(fossil_getenv("USERNAME")) ) return;
335
336
--- src/user.c
+++ src/user.c
@@ -303,15 +303,17 @@
303 **
304 ** (2) If the local database is open, check in VVAR.
305 **
306 ** (3) Check the default user in the repository
307 **
308 ** (4) Try the FOSSIL_USER environment variable.
309 **
310 ** (5) Try the USER environment variable.
311 **
312 ** (6) Try the USERNAME environment variable.
313 **
314 ** (7) Check if the user can be extracted from the remote URL.
315 **
316 ** The user name is stored in g.zLogin. The uid is in g.userUid.
317 */
318 void user_select(void){
319 char *zUrl;
@@ -326,10 +328,12 @@
328 }
329
330 if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return;
331
332 if( attempt_user(db_get("default-user", 0)) ) return;
333
334 if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return;
335
336 if( attempt_user(fossil_getenv("USER")) ) return;
337
338 if( attempt_user(fossil_getenv("USERNAME")) ) return;
339
340
+1 -1
--- src/wiki.c
+++ src/wiki.c
@@ -667,11 +667,11 @@
667667
if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
668668
blob_init(&w2, pW2->zWiki, -1);
669669
}
670670
blob_zero(&d);
671671
diffFlags = construct_diff_flags(1,0);
672
- text_diff(&w2, &w1, &d, diffFlags | DIFF_HTML | DIFF_LINENO);
672
+ text_diff(&w2, &w1, &d, 0, diffFlags | DIFF_HTML | DIFF_LINENO);
673673
@ <div class="udiff">
674674
@ %s(blob_str(&d))
675675
@ </div>
676676
manifest_destroy(pW1);
677677
manifest_destroy(pW2);
678678
--- src/wiki.c
+++ src/wiki.c
@@ -667,11 +667,11 @@
667 if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
668 blob_init(&w2, pW2->zWiki, -1);
669 }
670 blob_zero(&d);
671 diffFlags = construct_diff_flags(1,0);
672 text_diff(&w2, &w1, &d, diffFlags | DIFF_HTML | DIFF_LINENO);
673 @ <div class="udiff">
674 @ %s(blob_str(&d))
675 @ </div>
676 manifest_destroy(pW1);
677 manifest_destroy(pW2);
678
--- src/wiki.c
+++ src/wiki.c
@@ -667,11 +667,11 @@
667 if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
668 blob_init(&w2, pW2->zWiki, -1);
669 }
670 blob_zero(&d);
671 diffFlags = construct_diff_flags(1,0);
672 text_diff(&w2, &w1, &d, 0, diffFlags | DIFF_HTML | DIFF_LINENO);
673 @ <div class="udiff">
674 @ %s(blob_str(&d))
675 @ </div>
676 manifest_destroy(pW1);
677 manifest_destroy(pW2);
678
--- test/diff-test-1.wiki
+++ test/diff-test-1.wiki
@@ -18,10 +18,33 @@
1818
* <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6"
1919
target="testwindow">Large diff of sqlite3.c</a>. This diff was very
2020
slow prior to the preformance enhancement change [9e15437e97].
2121
* <a href="../../../info/bda00cbada#chunk42" target="testwindow">
2222
A difficult indentation change.
23
+ * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13"
24
+ target="testwindow">Another tricky indentation.</a> Notice especially
25
+ lines 59398 and 59407 on the left.
26
+ * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13"
27
+ target="testwindow">Inverse of the previous.</a>
28
+ * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk24"
29
+ target="testwindow">A complex change</a> that is difficult to align, and
30
+ hence falls back to the "delete left and insert right" strategy.
31
+ * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk24"
32
+ target="testwindow">Inverse of the previous.</a>
33
+ * <a href="../../../fdiff?v1=21f9a00fe2fa4a17&v2=d5c4ff0532bd89c3#chunk5"
34
+ target="testwindow">sqlite3.c changes</a>
35
+ that are difficult to align.
36
+ * <a href="../../../fdiff?v2=21f9a00fe2fa4a17&v1=d5c4ff0532bd89c3#chunk5"
37
+ target="testwindow">sqlite3.c changes inverted.</a>
2338
2439
External:
2540
2641
* <a href="http://www.sqlite.org/src/fdiff?v1=aafcb21a74e41f9a&v2=a6d127dd05daf0f9#chunk3" target="testwindow">
2742
Code indentation change.</a>
43
+ * <a href="http://www.sqlite.org/src/info/52e755943f" target="testwindow">
44
+ A complex change (chunk 1) in which the alignment becomes so complex
45
+ that it is better for clarity to abandon it and just show the left
46
+ and right sides contiguously.</a>
47
+ * <a href="http://www.sqlite.org/src/info/3d65c70343#chunk5"
48
+ target="testwindow">
49
+ An indentation change. See especially lines 2313 and 2317 on the right,
50
+ that their green indentation addition is left-justified.</a>
2851
--- test/diff-test-1.wiki
+++ test/diff-test-1.wiki
@@ -18,10 +18,33 @@
18 * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6"
19 target="testwindow">Large diff of sqlite3.c</a>. This diff was very
20 slow prior to the preformance enhancement change [9e15437e97].
21 * <a href="../../../info/bda00cbada#chunk42" target="testwindow">
22 A difficult indentation change.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 External:
25
26 * <a href="http://www.sqlite.org/src/fdiff?v1=aafcb21a74e41f9a&v2=a6d127dd05daf0f9#chunk3" target="testwindow">
27 Code indentation change.</a>
 
 
 
 
 
 
 
 
28
--- test/diff-test-1.wiki
+++ test/diff-test-1.wiki
@@ -18,10 +18,33 @@
18 * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6"
19 target="testwindow">Large diff of sqlite3.c</a>. This diff was very
20 slow prior to the preformance enhancement change [9e15437e97].
21 * <a href="../../../info/bda00cbada#chunk42" target="testwindow">
22 A difficult indentation change.
23 * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13"
24 target="testwindow">Another tricky indentation.</a> Notice especially
25 lines 59398 and 59407 on the left.
26 * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13"
27 target="testwindow">Inverse of the previous.</a>
28 * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk24"
29 target="testwindow">A complex change</a> that is difficult to align, and
30 hence falls back to the "delete left and insert right" strategy.
31 * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk24"
32 target="testwindow">Inverse of the previous.</a>
33 * <a href="../../../fdiff?v1=21f9a00fe2fa4a17&v2=d5c4ff0532bd89c3#chunk5"
34 target="testwindow">sqlite3.c changes</a>
35 that are difficult to align.
36 * <a href="../../../fdiff?v2=21f9a00fe2fa4a17&v1=d5c4ff0532bd89c3#chunk5"
37 target="testwindow">sqlite3.c changes inverted.</a>
38
39 External:
40
41 * <a href="http://www.sqlite.org/src/fdiff?v1=aafcb21a74e41f9a&v2=a6d127dd05daf0f9#chunk3" target="testwindow">
42 Code indentation change.</a>
43 * <a href="http://www.sqlite.org/src/info/52e755943f" target="testwindow">
44 A complex change (chunk 1) in which the alignment becomes so complex
45 that it is better for clarity to abandon it and just show the left
46 and right sides contiguously.</a>
47 * <a href="http://www.sqlite.org/src/info/3d65c70343#chunk5"
48 target="testwindow">
49 An indentation change. See especially lines 2313 and 2317 on the right,
50 that their green indentation addition is left-justified.</a>
51
+16 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -26,13 +26,13 @@
2626
TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
2727
LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
2828
2929
SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
3030
31
-SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c utf8_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
31
+SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
3232
33
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
33
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
3434
3535
3636
RC=$(DMDIR)\bin\rcc
3737
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
3838
@@ -46,11 +46,11 @@
4646
4747
$(OBJDIR)\fossil.res: $B\win\fossil.rc
4848
$(RC) $(RCFLAGS) -o$@ $**
4949
5050
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
51
- +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo 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_tag json_timeline json_user json_wiki leaf login main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user utf8 verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
51
+ +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo 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_tag json_timeline json_user json_wiki leaf login 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 verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
5252
+echo fossil >> $@
5353
+echo fossil >> $@
5454
+echo $(LIBS) >> $@
5555
+echo. >> $@
5656
+echo fossil >> $@
@@ -523,10 +523,16 @@
523523
$(OBJDIR)\rebuild$O : rebuild_.c rebuild.h
524524
$(TCC) -o$@ -c rebuild_.c
525525
526526
rebuild_.c : $(SRCDIR)\rebuild.c
527527
+translate$E $** > $@
528
+
529
+$(OBJDIR)\regexp$O : regexp_.c regexp.h
530
+ $(TCC) -o$@ -c regexp_.c
531
+
532
+regexp_.c : $(SRCDIR)\regexp.c
533
+ +translate$E $** > $@
528534
529535
$(OBJDIR)\report$O : report_.c report.h
530536
$(TCC) -o$@ -c report_.c
531537
532538
report_.c : $(SRCDIR)\report.c
@@ -643,10 +649,16 @@
643649
$(OBJDIR)\undo$O : undo_.c undo.h
644650
$(TCC) -o$@ -c undo_.c
645651
646652
undo_.c : $(SRCDIR)\undo.c
647653
+translate$E $** > $@
654
+
655
+$(OBJDIR)\unicode$O : unicode_.c unicode.h
656
+ $(TCC) -o$@ -c unicode_.c
657
+
658
+unicode_.c : $(SRCDIR)\unicode.c
659
+ +translate$E $** > $@
648660
649661
$(OBJDIR)\update$O : update_.c update.h
650662
$(TCC) -o$@ -c update_.c
651663
652664
update_.c : $(SRCDIR)\update.c
@@ -723,7 +735,7 @@
723735
724736
zip_.c : $(SRCDIR)\zip.c
725737
+translate$E $** > $@
726738
727739
headers: makeheaders$E page_index.h VERSION.h
728
- +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
740
+ +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
729741
@copy /Y nul: headers
730742
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -26,13 +26,13 @@
26 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
27 LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
28
29 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
30
31 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c utf8_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
32
33 OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
34
35
36 RC=$(DMDIR)\bin\rcc
37 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
38
@@ -46,11 +46,11 @@
46
47 $(OBJDIR)\fossil.res: $B\win\fossil.rc
48 $(RC) $(RCFLAGS) -o$@ $**
49
50 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
51 +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo 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_tag json_timeline json_user json_wiki leaf login main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user utf8 verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
52 +echo fossil >> $@
53 +echo fossil >> $@
54 +echo $(LIBS) >> $@
55 +echo. >> $@
56 +echo fossil >> $@
@@ -523,10 +523,16 @@
523 $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h
524 $(TCC) -o$@ -c rebuild_.c
525
526 rebuild_.c : $(SRCDIR)\rebuild.c
527 +translate$E $** > $@
 
 
 
 
 
 
528
529 $(OBJDIR)\report$O : report_.c report.h
530 $(TCC) -o$@ -c report_.c
531
532 report_.c : $(SRCDIR)\report.c
@@ -643,10 +649,16 @@
643 $(OBJDIR)\undo$O : undo_.c undo.h
644 $(TCC) -o$@ -c undo_.c
645
646 undo_.c : $(SRCDIR)\undo.c
647 +translate$E $** > $@
 
 
 
 
 
 
648
649 $(OBJDIR)\update$O : update_.c update.h
650 $(TCC) -o$@ -c update_.c
651
652 update_.c : $(SRCDIR)\update.c
@@ -723,7 +735,7 @@
723
724 zip_.c : $(SRCDIR)\zip.c
725 +translate$E $** > $@
726
727 headers: makeheaders$E page_index.h VERSION.h
728 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
729 @copy /Y nul: headers
730
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -26,13 +26,13 @@
26 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
27 LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
28
29 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
30
31 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
32
33 OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
34
35
36 RC=$(DMDIR)\bin\rcc
37 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
38
@@ -46,11 +46,11 @@
46
47 $(OBJDIR)\fossil.res: $B\win\fossil.rc
48 $(RC) $(RCFLAGS) -o$@ $**
49
50 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
51 +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo 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_tag json_timeline json_user json_wiki leaf login 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 verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
52 +echo fossil >> $@
53 +echo fossil >> $@
54 +echo $(LIBS) >> $@
55 +echo. >> $@
56 +echo fossil >> $@
@@ -523,10 +523,16 @@
523 $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h
524 $(TCC) -o$@ -c rebuild_.c
525
526 rebuild_.c : $(SRCDIR)\rebuild.c
527 +translate$E $** > $@
528
529 $(OBJDIR)\regexp$O : regexp_.c regexp.h
530 $(TCC) -o$@ -c regexp_.c
531
532 regexp_.c : $(SRCDIR)\regexp.c
533 +translate$E $** > $@
534
535 $(OBJDIR)\report$O : report_.c report.h
536 $(TCC) -o$@ -c report_.c
537
538 report_.c : $(SRCDIR)\report.c
@@ -643,10 +649,16 @@
649 $(OBJDIR)\undo$O : undo_.c undo.h
650 $(TCC) -o$@ -c undo_.c
651
652 undo_.c : $(SRCDIR)\undo.c
653 +translate$E $** > $@
654
655 $(OBJDIR)\unicode$O : unicode_.c unicode.h
656 $(TCC) -o$@ -c unicode_.c
657
658 unicode_.c : $(SRCDIR)\unicode.c
659 +translate$E $** > $@
660
661 $(OBJDIR)\update$O : update_.c update.h
662 $(TCC) -o$@ -c update_.c
663
664 update_.c : $(SRCDIR)\update.c
@@ -723,7 +735,7 @@
735
736 zip_.c : $(SRCDIR)\zip.c
737 +translate$E $** > $@
738
739 headers: makeheaders$E page_index.h VERSION.h
740 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
741 @copy /Y nul: headers
742
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -322,10 +322,11 @@
322322
$(SRCDIR)/pivot.c \
323323
$(SRCDIR)/popen.c \
324324
$(SRCDIR)/pqueue.c \
325325
$(SRCDIR)/printf.c \
326326
$(SRCDIR)/rebuild.c \
327
+ $(SRCDIR)/regexp.c \
327328
$(SRCDIR)/report.c \
328329
$(SRCDIR)/rss.c \
329330
$(SRCDIR)/schema.c \
330331
$(SRCDIR)/search.c \
331332
$(SRCDIR)/setup.c \
@@ -342,10 +343,11 @@
342343
$(SRCDIR)/th_main.c \
343344
$(SRCDIR)/timeline.c \
344345
$(SRCDIR)/tkt.c \
345346
$(SRCDIR)/tktsetup.c \
346347
$(SRCDIR)/undo.c \
348
+ $(SRCDIR)/unicode.c \
347349
$(SRCDIR)/update.c \
348350
$(SRCDIR)/url.c \
349351
$(SRCDIR)/user.c \
350352
$(SRCDIR)/utf8.c \
351353
$(SRCDIR)/verify.c \
@@ -426,10 +428,11 @@
426428
$(OBJDIR)/pivot_.c \
427429
$(OBJDIR)/popen_.c \
428430
$(OBJDIR)/pqueue_.c \
429431
$(OBJDIR)/printf_.c \
430432
$(OBJDIR)/rebuild_.c \
433
+ $(OBJDIR)/regexp_.c \
431434
$(OBJDIR)/report_.c \
432435
$(OBJDIR)/rss_.c \
433436
$(OBJDIR)/schema_.c \
434437
$(OBJDIR)/search_.c \
435438
$(OBJDIR)/setup_.c \
@@ -446,10 +449,11 @@
446449
$(OBJDIR)/th_main_.c \
447450
$(OBJDIR)/timeline_.c \
448451
$(OBJDIR)/tkt_.c \
449452
$(OBJDIR)/tktsetup_.c \
450453
$(OBJDIR)/undo_.c \
454
+ $(OBJDIR)/unicode_.c \
451455
$(OBJDIR)/update_.c \
452456
$(OBJDIR)/url_.c \
453457
$(OBJDIR)/user_.c \
454458
$(OBJDIR)/utf8_.c \
455459
$(OBJDIR)/verify_.c \
@@ -530,10 +534,11 @@
530534
$(OBJDIR)/pivot.o \
531535
$(OBJDIR)/popen.o \
532536
$(OBJDIR)/pqueue.o \
533537
$(OBJDIR)/printf.o \
534538
$(OBJDIR)/rebuild.o \
539
+ $(OBJDIR)/regexp.o \
535540
$(OBJDIR)/report.o \
536541
$(OBJDIR)/rss.o \
537542
$(OBJDIR)/schema.o \
538543
$(OBJDIR)/search.o \
539544
$(OBJDIR)/setup.o \
@@ -550,10 +555,11 @@
550555
$(OBJDIR)/th_main.o \
551556
$(OBJDIR)/timeline.o \
552557
$(OBJDIR)/tkt.o \
553558
$(OBJDIR)/tktsetup.o \
554559
$(OBJDIR)/undo.o \
560
+ $(OBJDIR)/unicode.o \
555561
$(OBJDIR)/update.o \
556562
$(OBJDIR)/url.o \
557563
$(OBJDIR)/user.o \
558564
$(OBJDIR)/utf8.o \
559565
$(OBJDIR)/verify.o \
@@ -747,10 +753,11 @@
747753
$(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
748754
$(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
749755
$(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
750756
$(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
751757
$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
758
+ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
752759
$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
753760
$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
754761
$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
755762
$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
756763
$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -767,10 +774,11 @@
767774
$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
768775
$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
769776
$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
770777
$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
771778
$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
779
+ $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
772780
$(OBJDIR)/update_.c:$(OBJDIR)/update.h \
773781
$(OBJDIR)/url_.c:$(OBJDIR)/url.h \
774782
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
775783
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
776784
$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
@@ -1340,10 +1348,18 @@
13401348
13411349
$(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
13421350
$(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
13431351
13441352
$(OBJDIR)/rebuild.h: $(OBJDIR)/headers
1353
+
1354
+$(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate
1355
+ $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
1356
+
1357
+$(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h
1358
+ $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
1359
+
1360
+$(OBJDIR)/regexp.h: $(OBJDIR)/headers
13451361
13461362
$(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
13471363
$(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
13481364
13491365
$(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
@@ -1500,10 +1516,18 @@
15001516
15011517
$(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
15021518
$(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
15031519
15041520
$(OBJDIR)/undo.h: $(OBJDIR)/headers
1521
+
1522
+$(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate
1523
+ $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
1524
+
1525
+$(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1526
+ $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1527
+
1528
+$(OBJDIR)/unicode.h: $(OBJDIR)/headers
15051529
15061530
$(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
15071531
$(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
15081532
15091533
$(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
15101534
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -322,10 +322,11 @@
322 $(SRCDIR)/pivot.c \
323 $(SRCDIR)/popen.c \
324 $(SRCDIR)/pqueue.c \
325 $(SRCDIR)/printf.c \
326 $(SRCDIR)/rebuild.c \
 
327 $(SRCDIR)/report.c \
328 $(SRCDIR)/rss.c \
329 $(SRCDIR)/schema.c \
330 $(SRCDIR)/search.c \
331 $(SRCDIR)/setup.c \
@@ -342,10 +343,11 @@
342 $(SRCDIR)/th_main.c \
343 $(SRCDIR)/timeline.c \
344 $(SRCDIR)/tkt.c \
345 $(SRCDIR)/tktsetup.c \
346 $(SRCDIR)/undo.c \
 
347 $(SRCDIR)/update.c \
348 $(SRCDIR)/url.c \
349 $(SRCDIR)/user.c \
350 $(SRCDIR)/utf8.c \
351 $(SRCDIR)/verify.c \
@@ -426,10 +428,11 @@
426 $(OBJDIR)/pivot_.c \
427 $(OBJDIR)/popen_.c \
428 $(OBJDIR)/pqueue_.c \
429 $(OBJDIR)/printf_.c \
430 $(OBJDIR)/rebuild_.c \
 
431 $(OBJDIR)/report_.c \
432 $(OBJDIR)/rss_.c \
433 $(OBJDIR)/schema_.c \
434 $(OBJDIR)/search_.c \
435 $(OBJDIR)/setup_.c \
@@ -446,10 +449,11 @@
446 $(OBJDIR)/th_main_.c \
447 $(OBJDIR)/timeline_.c \
448 $(OBJDIR)/tkt_.c \
449 $(OBJDIR)/tktsetup_.c \
450 $(OBJDIR)/undo_.c \
 
451 $(OBJDIR)/update_.c \
452 $(OBJDIR)/url_.c \
453 $(OBJDIR)/user_.c \
454 $(OBJDIR)/utf8_.c \
455 $(OBJDIR)/verify_.c \
@@ -530,10 +534,11 @@
530 $(OBJDIR)/pivot.o \
531 $(OBJDIR)/popen.o \
532 $(OBJDIR)/pqueue.o \
533 $(OBJDIR)/printf.o \
534 $(OBJDIR)/rebuild.o \
 
535 $(OBJDIR)/report.o \
536 $(OBJDIR)/rss.o \
537 $(OBJDIR)/schema.o \
538 $(OBJDIR)/search.o \
539 $(OBJDIR)/setup.o \
@@ -550,10 +555,11 @@
550 $(OBJDIR)/th_main.o \
551 $(OBJDIR)/timeline.o \
552 $(OBJDIR)/tkt.o \
553 $(OBJDIR)/tktsetup.o \
554 $(OBJDIR)/undo.o \
 
555 $(OBJDIR)/update.o \
556 $(OBJDIR)/url.o \
557 $(OBJDIR)/user.o \
558 $(OBJDIR)/utf8.o \
559 $(OBJDIR)/verify.o \
@@ -747,10 +753,11 @@
747 $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
748 $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
749 $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
750 $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
751 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
 
752 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
753 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
754 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
755 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
756 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -767,10 +774,11 @@
767 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
768 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
769 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
770 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
771 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
 
772 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
773 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
774 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
775 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
776 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
@@ -1340,10 +1348,18 @@
1340
1341 $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
1342 $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
1343
1344 $(OBJDIR)/rebuild.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1345
1346 $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
1347 $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
1348
1349 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
@@ -1500,10 +1516,18 @@
1500
1501 $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
1502 $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
1503
1504 $(OBJDIR)/undo.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1505
1506 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1507 $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
1508
1509 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1510
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -322,10 +322,11 @@
322 $(SRCDIR)/pivot.c \
323 $(SRCDIR)/popen.c \
324 $(SRCDIR)/pqueue.c \
325 $(SRCDIR)/printf.c \
326 $(SRCDIR)/rebuild.c \
327 $(SRCDIR)/regexp.c \
328 $(SRCDIR)/report.c \
329 $(SRCDIR)/rss.c \
330 $(SRCDIR)/schema.c \
331 $(SRCDIR)/search.c \
332 $(SRCDIR)/setup.c \
@@ -342,10 +343,11 @@
343 $(SRCDIR)/th_main.c \
344 $(SRCDIR)/timeline.c \
345 $(SRCDIR)/tkt.c \
346 $(SRCDIR)/tktsetup.c \
347 $(SRCDIR)/undo.c \
348 $(SRCDIR)/unicode.c \
349 $(SRCDIR)/update.c \
350 $(SRCDIR)/url.c \
351 $(SRCDIR)/user.c \
352 $(SRCDIR)/utf8.c \
353 $(SRCDIR)/verify.c \
@@ -426,10 +428,11 @@
428 $(OBJDIR)/pivot_.c \
429 $(OBJDIR)/popen_.c \
430 $(OBJDIR)/pqueue_.c \
431 $(OBJDIR)/printf_.c \
432 $(OBJDIR)/rebuild_.c \
433 $(OBJDIR)/regexp_.c \
434 $(OBJDIR)/report_.c \
435 $(OBJDIR)/rss_.c \
436 $(OBJDIR)/schema_.c \
437 $(OBJDIR)/search_.c \
438 $(OBJDIR)/setup_.c \
@@ -446,10 +449,11 @@
449 $(OBJDIR)/th_main_.c \
450 $(OBJDIR)/timeline_.c \
451 $(OBJDIR)/tkt_.c \
452 $(OBJDIR)/tktsetup_.c \
453 $(OBJDIR)/undo_.c \
454 $(OBJDIR)/unicode_.c \
455 $(OBJDIR)/update_.c \
456 $(OBJDIR)/url_.c \
457 $(OBJDIR)/user_.c \
458 $(OBJDIR)/utf8_.c \
459 $(OBJDIR)/verify_.c \
@@ -530,10 +534,11 @@
534 $(OBJDIR)/pivot.o \
535 $(OBJDIR)/popen.o \
536 $(OBJDIR)/pqueue.o \
537 $(OBJDIR)/printf.o \
538 $(OBJDIR)/rebuild.o \
539 $(OBJDIR)/regexp.o \
540 $(OBJDIR)/report.o \
541 $(OBJDIR)/rss.o \
542 $(OBJDIR)/schema.o \
543 $(OBJDIR)/search.o \
544 $(OBJDIR)/setup.o \
@@ -550,10 +555,11 @@
555 $(OBJDIR)/th_main.o \
556 $(OBJDIR)/timeline.o \
557 $(OBJDIR)/tkt.o \
558 $(OBJDIR)/tktsetup.o \
559 $(OBJDIR)/undo.o \
560 $(OBJDIR)/unicode.o \
561 $(OBJDIR)/update.o \
562 $(OBJDIR)/url.o \
563 $(OBJDIR)/user.o \
564 $(OBJDIR)/utf8.o \
565 $(OBJDIR)/verify.o \
@@ -747,10 +753,11 @@
753 $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
754 $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
755 $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
756 $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
757 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
758 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
759 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
760 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
761 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
762 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
763 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -767,10 +774,11 @@
774 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
775 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
776 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
777 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
778 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
779 $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
780 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
781 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
782 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
783 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
784 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
@@ -1340,10 +1348,18 @@
1348
1349 $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
1350 $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
1351
1352 $(OBJDIR)/rebuild.h: $(OBJDIR)/headers
1353
1354 $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate
1355 $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
1356
1357 $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h
1358 $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
1359
1360 $(OBJDIR)/regexp.h: $(OBJDIR)/headers
1361
1362 $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
1363 $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
1364
1365 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
@@ -1500,10 +1516,18 @@
1516
1517 $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
1518 $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
1519
1520 $(OBJDIR)/undo.h: $(OBJDIR)/headers
1521
1522 $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate
1523 $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
1524
1525 $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1526 $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1527
1528 $(OBJDIR)/unicode.h: $(OBJDIR)/headers
1529
1530 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1531 $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
1532
1533 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1534
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -322,10 +322,11 @@
322322
$(SRCDIR)/pivot.c \
323323
$(SRCDIR)/popen.c \
324324
$(SRCDIR)/pqueue.c \
325325
$(SRCDIR)/printf.c \
326326
$(SRCDIR)/rebuild.c \
327
+ $(SRCDIR)/regexp.c \
327328
$(SRCDIR)/report.c \
328329
$(SRCDIR)/rss.c \
329330
$(SRCDIR)/schema.c \
330331
$(SRCDIR)/search.c \
331332
$(SRCDIR)/setup.c \
@@ -342,10 +343,11 @@
342343
$(SRCDIR)/th_main.c \
343344
$(SRCDIR)/timeline.c \
344345
$(SRCDIR)/tkt.c \
345346
$(SRCDIR)/tktsetup.c \
346347
$(SRCDIR)/undo.c \
348
+ $(SRCDIR)/unicode.c \
347349
$(SRCDIR)/update.c \
348350
$(SRCDIR)/url.c \
349351
$(SRCDIR)/user.c \
350352
$(SRCDIR)/utf8.c \
351353
$(SRCDIR)/verify.c \
@@ -426,10 +428,11 @@
426428
$(OBJDIR)/pivot_.c \
427429
$(OBJDIR)/popen_.c \
428430
$(OBJDIR)/pqueue_.c \
429431
$(OBJDIR)/printf_.c \
430432
$(OBJDIR)/rebuild_.c \
433
+ $(OBJDIR)/regexp_.c \
431434
$(OBJDIR)/report_.c \
432435
$(OBJDIR)/rss_.c \
433436
$(OBJDIR)/schema_.c \
434437
$(OBJDIR)/search_.c \
435438
$(OBJDIR)/setup_.c \
@@ -446,10 +449,11 @@
446449
$(OBJDIR)/th_main_.c \
447450
$(OBJDIR)/timeline_.c \
448451
$(OBJDIR)/tkt_.c \
449452
$(OBJDIR)/tktsetup_.c \
450453
$(OBJDIR)/undo_.c \
454
+ $(OBJDIR)/unicode_.c \
451455
$(OBJDIR)/update_.c \
452456
$(OBJDIR)/url_.c \
453457
$(OBJDIR)/user_.c \
454458
$(OBJDIR)/utf8_.c \
455459
$(OBJDIR)/verify_.c \
@@ -530,10 +534,11 @@
530534
$(OBJDIR)/pivot.o \
531535
$(OBJDIR)/popen.o \
532536
$(OBJDIR)/pqueue.o \
533537
$(OBJDIR)/printf.o \
534538
$(OBJDIR)/rebuild.o \
539
+ $(OBJDIR)/regexp.o \
535540
$(OBJDIR)/report.o \
536541
$(OBJDIR)/rss.o \
537542
$(OBJDIR)/schema.o \
538543
$(OBJDIR)/search.o \
539544
$(OBJDIR)/setup.o \
@@ -550,10 +555,11 @@
550555
$(OBJDIR)/th_main.o \
551556
$(OBJDIR)/timeline.o \
552557
$(OBJDIR)/tkt.o \
553558
$(OBJDIR)/tktsetup.o \
554559
$(OBJDIR)/undo.o \
560
+ $(OBJDIR)/unicode.o \
555561
$(OBJDIR)/update.o \
556562
$(OBJDIR)/url.o \
557563
$(OBJDIR)/user.o \
558564
$(OBJDIR)/utf8.o \
559565
$(OBJDIR)/verify.o \
@@ -747,10 +753,11 @@
747753
$(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
748754
$(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
749755
$(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
750756
$(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
751757
$(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
758
+ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
752759
$(OBJDIR)/report_.c:$(OBJDIR)/report.h \
753760
$(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
754761
$(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
755762
$(OBJDIR)/search_.c:$(OBJDIR)/search.h \
756763
$(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -767,10 +774,11 @@
767774
$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
768775
$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
769776
$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
770777
$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
771778
$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
779
+ $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
772780
$(OBJDIR)/update_.c:$(OBJDIR)/update.h \
773781
$(OBJDIR)/url_.c:$(OBJDIR)/url.h \
774782
$(OBJDIR)/user_.c:$(OBJDIR)/user.h \
775783
$(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
776784
$(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
@@ -1340,10 +1348,18 @@
13401348
13411349
$(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
13421350
$(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
13431351
13441352
$(OBJDIR)/rebuild.h: $(OBJDIR)/headers
1353
+
1354
+$(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate
1355
+ $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
1356
+
1357
+$(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h
1358
+ $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
1359
+
1360
+$(OBJDIR)/regexp.h: $(OBJDIR)/headers
13451361
13461362
$(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
13471363
$(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
13481364
13491365
$(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
@@ -1500,10 +1516,18 @@
15001516
15011517
$(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
15021518
$(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
15031519
15041520
$(OBJDIR)/undo.h: $(OBJDIR)/headers
1521
+
1522
+$(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate
1523
+ $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
1524
+
1525
+$(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1526
+ $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1527
+
1528
+$(OBJDIR)/unicode.h: $(OBJDIR)/headers
15051529
15061530
$(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
15071531
$(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
15081532
15091533
$(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
15101534
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -322,10 +322,11 @@
322 $(SRCDIR)/pivot.c \
323 $(SRCDIR)/popen.c \
324 $(SRCDIR)/pqueue.c \
325 $(SRCDIR)/printf.c \
326 $(SRCDIR)/rebuild.c \
 
327 $(SRCDIR)/report.c \
328 $(SRCDIR)/rss.c \
329 $(SRCDIR)/schema.c \
330 $(SRCDIR)/search.c \
331 $(SRCDIR)/setup.c \
@@ -342,10 +343,11 @@
342 $(SRCDIR)/th_main.c \
343 $(SRCDIR)/timeline.c \
344 $(SRCDIR)/tkt.c \
345 $(SRCDIR)/tktsetup.c \
346 $(SRCDIR)/undo.c \
 
347 $(SRCDIR)/update.c \
348 $(SRCDIR)/url.c \
349 $(SRCDIR)/user.c \
350 $(SRCDIR)/utf8.c \
351 $(SRCDIR)/verify.c \
@@ -426,10 +428,11 @@
426 $(OBJDIR)/pivot_.c \
427 $(OBJDIR)/popen_.c \
428 $(OBJDIR)/pqueue_.c \
429 $(OBJDIR)/printf_.c \
430 $(OBJDIR)/rebuild_.c \
 
431 $(OBJDIR)/report_.c \
432 $(OBJDIR)/rss_.c \
433 $(OBJDIR)/schema_.c \
434 $(OBJDIR)/search_.c \
435 $(OBJDIR)/setup_.c \
@@ -446,10 +449,11 @@
446 $(OBJDIR)/th_main_.c \
447 $(OBJDIR)/timeline_.c \
448 $(OBJDIR)/tkt_.c \
449 $(OBJDIR)/tktsetup_.c \
450 $(OBJDIR)/undo_.c \
 
451 $(OBJDIR)/update_.c \
452 $(OBJDIR)/url_.c \
453 $(OBJDIR)/user_.c \
454 $(OBJDIR)/utf8_.c \
455 $(OBJDIR)/verify_.c \
@@ -530,10 +534,11 @@
530 $(OBJDIR)/pivot.o \
531 $(OBJDIR)/popen.o \
532 $(OBJDIR)/pqueue.o \
533 $(OBJDIR)/printf.o \
534 $(OBJDIR)/rebuild.o \
 
535 $(OBJDIR)/report.o \
536 $(OBJDIR)/rss.o \
537 $(OBJDIR)/schema.o \
538 $(OBJDIR)/search.o \
539 $(OBJDIR)/setup.o \
@@ -550,10 +555,11 @@
550 $(OBJDIR)/th_main.o \
551 $(OBJDIR)/timeline.o \
552 $(OBJDIR)/tkt.o \
553 $(OBJDIR)/tktsetup.o \
554 $(OBJDIR)/undo.o \
 
555 $(OBJDIR)/update.o \
556 $(OBJDIR)/url.o \
557 $(OBJDIR)/user.o \
558 $(OBJDIR)/utf8.o \
559 $(OBJDIR)/verify.o \
@@ -747,10 +753,11 @@
747 $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
748 $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
749 $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
750 $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
751 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
 
752 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
753 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
754 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
755 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
756 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -767,10 +774,11 @@
767 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
768 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
769 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
770 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
771 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
 
772 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
773 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
774 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
775 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
776 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
@@ -1340,10 +1348,18 @@
1340
1341 $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
1342 $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
1343
1344 $(OBJDIR)/rebuild.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1345
1346 $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
1347 $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
1348
1349 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
@@ -1500,10 +1516,18 @@
1500
1501 $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
1502 $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
1503
1504 $(OBJDIR)/undo.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1505
1506 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1507 $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
1508
1509 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1510
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -322,10 +322,11 @@
322 $(SRCDIR)/pivot.c \
323 $(SRCDIR)/popen.c \
324 $(SRCDIR)/pqueue.c \
325 $(SRCDIR)/printf.c \
326 $(SRCDIR)/rebuild.c \
327 $(SRCDIR)/regexp.c \
328 $(SRCDIR)/report.c \
329 $(SRCDIR)/rss.c \
330 $(SRCDIR)/schema.c \
331 $(SRCDIR)/search.c \
332 $(SRCDIR)/setup.c \
@@ -342,10 +343,11 @@
343 $(SRCDIR)/th_main.c \
344 $(SRCDIR)/timeline.c \
345 $(SRCDIR)/tkt.c \
346 $(SRCDIR)/tktsetup.c \
347 $(SRCDIR)/undo.c \
348 $(SRCDIR)/unicode.c \
349 $(SRCDIR)/update.c \
350 $(SRCDIR)/url.c \
351 $(SRCDIR)/user.c \
352 $(SRCDIR)/utf8.c \
353 $(SRCDIR)/verify.c \
@@ -426,10 +428,11 @@
428 $(OBJDIR)/pivot_.c \
429 $(OBJDIR)/popen_.c \
430 $(OBJDIR)/pqueue_.c \
431 $(OBJDIR)/printf_.c \
432 $(OBJDIR)/rebuild_.c \
433 $(OBJDIR)/regexp_.c \
434 $(OBJDIR)/report_.c \
435 $(OBJDIR)/rss_.c \
436 $(OBJDIR)/schema_.c \
437 $(OBJDIR)/search_.c \
438 $(OBJDIR)/setup_.c \
@@ -446,10 +449,11 @@
449 $(OBJDIR)/th_main_.c \
450 $(OBJDIR)/timeline_.c \
451 $(OBJDIR)/tkt_.c \
452 $(OBJDIR)/tktsetup_.c \
453 $(OBJDIR)/undo_.c \
454 $(OBJDIR)/unicode_.c \
455 $(OBJDIR)/update_.c \
456 $(OBJDIR)/url_.c \
457 $(OBJDIR)/user_.c \
458 $(OBJDIR)/utf8_.c \
459 $(OBJDIR)/verify_.c \
@@ -530,10 +534,11 @@
534 $(OBJDIR)/pivot.o \
535 $(OBJDIR)/popen.o \
536 $(OBJDIR)/pqueue.o \
537 $(OBJDIR)/printf.o \
538 $(OBJDIR)/rebuild.o \
539 $(OBJDIR)/regexp.o \
540 $(OBJDIR)/report.o \
541 $(OBJDIR)/rss.o \
542 $(OBJDIR)/schema.o \
543 $(OBJDIR)/search.o \
544 $(OBJDIR)/setup.o \
@@ -550,10 +555,11 @@
555 $(OBJDIR)/th_main.o \
556 $(OBJDIR)/timeline.o \
557 $(OBJDIR)/tkt.o \
558 $(OBJDIR)/tktsetup.o \
559 $(OBJDIR)/undo.o \
560 $(OBJDIR)/unicode.o \
561 $(OBJDIR)/update.o \
562 $(OBJDIR)/url.o \
563 $(OBJDIR)/user.o \
564 $(OBJDIR)/utf8.o \
565 $(OBJDIR)/verify.o \
@@ -747,10 +753,11 @@
753 $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \
754 $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \
755 $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \
756 $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \
757 $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \
758 $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \
759 $(OBJDIR)/report_.c:$(OBJDIR)/report.h \
760 $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \
761 $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \
762 $(OBJDIR)/search_.c:$(OBJDIR)/search.h \
763 $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \
@@ -767,10 +774,11 @@
774 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
775 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
776 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
777 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
778 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
779 $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
780 $(OBJDIR)/update_.c:$(OBJDIR)/update.h \
781 $(OBJDIR)/url_.c:$(OBJDIR)/url.h \
782 $(OBJDIR)/user_.c:$(OBJDIR)/user.h \
783 $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \
784 $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \
@@ -1340,10 +1348,18 @@
1348
1349 $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h
1350 $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c
1351
1352 $(OBJDIR)/rebuild.h: $(OBJDIR)/headers
1353
1354 $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate
1355 $(TRANSLATE) $(SRCDIR)/regexp.c >$(OBJDIR)/regexp_.c
1356
1357 $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h
1358 $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c
1359
1360 $(OBJDIR)/regexp.h: $(OBJDIR)/headers
1361
1362 $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate
1363 $(TRANSLATE) $(SRCDIR)/report.c >$(OBJDIR)/report_.c
1364
1365 $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h
@@ -1500,10 +1516,18 @@
1516
1517 $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h
1518 $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c
1519
1520 $(OBJDIR)/undo.h: $(OBJDIR)/headers
1521
1522 $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate
1523 $(TRANSLATE) $(SRCDIR)/unicode.c >$(OBJDIR)/unicode_.c
1524
1525 $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h
1526 $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c
1527
1528 $(OBJDIR)/unicode.h: $(OBJDIR)/headers
1529
1530 $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate
1531 $(TRANSLATE) $(SRCDIR)/update.c >$(OBJDIR)/update_.c
1532
1533 $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h
1534
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -107,10 +107,11 @@
107107
pivot_.c \
108108
popen_.c \
109109
pqueue_.c \
110110
printf_.c \
111111
rebuild_.c \
112
+ regexp_.c \
112113
report_.c \
113114
rss_.c \
114115
schema_.c \
115116
search_.c \
116117
setup_.c \
@@ -127,10 +128,11 @@
127128
th_main_.c \
128129
timeline_.c \
129130
tkt_.c \
130131
tktsetup_.c \
131132
undo_.c \
133
+ unicode_.c \
132134
update_.c \
133135
url_.c \
134136
user_.c \
135137
utf8_.c \
136138
verify_.c \
@@ -210,10 +212,11 @@
210212
$(OX)\pivot$O \
211213
$(OX)\popen$O \
212214
$(OX)\pqueue$O \
213215
$(OX)\printf$O \
214216
$(OX)\rebuild$O \
217
+ $(OX)\regexp$O \
215218
$(OX)\report$O \
216219
$(OX)\rss$O \
217220
$(OX)\schema$O \
218221
$(OX)\search$O \
219222
$(OX)\setup$O \
@@ -230,10 +233,11 @@
230233
$(OX)\th_main$O \
231234
$(OX)\timeline$O \
232235
$(OX)\tkt$O \
233236
$(OX)\tktsetup$O \
234237
$(OX)\undo$O \
238
+ $(OX)\unicode$O \
235239
$(OX)\update$O \
236240
$(OX)\url$O \
237241
$(OX)\user$O \
238242
$(OX)\utf8$O \
239243
$(OX)\verify$O \
@@ -330,10 +334,11 @@
330334
echo $(OX)\pivot.obj >> $@
331335
echo $(OX)\popen.obj >> $@
332336
echo $(OX)\pqueue.obj >> $@
333337
echo $(OX)\printf.obj >> $@
334338
echo $(OX)\rebuild.obj >> $@
339
+ echo $(OX)\regexp.obj >> $@
335340
echo $(OX)\report.obj >> $@
336341
echo $(OX)\rss.obj >> $@
337342
echo $(OX)\schema.obj >> $@
338343
echo $(OX)\search.obj >> $@
339344
echo $(OX)\setup.obj >> $@
@@ -354,10 +359,11 @@
354359
echo $(OX)\th_main.obj >> $@
355360
echo $(OX)\timeline.obj >> $@
356361
echo $(OX)\tkt.obj >> $@
357362
echo $(OX)\tktsetup.obj >> $@
358363
echo $(OX)\undo.obj >> $@
364
+ echo $(OX)\unicode.obj >> $@
359365
echo $(OX)\update.obj >> $@
360366
echo $(OX)\url.obj >> $@
361367
echo $(OX)\user.obj >> $@
362368
echo $(OX)\utf8.obj >> $@
363369
echo $(OX)\verify.obj >> $@
@@ -853,10 +859,16 @@
853859
$(OX)\rebuild$O : rebuild_.c rebuild.h
854860
$(TCC) /Fo$@ -c rebuild_.c
855861
856862
rebuild_.c : $(SRCDIR)\rebuild.c
857863
translate$E $** > $@
864
+
865
+$(OX)\regexp$O : regexp_.c regexp.h
866
+ $(TCC) /Fo$@ -c regexp_.c
867
+
868
+regexp_.c : $(SRCDIR)\regexp.c
869
+ translate$E $** > $@
858870
859871
$(OX)\report$O : report_.c report.h
860872
$(TCC) /Fo$@ -c report_.c
861873
862874
report_.c : $(SRCDIR)\report.c
@@ -973,10 +985,16 @@
973985
$(OX)\undo$O : undo_.c undo.h
974986
$(TCC) /Fo$@ -c undo_.c
975987
976988
undo_.c : $(SRCDIR)\undo.c
977989
translate$E $** > $@
990
+
991
+$(OX)\unicode$O : unicode_.c unicode.h
992
+ $(TCC) /Fo$@ -c unicode_.c
993
+
994
+unicode_.c : $(SRCDIR)\unicode.c
995
+ translate$E $** > $@
978996
979997
$(OX)\update$O : update_.c update.h
980998
$(TCC) /Fo$@ -c update_.c
981999
9821000
update_.c : $(SRCDIR)\update.c
@@ -1122,10 +1140,11 @@
11221140
pivot_.c:pivot.h \
11231141
popen_.c:popen.h \
11241142
pqueue_.c:pqueue.h \
11251143
printf_.c:printf.h \
11261144
rebuild_.c:rebuild.h \
1145
+ regexp_.c:regexp.h \
11271146
report_.c:report.h \
11281147
rss_.c:rss.h \
11291148
schema_.c:schema.h \
11301149
search_.c:search.h \
11311150
setup_.c:setup.h \
@@ -1142,10 +1161,11 @@
11421161
th_main_.c:th_main.h \
11431162
timeline_.c:timeline.h \
11441163
tkt_.c:tkt.h \
11451164
tktsetup_.c:tktsetup.h \
11461165
undo_.c:undo.h \
1166
+ unicode_.c:unicode.h \
11471167
update_.c:update.h \
11481168
url_.c:url.h \
11491169
user_.c:user.h \
11501170
utf8_.c:utf8.h \
11511171
verify_.c:verify.h \
11521172
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -107,10 +107,11 @@
107 pivot_.c \
108 popen_.c \
109 pqueue_.c \
110 printf_.c \
111 rebuild_.c \
 
112 report_.c \
113 rss_.c \
114 schema_.c \
115 search_.c \
116 setup_.c \
@@ -127,10 +128,11 @@
127 th_main_.c \
128 timeline_.c \
129 tkt_.c \
130 tktsetup_.c \
131 undo_.c \
 
132 update_.c \
133 url_.c \
134 user_.c \
135 utf8_.c \
136 verify_.c \
@@ -210,10 +212,11 @@
210 $(OX)\pivot$O \
211 $(OX)\popen$O \
212 $(OX)\pqueue$O \
213 $(OX)\printf$O \
214 $(OX)\rebuild$O \
 
215 $(OX)\report$O \
216 $(OX)\rss$O \
217 $(OX)\schema$O \
218 $(OX)\search$O \
219 $(OX)\setup$O \
@@ -230,10 +233,11 @@
230 $(OX)\th_main$O \
231 $(OX)\timeline$O \
232 $(OX)\tkt$O \
233 $(OX)\tktsetup$O \
234 $(OX)\undo$O \
 
235 $(OX)\update$O \
236 $(OX)\url$O \
237 $(OX)\user$O \
238 $(OX)\utf8$O \
239 $(OX)\verify$O \
@@ -330,10 +334,11 @@
330 echo $(OX)\pivot.obj >> $@
331 echo $(OX)\popen.obj >> $@
332 echo $(OX)\pqueue.obj >> $@
333 echo $(OX)\printf.obj >> $@
334 echo $(OX)\rebuild.obj >> $@
 
335 echo $(OX)\report.obj >> $@
336 echo $(OX)\rss.obj >> $@
337 echo $(OX)\schema.obj >> $@
338 echo $(OX)\search.obj >> $@
339 echo $(OX)\setup.obj >> $@
@@ -354,10 +359,11 @@
354 echo $(OX)\th_main.obj >> $@
355 echo $(OX)\timeline.obj >> $@
356 echo $(OX)\tkt.obj >> $@
357 echo $(OX)\tktsetup.obj >> $@
358 echo $(OX)\undo.obj >> $@
 
359 echo $(OX)\update.obj >> $@
360 echo $(OX)\url.obj >> $@
361 echo $(OX)\user.obj >> $@
362 echo $(OX)\utf8.obj >> $@
363 echo $(OX)\verify.obj >> $@
@@ -853,10 +859,16 @@
853 $(OX)\rebuild$O : rebuild_.c rebuild.h
854 $(TCC) /Fo$@ -c rebuild_.c
855
856 rebuild_.c : $(SRCDIR)\rebuild.c
857 translate$E $** > $@
 
 
 
 
 
 
858
859 $(OX)\report$O : report_.c report.h
860 $(TCC) /Fo$@ -c report_.c
861
862 report_.c : $(SRCDIR)\report.c
@@ -973,10 +985,16 @@
973 $(OX)\undo$O : undo_.c undo.h
974 $(TCC) /Fo$@ -c undo_.c
975
976 undo_.c : $(SRCDIR)\undo.c
977 translate$E $** > $@
 
 
 
 
 
 
978
979 $(OX)\update$O : update_.c update.h
980 $(TCC) /Fo$@ -c update_.c
981
982 update_.c : $(SRCDIR)\update.c
@@ -1122,10 +1140,11 @@
1122 pivot_.c:pivot.h \
1123 popen_.c:popen.h \
1124 pqueue_.c:pqueue.h \
1125 printf_.c:printf.h \
1126 rebuild_.c:rebuild.h \
 
1127 report_.c:report.h \
1128 rss_.c:rss.h \
1129 schema_.c:schema.h \
1130 search_.c:search.h \
1131 setup_.c:setup.h \
@@ -1142,10 +1161,11 @@
1142 th_main_.c:th_main.h \
1143 timeline_.c:timeline.h \
1144 tkt_.c:tkt.h \
1145 tktsetup_.c:tktsetup.h \
1146 undo_.c:undo.h \
 
1147 update_.c:update.h \
1148 url_.c:url.h \
1149 user_.c:user.h \
1150 utf8_.c:utf8.h \
1151 verify_.c:verify.h \
1152
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -107,10 +107,11 @@
107 pivot_.c \
108 popen_.c \
109 pqueue_.c \
110 printf_.c \
111 rebuild_.c \
112 regexp_.c \
113 report_.c \
114 rss_.c \
115 schema_.c \
116 search_.c \
117 setup_.c \
@@ -127,10 +128,11 @@
128 th_main_.c \
129 timeline_.c \
130 tkt_.c \
131 tktsetup_.c \
132 undo_.c \
133 unicode_.c \
134 update_.c \
135 url_.c \
136 user_.c \
137 utf8_.c \
138 verify_.c \
@@ -210,10 +212,11 @@
212 $(OX)\pivot$O \
213 $(OX)\popen$O \
214 $(OX)\pqueue$O \
215 $(OX)\printf$O \
216 $(OX)\rebuild$O \
217 $(OX)\regexp$O \
218 $(OX)\report$O \
219 $(OX)\rss$O \
220 $(OX)\schema$O \
221 $(OX)\search$O \
222 $(OX)\setup$O \
@@ -230,10 +233,11 @@
233 $(OX)\th_main$O \
234 $(OX)\timeline$O \
235 $(OX)\tkt$O \
236 $(OX)\tktsetup$O \
237 $(OX)\undo$O \
238 $(OX)\unicode$O \
239 $(OX)\update$O \
240 $(OX)\url$O \
241 $(OX)\user$O \
242 $(OX)\utf8$O \
243 $(OX)\verify$O \
@@ -330,10 +334,11 @@
334 echo $(OX)\pivot.obj >> $@
335 echo $(OX)\popen.obj >> $@
336 echo $(OX)\pqueue.obj >> $@
337 echo $(OX)\printf.obj >> $@
338 echo $(OX)\rebuild.obj >> $@
339 echo $(OX)\regexp.obj >> $@
340 echo $(OX)\report.obj >> $@
341 echo $(OX)\rss.obj >> $@
342 echo $(OX)\schema.obj >> $@
343 echo $(OX)\search.obj >> $@
344 echo $(OX)\setup.obj >> $@
@@ -354,10 +359,11 @@
359 echo $(OX)\th_main.obj >> $@
360 echo $(OX)\timeline.obj >> $@
361 echo $(OX)\tkt.obj >> $@
362 echo $(OX)\tktsetup.obj >> $@
363 echo $(OX)\undo.obj >> $@
364 echo $(OX)\unicode.obj >> $@
365 echo $(OX)\update.obj >> $@
366 echo $(OX)\url.obj >> $@
367 echo $(OX)\user.obj >> $@
368 echo $(OX)\utf8.obj >> $@
369 echo $(OX)\verify.obj >> $@
@@ -853,10 +859,16 @@
859 $(OX)\rebuild$O : rebuild_.c rebuild.h
860 $(TCC) /Fo$@ -c rebuild_.c
861
862 rebuild_.c : $(SRCDIR)\rebuild.c
863 translate$E $** > $@
864
865 $(OX)\regexp$O : regexp_.c regexp.h
866 $(TCC) /Fo$@ -c regexp_.c
867
868 regexp_.c : $(SRCDIR)\regexp.c
869 translate$E $** > $@
870
871 $(OX)\report$O : report_.c report.h
872 $(TCC) /Fo$@ -c report_.c
873
874 report_.c : $(SRCDIR)\report.c
@@ -973,10 +985,16 @@
985 $(OX)\undo$O : undo_.c undo.h
986 $(TCC) /Fo$@ -c undo_.c
987
988 undo_.c : $(SRCDIR)\undo.c
989 translate$E $** > $@
990
991 $(OX)\unicode$O : unicode_.c unicode.h
992 $(TCC) /Fo$@ -c unicode_.c
993
994 unicode_.c : $(SRCDIR)\unicode.c
995 translate$E $** > $@
996
997 $(OX)\update$O : update_.c update.h
998 $(TCC) /Fo$@ -c update_.c
999
1000 update_.c : $(SRCDIR)\update.c
@@ -1122,10 +1140,11 @@
1140 pivot_.c:pivot.h \
1141 popen_.c:popen.h \
1142 pqueue_.c:pqueue.h \
1143 printf_.c:printf.h \
1144 rebuild_.c:rebuild.h \
1145 regexp_.c:regexp.h \
1146 report_.c:report.h \
1147 rss_.c:rss.h \
1148 schema_.c:schema.h \
1149 search_.c:search.h \
1150 setup_.c:setup.h \
@@ -1142,10 +1161,11 @@
1161 th_main_.c:th_main.h \
1162 timeline_.c:timeline.h \
1163 tkt_.c:tkt.h \
1164 tktsetup_.c:tktsetup.h \
1165 undo_.c:undo.h \
1166 unicode_.c:unicode.h \
1167 update_.c:update.h \
1168 url_.c:url.h \
1169 user_.c:user.h \
1170 utf8_.c:utf8.h \
1171 verify_.c:verify.h \
1172
--- win/fossil.rc
+++ win/fossil.rc
@@ -104,10 +104,13 @@
104104
#endif
105105
#endif
106106
#ifdef FOSSIL_ENABLE_JSON
107107
VALUE "JsonEnabled", "Yes, cson\0"
108108
#endif
109
+#ifdef FOSSIL_ENABLE_MARKDOWN
110
+ VALUE "MarkdownEnabled", "Yes\0"
111
+#endif
109112
END
110113
END
111114
BLOCK "VarFileInfo"
112115
BEGIN
113116
VALUE "Translation", 0x409, 0x4B0
114117
--- win/fossil.rc
+++ win/fossil.rc
@@ -104,10 +104,13 @@
104 #endif
105 #endif
106 #ifdef FOSSIL_ENABLE_JSON
107 VALUE "JsonEnabled", "Yes, cson\0"
108 #endif
 
 
 
109 END
110 END
111 BLOCK "VarFileInfo"
112 BEGIN
113 VALUE "Translation", 0x409, 0x4B0
114
--- win/fossil.rc
+++ win/fossil.rc
@@ -104,10 +104,13 @@
104 #endif
105 #endif
106 #ifdef FOSSIL_ENABLE_JSON
107 VALUE "JsonEnabled", "Yes, cson\0"
108 #endif
109 #ifdef FOSSIL_ENABLE_MARKDOWN
110 VALUE "MarkdownEnabled", "Yes\0"
111 #endif
112 END
113 END
114 BLOCK "VarFileInfo"
115 BEGIN
116 VALUE "Translation", 0x409, 0x4B0
117
+398 -204
--- win/include/dirent.h
+++ win/include/dirent.h
@@ -20,21 +20,24 @@
2020
* IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
2121
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2222
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2323
* OTHER DEALINGS IN THE SOFTWARE.
2424
*
25
+ *
26
+ * Version 1.13, Dec 12 2012, Toni Ronkko
27
+ * Use traditional 8+3 file name if the name cannot be represented in the
28
+ * default ANSI code page. Now compiles again with MSVC 6.0. Thanks to
29
+ * Konstantin Khomoutov for testing.
2530
*
2631
* Version 1.12.1, Oct 1 2012, Toni Ronkko
2732
* Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with
2833
* capital W) in order to maintain compatibility with MingW.
2934
*
3035
* Version 1.12, Sep 30 2012, Toni Ronkko
31
- * Define PATH_MAX and NAME_MAX.
32
- *
33
- * Added wide-character variants _wDIR, _wdirent, _wopendir(),
34
- * _wreaddir(), _wclosedir() and _wrewinddir(). Thanks to Edgar Buerkle
35
- * and Jan Nijtmans for ideas and code.
36
+ * Define PATH_MAX and NAME_MAX. Added wide-character variants _wDIR,
37
+ * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir().
38
+ * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code.
3639
*
3740
* Do not include windows.h. This allows dirent.h to be integrated more
3841
* easily into programs using winsock. Thanks to Fernando Azaldegui.
3942
*
4043
* Version 1.11, Mar 15, 2011, Toni Ronkko
@@ -88,48 +91,72 @@
8891
*****************************************************************************/
8992
#ifndef DIRENT_H
9093
#define DIRENT_H
9194
9295
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
93
-#define _X86_
96
+# define _X86_
9497
#endif
9598
#include <stdio.h>
9699
#include <stdarg.h>
97100
#include <windef.h>
98101
#include <winbase.h>
99102
#include <wchar.h>
100
-#include <winnls.h>
101103
#include <string.h>
102104
#include <stdlib.h>
105
+#include <malloc.h>
103106
#include <sys/types.h>
104107
#include <sys/stat.h>
105108
#include <errno.h>
106109
107
-/* Windows 8 wide-character string functions */
108
-#if (_WIN32_WINNT >= 0x0602)
109
-# include <stringapiset.h>
110
-#endif
110
+/* Indicates that d_type field is available in dirent structure */
111
+#define _DIRENT_HAVE_D_TYPE
112
+
113
+/* Indicates that d_namlen field is available in dirent structure */
114
+#define _DIRENT_HAVE_D_NAMLEN
111115
112116
/* Entries missing from MSVC 6.0 */
113117
#if !defined(FILE_ATTRIBUTE_DEVICE)
114
-# define FILE_ATTRIBUTE_DEVICE 0x40
118
+# define FILE_ATTRIBUTE_DEVICE 0x40
115119
#endif
116120
117121
/* File type and permission flags for stat() */
118
-#if defined(_MSC_VER) && !defined(S_IREAD)
122
+#if !defined(S_IFMT)
119123
# define S_IFMT _S_IFMT /* File type mask */
124
+#endif
125
+#if !defined(S_IFDIR)
120126
# define S_IFDIR _S_IFDIR /* Directory */
127
+#endif
128
+#if !defined(S_IFCHR)
121129
# define S_IFCHR _S_IFCHR /* Character device */
130
+#endif
131
+#if !defined(S_IFFIFO)
122132
# define S_IFFIFO _S_IFFIFO /* Pipe */
133
+#endif
134
+#if !defined(S_IFREG)
123135
# define S_IFREG _S_IFREG /* Regular file */
136
+#endif
137
+#if !defined(S_IREAD)
124138
# define S_IREAD _S_IREAD /* Read permission */
139
+#endif
140
+#if !defined(S_IWRITE)
125141
# define S_IWRITE _S_IWRITE /* Write permission */
142
+#endif
143
+#if !defined(S_IEXEC)
126144
# define S_IEXEC _S_IEXEC /* Execute permission */
127145
#endif
128
-#define S_IFBLK 0 /* Block device */
129
-#define S_IFLNK 0 /* Link */
130
-#define S_IFSOCK 0 /* Socket */
146
+#if !defined(S_IFIFO)
147
+# define S_IFIFO _S_IFIFO /* Pipe */
148
+#endif
149
+#if !defined(S_IFBLK)
150
+# define S_IFBLK 0 /* Block device */
151
+#endif
152
+#if !defined(S_IFLNK)
153
+# define S_IFLNK 0 /* Link */
154
+#endif
155
+#if !defined(S_IFSOCK)
156
+# define S_IFSOCK 0 /* Socket */
157
+#endif
131158
132159
#if defined(_MSC_VER)
133160
# define S_IRUSR S_IREAD /* Read user */
134161
# define S_IWUSR S_IWRITE /* Write user */
135162
# define S_IXUSR 0 /* Execute user */
@@ -139,18 +166,26 @@
139166
# define S_IROTH 0 /* Read others */
140167
# define S_IWOTH 0 /* Write others */
141168
# define S_IXOTH 0 /* Execute others */
142169
#endif
143170
144
-/* Indicates that d_type field is available in dirent structure */
145
-#define _DIRENT_HAVE_D_TYPE
171
+/* Maximum length of file name */
172
+#if !defined(PATH_MAX)
173
+# define PATH_MAX MAX_PATH
174
+#endif
175
+#if !defined(FILENAME_MAX)
176
+# define FILENAME_MAX MAX_PATH
177
+#endif
178
+#if !defined(NAME_MAX)
179
+# define NAME_MAX FILENAME_MAX
180
+#endif
146181
147182
/* File type flags for d_type */
148183
#define DT_UNKNOWN 0
149184
#define DT_REG S_IFREG
150185
#define DT_DIR S_IFDIR
151
-#define DT_FIFO S_IFFIFO
186
+#define DT_FIFO S_IFIFO
152187
#define DT_SOCK S_IFSOCK
153188
#define DT_CHR S_IFCHR
154189
#define DT_BLK S_IFBLK
155190
156191
/* Macros for converting between st_mode and d_type */
@@ -161,41 +196,31 @@
161196
* File type macros. Note that block devices, sockets and links cannot be
162197
* distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
163198
* only defined for compatibility. These macros should always return false
164199
* on Windows.
165200
*/
166
-#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO)
201
+#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
167202
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
168203
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
169204
#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
170205
#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
171206
#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
172207
#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
173208
174
-/* For compatiblity with Unix */
175
-#if !defined(PATH_MAX)
176
-# define PATH_MAX MAX_PATH
177
-#endif
178
-#if !defined(FILENAME_MAX)
179
-# define FILENAME_MAX MAX_PATH
180
-#endif
181
-#if !defined(NAME_MAX)
182
-# define NAME_MAX FILENAME_MAX
183
-#endif
184
-
185
-/* Set errno variable */
186
-#if defined(_MSC_VER)
187
-#define DIRENT_SET_ERRNO(x) _set_errno (x)
188
-#else
189
-#define DIRENT_SET_ERRNO(x) (errno = (x))
190
-#endif
209
+/* Return the exact length of d_namlen without zero terminator */
210
+#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
211
+
212
+/* Return number of bytes needed to store d_namlen */
213
+#define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1)
214
+
191215
192216
#ifdef __cplusplus
193217
extern "C" {
194218
#endif
195219
196
-/* Wide-character versions */
220
+
221
+/* Wide-character version */
197222
struct _wdirent {
198223
long d_ino; /* Always zero */
199224
unsigned short d_reclen; /* Structure size */
200225
size_t d_namlen; /* Length of name without \0 */
201226
int d_type; /* File type */
@@ -203,11 +228,11 @@
203228
};
204229
typedef struct _wdirent _wdirent;
205230
206231
struct _WDIR {
207232
struct _wdirent ent; /* Current directory entry */
208
- WIN32_FIND_DATAW find_data; /* Private file data */
233
+ WIN32_FIND_DATAW data; /* Private file data */
209234
int cached; /* True if data is valid */
210235
HANDLE handle; /* Win32 search handle */
211236
wchar_t *patt; /* Initial directory name */
212237
};
213238
typedef struct _WDIR _WDIR;
@@ -214,10 +239,11 @@
214239
215240
static _WDIR *_wopendir (const wchar_t *dirname);
216241
static struct _wdirent *_wreaddir (_WDIR *dirp);
217242
static int _wclosedir (_WDIR *dirp);
218243
static void _wrewinddir (_WDIR* dirp);
244
+
219245
220246
/* For compatibility with Symbian */
221247
#define wdirent _wdirent
222248
#define WDIR _WDIR
223249
#define wopendir _wopendir
@@ -246,10 +272,30 @@
246272
static struct dirent *readdir (DIR *dirp);
247273
static int closedir (DIR *dirp);
248274
static void rewinddir (DIR* dirp);
249275
250276
277
+/* Internal utility functions */
278
+static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
279
+static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
280
+
281
+static int dirent_mbstowcs_s(
282
+ size_t *pReturnValue,
283
+ wchar_t *wcstr,
284
+ size_t sizeInWords,
285
+ const char *mbstr,
286
+ size_t count);
287
+
288
+static int dirent_wcstombs_s(
289
+ size_t *pReturnValue,
290
+ char *mbstr,
291
+ size_t sizeInBytes,
292
+ const wchar_t *wcstr,
293
+ size_t count);
294
+
295
+static void dirent_set_errno (int error);
296
+
251297
/*
252298
* Open directory stream DIRNAME for read and return a pointer to the
253299
* internal working area that is used to retrieve individual directory
254300
* entries.
255301
*/
@@ -256,31 +302,38 @@
256302
static _WDIR*
257303
_wopendir(
258304
const wchar_t *dirname)
259305
{
260306
_WDIR *dirp = NULL;
261
- int error = 0;
307
+ int error;
308
+
309
+ /* Must have directory name */
310
+ if (dirname == NULL || dirname[0] == '\0') {
311
+ dirent_set_errno (ENOENT);
312
+ return NULL;
313
+ }
262314
263315
/* Allocate new _WDIR structure */
264316
dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
265317
if (dirp != NULL) {
266318
DWORD n;
267319
268320
/* Reset _WDIR structure */
269321
dirp->handle = INVALID_HANDLE_VALUE;
270322
dirp->patt = NULL;
323
+ dirp->cached = 0;
271324
272325
/* Compute the length of full path plus zero terminator */
273326
n = GetFullPathNameW (dirname, 0, NULL, NULL);
274327
275
- /* Allocate room for full path and search patterns */
328
+ /* Allocate room for absolute directory name and search pattern */
276329
dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
277330
if (dirp->patt) {
278331
279332
/*
280333
* Convert relative directory name to an absolute one. This
281
- * allows rewinddir() to function correctly when the current
334
+ * allows rewinddir() to function correctly even when current
282335
* working directory is changed between opendir() and rewinddir().
283336
*/
284337
n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
285338
if (n > 0) {
286339
wchar_t *p;
@@ -303,25 +356,22 @@
303356
}
304357
*p++ = '*';
305358
*p = '\0';
306359
307360
/* Open directory stream and retrieve the first entry */
308
- dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data);
309
- if (dirp->handle != INVALID_HANDLE_VALUE) {
310
-
311
- /* Directory entry is now waiting in memory */
312
- dirp->cached = 1;
313
-
361
+ if (dirent_first (dirp)) {
362
+ /* Directory stream opened successfully */
363
+ error = 0;
314364
} else {
315
- /* Search pattern is not a directory name? */
316
- DIRENT_SET_ERRNO (ENOENT);
365
+ /* Cannot retrieve first entry */
317366
error = 1;
367
+ dirent_set_errno (ENOENT);
318368
}
319369
320370
} else {
321
- /* Cannot convert directory name to wide character string */
322
- DIRENT_SET_ERRNO (ENOENT);
371
+ /* Cannot retrieve full path name */
372
+ dirent_set_errno (ENOENT);
323373
error = 1;
324374
}
325375
326376
} else {
327377
/* Cannot allocate memory for search pattern */
@@ -350,70 +400,59 @@
350400
*/
351401
static struct _wdirent*
352402
_wreaddir(
353403
_WDIR *dirp)
354404
{
355
- DWORD attr;
356
- errno_t error;
357
-
358
- /* Get next directory entry */
359
- if (dirp->cached != 0) {
360
- /* A valid directory entry already in memory */
361
- dirp->cached = 0;
362
- } else {
363
- /* Get the next directory entry from stream */
364
- if (dirp->handle == INVALID_HANDLE_VALUE) {
365
- return NULL;
366
- }
367
- if (FindNextFileW (dirp->handle, &dirp->find_data) == FALSE) {
368
- /* The very last entry has been processed or an error occured */
369
- FindClose (dirp->handle);
370
- dirp->handle = INVALID_HANDLE_VALUE;
371
- return NULL;
372
- }
373
- }
374
-
375
- /* Copy file name as a wide-character string */
376
- error = wcsncpy_s(
377
- dirp->ent.d_name, /* Destination string */
378
- PATH_MAX, /* Size of dest in words */
379
- dirp->find_data.cFileName, /* Source string */
380
- PATH_MAX + 1); /* Max # of chars to copy */
381
- if (!error) {
382
-
383
- /* Compute the length of name */
384
- dirp->ent.d_namlen = wcsnlen (dirp->ent.d_name, PATH_MAX);
385
-
386
- /* Determine file type */
387
- attr = dirp->find_data.dwFileAttributes;
405
+ WIN32_FIND_DATAW *datap;
406
+ struct _wdirent *entp;
407
+
408
+ /* Read next directory entry */
409
+ datap = dirent_next (dirp);
410
+ if (datap) {
411
+ size_t n;
412
+ DWORD attr;
413
+
414
+ /* Pointer to directory entry to return */
415
+ entp = &dirp->ent;
416
+
417
+ /*
418
+ * Copy file name as wide-character string. If the file name is too
419
+ * long to fit in to the destination buffer, then truncate file name
420
+ * to PATH_MAX characters and zero-terminate the buffer.
421
+ */
422
+ n = 0;
423
+ while (n < PATH_MAX && datap->cFileName[n] != 0) {
424
+ entp->d_name[n] = datap->cFileName[n];
425
+ n++;
426
+ }
427
+ dirp->ent.d_name[n] = 0;
428
+
429
+ /* Length of file name excluding zero terminator */
430
+ entp->d_namlen = n;
431
+
432
+ /* File type */
433
+ attr = datap->dwFileAttributes;
388434
if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
389
- dirp->ent.d_type = DT_CHR;
435
+ entp->d_type = DT_CHR;
390436
} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
391
- dirp->ent.d_type = DT_DIR;
437
+ entp->d_type = DT_DIR;
392438
} else {
393
- dirp->ent.d_type = DT_REG;
439
+ entp->d_type = DT_REG;
394440
}
395441
396442
/* Reset dummy fields */
397
- dirp->ent.d_ino = 0;
398
- dirp->ent.d_reclen = sizeof (dirp->ent);
443
+ entp->d_ino = 0;
444
+ entp->d_reclen = sizeof (struct _wdirent);
399445
400446
} else {
401447
402
- /*
403
- * Cannot copy file name from find_data to ent. Construct a
404
- * dummy _wdirent structure to pass error to caller.
405
- */
406
- dirp->ent.d_name[0] = '?';
407
- dirp->ent.d_name[1] = '\0';
408
- dirp->ent.d_namlen = 1;
409
- dirp->ent.d_type = DT_UNKNOWN;
410
- dirp->ent.d_ino = 0;
411
- dirp->ent.d_reclen = 0;
448
+ /* Last directory entry read */
449
+ entp = NULL;
450
+
412451
}
413452
414
- return &dirp->ent;
453
+ return entp;
415454
}
416455
417456
/*
418457
* Close directory stream opened by opendir() function. This invalidates the
419458
* DIR structure as well as any directory entry read previously by
@@ -442,11 +481,11 @@
442481
free (dirp);
443482
ok = /*success*/0;
444483
445484
} else {
446485
/* Invalid directory stream */
447
- DIRENT_SET_ERRNO (EBADF);
486
+ dirent_set_errno (EBADF);
448487
ok = /*failure*/-1;
449488
}
450489
return ok;
451490
}
452491
@@ -456,26 +495,81 @@
456495
*/
457496
static void
458497
_wrewinddir(
459498
_WDIR* dirp)
460499
{
461
- if (dirp != NULL) {
462
- /* release search handle */
500
+ if (dirp) {
501
+ /* Release existing search handle */
463502
if (dirp->handle != INVALID_HANDLE_VALUE) {
464503
FindClose (dirp->handle);
465504
}
466505
467
- /* Open new search handle and retrieve the first directory entry */
468
- dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data);
469
- if (dirp->handle != INVALID_HANDLE_VALUE) {
470
- /* a directory entry is now waiting in memory */
471
- dirp->cached = 1;
506
+ /* Open new search handle */
507
+ dirent_first (dirp);
508
+ }
509
+}
510
+
511
+/* Get first directory entry (internal) */
512
+static WIN32_FIND_DATAW*
513
+dirent_first(
514
+ _WDIR *dirp)
515
+{
516
+ WIN32_FIND_DATAW *datap;
517
+
518
+ /* Open directory and retrieve the first entry */
519
+ dirp->handle = FindFirstFileW (dirp->patt, &dirp->data);
520
+ if (dirp->handle != INVALID_HANDLE_VALUE) {
521
+
522
+ /* a directory entry is now waiting in memory */
523
+ datap = &dirp->data;
524
+ dirp->cached = 1;
525
+
526
+ } else {
527
+
528
+ /* Failed to re-open directory: no directory entry in memory */
529
+ dirp->cached = 0;
530
+ datap = NULL;
531
+
532
+ }
533
+ return datap;
534
+}
535
+
536
+/* Get next directory entry (internal) */
537
+static WIN32_FIND_DATAW*
538
+dirent_next(
539
+ _WDIR *dirp)
540
+{
541
+ WIN32_FIND_DATAW *p;
542
+
543
+ /* Get next directory entry */
544
+ if (dirp->cached != 0) {
545
+
546
+ /* A valid directory entry already in memory */
547
+ p = &dirp->data;
548
+ dirp->cached = 0;
549
+
550
+ } else if (dirp->handle != INVALID_HANDLE_VALUE) {
551
+
552
+ /* Get the next directory entry from stream */
553
+ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
554
+ /* Got a file */
555
+ p = &dirp->data;
472556
} else {
473
- /* Failed to re-open directory: no directory entry in memory */
474
- dirp->cached = 0;
557
+ /* The very last entry has been processed or an error occured */
558
+ FindClose (dirp->handle);
559
+ dirp->handle = INVALID_HANDLE_VALUE;
560
+ p = NULL;
475561
}
562
+
563
+ } else {
564
+
565
+ /* End of directory stream reached */
566
+ p = NULL;
567
+
476568
}
569
+
570
+ return p;
477571
}
478572
479573
/*
480574
* Open directory stream using plain old C-string.
481575
*/
@@ -482,58 +576,34 @@
482576
static DIR*
483577
opendir(
484578
const char *dirname)
485579
{
486580
struct DIR *dirp;
487
- errno_t error = 0;
581
+ int error;
488582
489583
/* Must have directory name */
490
- if (dirname == NULL) {
491
- DIRENT_SET_ERRNO (ENOENT);
584
+ if (dirname == NULL || dirname[0] == '\0') {
585
+ dirent_set_errno (ENOENT);
492586
return NULL;
493587
}
494588
495
- /* Allocate memory for multi-byte string directory structures */
589
+ /* Allocate memory for DIR structure */
496590
dirp = (DIR*) malloc (sizeof (struct DIR));
497591
if (dirp) {
498592
wchar_t wname[PATH_MAX + 1];
499593
size_t n;
500594
501
- /*
502
- * Convert directory name to wide-character string.
503
- *
504
- * Be ware of the return schemantics of MultiByteToWideChar() --
505
- * the function basically returns the number of characters written to
506
- * output buffer or zero if the conversion fails. However, the
507
- * function does not necessarily zero-terminate the output
508
- * buffer and may return 0xFFFD if the string contains invalid
509
- * characters!
510
- */
511
- n = MultiByteToWideChar(
512
- CP_ACP, /* Input code page */
513
- MB_PRECOMPOSED, /* Conversion flags */
514
- dirname, /* Input string */
515
- -1, /* Length of input string */
516
- wname, /* Output buffer */
517
- PATH_MAX); /* Size of output buffer */
518
- if (n > 0 && n < PATH_MAX) {
519
-
520
- /* Zero-terminate output buffer */
521
- wname[n] = '\0';
522
-
523
- /* Open directory stream with wide-character string file name */
595
+ /* Convert directory name to wide-character string */
596
+ error = dirent_mbstowcs_s(
597
+ &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
598
+ if (!error) {
599
+
600
+ /* Open directory stream using wide-character name */
524601
dirp->wdirp = _wopendir (wname);
525602
if (dirp->wdirp) {
526
-
527
- /* Initialize directory structure */
528
- dirp->ent.d_name[0] = '\0';
529
- dirp->ent.d_namlen = 0;
530
- dirp->ent.d_type = 0;
531
- dirp->ent.d_ino = 0;
532
- dirp->ent.d_reclen = 0;
533
-
534
-
603
+ /* Directory stream opened */
604
+ error = 0;
535605
} else {
536606
/* Failed to open directory stream */
537607
error = 1;
538608
}
539609
@@ -562,95 +632,99 @@
562632
}
563633
564634
/*
565635
* Read next directory entry.
566636
*
567
- * When working with console, please note that file names returned by
568
- * readdir() are represented in the default ANSI code page while the
569
- * console typically runs on another code page. Thus, non-ASCII characters
570
- * will not usually display correctly. The problem can be fixed in two ways:
571
- * (1) change the character set of console to 1252 using chcp utility and use
572
- * Lucida Console font, or (2) always use _cprintf function when writing to
573
- * console. The _cprinf() will re-encode ANSI strings to the console code
574
- * page so non-ASCII characters will display correcly.
637
+ * When working with text consoles, please note that file names returned by
638
+ * readdir() are represented in the default ANSI code page while any output to
639
+ * console is typically formatted on another code page. Thus, non-ASCII
640
+ * characters in file names will not usually display correctly on console. The
641
+ * problem can be fixed in two ways: (1) change the character set of console
642
+ * to 1252 using chcp utility and use Lucida Console font, or (2) use
643
+ * _cprintf function when writing to console. The _cprinf() will re-encode
644
+ * ANSI strings to the console code page so many non-ASCII characters will
645
+ * display correcly.
575646
*/
576647
static struct dirent*
577648
readdir(
578649
DIR *dirp)
579650
{
580
- struct dirent *p;
581
- struct _wdirent *wp;
651
+ WIN32_FIND_DATAW *datap;
652
+ struct dirent *entp;
582653
583
- /* Read next directory entry using wide-character string functions */
584
- wp = _wreaddir (dirp->wdirp);
585
- if (wp) {
654
+ /* Read next directory entry */
655
+ datap = dirent_next (dirp->wdirp);
656
+ if (datap) {
586657
size_t n;
658
+ int error;
659
+
660
+ /* Attempt to convert file name to multi-byte string */
661
+ error = dirent_wcstombs_s(
662
+ &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH);
587663
588664
/*
589
- * Convert file name to multi-byte string.
590
- *
591
- * Be ware of the return schemantics of WideCharToMultiByte() --
592
- * the function basically returns the number of bytes
593
- * written to output buffer or zero if the conversion fails.
594
- * However, the function does not necessarily zero-terminate the
595
- * buffer and it may even return 0xFFFD the string contains
596
- * invalid characters!
665
+ * If the file name cannot be represented by a multi-byte string,
666
+ * then attempt to use old 8+3 file name. This allows traditional
667
+ * Unix-code to access some file names despite of unicode
668
+ * characters, although file names may seem unfamiliar to the user.
669
+ *
670
+ * Be ware that the code below cannot come up with a short file
671
+ * name unless the file system provides one. At least
672
+ * VirtualBox shared folders fail to do this.
597673
*/
598
- n = WideCharToMultiByte(
599
- CP_ACP, /* Output code page */
600
- 0, /* Conversion flags */
601
- wp->d_name, /* Input string */
602
- wp->d_namlen, /* Length of input string */
603
- dirp->ent.d_name, /* Output buffer */
604
- PATH_MAX, /* Size of output buffer */
605
- NULL, /* Replacement character */
606
- NULL); /* If chars were replaced */
607
- if (n > 0 && n < PATH_MAX) {
608
-
609
- /* Zero-terminate buffer */
610
- dirp->ent.d_name[n] = '\0';
674
+ if (error && datap->cAlternateFileName[0] != '\0') {
675
+ error = dirent_wcstombs_s(
676
+ &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName,
677
+ sizeof (datap->cAlternateFileName) /
678
+ sizeof (datap->cAlternateFileName[0]));
679
+ }
680
+
681
+ if (!error) {
682
+ DWORD attr;
611683
612684
/* Initialize directory entry for return */
613
- p = &dirp->ent;
685
+ entp = &dirp->ent;
614686
615
- /* Compute length */
616
- p->d_namlen = strnlen (dirp->ent.d_name, PATH_MAX);
687
+ /* Length of file name excluding zero terminator */
688
+ entp->d_namlen = n - 1;
617689
618
- /* Copy file attributes */
619
- p->d_type = wp->d_type;
690
+ /* File attributes */
691
+ attr = datap->dwFileAttributes;
692
+ if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
693
+ entp->d_type = DT_CHR;
694
+ } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
695
+ entp->d_type = DT_DIR;
696
+ } else {
697
+ entp->d_type = DT_REG;
698
+ }
620699
621700
/* Reset dummy fields */
622
- p->d_ino = 0;
623
- p->d_reclen = sizeof (dirp->ent);
624
-
701
+ entp->d_ino = 0;
702
+ entp->d_reclen = sizeof (struct dirent);
625703
626704
} else {
627
-
628705
/*
629706
* Cannot convert file name to multi-byte string so construct
630707
* an errornous directory entry and return that. Note that
631708
* we cannot return NULL as that would stop the processing
632709
* of directory entries completely.
633710
*/
634
- p = &dirp->ent;
635
- p->d_name[0] = '?';
636
- p->d_name[1] = '\0';
637
- p->d_namlen = 1;
638
- p->d_type = DT_UNKNOWN;
639
- p->d_ino = 0;
640
- p->d_reclen = 0;
641
-
711
+ entp = &dirp->ent;
712
+ entp->d_name[0] = '?';
713
+ entp->d_name[1] = '\0';
714
+ entp->d_namlen = 1;
715
+ entp->d_type = DT_UNKNOWN;
716
+ entp->d_ino = 0;
717
+ entp->d_reclen = 0;
642718
}
643719
644720
} else {
645
-
646
- /* End of directory stream */
647
- p = NULL;
648
-
721
+ /* No more directory entries */
722
+ entp = NULL;
649723
}
650724
651
- return p;
725
+ return entp;
652726
}
653727
654728
/*
655729
* Close directory stream.
656730
*/
@@ -667,13 +741,15 @@
667741
668742
/* Release multi-byte character version */
669743
free (dirp);
670744
671745
} else {
746
+
672747
/* Invalid directory stream */
673
- DIRENT_SET_ERRNO (EBADF);
748
+ dirent_set_errno (EBADF);
674749
ok = /*failure*/-1;
750
+
675751
}
676752
return ok;
677753
}
678754
679755
/*
@@ -684,12 +760,130 @@
684760
DIR* dirp)
685761
{
686762
/* Rewind wide-character string directory stream */
687763
_wrewinddir (dirp->wdirp);
688764
}
765
+
766
+/* Convert multi-byte string to wide character string */
767
+static int
768
+dirent_mbstowcs_s(
769
+ size_t *pReturnValue,
770
+ wchar_t *wcstr,
771
+ size_t sizeInWords,
772
+ const char *mbstr,
773
+ size_t count)
774
+{
775
+ int error;
776
+
777
+#if defined(_MSC_VER) && _MSC_VER >= 1400
778
+
779
+ /* Microsoft Visual Studio 2005 or later */
780
+ error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
781
+
782
+#else
783
+
784
+ /* Older Visual Studio or non-Microsoft compiler */
785
+ size_t n;
786
+
787
+ /* Convert to wide-character string */
788
+ n = mbstowcs (wcstr, mbstr, count);
789
+ if (n < sizeInWords) {
790
+
791
+ /* Zero-terminate output buffer */
792
+ if (wcstr) {
793
+ wcstr[n] = 0;
794
+ }
795
+
796
+ /* Length of resuting multi-byte string WITH zero terminator */
797
+ if (pReturnValue) {
798
+ *pReturnValue = n + 1;
799
+ }
800
+
801
+ /* Success */
802
+ error = 0;
803
+
804
+ } else {
805
+
806
+ /* Could not convert string */
807
+ error = 1;
808
+
809
+ }
810
+
811
+#endif
812
+
813
+ return error;
814
+}
815
+
816
+/* Convert wide-character string to multi-byte string */
817
+static int
818
+dirent_wcstombs_s(
819
+ size_t *pReturnValue,
820
+ char *mbstr,
821
+ size_t sizeInBytes,
822
+ const wchar_t *wcstr,
823
+ size_t count)
824
+{
825
+ int error;
826
+
827
+#if defined(_MSC_VER) && _MSC_VER >= 1400
828
+
829
+ /* Microsoft Visual Studio 2005 or later */
830
+ error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
831
+
832
+#else
833
+
834
+ /* Older Visual Studio or non-Microsoft compiler */
835
+ size_t n;
836
+
837
+ /* Convert to multi-byte string */
838
+ n = wcstombs (mbstr, wcstr, count);
839
+ if (n < sizeInBytes) {
840
+
841
+ /* Zero-terminate output buffer */
842
+ if (mbstr) {
843
+ mbstr[n] = '\0';
844
+ }
845
+
846
+ /* Lenght of resulting multi-bytes string WITH zero-terminator */
847
+ if (pReturnValue) {
848
+ *pReturnValue = n + 1;
849
+ }
850
+
851
+ /* Success */
852
+ error = 0;
853
+
854
+ } else {
855
+
856
+ /* Cannot convert string */
857
+ error = 1;
858
+
859
+ }
860
+
861
+#endif
862
+
863
+ return error;
864
+}
865
+
866
+/* Set errno variable */
867
+static void
868
+dirent_set_errno(
869
+ int error)
870
+{
871
+#if defined(_MSC_VER)
872
+
873
+ /* Microsoft Visual Studio */
874
+ _set_errno (error);
875
+
876
+#else
877
+
878
+ /* Non-Microsoft compiler */
879
+ errno = error;
880
+
881
+#endif
882
+}
689883
690884
691885
#ifdef __cplusplus
692886
}
693887
#endif
694888
#endif /*DIRENT_H*/
695889
696890
--- win/include/dirent.h
+++ win/include/dirent.h
@@ -20,21 +20,24 @@
20 * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 *
 
 
 
 
 
25 *
26 * Version 1.12.1, Oct 1 2012, Toni Ronkko
27 * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with
28 * capital W) in order to maintain compatibility with MingW.
29 *
30 * Version 1.12, Sep 30 2012, Toni Ronkko
31 * Define PATH_MAX and NAME_MAX.
32 *
33 * Added wide-character variants _wDIR, _wdirent, _wopendir(),
34 * _wreaddir(), _wclosedir() and _wrewinddir(). Thanks to Edgar Buerkle
35 * and Jan Nijtmans for ideas and code.
36 *
37 * Do not include windows.h. This allows dirent.h to be integrated more
38 * easily into programs using winsock. Thanks to Fernando Azaldegui.
39 *
40 * Version 1.11, Mar 15, 2011, Toni Ronkko
@@ -88,48 +91,72 @@
88 *****************************************************************************/
89 #ifndef DIRENT_H
90 #define DIRENT_H
91
92 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
93 #define _X86_
94 #endif
95 #include <stdio.h>
96 #include <stdarg.h>
97 #include <windef.h>
98 #include <winbase.h>
99 #include <wchar.h>
100 #include <winnls.h>
101 #include <string.h>
102 #include <stdlib.h>
 
103 #include <sys/types.h>
104 #include <sys/stat.h>
105 #include <errno.h>
106
107 /* Windows 8 wide-character string functions */
108 #if (_WIN32_WINNT >= 0x0602)
109 # include <stringapiset.h>
110 #endif
 
111
112 /* Entries missing from MSVC 6.0 */
113 #if !defined(FILE_ATTRIBUTE_DEVICE)
114 # define FILE_ATTRIBUTE_DEVICE 0x40
115 #endif
116
117 /* File type and permission flags for stat() */
118 #if defined(_MSC_VER) && !defined(S_IREAD)
119 # define S_IFMT _S_IFMT /* File type mask */
 
 
120 # define S_IFDIR _S_IFDIR /* Directory */
 
 
121 # define S_IFCHR _S_IFCHR /* Character device */
 
 
122 # define S_IFFIFO _S_IFFIFO /* Pipe */
 
 
123 # define S_IFREG _S_IFREG /* Regular file */
 
 
124 # define S_IREAD _S_IREAD /* Read permission */
 
 
125 # define S_IWRITE _S_IWRITE /* Write permission */
 
 
126 # define S_IEXEC _S_IEXEC /* Execute permission */
127 #endif
128 #define S_IFBLK 0 /* Block device */
129 #define S_IFLNK 0 /* Link */
130 #define S_IFSOCK 0 /* Socket */
 
 
 
 
 
 
 
 
 
131
132 #if defined(_MSC_VER)
133 # define S_IRUSR S_IREAD /* Read user */
134 # define S_IWUSR S_IWRITE /* Write user */
135 # define S_IXUSR 0 /* Execute user */
@@ -139,18 +166,26 @@
139 # define S_IROTH 0 /* Read others */
140 # define S_IWOTH 0 /* Write others */
141 # define S_IXOTH 0 /* Execute others */
142 #endif
143
144 /* Indicates that d_type field is available in dirent structure */
145 #define _DIRENT_HAVE_D_TYPE
 
 
 
 
 
 
 
 
146
147 /* File type flags for d_type */
148 #define DT_UNKNOWN 0
149 #define DT_REG S_IFREG
150 #define DT_DIR S_IFDIR
151 #define DT_FIFO S_IFFIFO
152 #define DT_SOCK S_IFSOCK
153 #define DT_CHR S_IFCHR
154 #define DT_BLK S_IFBLK
155
156 /* Macros for converting between st_mode and d_type */
@@ -161,41 +196,31 @@
161 * File type macros. Note that block devices, sockets and links cannot be
162 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
163 * only defined for compatibility. These macros should always return false
164 * on Windows.
165 */
166 #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO)
167 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
168 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
169 #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
170 #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
171 #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
172 #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
173
174 /* For compatiblity with Unix */
175 #if !defined(PATH_MAX)
176 # define PATH_MAX MAX_PATH
177 #endif
178 #if !defined(FILENAME_MAX)
179 # define FILENAME_MAX MAX_PATH
180 #endif
181 #if !defined(NAME_MAX)
182 # define NAME_MAX FILENAME_MAX
183 #endif
184
185 /* Set errno variable */
186 #if defined(_MSC_VER)
187 #define DIRENT_SET_ERRNO(x) _set_errno (x)
188 #else
189 #define DIRENT_SET_ERRNO(x) (errno = (x))
190 #endif
191
192 #ifdef __cplusplus
193 extern "C" {
194 #endif
195
196 /* Wide-character versions */
 
197 struct _wdirent {
198 long d_ino; /* Always zero */
199 unsigned short d_reclen; /* Structure size */
200 size_t d_namlen; /* Length of name without \0 */
201 int d_type; /* File type */
@@ -203,11 +228,11 @@
203 };
204 typedef struct _wdirent _wdirent;
205
206 struct _WDIR {
207 struct _wdirent ent; /* Current directory entry */
208 WIN32_FIND_DATAW find_data; /* Private file data */
209 int cached; /* True if data is valid */
210 HANDLE handle; /* Win32 search handle */
211 wchar_t *patt; /* Initial directory name */
212 };
213 typedef struct _WDIR _WDIR;
@@ -214,10 +239,11 @@
214
215 static _WDIR *_wopendir (const wchar_t *dirname);
216 static struct _wdirent *_wreaddir (_WDIR *dirp);
217 static int _wclosedir (_WDIR *dirp);
218 static void _wrewinddir (_WDIR* dirp);
 
219
220 /* For compatibility with Symbian */
221 #define wdirent _wdirent
222 #define WDIR _WDIR
223 #define wopendir _wopendir
@@ -246,10 +272,30 @@
246 static struct dirent *readdir (DIR *dirp);
247 static int closedir (DIR *dirp);
248 static void rewinddir (DIR* dirp);
249
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251 /*
252 * Open directory stream DIRNAME for read and return a pointer to the
253 * internal working area that is used to retrieve individual directory
254 * entries.
255 */
@@ -256,31 +302,38 @@
256 static _WDIR*
257 _wopendir(
258 const wchar_t *dirname)
259 {
260 _WDIR *dirp = NULL;
261 int error = 0;
 
 
 
 
 
 
262
263 /* Allocate new _WDIR structure */
264 dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
265 if (dirp != NULL) {
266 DWORD n;
267
268 /* Reset _WDIR structure */
269 dirp->handle = INVALID_HANDLE_VALUE;
270 dirp->patt = NULL;
 
271
272 /* Compute the length of full path plus zero terminator */
273 n = GetFullPathNameW (dirname, 0, NULL, NULL);
274
275 /* Allocate room for full path and search patterns */
276 dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
277 if (dirp->patt) {
278
279 /*
280 * Convert relative directory name to an absolute one. This
281 * allows rewinddir() to function correctly when the current
282 * working directory is changed between opendir() and rewinddir().
283 */
284 n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
285 if (n > 0) {
286 wchar_t *p;
@@ -303,25 +356,22 @@
303 }
304 *p++ = '*';
305 *p = '\0';
306
307 /* Open directory stream and retrieve the first entry */
308 dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data);
309 if (dirp->handle != INVALID_HANDLE_VALUE) {
310
311 /* Directory entry is now waiting in memory */
312 dirp->cached = 1;
313
314 } else {
315 /* Search pattern is not a directory name? */
316 DIRENT_SET_ERRNO (ENOENT);
317 error = 1;
 
318 }
319
320 } else {
321 /* Cannot convert directory name to wide character string */
322 DIRENT_SET_ERRNO (ENOENT);
323 error = 1;
324 }
325
326 } else {
327 /* Cannot allocate memory for search pattern */
@@ -350,70 +400,59 @@
350 */
351 static struct _wdirent*
352 _wreaddir(
353 _WDIR *dirp)
354 {
355 DWORD attr;
356 errno_t error;
357
358 /* Get next directory entry */
359 if (dirp->cached != 0) {
360 /* A valid directory entry already in memory */
361 dirp->cached = 0;
362 } else {
363 /* Get the next directory entry from stream */
364 if (dirp->handle == INVALID_HANDLE_VALUE) {
365 return NULL;
366 }
367 if (FindNextFileW (dirp->handle, &dirp->find_data) == FALSE) {
368 /* The very last entry has been processed or an error occured */
369 FindClose (dirp->handle);
370 dirp->handle = INVALID_HANDLE_VALUE;
371 return NULL;
372 }
373 }
374
375 /* Copy file name as a wide-character string */
376 error = wcsncpy_s(
377 dirp->ent.d_name, /* Destination string */
378 PATH_MAX, /* Size of dest in words */
379 dirp->find_data.cFileName, /* Source string */
380 PATH_MAX + 1); /* Max # of chars to copy */
381 if (!error) {
382
383 /* Compute the length of name */
384 dirp->ent.d_namlen = wcsnlen (dirp->ent.d_name, PATH_MAX);
385
386 /* Determine file type */
387 attr = dirp->find_data.dwFileAttributes;
388 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
389 dirp->ent.d_type = DT_CHR;
390 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
391 dirp->ent.d_type = DT_DIR;
392 } else {
393 dirp->ent.d_type = DT_REG;
394 }
395
396 /* Reset dummy fields */
397 dirp->ent.d_ino = 0;
398 dirp->ent.d_reclen = sizeof (dirp->ent);
399
400 } else {
401
402 /*
403 * Cannot copy file name from find_data to ent. Construct a
404 * dummy _wdirent structure to pass error to caller.
405 */
406 dirp->ent.d_name[0] = '?';
407 dirp->ent.d_name[1] = '\0';
408 dirp->ent.d_namlen = 1;
409 dirp->ent.d_type = DT_UNKNOWN;
410 dirp->ent.d_ino = 0;
411 dirp->ent.d_reclen = 0;
412 }
413
414 return &dirp->ent;
415 }
416
417 /*
418 * Close directory stream opened by opendir() function. This invalidates the
419 * DIR structure as well as any directory entry read previously by
@@ -442,11 +481,11 @@
442 free (dirp);
443 ok = /*success*/0;
444
445 } else {
446 /* Invalid directory stream */
447 DIRENT_SET_ERRNO (EBADF);
448 ok = /*failure*/-1;
449 }
450 return ok;
451 }
452
@@ -456,26 +495,81 @@
456 */
457 static void
458 _wrewinddir(
459 _WDIR* dirp)
460 {
461 if (dirp != NULL) {
462 /* release search handle */
463 if (dirp->handle != INVALID_HANDLE_VALUE) {
464 FindClose (dirp->handle);
465 }
466
467 /* Open new search handle and retrieve the first directory entry */
468 dirp->handle = FindFirstFileW (dirp->patt, &dirp->find_data);
469 if (dirp->handle != INVALID_HANDLE_VALUE) {
470 /* a directory entry is now waiting in memory */
471 dirp->cached = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472 } else {
473 /* Failed to re-open directory: no directory entry in memory */
474 dirp->cached = 0;
 
 
475 }
 
 
 
 
 
 
476 }
 
 
477 }
478
479 /*
480 * Open directory stream using plain old C-string.
481 */
@@ -482,58 +576,34 @@
482 static DIR*
483 opendir(
484 const char *dirname)
485 {
486 struct DIR *dirp;
487 errno_t error = 0;
488
489 /* Must have directory name */
490 if (dirname == NULL) {
491 DIRENT_SET_ERRNO (ENOENT);
492 return NULL;
493 }
494
495 /* Allocate memory for multi-byte string directory structures */
496 dirp = (DIR*) malloc (sizeof (struct DIR));
497 if (dirp) {
498 wchar_t wname[PATH_MAX + 1];
499 size_t n;
500
501 /*
502 * Convert directory name to wide-character string.
503 *
504 * Be ware of the return schemantics of MultiByteToWideChar() --
505 * the function basically returns the number of characters written to
506 * output buffer or zero if the conversion fails. However, the
507 * function does not necessarily zero-terminate the output
508 * buffer and may return 0xFFFD if the string contains invalid
509 * characters!
510 */
511 n = MultiByteToWideChar(
512 CP_ACP, /* Input code page */
513 MB_PRECOMPOSED, /* Conversion flags */
514 dirname, /* Input string */
515 -1, /* Length of input string */
516 wname, /* Output buffer */
517 PATH_MAX); /* Size of output buffer */
518 if (n > 0 && n < PATH_MAX) {
519
520 /* Zero-terminate output buffer */
521 wname[n] = '\0';
522
523 /* Open directory stream with wide-character string file name */
524 dirp->wdirp = _wopendir (wname);
525 if (dirp->wdirp) {
526
527 /* Initialize directory structure */
528 dirp->ent.d_name[0] = '\0';
529 dirp->ent.d_namlen = 0;
530 dirp->ent.d_type = 0;
531 dirp->ent.d_ino = 0;
532 dirp->ent.d_reclen = 0;
533
534
535 } else {
536 /* Failed to open directory stream */
537 error = 1;
538 }
539
@@ -562,95 +632,99 @@
562 }
563
564 /*
565 * Read next directory entry.
566 *
567 * When working with console, please note that file names returned by
568 * readdir() are represented in the default ANSI code page while the
569 * console typically runs on another code page. Thus, non-ASCII characters
570 * will not usually display correctly. The problem can be fixed in two ways:
571 * (1) change the character set of console to 1252 using chcp utility and use
572 * Lucida Console font, or (2) always use _cprintf function when writing to
573 * console. The _cprinf() will re-encode ANSI strings to the console code
574 * page so non-ASCII characters will display correcly.
 
575 */
576 static struct dirent*
577 readdir(
578 DIR *dirp)
579 {
580 struct dirent *p;
581 struct _wdirent *wp;
582
583 /* Read next directory entry using wide-character string functions */
584 wp = _wreaddir (dirp->wdirp);
585 if (wp) {
586 size_t n;
 
 
 
 
 
587
588 /*
589 * Convert file name to multi-byte string.
590 *
591 * Be ware of the return schemantics of WideCharToMultiByte() --
592 * the function basically returns the number of bytes
593 * written to output buffer or zero if the conversion fails.
594 * However, the function does not necessarily zero-terminate the
595 * buffer and it may even return 0xFFFD the string contains
596 * invalid characters!
597 */
598 n = WideCharToMultiByte(
599 CP_ACP, /* Output code page */
600 0, /* Conversion flags */
601 wp->d_name, /* Input string */
602 wp->d_namlen, /* Length of input string */
603 dirp->ent.d_name, /* Output buffer */
604 PATH_MAX, /* Size of output buffer */
605 NULL, /* Replacement character */
606 NULL); /* If chars were replaced */
607 if (n > 0 && n < PATH_MAX) {
608
609 /* Zero-terminate buffer */
610 dirp->ent.d_name[n] = '\0';
611
612 /* Initialize directory entry for return */
613 p = &dirp->ent;
614
615 /* Compute length */
616 p->d_namlen = strnlen (dirp->ent.d_name, PATH_MAX);
617
618 /* Copy file attributes */
619 p->d_type = wp->d_type;
 
 
 
 
 
 
 
620
621 /* Reset dummy fields */
622 p->d_ino = 0;
623 p->d_reclen = sizeof (dirp->ent);
624
625
626 } else {
627
628 /*
629 * Cannot convert file name to multi-byte string so construct
630 * an errornous directory entry and return that. Note that
631 * we cannot return NULL as that would stop the processing
632 * of directory entries completely.
633 */
634 p = &dirp->ent;
635 p->d_name[0] = '?';
636 p->d_name[1] = '\0';
637 p->d_namlen = 1;
638 p->d_type = DT_UNKNOWN;
639 p->d_ino = 0;
640 p->d_reclen = 0;
641
642 }
643
644 } else {
645
646 /* End of directory stream */
647 p = NULL;
648
649 }
650
651 return p;
652 }
653
654 /*
655 * Close directory stream.
656 */
@@ -667,13 +741,15 @@
667
668 /* Release multi-byte character version */
669 free (dirp);
670
671 } else {
 
672 /* Invalid directory stream */
673 DIRENT_SET_ERRNO (EBADF);
674 ok = /*failure*/-1;
 
675 }
676 return ok;
677 }
678
679 /*
@@ -684,12 +760,130 @@
684 DIR* dirp)
685 {
686 /* Rewind wide-character string directory stream */
687 _wrewinddir (dirp->wdirp);
688 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
690
691 #ifdef __cplusplus
692 }
693 #endif
694 #endif /*DIRENT_H*/
695
696
--- win/include/dirent.h
+++ win/include/dirent.h
@@ -20,21 +20,24 @@
20 * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 *
25 *
26 * Version 1.13, Dec 12 2012, Toni Ronkko
27 * Use traditional 8+3 file name if the name cannot be represented in the
28 * default ANSI code page. Now compiles again with MSVC 6.0. Thanks to
29 * Konstantin Khomoutov for testing.
30 *
31 * Version 1.12.1, Oct 1 2012, Toni Ronkko
32 * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with
33 * capital W) in order to maintain compatibility with MingW.
34 *
35 * Version 1.12, Sep 30 2012, Toni Ronkko
36 * Define PATH_MAX and NAME_MAX. Added wide-character variants _wDIR,
37 * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir().
38 * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code.
 
 
39 *
40 * Do not include windows.h. This allows dirent.h to be integrated more
41 * easily into programs using winsock. Thanks to Fernando Azaldegui.
42 *
43 * Version 1.11, Mar 15, 2011, Toni Ronkko
@@ -88,48 +91,72 @@
91 *****************************************************************************/
92 #ifndef DIRENT_H
93 #define DIRENT_H
94
95 #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86)
96 # define _X86_
97 #endif
98 #include <stdio.h>
99 #include <stdarg.h>
100 #include <windef.h>
101 #include <winbase.h>
102 #include <wchar.h>
 
103 #include <string.h>
104 #include <stdlib.h>
105 #include <malloc.h>
106 #include <sys/types.h>
107 #include <sys/stat.h>
108 #include <errno.h>
109
110 /* Indicates that d_type field is available in dirent structure */
111 #define _DIRENT_HAVE_D_TYPE
112
113 /* Indicates that d_namlen field is available in dirent structure */
114 #define _DIRENT_HAVE_D_NAMLEN
115
116 /* Entries missing from MSVC 6.0 */
117 #if !defined(FILE_ATTRIBUTE_DEVICE)
118 # define FILE_ATTRIBUTE_DEVICE 0x40
119 #endif
120
121 /* File type and permission flags for stat() */
122 #if !defined(S_IFMT)
123 # define S_IFMT _S_IFMT /* File type mask */
124 #endif
125 #if !defined(S_IFDIR)
126 # define S_IFDIR _S_IFDIR /* Directory */
127 #endif
128 #if !defined(S_IFCHR)
129 # define S_IFCHR _S_IFCHR /* Character device */
130 #endif
131 #if !defined(S_IFFIFO)
132 # define S_IFFIFO _S_IFFIFO /* Pipe */
133 #endif
134 #if !defined(S_IFREG)
135 # define S_IFREG _S_IFREG /* Regular file */
136 #endif
137 #if !defined(S_IREAD)
138 # define S_IREAD _S_IREAD /* Read permission */
139 #endif
140 #if !defined(S_IWRITE)
141 # define S_IWRITE _S_IWRITE /* Write permission */
142 #endif
143 #if !defined(S_IEXEC)
144 # define S_IEXEC _S_IEXEC /* Execute permission */
145 #endif
146 #if !defined(S_IFIFO)
147 # define S_IFIFO _S_IFIFO /* Pipe */
148 #endif
149 #if !defined(S_IFBLK)
150 # define S_IFBLK 0 /* Block device */
151 #endif
152 #if !defined(S_IFLNK)
153 # define S_IFLNK 0 /* Link */
154 #endif
155 #if !defined(S_IFSOCK)
156 # define S_IFSOCK 0 /* Socket */
157 #endif
158
159 #if defined(_MSC_VER)
160 # define S_IRUSR S_IREAD /* Read user */
161 # define S_IWUSR S_IWRITE /* Write user */
162 # define S_IXUSR 0 /* Execute user */
@@ -139,18 +166,26 @@
166 # define S_IROTH 0 /* Read others */
167 # define S_IWOTH 0 /* Write others */
168 # define S_IXOTH 0 /* Execute others */
169 #endif
170
171 /* Maximum length of file name */
172 #if !defined(PATH_MAX)
173 # define PATH_MAX MAX_PATH
174 #endif
175 #if !defined(FILENAME_MAX)
176 # define FILENAME_MAX MAX_PATH
177 #endif
178 #if !defined(NAME_MAX)
179 # define NAME_MAX FILENAME_MAX
180 #endif
181
182 /* File type flags for d_type */
183 #define DT_UNKNOWN 0
184 #define DT_REG S_IFREG
185 #define DT_DIR S_IFDIR
186 #define DT_FIFO S_IFIFO
187 #define DT_SOCK S_IFSOCK
188 #define DT_CHR S_IFCHR
189 #define DT_BLK S_IFBLK
190
191 /* Macros for converting between st_mode and d_type */
@@ -161,41 +196,31 @@
196 * File type macros. Note that block devices, sockets and links cannot be
197 * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
198 * only defined for compatibility. These macros should always return false
199 * on Windows.
200 */
201 #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
202 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
203 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
204 #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
205 #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
206 #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
207 #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
208
209 /* Return the exact length of d_namlen without zero terminator */
210 #define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
211
212 /* Return number of bytes needed to store d_namlen */
213 #define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1)
214
 
 
 
 
 
 
 
 
 
 
 
215
216 #ifdef __cplusplus
217 extern "C" {
218 #endif
219
220
221 /* Wide-character version */
222 struct _wdirent {
223 long d_ino; /* Always zero */
224 unsigned short d_reclen; /* Structure size */
225 size_t d_namlen; /* Length of name without \0 */
226 int d_type; /* File type */
@@ -203,11 +228,11 @@
228 };
229 typedef struct _wdirent _wdirent;
230
231 struct _WDIR {
232 struct _wdirent ent; /* Current directory entry */
233 WIN32_FIND_DATAW data; /* Private file data */
234 int cached; /* True if data is valid */
235 HANDLE handle; /* Win32 search handle */
236 wchar_t *patt; /* Initial directory name */
237 };
238 typedef struct _WDIR _WDIR;
@@ -214,10 +239,11 @@
239
240 static _WDIR *_wopendir (const wchar_t *dirname);
241 static struct _wdirent *_wreaddir (_WDIR *dirp);
242 static int _wclosedir (_WDIR *dirp);
243 static void _wrewinddir (_WDIR* dirp);
244
245
246 /* For compatibility with Symbian */
247 #define wdirent _wdirent
248 #define WDIR _WDIR
249 #define wopendir _wopendir
@@ -246,10 +272,30 @@
272 static struct dirent *readdir (DIR *dirp);
273 static int closedir (DIR *dirp);
274 static void rewinddir (DIR* dirp);
275
276
277 /* Internal utility functions */
278 static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp);
279 static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp);
280
281 static int dirent_mbstowcs_s(
282 size_t *pReturnValue,
283 wchar_t *wcstr,
284 size_t sizeInWords,
285 const char *mbstr,
286 size_t count);
287
288 static int dirent_wcstombs_s(
289 size_t *pReturnValue,
290 char *mbstr,
291 size_t sizeInBytes,
292 const wchar_t *wcstr,
293 size_t count);
294
295 static void dirent_set_errno (int error);
296
297 /*
298 * Open directory stream DIRNAME for read and return a pointer to the
299 * internal working area that is used to retrieve individual directory
300 * entries.
301 */
@@ -256,31 +302,38 @@
302 static _WDIR*
303 _wopendir(
304 const wchar_t *dirname)
305 {
306 _WDIR *dirp = NULL;
307 int error;
308
309 /* Must have directory name */
310 if (dirname == NULL || dirname[0] == '\0') {
311 dirent_set_errno (ENOENT);
312 return NULL;
313 }
314
315 /* Allocate new _WDIR structure */
316 dirp = (_WDIR*) malloc (sizeof (struct _WDIR));
317 if (dirp != NULL) {
318 DWORD n;
319
320 /* Reset _WDIR structure */
321 dirp->handle = INVALID_HANDLE_VALUE;
322 dirp->patt = NULL;
323 dirp->cached = 0;
324
325 /* Compute the length of full path plus zero terminator */
326 n = GetFullPathNameW (dirname, 0, NULL, NULL);
327
328 /* Allocate room for absolute directory name and search pattern */
329 dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16);
330 if (dirp->patt) {
331
332 /*
333 * Convert relative directory name to an absolute one. This
334 * allows rewinddir() to function correctly even when current
335 * working directory is changed between opendir() and rewinddir().
336 */
337 n = GetFullPathNameW (dirname, n, dirp->patt, NULL);
338 if (n > 0) {
339 wchar_t *p;
@@ -303,25 +356,22 @@
356 }
357 *p++ = '*';
358 *p = '\0';
359
360 /* Open directory stream and retrieve the first entry */
361 if (dirent_first (dirp)) {
362 /* Directory stream opened successfully */
363 error = 0;
 
 
 
364 } else {
365 /* Cannot retrieve first entry */
 
366 error = 1;
367 dirent_set_errno (ENOENT);
368 }
369
370 } else {
371 /* Cannot retrieve full path name */
372 dirent_set_errno (ENOENT);
373 error = 1;
374 }
375
376 } else {
377 /* Cannot allocate memory for search pattern */
@@ -350,70 +400,59 @@
400 */
401 static struct _wdirent*
402 _wreaddir(
403 _WDIR *dirp)
404 {
405 WIN32_FIND_DATAW *datap;
406 struct _wdirent *entp;
407
408 /* Read next directory entry */
409 datap = dirent_next (dirp);
410 if (datap) {
411 size_t n;
412 DWORD attr;
413
414 /* Pointer to directory entry to return */
415 entp = &dirp->ent;
416
417 /*
418 * Copy file name as wide-character string. If the file name is too
419 * long to fit in to the destination buffer, then truncate file name
420 * to PATH_MAX characters and zero-terminate the buffer.
421 */
422 n = 0;
423 while (n < PATH_MAX && datap->cFileName[n] != 0) {
424 entp->d_name[n] = datap->cFileName[n];
425 n++;
426 }
427 dirp->ent.d_name[n] = 0;
428
429 /* Length of file name excluding zero terminator */
430 entp->d_namlen = n;
431
432 /* File type */
433 attr = datap->dwFileAttributes;
 
 
 
 
434 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
435 entp->d_type = DT_CHR;
436 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
437 entp->d_type = DT_DIR;
438 } else {
439 entp->d_type = DT_REG;
440 }
441
442 /* Reset dummy fields */
443 entp->d_ino = 0;
444 entp->d_reclen = sizeof (struct _wdirent);
445
446 } else {
447
448 /* Last directory entry read */
449 entp = NULL;
450
 
 
 
 
 
 
 
451 }
452
453 return entp;
454 }
455
456 /*
457 * Close directory stream opened by opendir() function. This invalidates the
458 * DIR structure as well as any directory entry read previously by
@@ -442,11 +481,11 @@
481 free (dirp);
482 ok = /*success*/0;
483
484 } else {
485 /* Invalid directory stream */
486 dirent_set_errno (EBADF);
487 ok = /*failure*/-1;
488 }
489 return ok;
490 }
491
@@ -456,26 +495,81 @@
495 */
496 static void
497 _wrewinddir(
498 _WDIR* dirp)
499 {
500 if (dirp) {
501 /* Release existing search handle */
502 if (dirp->handle != INVALID_HANDLE_VALUE) {
503 FindClose (dirp->handle);
504 }
505
506 /* Open new search handle */
507 dirent_first (dirp);
508 }
509 }
510
511 /* Get first directory entry (internal) */
512 static WIN32_FIND_DATAW*
513 dirent_first(
514 _WDIR *dirp)
515 {
516 WIN32_FIND_DATAW *datap;
517
518 /* Open directory and retrieve the first entry */
519 dirp->handle = FindFirstFileW (dirp->patt, &dirp->data);
520 if (dirp->handle != INVALID_HANDLE_VALUE) {
521
522 /* a directory entry is now waiting in memory */
523 datap = &dirp->data;
524 dirp->cached = 1;
525
526 } else {
527
528 /* Failed to re-open directory: no directory entry in memory */
529 dirp->cached = 0;
530 datap = NULL;
531
532 }
533 return datap;
534 }
535
536 /* Get next directory entry (internal) */
537 static WIN32_FIND_DATAW*
538 dirent_next(
539 _WDIR *dirp)
540 {
541 WIN32_FIND_DATAW *p;
542
543 /* Get next directory entry */
544 if (dirp->cached != 0) {
545
546 /* A valid directory entry already in memory */
547 p = &dirp->data;
548 dirp->cached = 0;
549
550 } else if (dirp->handle != INVALID_HANDLE_VALUE) {
551
552 /* Get the next directory entry from stream */
553 if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) {
554 /* Got a file */
555 p = &dirp->data;
556 } else {
557 /* The very last entry has been processed or an error occured */
558 FindClose (dirp->handle);
559 dirp->handle = INVALID_HANDLE_VALUE;
560 p = NULL;
561 }
562
563 } else {
564
565 /* End of directory stream reached */
566 p = NULL;
567
568 }
569
570 return p;
571 }
572
573 /*
574 * Open directory stream using plain old C-string.
575 */
@@ -482,58 +576,34 @@
576 static DIR*
577 opendir(
578 const char *dirname)
579 {
580 struct DIR *dirp;
581 int error;
582
583 /* Must have directory name */
584 if (dirname == NULL || dirname[0] == '\0') {
585 dirent_set_errno (ENOENT);
586 return NULL;
587 }
588
589 /* Allocate memory for DIR structure */
590 dirp = (DIR*) malloc (sizeof (struct DIR));
591 if (dirp) {
592 wchar_t wname[PATH_MAX + 1];
593 size_t n;
594
595 /* Convert directory name to wide-character string */
596 error = dirent_mbstowcs_s(
597 &n, wname, PATH_MAX + 1, dirname, PATH_MAX);
598 if (!error) {
599
600 /* Open directory stream using wide-character name */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601 dirp->wdirp = _wopendir (wname);
602 if (dirp->wdirp) {
603 /* Directory stream opened */
604 error = 0;
 
 
 
 
 
 
 
605 } else {
606 /* Failed to open directory stream */
607 error = 1;
608 }
609
@@ -562,95 +632,99 @@
632 }
633
634 /*
635 * Read next directory entry.
636 *
637 * When working with text consoles, please note that file names returned by
638 * readdir() are represented in the default ANSI code page while any output to
639 * console is typically formatted on another code page. Thus, non-ASCII
640 * characters in file names will not usually display correctly on console. The
641 * problem can be fixed in two ways: (1) change the character set of console
642 * to 1252 using chcp utility and use Lucida Console font, or (2) use
643 * _cprintf function when writing to console. The _cprinf() will re-encode
644 * ANSI strings to the console code page so many non-ASCII characters will
645 * display correcly.
646 */
647 static struct dirent*
648 readdir(
649 DIR *dirp)
650 {
651 WIN32_FIND_DATAW *datap;
652 struct dirent *entp;
653
654 /* Read next directory entry */
655 datap = dirent_next (dirp->wdirp);
656 if (datap) {
657 size_t n;
658 int error;
659
660 /* Attempt to convert file name to multi-byte string */
661 error = dirent_wcstombs_s(
662 &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH);
663
664 /*
665 * If the file name cannot be represented by a multi-byte string,
666 * then attempt to use old 8+3 file name. This allows traditional
667 * Unix-code to access some file names despite of unicode
668 * characters, although file names may seem unfamiliar to the user.
669 *
670 * Be ware that the code below cannot come up with a short file
671 * name unless the file system provides one. At least
672 * VirtualBox shared folders fail to do this.
673 */
674 if (error && datap->cAlternateFileName[0] != '\0') {
675 error = dirent_wcstombs_s(
676 &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName,
677 sizeof (datap->cAlternateFileName) /
678 sizeof (datap->cAlternateFileName[0]));
679 }
680
681 if (!error) {
682 DWORD attr;
 
 
 
 
683
684 /* Initialize directory entry for return */
685 entp = &dirp->ent;
686
687 /* Length of file name excluding zero terminator */
688 entp->d_namlen = n - 1;
689
690 /* File attributes */
691 attr = datap->dwFileAttributes;
692 if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
693 entp->d_type = DT_CHR;
694 } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
695 entp->d_type = DT_DIR;
696 } else {
697 entp->d_type = DT_REG;
698 }
699
700 /* Reset dummy fields */
701 entp->d_ino = 0;
702 entp->d_reclen = sizeof (struct dirent);
 
703
704 } else {
 
705 /*
706 * Cannot convert file name to multi-byte string so construct
707 * an errornous directory entry and return that. Note that
708 * we cannot return NULL as that would stop the processing
709 * of directory entries completely.
710 */
711 entp = &dirp->ent;
712 entp->d_name[0] = '?';
713 entp->d_name[1] = '\0';
714 entp->d_namlen = 1;
715 entp->d_type = DT_UNKNOWN;
716 entp->d_ino = 0;
717 entp->d_reclen = 0;
 
718 }
719
720 } else {
721 /* No more directory entries */
722 entp = NULL;
 
 
723 }
724
725 return entp;
726 }
727
728 /*
729 * Close directory stream.
730 */
@@ -667,13 +741,15 @@
741
742 /* Release multi-byte character version */
743 free (dirp);
744
745 } else {
746
747 /* Invalid directory stream */
748 dirent_set_errno (EBADF);
749 ok = /*failure*/-1;
750
751 }
752 return ok;
753 }
754
755 /*
@@ -684,12 +760,130 @@
760 DIR* dirp)
761 {
762 /* Rewind wide-character string directory stream */
763 _wrewinddir (dirp->wdirp);
764 }
765
766 /* Convert multi-byte string to wide character string */
767 static int
768 dirent_mbstowcs_s(
769 size_t *pReturnValue,
770 wchar_t *wcstr,
771 size_t sizeInWords,
772 const char *mbstr,
773 size_t count)
774 {
775 int error;
776
777 #if defined(_MSC_VER) && _MSC_VER >= 1400
778
779 /* Microsoft Visual Studio 2005 or later */
780 error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count);
781
782 #else
783
784 /* Older Visual Studio or non-Microsoft compiler */
785 size_t n;
786
787 /* Convert to wide-character string */
788 n = mbstowcs (wcstr, mbstr, count);
789 if (n < sizeInWords) {
790
791 /* Zero-terminate output buffer */
792 if (wcstr) {
793 wcstr[n] = 0;
794 }
795
796 /* Length of resuting multi-byte string WITH zero terminator */
797 if (pReturnValue) {
798 *pReturnValue = n + 1;
799 }
800
801 /* Success */
802 error = 0;
803
804 } else {
805
806 /* Could not convert string */
807 error = 1;
808
809 }
810
811 #endif
812
813 return error;
814 }
815
816 /* Convert wide-character string to multi-byte string */
817 static int
818 dirent_wcstombs_s(
819 size_t *pReturnValue,
820 char *mbstr,
821 size_t sizeInBytes,
822 const wchar_t *wcstr,
823 size_t count)
824 {
825 int error;
826
827 #if defined(_MSC_VER) && _MSC_VER >= 1400
828
829 /* Microsoft Visual Studio 2005 or later */
830 error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count);
831
832 #else
833
834 /* Older Visual Studio or non-Microsoft compiler */
835 size_t n;
836
837 /* Convert to multi-byte string */
838 n = wcstombs (mbstr, wcstr, count);
839 if (n < sizeInBytes) {
840
841 /* Zero-terminate output buffer */
842 if (mbstr) {
843 mbstr[n] = '\0';
844 }
845
846 /* Lenght of resulting multi-bytes string WITH zero-terminator */
847 if (pReturnValue) {
848 *pReturnValue = n + 1;
849 }
850
851 /* Success */
852 error = 0;
853
854 } else {
855
856 /* Cannot convert string */
857 error = 1;
858
859 }
860
861 #endif
862
863 return error;
864 }
865
866 /* Set errno variable */
867 static void
868 dirent_set_errno(
869 int error)
870 {
871 #if defined(_MSC_VER)
872
873 /* Microsoft Visual Studio */
874 _set_errno (error);
875
876 #else
877
878 /* Non-Microsoft compiler */
879 errno = error;
880
881 #endif
882 }
883
884
885 #ifdef __cplusplus
886 }
887 #endif
888 #endif /*DIRENT_H*/
889
890
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -14,12 +14,11 @@
1414
<li> Special names:
1515
<ul>
1616
<li> <b>tip</b>
1717
<li> <b>current</b>
1818
<li> <b>next</b>
19
-<li> <b>previous</b>
20
-<li> <b>ckout</b>
19
+<li> <b>previous</b> or <b>prev</b>
2120
</ul>
2221
</ul>
2322
</td></tr>
2423
</table>
2524
Many Fossil [/help|commands] and [./webui.wiki | web-interface] URLs accept
@@ -194,12 +193,12 @@
194193
equivalent to the timestamp tag "5000-01-01".
195194
196195
If the command is being run from a working check-out (not against a bare
197196
repository) then a few extra tags apply. The "current" tag means the
198197
current check-out. The "next" tag means the youngest child of the
199
-current check-out. And the "previous" tag means the primary (non-merge)
200
-parent of the current check-out.
198
+current check-out. And the "previous" or "prev" tag means the primary
199
+(non-merge) parent of the current check-out.
201200
202201
<h2>Additional Examples</h2>
203202
204203
To view the changes in the most recent check-in prior to the version currently
205204
checked out:
206205
207206
ADDED www/fiveminutes.wiki
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -14,12 +14,11 @@
14 <li> Special names:
15 <ul>
16 <li> <b>tip</b>
17 <li> <b>current</b>
18 <li> <b>next</b>
19 <li> <b>previous</b>
20 <li> <b>ckout</b>
21 </ul>
22 </ul>
23 </td></tr>
24 </table>
25 Many Fossil [/help|commands] and [./webui.wiki | web-interface] URLs accept
@@ -194,12 +193,12 @@
194 equivalent to the timestamp tag "5000-01-01".
195
196 If the command is being run from a working check-out (not against a bare
197 repository) then a few extra tags apply. The "current" tag means the
198 current check-out. The "next" tag means the youngest child of the
199 current check-out. And the "previous" tag means the primary (non-merge)
200 parent of the current check-out.
201
202 <h2>Additional Examples</h2>
203
204 To view the changes in the most recent check-in prior to the version currently
205 checked out:
206
207 DDED www/fiveminutes.wiki
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -14,12 +14,11 @@
14 <li> Special names:
15 <ul>
16 <li> <b>tip</b>
17 <li> <b>current</b>
18 <li> <b>next</b>
19 <li> <b>previous</b> or <b>prev</b>
 
20 </ul>
21 </ul>
22 </td></tr>
23 </table>
24 Many Fossil [/help|commands] and [./webui.wiki | web-interface] URLs accept
@@ -194,12 +193,12 @@
193 equivalent to the timestamp tag "5000-01-01".
194
195 If the command is being run from a working check-out (not against a bare
196 repository) then a few extra tags apply. The "current" tag means the
197 current check-out. The "next" tag means the youngest child of the
198 current check-out. And the "previous" or "prev" tag means the primary
199 (non-merge) parent of the current check-out.
200
201 <h2>Additional Examples</h2>
202
203 To view the changes in the most recent check-in prior to the version currently
204 checked out:
205
206 DDED www/fiveminutes.wiki
--- a/www/fiveminutes.wiki
+++ b/www/fiveminutes.wiki
@@ -0,0 +1,40 @@
1
+<title>Up and running in 5 minutes as a single user</title>
2
+
3
+<p align="center"><b><i>
4
+The following document was contributed by Gilles Ganault on 2013-01-08.
5
+</i></b>
6
+</p><hr>
7
+
8
+<h1>Up and running in 5 min<p>tes as a single user</h1>
9
+
10
+This short document explains the main basic F ossil comands for a single
11
+user, i.e. with no additional users, with no need to s
12
+repository, and no </p>need for branching/forking.
13
+
14
+<h2>Cr<p>fossil new c:\test.repo</p>
15
+<p>sil new c:\test.repo</tt>
16
+
17
+This will create the new SQLite binary file that holds the repository, itc. It can be located anywhere, although it's considered
18
+best practice to keep it outside the wse to keep it outside the work directory where you will work on files here you will work on files
19
+after they've been che</p>
20
+
21
+<h2>Open the repository</h2>
22
+<p>cd c:\temp\test.fossil</p>
23
+<p>fossil open c:\test.repo</p>
24
+<p>il open c:\test.repo</tt>
25
+
26
+This will check out the last revision of all t
27
+if any, into the current work directory. In addition, it will create a binary
28
+file _UUIDIL_ to keep track of changes (on</p>called
29
+<tt>.fslckout</tt><p>fossilUUID#1 --to UUIDes to the repository. The files aren't actually
30
+added until you run &quot;commit&quot
31
+
32
+<h2>Close the repository</h2>
33
+<p>fossil c repository</h2>
34
+<p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files aren'es in the repository,
35
+if any, into the current work directory. In addition--to UUIDes to the repository. The files aren't actually
36
+added until you run &quot;commit&quot
37
+
38
+<h2>Close the repository</h2>
39
+<p>fossil c repository</h2>
40
+<p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files ar
--- a/www/fiveminutes.wiki
+++ b/www/fiveminutes.wiki
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/www/fiveminutes.wiki
+++ b/www/fiveminutes.wiki
@@ -0,0 +1,40 @@
1 <title>Up and running in 5 minutes as a single user</title>
2
3 <p align="center"><b><i>
4 The following document was contributed by Gilles Ganault on 2013-01-08.
5 </i></b>
6 </p><hr>
7
8 <h1>Up and running in 5 min<p>tes as a single user</h1>
9
10 This short document explains the main basic F ossil comands for a single
11 user, i.e. with no additional users, with no need to s
12 repository, and no </p>need for branching/forking.
13
14 <h2>Cr<p>fossil new c:\test.repo</p>
15 <p>sil new c:\test.repo</tt>
16
17 This will create the new SQLite binary file that holds the repository, itc. It can be located anywhere, although it's considered
18 best practice to keep it outside the wse to keep it outside the work directory where you will work on files here you will work on files
19 after they've been che</p>
20
21 <h2>Open the repository</h2>
22 <p>cd c:\temp\test.fossil</p>
23 <p>fossil open c:\test.repo</p>
24 <p>il open c:\test.repo</tt>
25
26 This will check out the last revision of all t
27 if any, into the current work directory. In addition, it will create a binary
28 file _UUIDIL_ to keep track of changes (on</p>called
29 <tt>.fslckout</tt><p>fossilUUID#1 --to UUIDes to the repository. The files aren't actually
30 added until you run &quot;commit&quot
31
32 <h2>Close the repository</h2>
33 <p>fossil c repository</h2>
34 <p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files aren'es in the repository,
35 if any, into the current work directory. In addition--to UUIDes to the repository. The files aren't actually
36 added until you run &quot;commit&quot
37
38 <h2>Close the repository</h2>
39 <p>fossil c repository</h2>
40 <p>fossil close_FOSSIL_ at the root of the work di>.fslckout</tt>< to the repository. The files ar
--- www/index.wiki
+++ www/index.wiki
@@ -149,10 +149,12 @@
149149
* How to [./server.wiki | set up a server] for your repository.
150150
* Customizing the [./custom_ticket.wiki | ticket system].
151151
* Methods to [./checkin_names.wiki | identify a specific check-in].
152152
* [./inout.wiki | Import and export] from and to Git.
153153
* [./fossil-v-git.wiki | Fossil versus Git].
154
+ * [./fiveminutes.wiki | Up and running in 5 minutes as a single user]
155
+ (contributed by Gilles Ganault on 2013-01-08).
154156
155157
<h3>Links For Fossil Developer:</h3>
156158
157159
* [./contribute.wiki | Contributing] code or documentation to the
158160
Fossil project.
159161
--- www/index.wiki
+++ www/index.wiki
@@ -149,10 +149,12 @@
149 * How to [./server.wiki | set up a server] for your repository.
150 * Customizing the [./custom_ticket.wiki | ticket system].
151 * Methods to [./checkin_names.wiki | identify a specific check-in].
152 * [./inout.wiki | Import and export] from and to Git.
153 * [./fossil-v-git.wiki | Fossil versus Git].
 
 
154
155 <h3>Links For Fossil Developer:</h3>
156
157 * [./contribute.wiki | Contributing] code or documentation to the
158 Fossil project.
159
--- www/index.wiki
+++ www/index.wiki
@@ -149,10 +149,12 @@
149 * How to [./server.wiki | set up a server] for your repository.
150 * Customizing the [./custom_ticket.wiki | ticket system].
151 * Methods to [./checkin_names.wiki | identify a specific check-in].
152 * [./inout.wiki | Import and export] from and to Git.
153 * [./fossil-v-git.wiki | Fossil versus Git].
154 * [./fiveminutes.wiki | Up and running in 5 minutes as a single user]
155 (contributed by Gilles Ganault on 2013-01-08).
156
157 <h3>Links For Fossil Developer:</h3>
158
159 * [./contribute.wiki | Contributing] code or documentation to the
160 Fossil project.
161
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -21,10 +21,11 @@
2121
delta_format.wiki {Fossil Delta Format}
2222
embeddeddoc.wiki {Embedded Project Documentation}
2323
event.wiki {Events}
2424
faq.wiki {Frequently Asked Questions}
2525
fileformat.wiki {Fossil File Format}
26
+ fiveminutes.wiki {Update and Running in 5 Minutes as a Single User}
2627
foss-cklist.wiki {Checklist For Successful Open-Source Projects}
2728
fossil-v-git.wiki {Fossil Versus Git}
2829
index.wiki {Home Page}
2930
inout.wiki {Import And Export To And From Git}
3031
makefile.wiki {The Fossil Build Process}
3132
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -21,10 +21,11 @@
21 delta_format.wiki {Fossil Delta Format}
22 embeddeddoc.wiki {Embedded Project Documentation}
23 event.wiki {Events}
24 faq.wiki {Frequently Asked Questions}
25 fileformat.wiki {Fossil File Format}
 
26 foss-cklist.wiki {Checklist For Successful Open-Source Projects}
27 fossil-v-git.wiki {Fossil Versus Git}
28 index.wiki {Home Page}
29 inout.wiki {Import And Export To And From Git}
30 makefile.wiki {The Fossil Build Process}
31
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -21,10 +21,11 @@
21 delta_format.wiki {Fossil Delta Format}
22 embeddeddoc.wiki {Embedded Project Documentation}
23 event.wiki {Events}
24 faq.wiki {Frequently Asked Questions}
25 fileformat.wiki {Fossil File Format}
26 fiveminutes.wiki {Update and Running in 5 Minutes as a Single User}
27 foss-cklist.wiki {Checklist For Successful Open-Source Projects}
28 fossil-v-git.wiki {Fossil Versus Git}
29 index.wiki {Home Page}
30 inout.wiki {Import And Export To And From Git}
31 makefile.wiki {The Fossil Build Process}
32
--- www/permutedindex.wiki
+++ www/permutedindex.wiki
@@ -10,13 +10,15 @@
1010
<li> [/help | Command-line help]
1111
</ul>
1212
<a name="pindex"></a>
1313
<h2>Permuted Index:</h2>
1414
<ul>
15
+<li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Update and Running in</a></li>
1516
<li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li>
1617
<li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
1718
<li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
19
+<li><a href="fiveminutes.wiki">as a Single User &mdash; Update and Running in 5 Minutes</a></li>
1820
<li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
1921
<li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
2022
<li><a href="private.wiki">Branches &mdash; Creating, Syncing, and Deleting Private</a></li>
2123
<li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li>
2224
<li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li>
@@ -90,10 +92,11 @@
9092
<li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
9193
<li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
9294
<li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
9395
<li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>
9496
<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
97
+<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Update and Running in 5</a></li>
9598
<li><a href="checkin_names.wiki">Names &mdash; Checkin And Version</a></li>
9699
<li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
97100
<li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
98101
<li><a href="pop.wiki">Operations &mdash; Principles Of</a></li>
99102
<li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
@@ -115,16 +118,18 @@
115118
<li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li>
116119
<li><a href="selfhost.wiki">Repositories &mdash; Fossil Self Hosting</a></li>
117120
<li><a href="newrepo.wiki">Repository &mdash; How To Create A New Fossil</a></li>
118121
<li><a href="selfcheck.wiki">Repository Integrity Self Checks &mdash; Fossil</a></li>
119122
<li><a href="reviews.wiki">Reviews</a></li>
123
+<li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User &mdash; Update and</a></li>
120124
<li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are</a></li>
121125
<li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
122126
<li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
123127
<li><a href="server.wiki">Server &mdash; How To Configure A Fossil</a></li>
124128
<li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
125129
<li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li>
130
+<li><a href="fiveminutes.wiki">Single User &mdash; Update and Running in 5 Minutes as a</a></li>
126131
<li><a href="style.wiki">Source Code Style Guidelines</a></li>
127132
<li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li>
128133
<li><a href="ssl.wiki">SSL with Fossil &mdash; Using</a></li>
129134
<li><a href="quickstart.wiki">Start Guide &mdash; Fossil Quick</a></li>
130135
<li><a href="stats.wiki">Statistics &mdash; Performance</a></li>
@@ -140,13 +145,15 @@
140145
<li><a href="sync.wiki">The Fossil Sync Protocol</a></li>
141146
<li><a href="webui.wiki">The Fossil Web Interface</a></li>
142147
<li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li>
143148
<li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
144149
<li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
150
+<li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li>
151
+<li><a href="fiveminutes.wiki">User &mdash; Update and Running in 5 Minutes as a Single</a></li>
145152
<li><a href="ssl.wiki">Using SSL with Fossil</a></li>
146153
<li><a href="checkin_names.wiki">Version Names &mdash; Checkin And</a></li>
147154
<li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
148155
<li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
149156
<li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
150157
<li><a href="wikitheory.wiki">Wiki In Fossil</a></li>
151158
<li><a href="ssl.wiki">with Fossil &mdash; Using SSL</a></li>
152159
</ul>
153160
--- www/permutedindex.wiki
+++ www/permutedindex.wiki
@@ -10,13 +10,15 @@
10 <li> [/help | Command-line help]
11 </ul>
12 <a name="pindex"></a>
13 <h2>Permuted Index:</h2>
14 <ul>
 
15 <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li>
16 <li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
17 <li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
 
18 <li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
19 <li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
20 <li><a href="private.wiki">Branches &mdash; Creating, Syncing, and Deleting Private</a></li>
21 <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li>
22 <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li>
@@ -90,10 +92,11 @@
90 <li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
91 <li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
92 <li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
93 <li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>
94 <li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
 
95 <li><a href="checkin_names.wiki">Names &mdash; Checkin And Version</a></li>
96 <li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
97 <li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
98 <li><a href="pop.wiki">Operations &mdash; Principles Of</a></li>
99 <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
@@ -115,16 +118,18 @@
115 <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li>
116 <li><a href="selfhost.wiki">Repositories &mdash; Fossil Self Hosting</a></li>
117 <li><a href="newrepo.wiki">Repository &mdash; How To Create A New Fossil</a></li>
118 <li><a href="selfcheck.wiki">Repository Integrity Self Checks &mdash; Fossil</a></li>
119 <li><a href="reviews.wiki">Reviews</a></li>
 
120 <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are</a></li>
121 <li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
122 <li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
123 <li><a href="server.wiki">Server &mdash; How To Configure A Fossil</a></li>
124 <li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
125 <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li>
 
126 <li><a href="style.wiki">Source Code Style Guidelines</a></li>
127 <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li>
128 <li><a href="ssl.wiki">SSL with Fossil &mdash; Using</a></li>
129 <li><a href="quickstart.wiki">Start Guide &mdash; Fossil Quick</a></li>
130 <li><a href="stats.wiki">Statistics &mdash; Performance</a></li>
@@ -140,13 +145,15 @@
140 <li><a href="sync.wiki">The Fossil Sync Protocol</a></li>
141 <li><a href="webui.wiki">The Fossil Web Interface</a></li>
142 <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li>
143 <li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
144 <li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
 
 
145 <li><a href="ssl.wiki">Using SSL with Fossil</a></li>
146 <li><a href="checkin_names.wiki">Version Names &mdash; Checkin And</a></li>
147 <li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
148 <li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
149 <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
150 <li><a href="wikitheory.wiki">Wiki In Fossil</a></li>
151 <li><a href="ssl.wiki">with Fossil &mdash; Using SSL</a></li>
152 </ul>
153
--- www/permutedindex.wiki
+++ www/permutedindex.wiki
@@ -10,13 +10,15 @@
10 <li> [/help | Command-line help]
11 </ul>
12 <a name="pindex"></a>
13 <h2>Permuted Index:</h2>
14 <ul>
15 <li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Update and Running in</a></li>
16 <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li>
17 <li><a href="copyright-release.html">Agreement &mdash; Contributor License</a></li>
18 <li><a href="delta_encoder_algorithm.wiki">Algorithm &mdash; Fossil Delta Encoding</a></li>
19 <li><a href="fiveminutes.wiki">as a Single User &mdash; Update and Running in 5 Minutes</a></li>
20 <li><a href="faq.wiki">Asked Questions &mdash; Frequently</a></li>
21 <li><a href="password.wiki">Authentication &mdash; Password Management And</a></li>
22 <li><a href="private.wiki">Branches &mdash; Creating, Syncing, and Deleting Private</a></li>
23 <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li>
24 <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li>
@@ -90,10 +92,11 @@
92 <li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
93 <li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
94 <li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
95 <li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>
96 <li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
97 <li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Update and Running in 5</a></li>
98 <li><a href="checkin_names.wiki">Names &mdash; Checkin And Version</a></li>
99 <li><a href="newrepo.wiki">New Fossil Repository &mdash; How To Create A</a></li>
100 <li><a href="foss-cklist.wiki">Open-Source Projects &mdash; Checklist For Successful</a></li>
101 <li><a href="pop.wiki">Operations &mdash; Principles Of</a></li>
102 <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil &mdash; A Technical</a></li>
@@ -115,16 +118,18 @@
118 <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li>
119 <li><a href="selfhost.wiki">Repositories &mdash; Fossil Self Hosting</a></li>
120 <li><a href="newrepo.wiki">Repository &mdash; How To Create A New Fossil</a></li>
121 <li><a href="selfcheck.wiki">Repository Integrity Self Checks &mdash; Fossil</a></li>
122 <li><a href="reviews.wiki">Reviews</a></li>
123 <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User &mdash; Update and</a></li>
124 <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What People Are</a></li>
125 <li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
126 <li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
127 <li><a href="server.wiki">Server &mdash; How To Configure A Fossil</a></li>
128 <li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
129 <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li>
130 <li><a href="fiveminutes.wiki">Single User &mdash; Update and Running in 5 Minutes as a</a></li>
131 <li><a href="style.wiki">Source Code Style Guidelines</a></li>
132 <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li>
133 <li><a href="ssl.wiki">SSL with Fossil &mdash; Using</a></li>
134 <li><a href="quickstart.wiki">Start Guide &mdash; Fossil Quick</a></li>
135 <li><a href="stats.wiki">Statistics &mdash; Performance</a></li>
@@ -140,13 +145,15 @@
145 <li><a href="sync.wiki">The Fossil Sync Protocol</a></li>
146 <li><a href="webui.wiki">The Fossil Web Interface</a></li>
147 <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li>
148 <li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
149 <li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
150 <li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li>
151 <li><a href="fiveminutes.wiki">User &mdash; Update and Running in 5 Minutes as a Single</a></li>
152 <li><a href="ssl.wiki">Using SSL with Fossil</a></li>
153 <li><a href="checkin_names.wiki">Version Names &mdash; Checkin And</a></li>
154 <li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
155 <li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
156 <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
157 <li><a href="wikitheory.wiki">Wiki In Fossil</a></li>
158 <li><a href="ssl.wiki">with Fossil &mdash; Using SSL</a></li>
159 </ul>
160

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button